WWW2Exec - .dtors & .fini_array

Jifunze AWS hacking kutoka sifuri hadi shujaa na htARTE (Mtaalam wa Timu Nyekundu ya AWS ya HackTricks)!

Njia nyingine za kusaidia HackTricks:

.dtors

Leo ni kawaida sana kupata binary na sehemu ya .dtors!

Waharibifu ni kazi ambazo hutekelezwa kabla ya programu kukamilika (baada ya kazi ya main kurejea). Anwani za kazi hizi zimehifadhiwa ndani ya sehemu ya .dtors ya binary na kwa hivyo, ikiwa utaweza kuandika anwani ya shellcode katika __DTOR_END__ , hiyo itatekelezwa kabla ya programu kukamilika.

Pata anwani ya sehemu hii na:

objdump -s -j .dtors /exec
rabin -s /exec | grep “__DTOR”

Kawaida utapata alama za DTOR kati ya thamani ffffffff na 00000000. Kwa hivyo ikiwa unaona thamani hizo tu, inamaanisha kwamba hakuna kazi iliyosajiliwa. Kwa hivyo badilisha 00000000 na anwani ya shellcode ili kuitekeleza.

Kwa hakika, kwanza unahitaji kupata mahali pa kuhifadhi shellcode ili baadaye uweze kuita.

.fini_array

Kimsingi hii ni muundo na kazi ambazo zitaitwa kabla ya programu kukamilika, kama .dtors. Hii ni ya kuvutia ikiwa unaweza kuita shellcode yako kwa kuruka kwenye anwani, au katika hali ambapo unahitaji kurudi kwa main tena ili kutumia udhaifu mara ya pili.

objdump -s -j .fini_array ./greeting

./greeting:     file format elf32-i386

Contents of section .fini_array:
8049934 a0850408

#Put your address in 0x8049934

Tafadhali kumbuka kwamba wakati kazi kutoka kwa .fini_array inatekelezwa inahamia kwa ile inayofuata, hivyo haitatekelezwa mara kadhaa (kuzuia mizunguko ya milele), lakini pia itakupa tu utekelezaji wa kazi moja iliyowekwa hapa.

Tafadhali kumbuka kuwa vipengele katika .fini_array huitwa kwa mpangilio wa nyuma, hivyo labda unataka kuanza kuandika kutoka kwa ya mwisho.

Mzunguko wa Milele

Ili kutumia .fini_array kupata mzunguko wa milele unaweza angalia kilichofanywa hapa: Ikiwa una angalau vipengele 2 katika .fini_array, unaweza:

  • Tumia andika yako ya kwanza kuita kazi ya andika ya kupindukia isiyo na kinga tena

  • Kisha, hesabu anwani ya kurudi kwenye rundo iliyohifadhiwa na __libc_csu_fini (kazi inayoitwa na .fini_array zote) na weka huko anwani ya __libc_csu_fini

  • Hii itafanya __libc_csu_fini kuita yenyewe tena ikitekeleza tena kazi za .fini_array ambazo zitaita kazi ya WWW isiyokuwa na kinga mara 2: moja kwa andika ya kupindukia na nyingine kwa kubadilisha tena anwani ya kurudi ya __libc_csu_fini kwenye rundo ili kuita yenyewe tena.

Tafadhali kumbuka kwamba na Full RELRO, sehemu ya .fini_array inafanywa kuwa soma-tu.

Kama ilivyoelezwa katika chapisho hili, Ikiwa programu inaisha kwa kutumia return au exit() itatekeleza __run_exit_handlers() ambayo itaita wabomoleaji waliosajiliwa.

Ikiwa programu inaishia kupitia _exit() function, itaita exit syscall na wabomoleaji wa kumaliza hawatatekelezwa. Kwa hivyo, kuthibitisha kuwa __run_exit_handlers() inatekelezwa unaweza kuweka kizuizi cha muda kwenye hiyo.

Msimbo muhimu ni (chanzo):

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
}

Tazama jinsi map -> l_addr + fini_array -> d_un.d_ptr inavyotumiwa kukadiria nafasi ya array ya kazi za kupiga.

Kuna chaguo kadhaa:

  • Badilisha thamani ya map->l_addr ili iweze kuashiria fini_array bandia yenye maagizo ya kutekeleza msimbo wa kupiga

  • Badilisha kuingia za l_info[DT_FINI_ARRAY] na l_info[DT_FINI_ARRAYSZ] (ambazo ziko karibu kwenye kumbukumbu), ili ziweze kuashiria muundo wa Elf64_Dyn uliobuniwa ambao utafanya tena array iashirie eneo la kumbukumbu linalodhibitiwa na mshambuliaji.

  • Hii andishi inabadilisha l_info[DT_FINI_ARRAY] na anwani ya kumbukumbu inayodhibitiwa katika .bss inayohifadhi fini_array bandia. Array hii bandia ina kwanza anwani ya gadget moja ambayo itatekelezwa na kisha tofauti kati ya anwani ya array bandia na thamani ya map->l_addr ili *array iashirie array bandia.

  • Kulingana na chapisho kuu la mbinu hii na hii andishi ld.so huacha kidude kwenye stakishi kinachoashiria link_map ya binary katika ld.so. Kwa kuandika kwa hiari inawezekana kuibadilisha na kuifanya iashirie fini_array bandia inayodhibitiwa na mshambuliaji na anwani ya gadget moja kwa mfano.

Ukiendelea na msimbo uliopita unaweza kupata sehemu nyingine ya kuvutia na msimbo:

/* 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));
}

Katika kesi hii ingewezekana kubadilisha thamani ya map->l_info[DT_FINI] ikielekeza kwa muundo wa ElfW(Dyn) ulioundwa. Pata maelezo zaidi hapa.

Kubadilisha TLS-Storage dtor_list katika __run_exit_handlers

Kama inavyoelezwa hapa, ikiwa programu inaishia kupitia return au exit(), itaendesha __run_exit_handlers() ambayo itaita kazi yoyote ya mabomoleaji iliyosajiliwa.

Msimbo kutoka _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 ();

Msimbo kutoka __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);
[...]
}
}

Kwa kila kazi iliyosajiliwa katika tls_dtor_list, itatengeneza upya pointer kutoka kwa cur->func na kuipiga simu na hoja cur->obj.

Kwa kutumia kazi ya tls kutoka kwa hii fork ya GEF, ni rahisi kuona kwamba dtor_list iko karibu sana na stack canary na PTR_MANGLE cookie. Hivyo, kwa kujaza kwa wingi, ingewezekana kubadilisha cookie na stack canary. Kwa kubadilisha PTR_MANGLE cookie, ingewezekana kupita kwenye kazi ya PTR_DEMANLE kwa kuweka kuwa 0x00, maana yake xor iliyotumika kupata anwani halisi ni tu anwani iliyowekwa. Kisha, kuandika kwenye dtor_list inawezekana kuunganisha kazi kadhaa na kazi ya anwani na hoja yake.

Hatimaye, kumbuka kwamba pointer iliyohifadhiwa haitakuwa tu ikifanyiwa xor na cookie bali pia inazungushwa biti 17:

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

Kwa hivyo unahitaji kuzingatia hili kabla ya kuongeza anwani mpya.

Pata mfano katika chapisho la asili.

Pointi zingine zilizopotoshwa katika __run_exit_handlers

Mbinu hii inaeleza hapa na inategemea tena programu kutoka kwa wito wa return au exit() hivyo __run_exit_handlers() inaitwa.

Hebu angalia zaidi ya nambari ya kazi hii:

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);

Variable f inaelekeza kwa muundo wa initial na kulingana na thamani ya f->flavor kazi tofauti zitaitwa. Kulingana na thamani, anwani ya kazi ya kuita itakuwa mahali tofauti, lakini itakuwa demangled daima.

Zaidi, katika chaguo za ef_on na ef_cxa pia niwezekano wa kudhibiti argument.

Inawezekana kuangalia muundo wa initial katika kikao cha kutatua matatizo na GEF ikikimbia gef> p initial.

Kutumia hii unahitaji kuvuja au kufuta PTR_MANGLEcookie na kisha kubadilisha kuingia cha cxa katika initial na system('/bin/sh'). Unaweza kupata mfano wa hii katika chapisho la blogu la awali kuhusu mbinu.

Last updated