11 KiB
WWW2Exec - atexit(), TLS Storage & Pointers Zingine zilizochanganyikiwa
{% hint style="success" %}
Jifunze & zoezi AWS Hacking:Mafunzo ya HackTricks AWS Red Team Expert (ARTE)
Jifunze & zoezi GCP Hacking: Mafunzo ya HackTricks GCP Red Team Expert (GRTE)
Support HackTricks
- Angalia mpango wa usajili!
- Jiunge na 💬 Kikundi cha Discord au kikundi cha telegram au tufuate kwenye Twitter 🐦 @hacktricks_live.
- Shiriki mbinu za udukuzi kwa kuwasilisha PRs kwa HackTricks na HackTricks Cloud repos za github.
__atexit Miundo
{% hint style="danger" %} Leo ni raha sana kuitumia! {% endhint %}
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
link_map
Kama ilivyoelezwa katika chapisho hili, Ikiwa programu inatoka kwa kutumia return
au exit()
itatekeleza __run_exit_handlers()
ambayo itaita wabomoleaji waliosajiliwa.
{% hint style="danger" %}
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.
{% endhint %}
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 kwenyefini_array
bandia yenye maagizo ya kutekeleza msimbo wa kupendelea - Badilisha kuingia za
l_info[DT_FINI_ARRAY]
nal_info[DT_FINI_ARRAYSZ]
(ambazo ziko karibu kwenye kumbukumbu), ili ziweze kuelekeza kwenye muundo waElf64_Dyn
ulioundwa ambao utafanya tenaarray
ielekee eneo la kumbukumbu linalodhibitiwa na mshambuliaji. - Hii andishi inabadilisha
l_info[DT_FINI_ARRAY]
na anwani ya kumbukumbu inayodhibitiwa katika.bss
inayoendelea nafini_array
bandia. Array hii bandia ina kwanza anwani ya gadget moja ambayo itatekelezwa na kisha tofauti kati ya anwani ya array bandia na thamani yamap->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 kwenyefini_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_MANGLE
cookie na kisha kubadilisha kuingia kwa cxa
katika mwanzo na system('/bin/sh')
.
Unaweza kupata mfano wa hii katika chapisho la blogi asilia kuhusu mbinu.