Unsorted Bin Attack
Last updated
Last updated
Dowiedz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE) Dowiedz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aby uzyskać więcej informacji na temat tego, co oznacza nieposortowany blok, sprawdź tę stronę:
Bins & Memory AllocationsNieposortowane listy mogą zapisać adres w unsorted_chunks (av)
w adresie bk
fragmentu. Dlatego jeśli atakujący może zmodyfikować adres wskaźnika bk
w fragmencie wewnątrz nieposortowanego bloku, może zapisać ten adres w dowolnym adresie, co może być pomocne do wycieku adresów Glibc lub obejścia niektórych zabezpieczeń.
W zasadzie ten atak pozwala ustawić dużą liczbę pod dowolnym adresem. Ta duża liczba to adres, który może być adresem sterty lub adresem Glibc. Typowym celem jest global_max_fast
, aby umożliwić tworzenie szybkich bloków o większych rozmiarach (i przejście od ataku na nieposortowany blok do ataku na szybki blok).
Przyjrzenie się przykładowi podanemu w https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/unsorted_bin_attack/#principle i użycie 0x4000 i 0x5000 zamiast 0x400 i 0x500 jako rozmiarów fragmentów (aby uniknąć Tcache) pozwala zobaczyć, że obecnie błąd malloc(): unsorted double linked list corrupted
jest wywoływany.
Dlatego ten atak na nieposortowany blok teraz (oprócz innych sprawdzeń) wymaga również naprawienia podwójnej listy połączonej, aby to ominąć victim->bk->fd == victim
lub nie victim->fd == av (arena)
, co oznacza, że adres, gdzie chcemy zapisać, musi mieć adres fałszywego fragmentu w swojej pozycji fd
i że fałszywy fragment fd
wskazuje na arenę.
Zauważ, że ten atak psuje nieposortowany blok (a także mały i duży). Dlatego teraz możemy korzystać tylko z alokacji z szybkiego bloku (bardziej złożony program może wykonywać inne alokacje i się zawieszać), a aby to wywołać, musimy alokować ten sam rozmiar, w przeciwnym razie program się zawiesi.
Zauważ, że nadpisanie global_max_fast
może pomóc w tym przypadku, zakładając, że szybki blok będzie w stanie obsłużyć wszystkie inne alokacje, dopóki exploit nie zostanie zakończony.
Kod od guyinatuxedo dobrze to wyjaśnia, chociaż jeśli zmodyfikujesz alokacje pamięci, aby alokować pamięć wystarczająco dużą, aby nie trafić do Tcache, zobaczysz, że wcześniej wspomniany błąd uniemożliwia zastosowanie tej techniki: malloc(): unsorted double linked list corrupted
To tak naprawdę bardzo podstawowe pojęcie. Fragmenty w nieposortowanym bloku będą miały wskaźniki. Pierwszy fragment w nieposortowanym bloku będzie faktycznie mieć linki fd
i bk
wskazujące na część głównej areny (Glibc).
Dlatego jeśli możesz umieścić fragment wewnątrz nieposortowanego bloku i odczytać go (użyj po zwolnieniu) lub ponownie go zaalokować bez nadpisywania co najmniej 1 z wskaźników, aby następnie go odczytać, możesz uzyskać wyciek informacji z Glibc.
Podobny atak użyty w tym opisie, polegał na wykorzystaniu struktury 4 fragmentów (A, B, C i D - D służył tylko do zapobiegania konsolidacji z górnym fragmentem), więc przepełnienie null bajtów w B zostało wykorzystane do spowodowania, że C wskazywało, że B jest nieużywane. Ponadto w B zmodyfikowano dane prev_size
, więc rozmiar zamiast być rozmiarem B, był A+B.
Następnie C został zwolniony i zkonsolidowany z A+B (ale B nadal był używany). Zaalokowano nowy fragment o rozmiarze A, a następnie wyciekły adresy Glibc, które zostały zapisane w B, skąd zostały wycieknięte.
Celem jest nadpisanie zmiennej globalnej wartością większą niż 4869, aby można było uzyskać flagę, a PIE nie jest włączone.
Możliwe jest generowanie fragmentów o dowolnych rozmiarach i występuje przepełnienie sterty o pożądanym rozmiarze.
Atak rozpoczyna się od utworzenia 3 fragmentów: fragmentu0 do wykorzystania przepełnienia, fragmentu1 do przepełnienia i fragmentu2, aby górny fragment nie konsolidował poprzednich.
Następnie fragment1 jest zwalniany, a fragment0 jest przepełniany, aby wskaźnik bk
fragmentu1 wskazywał na: bk = magic - 0x10
Następnie alokowany jest fragment3 o tym samym rozmiarze co fragment1, co spowoduje atak na nieposortowany blok i zmodyfikuje wartość zmiennej globalnej, umożliwiając uzyskanie flagi.
Funkcja łączenia jest podatna, ponieważ jeśli oba przekazane indeksy są takie same, to zrealokuje na nim i zwolni go, ale zwróci wskaźnik do tego zwolnionego obszaru, który można wykorzystać.
Dlatego tworzone są 2 fragmenty: fragment0, który zostanie połączony ze sobą i fragment1, aby zapobiec konsolidacji z górnym fragmentem. Następnie funkcja merge jest wywoływana z fragmentem0 dwukrotnie, co spowoduje użycie po zwolnieniu.
Następnie wywoływana jest funkcja view
z indeksem 2 (który jest indeksem używanego po zwolnieniu fragmentu), co spowoduje wyciek adresu libc.
Ponieważ binarny ma zabezpieczenia, aby alokować tylko rozmiary większe niż global_max_fast
, więc nie używany jest szybki blok, atak na nieposortowany blok zostanie wykorzystany do nadpisania zmiennej globalnej global_max_fast
.
Następnie możliwe jest wywołanie funkcji edycji z indeksem 2 (wskaźnik używany po zwolnieniu) i nadpisanie wskaźnika bk
, aby wskazywał na p64(global_max_fast-0x10)
. Następnie tworzenie nowego fragmentu użyje wcześniej skompromitowanego adresu zwolnienia (0x20) i wywoła atak na nieposortowany blok, nadpisując global_max_fast
dużą wartością, co teraz pozwala na tworzenie fragmentów w szybkich blokach.
Teraz wykonywany jest atak na szybki blok:
Po pierwsze odkryto, że możliwe jest pracowanie z szybkimi fragmentami o rozmiarze 200 w lokalizacji __free_hook
:
$1 = (void (**)(void *, const void *)) 0x7ff1e9e607a8 <__free_hook> gef➤ x/60gx 0x7ff1e9e607a8 - 0x59
0x7ff1e9e6074f: 0x0000000000000000 0x0000000000000200
0x7ff1e9e6075f: 0x0000000000000000 0x0000000000000000 0x7ff1e9e6076f <list_all_lock+15>: 0x0000000000000000 0x0000000000000000 0x7ff1e9e6077f <_IO_stdfile_2_lock+15>: 0x0000000000000000 0x0000000000000000
Jeśli uda nam się uzyskać szybki kawałek o rozmiarze 0x200 w tym miejscu, będzie możliwe nadpisanie wskaźnika funkcji, który zostanie wykonany
W tym celu tworzony jest nowy kawałek o rozmiarze 0xfc
i funkcja scalająca jest wywoływana z tym wskaźnikiem dwukrotnie, w ten sposób uzyskujemy wskaźnik do zwolnionego kawałka o rozmiarze 0xfc*2 = 0x1f8
w szybkim pojemniku.
Następnie funkcja edycji jest wywoływana w tym kawałku, aby zmodyfikować adres fd
tego szybkiego pojemnika wskazujący na poprzednią funkcję __free_hook
.
Następnie tworzony jest kawałek o rozmiarze 0x1f8
w celu pobrania z szybkiego pojemnika poprzedniego nieużytecznego kawałka, więc tworzony jest kolejny kawałek o rozmiarze 0x1f8
, aby uzyskać kawałek z szybkiego pojemnika w __free_hook
, który jest nadpisany adresem funkcji system
.
I wreszcie zwalniany jest kawałek zawierający ciąg znaków /bin/sh\x00
, wywołując funkcję usuwania, co powoduje wywołanie funkcji __free_hook
, która wskazuje na system z parametrem /bin/sh\x00
.
Kolejny przykład nadużycia przepełnienia o 1B w celu skonsolidowania kawałków w nieuporządkowanym pojemniku i uzyskania wycieku informacji o libc, a następnie przeprowadzenia ataku na szybki pojemnik w celu nadpisania haka malloc adresem jednego gadżetu
Możemy przydzielać tylko kawałki o rozmiarze większym niż 0x100
.
Nadpisanie global_max_fast
za pomocą ataku na nieuporządkowany pojemnik (działa 1/16 razy ze względu na ASLR, ponieważ musimy zmodyfikować 12 bitów, ale musimy zmodyfikować 16 bitów).
Atak na szybki pojemnik w celu zmodyfikowania globalnej tablicy kawałków. Zapewnia to arbitralne odczyt/zapis, co pozwala na modyfikację GOT i ustawienie pewnej funkcji na wskazanie system
.