15 KiB
WWW2Exec - atexit(), TLS Skladištenje & Ostali izmenjeni Pokazivači
{% hint style="success" %}
Naučite & vežbajte AWS Hakovanje:HackTricks Obuka AWS Crveni Tim Stručnjak (ARTE)
Naučite & vežbajte GCP Hakovanje: HackTricks Obuka GCP Crveni Tim Stručnjak (GRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili nas pratite na Twitteru 🐦 @hacktricks_live.
- Podelite hakovanje trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
__atexit Strukture
{% hint style="danger" %} Danas je vrlo čudno iskoristiti ovo! {% endhint %}
atexit()
je funkcija kojoj se druge funkcije prosleđuju kao parametri. Ove funkcije će biti izvršene prilikom izvršavanja exit()
ili povratka iz main funkcije.
Ako možete izmeniti adresu bilo koje od ovih funkcija da pokazuje na shell kod na primer, dobićete kontrolu nad procesom, ali trenutno je to komplikovanije.
Trenutno su adrese funkcija koje treba izvršiti sakrivene iza nekoliko struktura i na kraju adrese na koje pokazuju nisu adrese funkcija, već su šifrovane sa XOR i pomeraji sa slučajnim ključem. Tako da je trenutno ovaj vektor napada nije vrlo koristan bar na x86 i x64_86.
Funkcija za šifrovanje je PTR_MANGLE
. Druga arhitekture poput m68k, mips32, mips64, aarch64, arm, hppa... ne implementiraju funkciju za šifrovanje jer vraća isto što je primila kao ulaz. Tako da bi ove arhitekture bile podložne ovom vektoru napada.
Možete pronaći detaljno objašnjenje 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 izađe koristeći return
ili exit()
pokrenuće se __run_exit_handlers()
koji će pozvati registrovane destruktore.
{% hint style="danger" %}
Ako program izađe putem _exit()
funkcije, pozvaće exit
syscall i hendleri izlaska neće biti izvršeni. Dakle, da potvrdite da se __run_exit_handlers()
izvršava možete postaviti prekidnu tačku na nju.
{% endhint %}
Važan kod je (izvor):
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
}
Primetite kako se map -> l_addr + fini_array -> d_un.d_ptr
koristi za izračunavanje pozicije niza funkcija za pozivanje.
Postoje nekoliko opcija:
- Prepisati vrednost
map->l_addr
kako bi pokazivala na lažnifini_array
sa instrukcijama za izvršavanje proizvoljnog koda - Prepisati unose
l_info[DT_FINI_ARRAY]
il_info[DT_FINI_ARRAYSZ]
(koji su više-manje uzastopni u memoriji), kako bi pokazivali na lažnu strukturuElf64_Dyn
koja će ponovo nateratiarray
da pokazuje na memoriju kojom upravlja napadač. - Ovaj opis 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 adresu one gadgeta koja će biti izvršena, a zatim razliku između adrese ovog lažnog niza i vrednostimap->l_addr
tako da će*array
pokazivati na lažni niz. - Prema glavnoj objavi ove tehnike i ovom opisu ld.so ostavlja pokazivač na steku koji pokazuje na binarni
link_map
u ld.so. Pomoću proizvoljnog pisanja moguće je prepisati ga i naterati da pokazuje na lažnifini_array
koji kontroliše napadač sa adresom one gadgeta na primer.
Nakon prethodnog koda možete pronaći još jedan interesantan 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 bilo bi moguće prebrisati vrednost map->l_info[DT_FINI]
koja pokazuje na lažiranu strukturu ElfW(Dyn)
. Pronađite više informacija ovde.
Prepisivanje TLS-Skladišta dtor_list u __run_exit_handlers
Kao što je objašnjeno ovde, ako program izađe putem return
ili exit()
, izvršiće se __run_exit_handlers()
koji će pozvati sve registrovane funkcije destruktora.
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()
funkcije:
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
, demantoviraće pokazivač iz cur->func
i pozvaće je sa argumentom cur->obj
.
Korišćenjem tls
funkcije iz ovog forka GEF-a, moguće je videti da je dtor_list
veoma blizu stack canary-ja i PTR_MANGLE cookie-ja. Dakle, sa prekoračenjem na njemu bilo bi moguće prepisati cookie i stack canary.
Prepisivanjem PTR_MANGLE cookie-ja, bilo bi moguće zaobići funkciju PTR_DEMANLE
postavljanjem je na 0x00, što znači da je xor
korišćen za dobijanje stvarne adrese samo adresa koja je konfigurisana. Zatim, upisivanjem u dtor_list
moguće je povezati nekoliko funkcija sa funkcijom adrese i njenim argumentom.
Na kraju, primetite da uskladišteni pokazivač neće biti samo ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno ekskluzivno.
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
Ostali izmenjeni pokazivači u __run_exit_handlers
Ova tehnika je objašnjena ovde i ponovo zavisi od toga da program izađe pozivajući return
ili exit()
tako da se pozove __run_exit_handlers()
.
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 strukturu initial
i u zavisnosti od vrednosti f->flavor
biće pozvane različite funkcije.
Adresa funkcije koja će biti pozvana biće na različitom mestu u zavisnosti od vrednosti, ali će uvek biti demangleovana.
Osim toga, u opcijama ef_on
i ef_cxa
takođe je moguće kontrolisati argument.
Moguće je proveriti strukturu initial
u sesiji za debagovanje sa GEF pokretanjem gef> p initial
.
Da biste iskoristili ovo, potrebno je ili procureti ili izbrisati PTR_MANGLE
kolačić a zatim prepisati cxa
unos u initial sa system('/bin/sh')
.
Možete pronaći primer ovoga u originalnom blog postu o tehnici.