由于Pwn的堆方向感觉异常抽象,所以,我想着通过how2heap这个项目来入门heap。但是,由于有的程序不知为何调试总是出问题,不过,我会慢慢来解决的,所以这个文章也是处于慢慢更新的状态。
glibc版本:最新
源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <stdio.h> #include <stdlib.h> #include <string.h>
int main() { fprintf(stderr, "尽管这个例子没有演示攻击效果,但是它演示了 glibc 的分配机制\n"); fprintf(stderr, "glibc 使用首次适应算法选择空闲的堆块\n"); fprintf(stderr, "如果有一个空闲堆块且足够大,那么 malloc 将选择它\n"); fprintf(stderr, "如果存在 use-after-free 的情况那可以利用这一特性\n");
fprintf(stderr, "首先申请两个比较大的 chunk\n"); char* a = malloc(0x512); char* b = malloc(0x256); char* c;
fprintf(stderr, "第一个 a = malloc(0x512) 在: %p\n", a); fprintf(stderr, "第二个 a = malloc(0x256) 在: %p\n", b); fprintf(stderr, "我们可以继续分配\n"); fprintf(stderr, "现在我们把 \"AAAAAAAA\" 这个字符串写到 a 那里 \n"); strcpy(a, "AAAAAAAA"); fprintf(stderr, "第一次申请的 %p 指向 %s\n", a, a);
fprintf(stderr, "接下来 free 掉第一个...\n"); free(a);
fprintf(stderr, "接下来只要我们申请一块小于 0x512 的 chunk,那就会分配到原本 a 那里: %p\n", a);
c = malloc(0x500); fprintf(stderr, "第三次 c = malloc(0x500) 在: %p\n", c); fprintf(stderr, "我们这次往里写一串 \"CCCCCCCC\" 到刚申请的 c 中\n"); strcpy(c, "CCCCCCCC"); fprintf(stderr, "第三次申请的 c %p 指向 %s\n", c, c); fprintf(stderr, "第一次申请的 a %p 指向 %s\n", a, a); fprintf(stderr, "可以看到,虽然我们刚刚看的是 a 的,但它的内容却是 \"CCCCCCCC\"\n"); }
|
编译一下:gcc -g first_fit.c
运行一下看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| g01den@MSI:~/Temp$ ./heap 尽管这个例子没有演示攻击效果,但是它演示了 glibc 的分配机制 glibc 使用首次适应算法选择空闲的堆块 如果有一个空闲堆块且足够大,那么 malloc 将选择它 如果存在 use-after-free 的情况那可以利用这一特性 首先申请两个比较大的 chunk 第一个 a = malloc(0x512) 在: 0x5595c3a182a0 第二个 a = malloc(0x256) 在: 0x5595c3a187c0 我们可以继续分配 现在我们把 "AAAAAAAA" 这个字符串写到 a 那里 第一次申请的 0x5595c3a182a0 指向 AAAAAAAA 接下来 free 掉第一个... 接下来只要我们申请一块小于 0x512 的 chunk,那就会分配到原本 a 那里: 0x5595c3a182a0 第三次 c = malloc(0x500) 在: 0x5595c3a182a0 我们这次往里写一串 "CCCCCCCC" 到刚申请的 c 中 第三次申请的 c 0x5595c3a182a0 指向 CCCCCCCC 第一次申请的 a 0x5595c3a182a0 指向 CCCCCCCC 可以看到,虽然我们刚刚看的是 a 的,但它的内容却是 "CCCCCCCC"
|
开始调试:
首先,最开始,在第一次malloc分配内存之前,使用vmmap查看内存布局:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA Start End Perm Size Offset File 0x555555554000 0x555555555000 r--p 1000 0 /home/g01den/Temp/how2heap/first 0x555555555000 0x555555556000 r-xp 1000 1000 /home/g01den/Temp/how2heap/first 0x555555556000 0x555555557000 r--p 1000 2000 /home/g01den/Temp/how2heap/first 0x555555557000 0x555555558000 r--p 1000 2000 /home/g01den/Temp/how2heap/first 0x555555558000 0x555555559000 rw-p 1000 3000 /home/g01den/Temp/how2heap/first 0x7ffff7dcb000 0x7ffff7ded000 r--p 22000 0 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7ded000 0x7ffff7f65000 r-xp 178000 22000 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7f65000 0x7ffff7fb3000 r--p 4e000 19a000 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7fb3000 0x7ffff7fb7000 r--p 4000 1e7000 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7fb7000 0x7ffff7fb9000 rw-p 2000 1eb000 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7fb9000 0x7ffff7fbf000 rw-p 6000 0 [anon_7ffff7fb9] 0x7ffff7fc9000 0x7ffff7fcd000 r--p 4000 0 [vvar] 0x7ffff7fcd000 0x7ffff7fcf000 r-xp 2000 0 [vdso] 0x7ffff7fcf000 0x7ffff7fd0000 r--p 1000 0 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7fd0000 0x7ffff7ff3000 r-xp 23000 1000 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7ff3000 0x7ffff7ffb000 r--p 8000 24000 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7ffc000 0x7ffff7ffd000 r--p 1000 2c000 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7ffd000 0x7ffff7ffe000 rw-p 1000 2d000 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7ffe000 0x7ffff7fff000 rw-p 1000 0 [anon_7ffff7ffe] 0x7ffffffde000 0x7ffffffff000 rw-p 21000 0 [stack]
|
明显,这里不存在heap段,进行malloc之后,发现多出来了个heap段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA Start End Perm Size Offset File 0x555555554000 0x555555555000 r--p 1000 0 /home/g01den/Temp/how2heap/first 0x555555555000 0x555555556000 r-xp 1000 1000 /home/g01den/Temp/how2heap/first 0x555555556000 0x555555557000 r--p 1000 2000 /home/g01den/Temp/how2heap/first 0x555555557000 0x555555558000 r--p 1000 2000 /home/g01den/Temp/how2heap/first 0x555555558000 0x555555559000 rw-p 1000 3000 /home/g01den/Temp/how2heap/first 0x555555559000 0x55555557a000 rw-p 21000 0 [heap] 0x7ffff7dcb000 0x7ffff7ded000 r--p 22000 0 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7ded000 0x7ffff7f65000 r-xp 178000 22000 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7f65000 0x7ffff7fb3000 r--p 4e000 19a000 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7fb3000 0x7ffff7fb7000 r--p 4000 1e7000 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7fb7000 0x7ffff7fb9000 rw-p 2000 1eb000 /usr/lib/x86_64-linux-gnu/libc-2.31.so 0x7ffff7fb9000 0x7ffff7fbf000 rw-p 6000 0 [anon_7ffff7fb9] 0x7ffff7fc9000 0x7ffff7fcd000 r--p 4000 0 [vvar] 0x7ffff7fcd000 0x7ffff7fcf000 r-xp 2000 0 [vdso] 0x7ffff7fcf000 0x7ffff7fd0000 r--p 1000 0 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7fd0000 0x7ffff7ff3000 r-xp 23000 1000 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7ff3000 0x7ffff7ffb000 r--p 8000 24000 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7ffc000 0x7ffff7ffd000 r--p 1000 2c000 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7ffd000 0x7ffff7ffe000 rw-p 1000 2d000 /usr/lib/x86_64-linux-gnu/ld-2.31.so 0x7ffff7ffe000 0x7ffff7fff000 rw-p 1000 0 [anon_7ffff7ffe] 0x7ffffffde000 0x7ffffffff000 rw-p 21000 0 [stack]
|
先查看下chunk有些啥?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x555555559000 Size: 0x290 (with flag bits: 0x291)
Allocated chunk | PREV_INUSE Addr: 0x555555559290 Size: 0x520 (with flag bits: 0x521)
Allocated chunk | PREV_INUSE Addr: 0x5555555597b0 Size: 0x260 (with flag bits: 0x261)
Top chunk | PREV_INUSE Addr: 0x555555559a10 Size: 0x205f0 (with flag bits: 0x205f1)
|
根据下面两个输出:
1 2
| 第一个 a = malloc(0x512) 在: 0x5555555592a0 第二个 a = malloc(0x256) 在: 0x5555555597c0
|
可以看出来,上面那个heap结构中,第二个为a所在地址,第三个为b所在地址,为啥上下看到的地址不同,这个主要是和malloc函数返回指针的地址有关,这个这儿就不细说了。
在把AAAAAAAA这个字符串写入a中,我们看看a的内存布局:
1 2 3 4 5 6
| pwndbg> x/10gx 0x555555559290 0x555555559290: 0x0000000000000000 0x0000000000000521 0x5555555592a0: 0x4141414141414141 0x0000000000000000 0x5555555592b0: 0x0000000000000000 0x0000000000000000 0x5555555592c0: 0x0000000000000000 0x0000000000000000 0x5555555592d0: 0x0000000000000000 0x0000000000000000
|
可以看出来,已经成功写入了,先free掉a,之后malloc一个比a小一点的,把这个地址赋给c,可以看出,输出的时候发现a原本的地址和c的地址相等:
1 2
| 第三次 c = malloc(0x500) 在: 0x5555555592a0
|
之后将CCCCCCCC写入c的地址,之后通过a和c输出试试看:
1 2
| 第三次申请的 c 0x5555555592a0 指向 CCCCCCCC 第一次申请的 a 0x5555555592a0 指向 CCCCCCCC
|
当释放了一块内存之后再去申请一个大小略小的空间,那么 glibc 倾向于将先前释放的空间重新分配,由于a的指针没有被置零,这就造成了可以直接通过a来访问已经free掉的chunk和重新分配好的略小的chunk,由此,造成了UAF漏洞。