SROP

前言
SROP(Sigreturn Oriented Programming)这种利用方法经常会用在bypass一些沙盒保护或者是一些高版本的glibc的利用方式如house of pig ,house of banana 等,是很多利用方式的一个前置技能,故特地来学习下。
原理
下图是当Linux/Unix操作系统中一个进程从接收到一个信号(signal)到恢复进程执行所经历的一个过程。
Step1: 用户层接收到一个signal
Step2: 内核层会保存当前进程的上下文即sigFrame(即寄存器的状态)到栈上,然后被内核挂起。此时sigFrame顶部的八/四个字节会被设置为rt_sigreturn,rt_sigreturn处的内容指向sigreturn系统调用代码。
Step3: 用户层的Signal Handle将会对接受到的signal做一个处理,处理结束后会将栈顶指针指向rt_sigreturn。
Step4: sigreturn系统调用最终会根据SigFrame中的数据将上下文恢复
Step5: 继续执行进程
那么我们的攻击思路就很显而易见了,如果我们在sigreturn恢复进程上下文之前就将SigFrame中的数据篡改为我们的恶意代码,就能控制寄存器来劫持程序执行流
Step4中sigreturn恢复上下文的操作其实是根据ucontext_t结构体来恢复,ucontext结构体内容如下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
53
54
55
56
57// defined in /usr/include/sys/ucontext.h
/* Userlevel context.  */
typedef struct ucontext_t
  {
    unsigned long int uc_flags; // 0-8
    struct ucontext_t *uc_link; // 8-16
    stack_t uc_stack;           // 16-40 the stack used by this context
    mcontext_t uc_mcontext;     // 40-296 the saved context
    sigset_t uc_sigmask;
    struct _libc_fpstate __fpregs_mem;
  } ucontext_t;
// defined in /usr/include/bits/types/stack_t.h
/* Structure describing a signal stack.  */
typedef struct
  {
    void *ss_sp;
    size_t ss_size;
    int ss_flags;
  } stack_t;
// difined in /usr/include/bits/sigcontext.h
struct sigcontext
{
  __uint64_t r8;     // 40-48
  __uint64_t r9;     // 48-56
  __uint64_t r10;    // 56-64
  __uint64_t r11;    // 64-72
  __uint64_t r12;    // 72-80
  __uint64_t r13;    // 80-88
  __uint64_t r14;    // 88-96
  __uint64_t r15;    // 96-104
  __uint64_t rdi;    // 104-112
  __uint64_t rsi;    // 112-120
  __uint64_t rbp;    // 120-128
  __uint64_t rbx;    // 128-136
  __uint64_t rdx;    // 136-144
  __uint64_t rax;    // 144-152
  __uint64_t rcx;    // 152-160
  __uint64_t rsp;    // 160-168
  __uint64_t rip;    // 168-176
  __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];
};
而SROP其实就是伪造ucontext_t结构体,而在pwntools中已经集成了相关的方法
具体构造方法见:http://docs.pwntools.com/en/stable/rop/srop.html?highlight=srop
Demo(摘自EX师傅的博客)
源码
| 1 | // compiled: | 
EXP
| 1 | from pwn import * | 
列题
[CISCN 2019华南]PWN3
题目分析
vuln函数中存在明显的栈溢出,且read和write函数都是通过syscall来调用的,同时write会打印出一个栈地址

同时题目还给到一个gadget
这题目的用意就很明显了,“mov rax 0x0f”,而sigreturn的系统调用号就正好是15号,题目中也给出了“syscall ret ”这意味着我们可以直接调用sigreturn,通过伪造SigFrame来控制寄存器,/bin/sh的地址我们可以通过之前打印出来的栈地址减去偏移来计算。
Exp
| 1 | from pwn import * | 
Srop进阶
在一些堆的利用中,一般会用setcontext+xx的位置来完成SROP来进行orw,在glibc2.27版本及一下一般是setcontext+53,2.3+版本一般为setcontext+61在后续的一些高版本glibc利用方式的中会详细介绍。
参考
https://www.bilibili.com/video/BV1Uv411j7fr?p=11
https://www.anquanke.com/post/id/85810
http://blog.eonew.cn/archives/975