14 KiB
WWW2Exec - .dtors & .fini_array
Naučite hakovanje AWS-a od nule do heroja sa htARTE (HackTricks AWS Red Team Expert)!
Drugi načini podrške HackTricks-u:
- Ako želite da vidite vašu kompaniju reklamiranu na HackTricks-u ili preuzmete HackTricks u PDF formatu proverite PLANOVE ZA PRIJEM!
- Nabavite zvanični PEASS & HackTricks swag
- Otkrijte The PEASS Family, našu kolekciju ekskluzivnih NFT-ova
- Pridružite se 💬 Discord grupi ili telegram grupi ili nas pratite na Twitteru 🐦 @hacktricks_live.
- Podelite svoje hakovanje trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
.dtors
{% hint style="danger" %} Danas je veoma čudno naći binarni fajl sa .dtors sekcijom! {% endhint %}
Destruktori su funkcije koje se izvršavaju pre završetka programa (nakon što se funkcija main
završi).
Adrese ovih funkcija se čuvaju unutar .dtors
sekcije binarnog fajla i stoga, ako uspete da upišete adresu shell koda u __DTOR_END__
, taj će se izvršiti pre nego što program završi.
Dobijte adresu ove sekcije sa:
objdump -s -j .dtors /exec
rabin -s /exec | grep “__DTOR”
Obično ćete pronaći DTOR markere između vrednosti ffffffff
i 00000000
. Dakle, ako vidite samo te vrednosti, to znači da nema registrovane funkcije. Zato prepišite 00000000
sa adresom shell koda kako biste ga izvršili.
{% hint style="warning" %} Naravno, prvo morate pronaći mesto za čuvanje shell koda kako biste ga kasnije mogli pozvati. {% endhint %}
.fini_array
Essentially this is a structure with functions that will be called before the program finishes, like .dtors
. This is interesting if you can call your shellcode just jumping to an address, or in cases where you need to go back to main
again to exploit the vulnerability a second time.
objdump -s -j .fini_array ./greeting
./greeting: file format elf32-i386
Contents of section .fini_array:
8049934 a0850408
#Put your address in 0x8049934
Napomena da kada se izvrši funkcija iz .fini_array
prelazi se na sledeću, tako da neće biti izvršena više puta (sprječavajući večne petlje), ali će vam dati samo 1 izvršenje funkcije postavljene ovde.
Napomena da se unosi u .fini_array
nazivaju u obrnutom redosledu, pa verovatno želite da počnete sa pisanjem od poslednjeg.
Večna petlja
Da biste zloupotrebili .fini_array
kako biste dobili večnu petlju, možete proveriti šta je urađeno ovde: Ako imate najmanje 2 unosa u .fini_array
, možete:
- Koristite svoj prvi zapis da ponovo pozovete ranjivu funkciju za proizvoljno pisanje
- Zatim, izračunajte povratnu adresu na steku koju čuva
__libc_csu_fini
(funkcija koja poziva sve funkcije.fini_array
) i stavite tamo adresu__libc_csu_fini
- To će naterati
__libc_csu_fini
da pozove sam sebe ponovo izvršavajući ponovo funkcije.fini_array
koje će pozvati ranjivu WWW funkciju 2 puta: jednom za proizvoljno pisanje i još jednom da ponovo prepiše povratnu adresu__libc_csu_fini
na steku da bi se ponovo pozvao.
{% hint style="danger" %}
Napomena da sa Full RELRO, odeljak .fini_array
je postavljen kao samo za čitanje.
{% endhint %}
link_map
Kao što je objašnjeno u ovom postu, ako program završi korišćenjem return
ili exit()
, pokrenuće se __run_exit_handlers()
koji će pozvati registrovane destruktore.
{% hint style="danger" %}
Ako program izađe putem funkcije _exit()
, pozvaće se exit
syscall i hendleri za izlaz neće biti izvršeni. Dakle, da biste potvrdili 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 jednog alata 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 jednog alata 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-Storage dtor_list-a u __run_exit_handlers
Kao što je objašnjeno ovde, ako program završi 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
, demantovace pokazivac iz cur->func
i pozvace je sa argumentom cur->obj
.
Koriscenjem tls
funkcije iz ovog forka GEF-a, moguce je videti da je dtor_list
veoma blizu stack canary-ja i PTR_MANGLE cookie-ja. Dakle, prelivanjem preko njega bilo bi moguce prepisati cookie i stack canary.
Prelivanjem PTR_MANGLE cookie-ja, bilo bi moguce zaobici funkciju PTR_DEMANLE
postavljanjem je na 0x00, sto znaci da je xor
koji se koristi za dobijanje prave adrese samo adresa koja je konfigurisana. Zatim, pisanjem na dtor_list
moguce je povezati nekoliko funkcija sa funkcijom adrese i njenim argumentom.
Na kraju primetite da sacuvani pokazivac nece biti samo ekskluzivno xorovan sa cookie-jem vec i rotiran za 17 bitova:
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
Dakle, morate uzeti ovo u obzir pre dodavanja nove adrese.
Pronađite primer u originalnom postu.
Ostali izmenjeni pokazivači u __run_exit_handlers
Ova tehnika je objašnjena ovde i ponovo zavisi od programa koji izlazi pozivajući return
ili exit()
tako da se poziva __run_exit_handlers()
.
Pogledajmo 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')
.
Primer ovoga možete pronaći u originalnom blog postu o tehnici.
Naučite hakovanje AWS-a od nule do heroja sa htARTE (HackTricks AWS Red Team Expert)!
Drugi načini podrške HackTricks-u:
- Ako želite da vidite svoju kompaniju reklamiranu na HackTricks-u ili da preuzmete HackTricks u PDF formatu proverite PLANOVE ZA PRIJAVU!
- Nabavite zvanični PEASS & HackTricks swag
- Otkrijte The PEASS Family, našu kolekciju ekskluzivnih NFT-ova
- Pridružite se 💬 Discord grupi ili telegram grupi ili nas pratite na Twitteru 🐦 @hacktricks_live.
- Podelite svoje hakovanje trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.