WWW2Exec - atexit(), TLS Storage & Other mangled Pointers
Reading time: 8 minutes
tip
Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
__atexit Structures
caution
Danas je veoma čudno iskoristiti ovo!
atexit()
je funkcija kojoj se prolaze druge funkcije kao parametri. Ove funkcije će biti izvršene prilikom izvršavanja exit()
ili povratka iz main.
Ako možete modifikovati adresu bilo koje od ovih funkcija da pokazuje na shellcode, na primer, dobićete kontrolu nad procesom, ali je to trenutno komplikovanije.
Trenutno su adrese funkcija koje treba izvršiti sakrivene iza nekoliko struktura i konačno adresa na koju pokazuje nije adresa funkcija, već je kriptovana XOR i pomeranjima sa nasumičnim ključem. Tako da je trenutno ovaj napadni vektor ne baš koristan barem na x86 i x64_86.
Funkcija za enkripciju je PTR_MANGLE
. Druge arhitekture kao što su m68k, mips32, mips64, aarch64, arm, hppa... ne implementiraju funkciju enkripcije jer vraća isto što je primila kao ulaz. Tako da bi ove arhitekture bile napadljive ovim vektorom.
Možete pronaći detaljno objašnjenje o tome kako ovo funkcioniše na https://m101.github.io/binholic/2017/05/20/notes-on-abusing-exit-handlers.html
link_map
Kao što je objašnjeno u ovom postu, Ako program završi koristeći return
ili exit()
pokrenuće __run_exit_handlers()
koji će pozvati registrovane destruktore.
caution
Ako program završi putem _exit()
funkcije, pozvaće exit
syscall i izlazni handleri neće biti izvršeni. Dakle, da biste potvrdili da je __run_exit_handlers()
izvršen, možete postaviti breakpoint na njega.
Važan kod je (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
}
Napomena kako map -> l_addr + fini_array -> d_un.d_ptr
se koristi za izračunavanje pozicije niza funkcija koje treba pozvati.
Postoji nekoliko opcija:
- Prepisati vrednost
map->l_addr
da pokazuje na lažnifini_array
sa instrukcijama za izvršavanje proizvoljnog koda - Prepisati
l_info[DT_FINI_ARRAY]
il_info[DT_FINI_ARRAYSZ]
unose (koji su više-manje uzastopni u memoriji), da ih usmere na falsifikovanuElf64_Dyn
strukturu koja će ponovoarray
usmeriti na memorijsku zonu koju kontroliše napadač. - Ova analiza prepisuje
l_info[DT_FINI_ARRAY]
sa adresom kontrolisane memorije u.bss
koja sadrži lažnifini_array
. Ovaj lažni niz sadrži prvo jedan one gadget adresu koja će biti izvršena, a zatim razliku između adrese ovog lažnog niza i vrednostimap->l_addr
tako da*array
pokazuje na lažni niz. - Prema glavnom postu ove tehnike i ovoj analizi ld.so ostavlja pokazivač na steku koji pokazuje na binarni
link_map
u ld.so. Sa proizvoljnim pisanjem moguće je prepisati ga i usmeriti na lažnifini_array
koji kontroliše napadač sa adresom do one gadget na primer.
Iza prethodnog koda možete pronaći još jedan zanimljiv odeljak sa kodom:
/* 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));
}
U ovom slučaju bi bilo moguće prepisati vrednost map->l_info[DT_FINI]
koja pokazuje na lažnu ElfW(Dyn)
strukturu. Pronađite više informacija ovde.
TLS-Storage dtor_list prepisivanje u __run_exit_handlers
Kao što je objašnjeno ovde, ako program završi putem return
ili exit()
, izvršiće __run_exit_handlers()
koji će pozvati sve funkcije destruktora koje su registrovane.
Kod iz _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 ();
Kod iz __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);
[...]
}
}
Za svaku registrovanu funkciju u tls_dtor_list
, demangliraće pokazivač iz cur->func
i pozvati ga sa argumentom cur->obj
.
Koristeći tls
funkciju iz ovog fork-a GEF, moguće je videti da je zapravo dtor_list
veoma blizu stack canary i PTR_MANGLE cookie. Dakle, sa prelivanjem na njemu bilo bi moguće prepisati cookie i stack canary.
Prepisivanjem PTR_MANGLE cookie-a, bilo bi moguće obići PTR_DEMANLE
funkciju postavljanjem na 0x00, što će značiti da je xor
korišćen za dobijanje stvarne adrese samo adresa koja je konfigurisana. Zatim, pisanjem na dtor_list
moguće je povezati nekoliko funkcija sa adresom funkcije i njenim argumentom.
Na kraju, primetite da se sačuvani pokazivač ne samo da će biti xored sa cookie-jem, već će biti i rotiran 17 bita:
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
Tako da treba da uzmete ovo u obzir pre nego što dodate novu adresu.
Pronađite primer u originalnom postu.
Ostali izmenjeni pokazivači u __run_exit_handlers
Ova tehnika je objašnjena ovde i ponovo zavisi od toga da program izlazi pozivajući return
ili exit()
tako da se __run_exit_handlers()
poziva.
Hajde da proverimo više koda ove funkcije:
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);
Promenljiva f
pokazuje na initial
strukturu i u zavisnosti od vrednosti f->flavor
biće pozvane različite funkcije.
U zavisnosti od vrednosti, adresa funkcije koja će biti pozvana biće na različitom mestu, ali će uvek biti demangled.
Pored toga, u opcijama ef_on
i ef_cxa
takođe je moguće kontrolisati argument.
Moguće je proveriti initial
strukturu u sesiji debagovanja sa pokrenutim GEF gef> p initial
.
Da bi se ovo iskoristilo, potrebno je ili leakovati ili obrisati PTR_MANGLE
cookie i zatim prepisati cxa
unos u initial sa system('/bin/sh')
.
Možete pronaći primer ovoga u originalnom blog postu o tehnici.
tip
Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.