原理
程序中断返回时会执行sigreturn系统调用,为该进程恢复之前保存的上下文,但是由于没有严格检查,并且上下文是保存在用户空间地址中的,所以我们可以轻易伪造上下文内容,可借此劫持程序流。
需要注意的是,在构建ROP链利用的时候我们要满足以下要求:
- 能调用执行sigreturn
- 有足够的空间塞下整个sigal frame
srop的手法不算复杂,利用方便而且要求不高,是个好方法
Signal Frame
被保存在用户地址空间(栈上)中,用户是可读写的。下面是源码中定义的相关结构体(x64)
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];
};
我们要伪造的就是类型名为sigcontext
的结构体。不难发现,通过对该结构体的伪造,我们几乎控制除了rax外的任何寄存器值
signal 机制
信号机制,类unix系统中不同进程间传递信号的一种方法,又称软中断。在该阶段,
- 内核向某个进程发送 signal 机制,该进程会被暂时挂起,进入内核态。
- 内核会为该进程保存相应的上下文,主要是将所有寄存器压入栈中,以及压入 signal 信息,以及指向 sigreturn 的系统调用地址。此时栈的结构如下图所示,我们称 ucontext 以及 siginfo 这一段为 Signal Frame。需要注意的是,这一部分是在用户进程的地址空间的。之后会跳转到注册过的 signal handler 中处理相应的 signal。因此,当 signal handler 执行完之后,就会执行 sigreturn 代码。
kernel agnostic about xitong signal handlers
由于内核与signal 处理程序无关,所以无人记录Signal 对应的Signal Frame 是否是同一个。这为我们伪造Signal Frame提供了条件,我们可以在伪造好Signal Frame后直接调用Sigreturn。
利用手法
利用工具
在目前的 pwntools 中已经集成了对于 srop 的攻击。