WWW2Exec - atexit(), TLS Storage & Other mangled Pointers
Reading time: 8 minutes
tip
Jifunze na fanya mazoezi ya AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Angalia mpango wa usajili!
- Jiunge na 💬 kikundi cha Discord au kikundi cha telegram au tufuatilie kwenye Twitter 🐦 @hacktricks_live.
- Shiriki mbinu za udukuzi kwa kuwasilisha PRs kwa HackTricks na HackTricks Cloud repos za github.
__atexit Structures
caution
Sasa ni ajabu sana kutumia hili!
atexit()
ni kazi ambayo kazi nyingine zinapewa kama vigezo. Hizi kazi zita tekelezwa wakati wa kutekeleza exit()
au kurudi kwa main.
Ikiwa unaweza kubadilisha anwani ya yoyote ya hizi kazi ili kuelekeza kwenye shellcode kwa mfano, utapata udhibiti wa mchakato, lakini hii kwa sasa ni ngumu zaidi.
Kwa sasa anwani za kazi zitakazotekelezwa zime fichwa nyuma ya muundo kadhaa na hatimaye anwani ambayo inaelekeza si anwani za kazi, bali zime sifiriwa kwa XOR na displacement na funguo za nasibu. Hivyo kwa sasa vector hii ya shambulio si ya manufaa sana angalau kwenye x86 na x64_86.
Kazi ya usimbuaji ni PTR_MANGLE
. Mifumo mingine kama m68k, mips32, mips64, aarch64, arm, hppa... haitekelezi kazi ya usimbuaji kwa sababu inarudisha kile kile ilichopokea kama pembejeo. Hivyo mifumo hii ingekuwa na uwezo wa kushambuliwa kupitia vector hii.
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 waondoa walioandikishwa.
caution
Ikiwa programu inatoka kupitia _exit()
kazi, itaita exit
syscall na waondoa wa kutoka hawata tekelezwa. Hivyo, ili kuthibitisha __run_exit_handlers()
inatekelezwa unaweza kuweka breakpoint juu yake.
Kifungu muhimu ni (source):
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
}
Note how map -> l_addr + fini_array -> d_un.d_ptr
inatumika kuhesabu nafasi ya array ya kazi za kuita.
Kuna chaguzi kadhaa:
- Badilisha thamani ya
map->l_addr
ili iweke kwenye fekifini_array
yenye maagizo ya kutekeleza msimbo wa kiholela - Badilisha
l_info[DT_FINI_ARRAY]
nal_info[DT_FINI_ARRAYSZ]
(ambazo ziko karibu karibu katika kumbukumbu), ili ziwe zinapoint kwaElf64_Dyn
iliyoundwa ambayo itafanya tenaarray
iweke kwenye eneo la kumbukumbu ambalo mshambuliaji anadhibiti. - Hii ripoti inabadilisha
l_info[DT_FINI_ARRAY]
kwa anwani ya kumbukumbu inayodhibitiwa katika.bss
yenye fekifini_array
. Hii feki array ina kwanza anwani moja ambayo itatekelezwa na kisha tofauti kati ya anwani ya hii feki array na thamani yamap->l_addr
ili*array
iweke kwenye feki array. - Kulingana na chapisho kuu la mbinu hii na hii ripoti ld.so inacha kiashiria kwenye stack kinachopoint kwa binary
link_map
katika ld.so. Kwa kuandika kiholela inawezekana kubadilisha na kufanya ipointe kwa fekifini_array
inayodhibitiwa na mshambuliaji yenye anwani ya moja gadget kwa mfano.
Kufuata msimbo wa awali unaweza kupata sehemu nyingine ya kuvutia yenye 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, itakuwa inawezekana kubadilisha thamani ya map->l_info[DT_FINI]
ikielekeza kwenye muundo wa ElfW(Dyn)
ulioandaliwa. Pata maelezo zaidi hapa.
TLS-Storage dtor_list overwrite katika __run_exit_handlers
Kama ilivyoelezwa hapa, ikiwa programu inatoka kupitia return
au exit()
, itatekeleza __run_exit_handlers()
ambayo itaita kazi yoyote ya destructor iliyosajiliwa.
Code 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 ();
Code 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
, itachambua kiashiria kutoka cur->func
na kuikalia na hoja cur->obj
.
Kwa kutumia kazi ya tls
kutoka kwa fork ya GEF, inawezekana kuona kwamba kwa kweli dtor_list
iko karibu sana na stack canary na PTR_MANGLE cookie. Hivyo, kwa kujaa kwenye hiyo, itakuwa inawezekana kuandika upya cookie na stack canary.
Kwa kuandika upya PTR_MANGLE cookie, itakuwa inawezekana kuzidi kazi ya PTR_DEMANLE
kwa kuipatia 0x00, itamaanisha kwamba xor
inayotumika kupata anwani halisi ni tu anwani iliyowekwa. Kisha, kwa kuandika kwenye dtor_list
inawezekana kuunganisha kazi kadhaa na anwani ya kazi hiyo na hoja yake.
Hatimaye, zingatia kwamba kiashiria kilichohifadhiwa hakitakuwa kiki xored tu na cookie bali pia kitageuzwa kwa bits 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
Hivyo unahitaji kuzingatia hili kabla ya kuongeza anwani mpya.
Pata mfano katika post ya asili.
Pointers nyingine zilizochanganywa katika __run_exit_handlers
Tekniki hii imeelezwa hapa na inategemea tena programu kutoka kwa kuita return
au exit()
hivyo __run_exit_handlers()
inaitwa.
Hebu tuangalie zaidi msimbo 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);
Variable f
inaonyesha kwenye muundo wa initial
na kulingana na thamani ya f->flavor
kazi tofauti zitaanzishwa.
Kulingana na thamani, anwani ya kazi ya kuita itakuwa mahali pengine, lakini itakuwa demangled kila wakati.
Zaidi ya hayo, katika chaguo ef_on
na ef_cxa
pia inawezekana kudhibiti argument.
Inawezekana kuangalia muundo wa initial
katika kikao cha ufuatiliaji na GEF ikikimbia gef> p initial
.
Ili kutumia hii unahitaji ama leak au kufuta PTR_MANGLE
cookie na kisha kuandika tena kipengee cha cxa
katika initial na system('/bin/sh')
.
Unaweza kupata mfano wa hii katika blogu ya asili kuhusu mbinu.
tip
Jifunze na fanya mazoezi ya AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Angalia mpango wa usajili!
- Jiunge na 💬 kikundi cha Discord au kikundi cha telegram au tufuatilie kwenye Twitter 🐦 @hacktricks_live.
- Shiriki mbinu za udukuzi kwa kuwasilisha PRs kwa HackTricks na HackTricks Cloud repos za github.