SROP

Posted by l0tus on 2023-08-14
Estimated Reading Time 3 Minutes
Words 861 In Total
Viewed Times

SROP

基本介绍:https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/advanced-rop/srop/
wiki写的很详细了。
在传统的ROP上基于Sigreturn系统调用,伪造回调栈的frame,就是SROP
32位sigframe context:

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
struct sigcontext
{
unsigned short gs, __gsh;
unsigned short fs, __fsh;
unsigned short es, __esh;
unsigned short ds, __dsh;
unsigned long edi;
unsigned long esi;
unsigned long ebp;
unsigned long esp;
unsigned long ebx;
unsigned long edx;
unsigned long ecx;
unsigned long eax;
unsigned long trapno;
unsigned long err;
unsigned long eip;
unsigned short cs, __csh;
unsigned long eflags;
unsigned long esp_at_signal;
unsigned short ss, __ssh;
struct _fpstate * fpstate;
unsigned long oldmask;
unsigned long cr2;
};

64位:

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
struct _fpstate
{
/* FPU environment matching the 64-bit FXSAVE layout. */
__uint16_t cwd;
__uint16_t swd;
__uint16_t ftw;
__uint16_t fop;
__uint64_t rip;
__uint64_t rdp;
__uint32_t mxcsr;
__uint32_t mxcr_mask;
struct _fpxreg _st[8];
struct _xmmreg _xmm[16];
__uint32_t padding[24];
};

struct sigcontext
{
__uint64_t r8;
__uint64_t r9;
__uint64_t r10;
__uint64_t r11;
__uint64_t r12;
__uint64_t r13;
__uint64_t r14;
__uint64_t r15;
__uint64_t rdi;
__uint64_t rsi;
__uint64_t rbp;
__uint64_t rbx;
__uint64_t rdx;
__uint64_t rax;
__uint64_t rcx;
__uint64_t rsp;
__uint64_t rip;
__uint64_t eflags;
unsigned short cs;
unsigned short gs;
unsigned short fs;
unsigned short __pad0;
__uint64_t err;
__uint64_t trapno;
__uint64_t oldmask;
__uint64_t cr2;
__extension__ union
{
struct _fpstate * fpstate;
__uint64_t __fpstate_word;
};
__uint64_t __reserved1 [8];
};

以上两个栈帧context排布看起来比较复杂。但是我们完全不需要记,pwntools已经集成了 SigreturnFrame()这个函数,用来生成一个frame,然后我们可以指定其中寄存器的值。相当于一键伪造栈并快速控制其中寄存器的值。
pic1
伪造之后栈上的布局示例如上图,当然这些都是SigreturnFrame()会排布好的。
srop的题目肯定会给一条syscall;或者syscall;ret;的gadget,这是用来触发syscall_rt_sigreturn和read等等系统调用的关键
其次是对于rax的控制
我们知道ret2syscall是必须控制rax为我们想要的函数的系统调用号,在SROP的题目中一般有以下三种控制rax的方式:
1、题目直接给了mov rax, 0xf;(0xf是64位的sigreturn系统调用号,32位是0x77)
2、题目有其他间接控制rax的gadget比如mov rax,***;
3、没有直接与rax控制相关的gadget
前两种都能比较容易控制rax为我们想要的值。对于第三种,我们控制rax的手法就是利用read的返回值。
通常我们的攻击方法是先将栈地址转移到我们能明确知道地址的位置比如bss+0x500,或者先通过write syscall,rdx控制得大一点泄露出栈上的脏数据,进而泄露栈地址。
首先就是往我们一开始能控制的栈写东西,也就是题目给的栈溢出read,这一次写用的是SROP,也就是sigreturn syscall,这样可以在栈上的frame里控制rsp为目标写入地址,rip控制为syscall,rax控制为read(也就是0),其他rdi,rsi,rdx分别控制一下,就可以实现在将目标地址作为栈顶写入gadget。同时我们可以在程序开始之初指定"/bin/sh\x00"的地址,比如说控制成new_stack+0x200这样,那么在新的栈上控制frame进行SROP,执行execve_syscall的时候就可以指定rdi为new_stack+0x200,这样就顺利将binsh的地址传入了,但比较妙的是这时候"/bin/sh\x00"这几个字符还没出现在new_stack+0x200这个地址上,我们在newstack写入SROP的时候,最终将payload长度控制成0x200,再加上"/bin/sh\x00"就可以做到将binsh写到预设的binsh应该出现的地址。然后就getshell了。
cnss summer的那道SROP是比较模板的好题,等他们summer camp结束了我再把wp放这。


如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !