WWW2Exec - atexit(), TLS-Speicher & andere beschädigte Zeiger
Reading time: 9 minutes
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
__atexit-Strukturen
caution
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()
oder die Rückkehr von der main erfolgt.
Wenn Sie die Adresse einer dieser Funktionen so modifizieren können, dass sie beispielsweise auf einen Shellcode zeigt, werden Sie Kontrolle über den Prozess erlangen, aber das ist derzeit komplizierter.
Derzeit sind die Adressen der auszuführenden Funktionen hinter mehreren Strukturen versteckt und schließlich zeigen die Adressen, auf die sie verweisen, nicht auf die Adressen der Funktionen, sondern sind mit XOR und Verschiebungen mit einem zufälligen Schlüssel verschlüsselt. Daher ist dieser Angriffsvektor derzeit nicht sehr nützlich, zumindest nicht auf x86 und x64_86.
Die Verschlüsselungsfunktion ist PTR_MANGLE
. Andere Architekturen wie m68k, mips32, mips64, aarch64, arm, hppa... implementieren die Verschlüsselung nicht, da sie das gleiche zurückgeben, was sie als Eingabe erhalten haben. Diese Architekturen wären also durch diesen Vektor angreifbar.
Eine ausführliche Erklärung, wie das funktioniert, finden Sie unter https://m101.github.io/binholic/2017/05/20/notes-on-abusing-exit-handlers.html
link_map
Wie in diesem Beitrag erklärt, wenn das Programm mit return
oder exit()
beendet wird, wird __run_exit_handlers()
aufgerufen, das registrierte Destruktoren aufruft.
caution
Wenn das Programm über die _exit()
-Funktion 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 Haltepunkt darauf setzen.
Der wichtige Code ist (source):
ElfW(Dyn) *fini_array = map->l_info[DT_FINI_ARRAY];
if (fini_array != NULL)
{
ElfW(Addr) *array = (ElfW(Addr) *) (map->l_addr + fini_array->d_un.d_ptr);
size_t sz = (map->l_info[DT_FINI_ARRAYSZ]->d_un.d_val / sizeof (ElfW(Addr)));
while (sz-- > 0)
((fini_t) array[sz]) ();
}
[...]
// This is the d_un structure
ptype l->l_info[DT_FINI_ARRAY]->d_un
type = union {
Elf64_Xword d_val; // address of function that will be called, we put our onegadget here
Elf64_Addr d_ptr; // offset from l->l_addr of our structure
}
Beachten Sie, wie map -> l_addr + fini_array -> d_un.d_ptr
verwendet wird, um die **Position des Arrays von Funktionen, die aufgerufen werden sollen, zu berechnen.
Es gibt eine Handvoll Optionen:
- Überschreiben Sie den Wert von
map->l_addr
, um ihn auf ein gefälschtesfini_array
mit Anweisungen zum Ausführen von beliebigem Code zu verweisen. - Überschreiben Sie die Einträge
l_info[DT_FINI_ARRAY]
undl_info[DT_FINI_ARRAYSZ]
(die mehr oder weniger aufeinanderfolgend im Speicher sind), um sie auf eine gefälschteElf64_Dyn
-Struktur zeigen zu lassen, die erneutarray
auf eine Speicherzone zeigt, die der Angreifer kontrolliert. - Dieser Bericht überschreibt
l_info[DT_FINI_ARRAY]
mit der Adresse eines kontrollierten Speichers in.bss
, der ein gefälschtesfini_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 vonmap->l_addr
, sodass*array
auf das gefälschte Array zeigt. - Laut dem Hauptbeitrag dieser Technik und diesem Bericht hinterlässt ld.so einen Zeiger auf dem Stack, der auf die binäre
link_map
in ld.so zeigt. Mit einem beliebigen Schreibvorgang ist es möglich, ihn zu überschreiben und ihn auf ein gefälschtesfini_array
zu verweisen, das vom Angreifer mit der Adresse zu einem one gadget kontrolliert wird, zum Beispiel.
Im Anschluss an den vorherigen Code finden Sie einen weiteren interessanten Abschnitt mit dem Code:
/* Next try the old-style destructor. */
ElfW(Dyn) *fini = map->l_info[DT_FINI];
if (fini != NULL)
DL_CALL_DT_FINI (map, ((void *) map->l_addr + fini->d_un.d_ptr));
}
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. Finden Sie weitere Informationen hier.
TLS-Storage dtor_list Überschreibung in __run_exit_handlers
Wie hier erklärt wird, wenn ein Programm über return
oder exit()
beendet wird, __run_exit_handlers()
ausgeführt, das alle registrierten Destruktorsfunktionen aufruft.
Code von _run_exit_handlers()
:
/* Call all functions registered with `atexit' and `on_exit',
in the reverse of the order in which they were registered
perform stdio cleanup, and terminate program execution with STATUS. */
void
attribute_hidden
__run_exit_handlers (int status, struct exit_function_list **listp,
bool run_list_atexit, bool run_dtors)
{
/* First, call the TLS destructors. */
#ifndef SHARED
if (&__call_tls_dtors != NULL)
#endif
if (run_dtors)
__call_tls_dtors ();
Code von __call_tls_dtors()
:
typedef void (*dtor_func) (void *);
struct dtor_list //struct added
{
dtor_func func;
void *obj;
struct link_map *map;
struct dtor_list *next;
};
[...]
/* Call the destructors. This is called either when a thread returns from the
initial function or when the process exits via the exit function. */
void
__call_tls_dtors (void)
{
while (tls_dtor_list) // parse the dtor_list chained structures
{
struct dtor_list *cur = tls_dtor_list; // cur point to tls-storage dtor_list
dtor_func func = cur->func;
PTR_DEMANGLE (func); // demangle the function ptr
tls_dtor_list = tls_dtor_list->next; // next dtor_list structure
func (cur->obj);
[...]
}
}
Für jede registrierte Funktion in tls_dtor_list
wird der Zeiger von cur->func
demangelt und mit dem Argument cur->obj
aufgerufen.
Mit der tls
-Funktion aus diesem Fork von GEF ist es möglich zu sehen, dass die dtor_list
tatsächlich sehr nahe am Stack Canary und dem PTR_MANGLE-Cookie ist. Ein Überlauf darauf würde es ermöglichen, 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 man es auf 0x00 setzt, was bedeutet, dass das xor
, das verwendet wird, um die echte Adresse zu erhalten, nur die konfigurierte Adresse ist. Dann ist es durch das Schreiben auf die dtor_list
möglich, mehrere Funktionen mit der Funktionsadresse und ihrem Argument zu verknüpfen.
Beachten Sie schließlich, dass der gespeicherte Zeiger nicht nur mit dem Cookie xored, sondern auch um 17 Bits rotiert wird:
0x00007fc390444dd4 <+36>: mov rax,QWORD PTR [rbx] --> mangled ptr
0x00007fc390444dd7 <+39>: ror rax,0x11 --> rotate of 17 bits
0x00007fc390444ddb <+43>: xor rax,QWORD PTR fs:0x30 --> xor with PTR_MANGLE
Also müssen Sie dies berücksichtigen, bevor Sie eine neue Adresse hinzufügen.
Finden Sie ein Beispiel im originalen Beitrag.
Andere beschädigte Zeiger in __run_exit_handlers
Diese Technik ist 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:
while (true)
{
struct exit_function_list *cur;
restart:
cur = *listp;
if (cur == NULL)
{
/* Exit processing complete. We will not allow any more
atexit/on_exit registrations. */
__exit_funcs_done = true;
break;
}
while (cur->idx > 0)
{
struct exit_function *const f = &cur->fns[--cur->idx];
const uint64_t new_exitfn_called = __new_exitfn_called;
switch (f->flavor)
{
void (*atfct) (void);
void (*onfct) (int status, void *arg);
void (*cxafct) (void *arg, int status);
void *arg;
case ef_free:
case ef_us:
break;
case ef_on:
onfct = f->func.on.fn;
arg = f->func.on.arg;
PTR_DEMANGLE (onfct);
/* Unlock the list while we call a foreign function. */
__libc_lock_unlock (__exit_funcs_lock);
onfct (status, arg);
__libc_lock_lock (__exit_funcs_lock);
break;
case ef_at:
atfct = f->func.at;
PTR_DEMANGLE (atfct);
/* Unlock the list while we call a foreign function. */
__libc_lock_unlock (__exit_funcs_lock);
atfct ();
__libc_lock_lock (__exit_funcs_lock);
break;
case ef_cxa:
/* To avoid dlclose/exit race calling cxafct twice (BZ 22180),
we must mark this function as ef_free. */
f->flavor = ef_free;
cxafct = f->func.cxa.fn;
arg = f->func.cxa.arg;
PTR_DEMANGLE (cxafct);
/* Unlock the list while we call a foreign function. */
__libc_lock_unlock (__exit_funcs_lock);
cxafct (arg, status);
__libc_lock_lock (__exit_funcs_lock);
break;
}
if (__glibc_unlikely (new_exitfn_called != __new_exitfn_called))
/* The last exit function, or another thread, has registered
more exit functions. Start the loop over. */
goto restart;
}
*listp = cur->next;
if (*listp != NULL)
/* Don't free the last element in the chain, this is the statically
allocate element. */
free (cur);
}
__libc_lock_unlock (__exit_funcs_lock);
Die Variable f
verweist auf die initial
Struktur und je nach Wert von f->flavor
werden unterschiedliche Funktionen aufgerufen.
Je nach Wert wird die Adresse der aufzurufenden Funktion an einem anderen Ort sein, aber sie wird immer demangled 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 zu überprüfen, indem man gef> p initial
eingibt.
Um dies auszunutzen, müssen Sie entweder den PTR_MANGLE
-Cookie leaken oder löschen und dann einen cxa
-Eintrag in initial mit system('/bin/sh')
überschreiben.
Ein Beispiel dafür finden Sie im originalen Blogbeitrag über die Technik.
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.