WWW2Exec - atexit()

Support HackTricks

__atexit Miundo

Leo ni raha sana kuitumia!

atexit() ni kazi ambayo kazi zingine hupitishwa kama paramita. Hizi kazi zitatekelezwa wakati wa kutekeleza exit() au kurudi kwa msingi. Ikiwa unaweza kurekebisha anwani ya mojawapo ya hizi kazi ili ielekee kwenye shellcode kwa mfano, utapata udhibiti wa mchakato, lakini hii ni ngumu zaidi kwa sasa. Kwa sasa anwani za kazi zitakazotekelezwa zimefichwa nyuma ya miundo kadhaa na mwishowe anwani ambayo inaelekezwa sio anwani za kazi, bali zime fichwa kwa XOR na viondoleo na ufunguo wa nasibu. Kwa hivyo kwa sasa vector huu wa shambulio si muhimu sana angalau kwenye x86 na x64_86. Kazi ya ufichaji ni PTR_MANGLE. Miundo mingine kama m68k, mips32, mips64, aarch64, arm, hppa... hawatekelezi kazi ya ufichaji kwa sababu inarejesha sawa na ilivyopokea kama kuingia. Kwa hivyo miundo hii ingeweza kushambuliwa kupitia vector huu.

Unaweza kupata maelezo ya kina juu ya jinsi hii inavyofanya kazi katika https://m101.github.io/binholic/2017/05/20/notes-on-abusing-exit-handlers.html

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

Ikiwa programu inatoka kupitia kazi ya _exit(), itaita wito wa mfumo wa exit na wabomoleaji wa kutoka hawatatekelezwa. Kwa hivyo, kuthibitisha kuwa __run_exit_handlers() inatekelezwa unaweza kuweka kizuizi cha muda ndani yake.

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
}

Tafadhali angalia jinsi map -> l_addr + fini_array -> d_un.d_ptr inavyotumiwa kukadiria nafasi ya array ya kazi za kuita.

Kuna chaguo kadhaa:

  • Badilisha thamani ya map->l_addr ili iweze kuelekeza kwenye fini_array bandia yenye maagizo ya kutekeleza msimbo wa kupendelea

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

  • Hii andishi inabadilisha l_info[DT_FINI_ARRAY] na anwani ya kumbukumbu inayodhibitiwa katika .bss inayoendelea na 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 iweze kuelekeza kwenye array bandia.

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

Ukiendelea na msimbo uliopita unaweza kupata sehemu nyingine yenye msimbo wa kuvutia:

/* 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 masharti zaidi hapa.

Kubadilisha TLS-Storage dtor_list katika __run_exit_handlers

Kama ilivyoelezwa hapa, ikiwa programu inaishia kupitia return au exit(), itaendesha __run_exit_handlers() ambayo itaita kazi yoyote ya kuharibu 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, itatenganisha pointer kutoka kwa cur->func na kuita na hoja cur->obj.

Kwa kutumia kazi ya tls kutoka kwa fork ya GEF hii, ni rahisi kuona kwamba dtor_list iko karibu sana na stack canary na PTR_MANGLE cookie. Kwa 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 kwa 0x00, itamaanisha kwamba xor iliyotumika kupata anwani halisi ni tu anwani iliyoconfigure. Kisha, kwa 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 lakini pia itazungushwa 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.

Pointer zingine zilizopotoshwa katika __run_exit_handlers

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

Hebu angalia msimbo zaidi wa 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);

Mbadala 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 imefanywa wazi daima.

Zaidi, katika chaguo ef_on na ef_cxa pia ni sawa kudhibiti hoja.

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

Kutumia hii unahitaji au kufichua PTR_MANGLEcookie na kisha kubadilisha kuingia kwa cxa katika mwanzo na system('/bin/sh'). Unaweza kupata mfano wa hii katika chapisho la blogi asilia kuhusu mbinu.

Last updated