WWW2Exec - atexit()
Last updated
Last updated
Lernen Sie AWS-Hacking:HackTricks Training AWS Red Team Expert (ARTE) Lernen Sie GCP-Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Heutzutage ist es sehr seltsam, dies auszunutzen!
atexit()
ist eine Funktion, der andere Funktionen als Parameter übergeben werden. Diese Funktionen werden ausgeführt, wenn ein exit()
ausgeführt wird oder das Hauptprogramm beendet wird.
Wenn Sie die Adresse einer dieser Funktionen so ändern können, dass sie beispielsweise auf einen Shellcode zeigt, erhalten Sie die Kontrolle über den Prozess, aber dies ist derzeit komplizierter.
Derzeit sind die Adressen der auszuführenden Funktionen hinter mehreren Strukturen versteckt und schließlich sind die Adressen, auf die sie zeigen, nicht die Adressen der Funktionen, sondern sind verschlüsselt mit XOR und Verschiebungen mit einem zufälligen Schlüssel. Daher ist dieser Angriffsvektor derzeit nicht sehr nützlich, zumindest auf x86 und x64_86.
Die Verschlüsselungsfunktion ist PTR_MANGLE
. Andere Architekturen wie m68k, mips32, mips64, aarch64, arm, hppa... implementieren die Verschlüsselungsfunktion nicht, weil sie das gleiche zurückgeben wie sie als Eingabe erhalten haben. Daher wären diese Architekturen durch diesen Vektor angreifbar.
Eine ausführliche Erklärung, wie dies funktioniert, finden Sie unter https://m101.github.io/binholic/2017/05/20/notes-on-abusing-exit-handlers.html
Wie in diesem Beitrag erklärt, Wenn das Programm mit return
oder exit()
beendet wird, wird __run_exit_handlers()
ausgeführt, das registrierte Destruktoren aufruft.
Wenn das Programm über die Funktion _exit()
beendet wird, wird der exit
-Syscall aufgerufen und die Exit-Handler werden nicht ausgeführt. Um zu bestätigen, dass __run_exit_handlers()
ausgeführt wird, können Sie einen Breakpoint darauf setzen.
Der wichtige Code ist (Quelle):
Beachten Sie, wie map -> l_addr + fini_array -> d_un.d_ptr
verwendet wird, um die Position des Arrays von aufzurufenden Funktionen zu berechnen.
Es gibt ein paar Optionen:
Überschreiben Sie den Wert von map->l_addr
, um ihn auf ein gefälschtes fini_array
mit Anweisungen zur Ausführung beliebigen Codes zu zeigen.
Überschreiben Sie die Einträge l_info[DT_FINI_ARRAY]
und l_info[DT_FINI_ARRAYSZ]
(die im Speicher mehr oder weniger aufeinander folgen), um sie auf eine gefälschte Elf64_Dyn
-Struktur zeigen zu lassen, die erneut dazu führt, dass array
auf einen vom Angreifer kontrollierten Speicherbereich zeigt.
Dieser Bericht überschreibt l_info[DT_FINI_ARRAY]
mit der Adresse eines vom Angreifer kontrollierten Speichers im .bss
, der ein gefälschtes fini_array
enthält. Dieses gefälschte Array enthält zuerst eine One-Gadget-Adresse, die ausgeführt wird, und dann die Differenz zwischen der Adresse dieses gefälschten Arrays und dem Wert von map->l_addr
, sodass *array
auf das gefälschte Array zeigt.
Laut Hauptbeitrag dieser Technik und diesem Bericht hinterlässt ld.so einen Zeiger auf dem Stapel, der auf den binären link_map
in ld.so zeigt. Mit einem beliebigen Schreibvorgang ist es möglich, ihn zu überschreiben und auf ein vom Angreifer kontrolliertes gefälschtes fini_array
zeigen zu lassen, das die Adresse zu einem One-Gadget beispielsweise enthält.
Nach dem vorherigen Code finden Sie einen weiteren interessanten Abschnitt mit dem Code:
In diesem Fall wäre es möglich, den Wert von map->l_info[DT_FINI]
zu überschreiben, der auf eine gefälschte ElfW(Dyn)
-Struktur zeigt. Finde hier weitere Informationen.
__run_exit_handlers
Wie hier erklärt, wenn ein Programm über return
oder exit()
beendet wird, wird es __run_exit_handlers()
ausführen, das alle registrierten Destruktoren aufrufen wird.
Code aus _run_exit_handlers()
:
Code von __call_tls_dtors()
:
Für jede registrierte Funktion in tls_dtor_list
wird der Zeiger aus cur->func
demangled und mit dem Argument cur->obj
aufgerufen.
Mit der tls
Funktion aus diesem Fork von GEF ist es möglich zu sehen, dass tatsächlich die dtor_list
sehr nah am Stack-Canary und am PTR_MANGLE-Cookie liegt. Daher wäre es bei einem Überlauf möglich, das Cookie und den Stack-Canary zu überschreiben.
Durch das Überschreiben des PTR_MANGLE-Cookies wäre es möglich, die PTR_DEMANLE
-Funktion zu umgehen, indem sie auf 0x00 gesetzt wird, was bedeutet, dass das xor
, das verwendet wird, um die tatsächliche Adresse zu erhalten, einfach die konfigurierte Adresse ist. Dann ist es durch Schreiben in die dtor_list
möglich, mehrere Funktionen mit der Funktions-Adresse und ihrem Argument zu verketten.
Schließlich ist zu beachten, dass der gespeicherte Zeiger nicht nur mit dem Cookie xor-verknüpft wird, sondern auch um 17 Bits rotiert wird:
So müssen Sie dies berücksichtigen, bevor Sie eine neue Adresse hinzufügen.
Finden Sie ein Beispiel im Originalbeitrag.
__run_exit_handlers
Diese Technik wird hier erklärt und hängt erneut davon ab, dass das Programm beendet wird, indem return
oder exit()
aufgerufen wird, sodass __run_exit_handlers()
aufgerufen wird.
Lassen Sie uns mehr Code dieser Funktion überprüfen:
Die Variable f
zeigt auf die initial
Struktur und je nach Wert von f->flavor
werden unterschiedliche Funktionen aufgerufen.
Abhängig vom Wert befindet sich die Adresse der aufzurufenden Funktion an einem anderen Ort, aber sie wird immer entmangelt sein.
Darüber hinaus ist es in den Optionen ef_on
und ef_cxa
auch möglich, ein Argument zu steuern.
Es ist möglich, die initial
Struktur in einer Debugging-Sitzung mit GEF auszuchecken, indem man gef> p initial
ausführt.
Um dies auszunutzen, müssen Sie entweder das PTR_MANGLE
-Cookie leaken oder löschen und dann einen cxa
-Eintrag in initial
mit system('/bin/sh')
überschreiben.
Ein Beispiel hierfür finden Sie im ursprünglichen Blog-Beitrag über die Technik.