WWW2Exec - atexit()
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)
Obecnie jest bardzo dziwne, aby to wykorzystać!
atexit()
to funkcja, do której inne funkcje są przekazywane jako parametry. Te funkcje zostaną wykonane podczas wykonywania exit()
lub powrotu z main.
Jeśli możesz zmodyfikować adres którejkolwiek z tych funkcji, aby wskazywał na shellcode na przykład, zdobędziesz kontrolę nad procesem, ale obecnie jest to bardziej skomplikowane.
Obecnie adresy funkcji do wykonania są ukryte za kilkoma strukturami, a ostatecznie adres, do którego wskazują, nie są adresami funkcji, ale są zaszyfrowane za pomocą XOR i przesunięć z losowym kluczem. Obecnie ten wektor ataku nie jest zbyt przydatny przynajmniej na x86 i x64_86.
Funkcja szyfrowania to PTR_MANGLE
. Inne architektury takie jak m68k, mips32, mips64, aarch64, arm, hppa... nie implementują funkcji szyfrowania, ponieważ zwracają to samo, co otrzymały jako wejście. Dlatego te architektury byłyby podatne na ten wektor ataku.
Możesz znaleźć dogłębne wyjaśnienie, jak to działa na https://m101.github.io/binholic/2017/05/20/notes-on-abusing-exit-handlers.html
Jak wyjaśniono w tym poście, Jeśli program kończy działanie za pomocą return
lub exit()
, uruchomi __run_exit_handlers()
, który wywoła zarejestrowane destruktory.
Jeśli program kończy działanie za pomocą funkcji _exit()
, wywoła exit
syscall i obsługiwacze wyjścia nie zostaną wykonane. Dlatego, aby potwierdzić wykonanie __run_exit_handlers()
, można ustawić punkt przerwania na to.
Ważny kod to (źródło):
Zauważ, jak map -> l_addr + fini_array -> d_un.d_ptr
jest używane do obliczenia pozycji tablicy funkcji do wywołania.
Istnieje kilka opcji:
Nadpisz wartość map->l_addr
, aby wskazywała na fałszywy fini_array
z instrukcjami do wykonania arbitralnego kodu
Nadpisz wpisy l_info[DT_FINI_ARRAY]
i l_info[DT_FINI_ARRAYSZ]
(które są mniej więcej kolejne w pamięci), aby wskazywały na sfałszowaną strukturę Elf64_Dyn
, która ponownie sprawi, że array
wskazuje na obszar pamięci kontrolowany przez atakującego.
Ten opis nadpisuje l_info[DT_FINI_ARRAY]
adresem kontrolowanej pamięci w .bss
, zawierającym fałszywy fini_array
. Ten fałszywy array zawiera najpierw adres one gadget, który zostanie wykonany, a następnie różnicę między adresem tego fałszywego arraya a wartością map->l_addr
, aby *array
wskazywał na fałszywy array.
Zgodnie z głównym postem tej techniki i tym opisem ld.so pozostawia wskaźnik na stosie, który wskazuje na link_map
binarnego w ld.so. Dzięki arbitralnemu zapisowi możliwe jest nadpisanie go i spowodowanie, że wskazuje on na fałszywy fini_array
kontrolowany przez atakującego z adresem one gadget na przykład.
Po poprzednim kodzie znajdziesz kolejny interesujący fragment kodu:
W tym przypadku możliwe byłoby nadpisanie wartości map->l_info[DT_FINI]
, wskazującej na sfałszowaną strukturę ElfW(Dyn)
. Znajdź więcej informacji tutaj.
__run_exit_handlers
Jak wyjaśniono tutaj, jeśli program kończy działanie za pomocą return
lub exit()
, zostanie wykonane __run_exit_handlers()
, które wywoła zarejestrowane funkcje destrukcyjne.
Kod z _run_exit_handlers()
:
Kod z __call_tls_dtors()
:
Dla każdej zarejestrowanej funkcji w tls_dtor_list
, zdemontuje wskaźnik z cur->func
i wywoła go z argumentem cur->obj
.
Korzystając z funkcji tls
z tego forka GEF, można zobaczyć, że dtor_list
jest bardzo blisko stack canary i PTR_MANGLE cookie. Dlatego, przy przepełnieniu go, możliwe byłoby nadpisanie cookie i stack canary.
Poprzez nadpisanie PTR_MANGLE cookie, możliwe byłoby obejście funkcji PTR_DEMANLE
poprzez ustawienie go na 0x00, co oznaczałoby, że xor
użyty do uzyskania rzeczywistego adresu to po prostu skonfigurowany adres. Następnie, poprzez zapisanie w dtor_list
, możliwe jest łańczenie kilku funkcji z adresem funkcji i jej argumentem.
Na koniec zauważ, że przechowywany wskaźnik nie tylko będzie xorowany z cookie, ale także obracany o 17 bitów:
Więc musisz wziąć to pod uwagę przed dodaniem nowego adresu.
Znajdź przykład w oryginalnym poście.
__run_exit_handlers
Ta technika jest wyjaśniona tutaj i zależy ponownie od programu zakończającego działanie za pomocą return
lub exit()
, więc zostaje wywołane __run_exit_handlers()
.
Sprawdźmy więcej kodu tej funkcji:
Zmienna f
wskazuje na strukturę initial
i w zależności od wartości f->flavor
zostaną wywołane różne funkcje.
W zależności od wartości, adres funkcji do wywołania będzie w innym miejscu, ale zawsze będzie rozwiązany.
Ponadto, w opcjach ef_on
i ef_cxa
można również kontrolować argument.
Możliwe jest sprawdzenie struktury initial
w sesji debugowania z uruchomionym GEF za pomocą gef> p initial
.
Aby to wykorzystać, należy albo ujawnić lub usunąć ciasteczko PTR_MANGLE
a następnie nadpisać wpis cxa
w initial na system('/bin/sh')
.
Przykład tego można znaleźć w oryginalnym wpisie na blogu dotyczącym tej techniki.