SROP - Sigreturn-Oriented Programming

Basic Information

Sigreturn is a special syscall that's primarily used to clean up after a signal handler has completed its execution. Signals are interruptions sent to a program by the operating system, often to indicate that some exceptional situation has occurred. When a program receives a signal, it temporarily pauses its current work to handle the signal with a signal handler, a special function designed to deal with signals.

After the signal handler finishes, the program needs to resume its previous state as if nothing happened. This is where sigreturn comes into play. It helps the program to return from the signal handler and restores the program's state by cleaning up the stack frame (the section of memory that stores function calls and local variables) that was used by the signal handler.

The interesting part is how sigreturn restores the program's state: it does so by storing all the CPU's register values on the stack. When the signal is no longer blocked, sigreturn pops these values off the stack, effectively resetting the CPU's registers to their state before the signal was handled. This includes the stack pointer register (RSP), which points to the current top of the stack.

Note how this would be a type of Ret2syscall that makes much easier to control params to call other Ret2syscalls:

Ret2syscall

If you are curious this is the sigcontext structure stored in the stack to later recover the values (diagram from here):

+--------------------+--------------------+
| rt_sigeturn()      | uc_flags           |
+--------------------+--------------------+
| &uc                | uc_stack.ss_sp     |
+--------------------+--------------------+
| uc_stack.ss_flags  | uc.stack.ss_size   |
+--------------------+--------------------+
| r8                 | r9                 |
+--------------------+--------------------+
| r10                | r11                |
+--------------------+--------------------+
| r12                | r13                |
+--------------------+--------------------+
| r14                | r15                |
+--------------------+--------------------+
| rdi                | rsi                |
+--------------------+--------------------+
| rbp                | rbx                |
+--------------------+--------------------+
| rdx                | rax                |
+--------------------+--------------------+
| rcx                | rsp                |
+--------------------+--------------------+
| rip                | eflags             |
+--------------------+--------------------+
| cs / gs / fs       | err                |
+--------------------+--------------------+
| trapno             | oldmask (unused)   |
+--------------------+--------------------+
| cr2 (segfault addr)| &fpstate           |
+--------------------+--------------------+
| __reserved         | sigmask            |
+--------------------+--------------------+

For a better explanation check also:

Example

You can find an example here where the call to signeturn is constructed via ROP (putting in rxa the value 0xf), although this is the final exploit from there:

Check also the exploit from here where the binary was already calling sigreturn and therefore it's not needed to build that with a ROP:

Other Examples & References

Last updated