WWW2Exec - atexit(), TLS Depolama ve Diğer Karışık Göstergeler
Reading time: 8 minutes
tip
AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- Bize katılın 💬 Discord grubuna veya telegram grubuna veya bizi takip edin Twitter'da 🐦 @hacktricks_live.
- Hacking ipuçlarını paylaşın, HackTricks ve HackTricks Cloud github reposuna PR göndererek.
__atexit Yapıları
caution
Günümüzde bunu istismar etmek çok garip!
atexit()
bir diğer fonksiyonların parametre olarak geçirildiği bir fonksiyondur. Bu fonksiyonlar, bir exit()
veya main'in dönmesi sırasında çalıştırılacaktır.
Eğer bu fonksiyonlardan herhangi birinin adresini örneğin bir shellcode'a işaret edecek şekilde değiştirebilirseniz, sürecin kontrolünü ele geçireceksiniz, ancak bu şu anda daha karmaşık.
Şu anda çalıştırılacak fonksiyonların adresleri birkaç yapı arkasında gizlidir ve nihayetinde işaret ettikleri adresler, fonksiyonların adresleri değil, XOR ile şifrelenmiş ve rastgele bir anahtar ile kaydırılmıştır. Bu nedenle, şu anda bu saldırı vektörü en azından x86 ve x64_86 üzerinde çok kullanışlı değildir.
Şifreleme fonksiyonu PTR_MANGLE
'dir. Diğer mimariler m68k, mips32, mips64, aarch64, arm, hppa... şifreleme fonksiyonunu uygulamaz çünkü bu fonksiyon girdi olarak aldığı ile aynı çıktıyı döner. Bu nedenle bu mimariler bu vektörle saldırıya uğrayabilir.
Bunun nasıl çalıştığına dair derinlemesine bir açıklamayı https://m101.github.io/binholic/2017/05/20/notes-on-abusing-exit-handlers.html adresinde bulabilirsiniz.
link_map
Açıklandığı gibi bu yazıda, Program return
veya exit()
kullanarak çıkarsa, __run_exit_handlers()
çalıştırılacak ve kayıtlı yıkıcıları çağıracaktır.
caution
Eğer program _exit()
fonksiyonu aracılığıyla çıkarsa, exit
syscall'ini çağıracak ve çıkış yöneticileri çalıştırılmayacaktır. Bu nedenle, __run_exit_handlers()
'ın çalıştırıldığını doğrulamak için üzerine bir kesme noktası koyabilirsiniz.
Önemli kod (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
}
map -> l_addr + fini_array -> d_un.d_ptr
ifadesinin çağrılacak fonksiyonlar dizisinin konumunu hesaplamak için kullanıldığına dikkat edin.
Bir kaç seçenek var:
map->l_addr
değerini, rastgele kod çalıştırma talimatları içeren sahte birfini_array
gösterecek şekilde yazın.l_info[DT_FINI_ARRAY]
vel_info[DT_FINI_ARRAYSZ]
girişlerini (hafızada daha az veya daha çok ardışık olan) yazın, böylece bunlar sahte birElf64_Dyn
yapısına işaret etsin ve tekrararray
'in saldırganın kontrol ettiği bir bellek bölgesine işaret etmesini sağlayın.- Bu yazım,
l_info[DT_FINI_ARRAY]
'ı.bss
içinde kontrol edilen bir bellek adresinin sahtefini_array
'ına işaret edecek şekilde yazıyor. Bu sahte dizi öncelikle bir one gadget adresini içeriyor, bu adres çalıştırılacak ve ardından bu sahte dizi ilemap->l_addr
değerinin adresi arasındaki fark olacak, böylece*array
sahte diziye işaret edecek. - Bu tekniğin ana gönderisine ve bu yazıma göre, ld.so yığında, ld.so'daki ikili
link_map
'e işaret eden bir işaretçi bırakır. Rastgele bir yazma ile bunu yazmak ve saldırganın kontrolündeki sahte birfini_array
'a işaret etmesini sağlamak mümkündür; örneğin bir one gadget adresi ile.
Önceki kodun ardından, kodla birlikte başka ilginç bir bölüm bulabilirsiniz:
/* 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));
}
Bu durumda, sahte bir ElfW(Dyn)
yapısına işaret eden map->l_info[DT_FINI]
değerini geçersiz kılmak mümkün olacaktır. daha fazla bilgi burada.
TLS-Storage dtor_list geçersiz kılma __run_exit_handlers
içinde
burada açıklandığı gibi, bir program return
veya exit()
ile çıkarsa, kayıtlı olan herhangi bir yıkıcı işlevi çağıracak olan __run_exit_handlers()
işlevini çalıştıracaktır.
_run_exit_handlers()
kodu:
/* 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 ();
__call_tls_dtors()
fonksiyonu kodu:
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);
[...]
}
}
Her kayıtlı fonksiyon için tls_dtor_list
, cur->func
'dan işaretçiyi çözerek alacak ve cur->obj
ile çağıracaktır.
Bu GEF çatallamasındaki tls
fonksiyonunu kullanarak, aslında dtor_list
'in stack canary ve PTR_MANGLE cookie'ye çok yakın olduğunu görebiliriz. Bu nedenle, üzerinde bir taşma ile cookie ve stack canary'yi üst üste yazmak mümkün olacaktır.
PTR_MANGLE cookie'sini üst üste yazarak, PTR_DEMANLE
fonksiyonunu 0x00 olarak ayarlayarak bypass etmek mümkün olacaktır; bu, gerçek adresi elde etmek için kullanılan xor
'un yalnızca yapılandırılan adres olduğunu gösterecektir. Ardından, dtor_list
üzerinde yazarak, birkaç fonksiyonu fonksiyon adresleri ve argümanları ile zincirlemek mümkündür.
Son olarak, saklanan işaretçinin yalnızca cookie ile xoredilmekle kalmayıp, aynı zamanda 17 bit döndürüleceğini unutmayın:
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
Bu nedenle, yeni bir adres eklemeden önce bunu dikkate almanız gerekir.
orijinal gönderide bir örnek bulun.
Diğer bozulmuş işaretçiler __run_exit_handlers
içinde
Bu teknik burada açıklanmıştır ve yine programın return
veya exit()
çağrısı ile çıkmasına bağlıdır, böylece __run_exit_handlers()
çağrılır.
Bu fonksiyonun daha fazla kodunu kontrol edelim:
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);
Değişken f
, initial
yapısına işaret eder ve f->flavor
değerine bağlı olarak farklı fonksiyonlar çağrılacaktır.
Değere bağlı olarak, çağrılacak fonksiyonun adresi farklı bir yerde olacaktır, ancak her zaman demangled olacaktır.
Ayrıca, ef_on
ve ef_cxa
seçeneklerinde bir argümanı kontrol etmek de mümkündür.
Bir hata ayıklama oturumunda gef> p initial
komutunu çalıştırarak initial
yapısını kontrol etmek mümkündür.
Bunu kötüye kullanmak için ya PTR_MANGLE
çerezini sızdırmanız ya da silmeniz ve ardından initial
içindeki bir cxa
girişini system('/bin/sh')
ile yazmanız gerekir.
Bunun bir örneğini teknik hakkında orijinal blog yazısında bulabilirsiniz.
tip
AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- Bize katılın 💬 Discord grubuna veya telegram grubuna veya bizi takip edin Twitter'da 🐦 @hacktricks_live.
- Hacking ipuçlarını paylaşın, HackTricks ve HackTricks Cloud github reposuna PR göndererek.