13 KiB
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:
- Ikiwa unataka kuona kampuni yako ikitangazwa kwenye HackTricks au kupakua HackTricks kwa PDF Angalia MIPANGO YA KUJIUNGA!
- Pata swag rasmi wa PEASS & HackTricks
- Gundua Familia ya PEASS, mkusanyiko wetu wa NFTs ya kipekee
- Jiunge na 💬 Kikundi cha Discord au kikundi cha telegram au tufuate kwenye Twitter 🐦 @hacktricks_live.
- Shiriki mbinu zako za udukuzi kwa kuwasilisha PRs kwa HackTricks na HackTricks Cloud repos za github.
.dtors
{% hint style="danger" %} Leo ni kawaida sana kupata binary na sehemu ya .dtors! {% endhint %}
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.
{% hint style="warning" %} Kwa hakika, kwanza unahitaji kupata mahali pa kuhifadhi shellcode ili baadaye uweze kuita. {% endhint %}
.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.
{% hint style="danger" %}
Tafadhali kumbuka kwamba na Full RELRO, sehemu ya .fini_array
inafanywa kuwa soma-tu.
{% endhint %}
link_map
Kama ilivyoelezwa katika chapisho hili, Ikiwa programu inaisha kwa kutumia return
au exit()
itatekeleza __run_exit_handlers()
ambayo itaita wabomoleaji waliosajiliwa.
{% hint style="danger" %}
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.
{% 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
}
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 kuashiriafini_array
bandia yenye maagizo ya kutekeleza msimbo wa kupiga - Badilisha kuingia za
l_info[DT_FINI_ARRAY]
nal_info[DT_FINI_ARRAYSZ]
(ambazo ziko karibu kwenye kumbukumbu), ili ziweze kuashiria muundo waElf64_Dyn
uliobuniwa ambao utafanya tenaarray
iashirie eneo la kumbukumbu linalodhibitiwa na mshambuliaji. - Hii andishi inabadilisha
l_info[DT_FINI_ARRAY]
na anwani ya kumbukumbu inayodhibitiwa katika.bss
inayohifadhifini_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
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 iashiriefini_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_MANGLE
cookie 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.