WWW2Exec - .dtors & .fini_array

AWS hackleme konusunda sıfırdan kahramana kadar öğrenin htARTE (HackTricks AWS Red Team Expert)!

HackTricks'i desteklemenin diğer yolları:

.dtors

Günümüzde bir binary dosyasında .dtors bölümü bulmak çok garip!

Yıkıcılar, programın sona ermeden önce (ana main fonksiyonu geri döndükten sonra) çalıştırılan fonksiyonlardır. Bu fonksiyonların adresleri, binary dosyasının .dtors bölümünde saklanır ve bu nedenle, __DTOR_END__ içine bir shellcode adresi yazmayı başarırsanız, bu programın sona ermeden önce çalıştırılacaktır.

Bu bölümün adresini şu şekilde alın:

objdump -s -j .dtors /exec
rabin -s /exec | grep “__DTOR”

Genellikle DTOR işaretçilerini ffffffff ve 00000000 değerleri arasında bulacaksınız. Yani eğer sadece bu değerleri görüyorsanız, bu herhangi bir fonksiyonun kaydedilmediği anlamına gelir. Bu yüzden 00000000 değerini shellcode'ın adresiyle üzerine yazın ve onu çalıştırın.

Tabii ki, daha sonra onu çağırmak için shellcode'ı saklamak için bir yer bulmanız gerekiyor.

.fini_array

Temelde bu, programın sona ermeden önce çağrılacak fonksiyonların bulunduğu bir yapıdır, .dtors gibi. Bu, shellcode'unuzu bir adrese atlayarak çağırabildiğinizde veya zafiyeti ikinci kez sömürmek için main'e geri gitmeniz gereken durumlarda ilginçtir.

objdump -s -j .fini_array ./greeting

./greeting:     file format elf32-i386

Contents of section .fini_array:
8049934 a0850408

#Put your address in 0x8049934

Not edin ki .fini_array'den bir işlev çalıştırıldığında bir sonrakine geçer, bu yüzden birkaç kez çalıştırılmaz (sonsuz döngüleri önler), ancak yalnızca buraya yerleştirilen bir işlevin çalıştırılmasını sağlar.

.fini_array içindeki girişlerin ters sırayla çağrıldığını unutmayın, bu yüzden muhtemelen en sondan yazmaya başlamak istersiniz.

Sonsuz döngü

.fini_array'yi kötüye kullanmak için sonsuz bir döngü elde etmek için burada ne yapıldığını kontrol edebilirsiniz: En az 2 girişiniz varsa .fini_array içinde, şunları yapabilirsiniz:

  • İlk yazmanızı zafiyetli keyfi yazma işlevini çağırmak için kullanın

  • Ardından, __libc_csu_fini tarafından depolanan yığında dönüş adresini hesaplayın ve oraya __libc_csu_fini'nin adresini koyun

  • Bu, __libc_csu_fini'nin kendisini tekrar çağırarak .fini_array işlevlerini tekrar çalıştırmasını sağlayacak ve zafiyetli WWW işlevini 2 kez çağıracaktır: biri için keyfi yazma ve diğeri yığında kendi kendini tekrar çağırmak için __libc_csu_fini'nin dönüş adresini tekrar üzerine yazmak.

Tam RELRO** ile,** bölüm .fini_array salt okunur hale getirilir.

Bu gönderide açıklandığı gibi, Program return veya exit() kullanarak çıkarsa, __run_exit_handlers()'ı çalıştıracak ve kayıtlı yıkıcıları çağıracaktır.

Program _exit() işlevi ile çıkarsa, exit sistem çağrısını çağırır ve çıkış işleyicileri çalıştırılmaz. Bu nedenle, __run_exit_handlers()'ın çalıştırıldığını doğrulamak için bir kesme noktası ayarlayabilirsiniz.

Önemli kod (kaynak):

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
}

Not: map -> l_addr + fini_array -> d_un.d_ptr kullanılarak dizinin çağrılacak fonksiyonların konumunu hesaplamak için.

Birkaç seçenek bulunmaktadır:

  • map->l_addr değerini üzerine yazarak, talimatları yürütmek için sahte bir fini_array'e işaret edecek şekilde yapay bir fini_array oluşturmak

  • Bellekte daha çok ardışık olan l_info[DT_FINI_ARRAY] ve l_info[DT_FINI_ARRAYSZ] girişlerini üzerine yazarak, tekrar array'ın saldırganın kontrol ettiği bir bellek bölgesine işaret etmesini sağlayacak sahte bir Elf64_Dyn yapısına işaret etmelerini sağlamak.

  • Bu yazıda l_info[DT_FINI_ARRAY] üzerine .bss içindeki kontrol edilen bir bellekteki adresle üzerine yazarak sahte bir fini_array'nın adresini üzerine yazıyor. Bu sahte dizi önce yürütülecek olan bir one gadget adresini içerir ve ardından bu sahte dizi adresi ile map->l_addr değeri arasındaki farkı içerir, böylece *array sahte diziye işaret eder.

  • Bu tekniğin ana yazısına ve bu yazıya göre ld.so, ld.so'da binary link_map'e işaret eden bir işaretçi bırakır. Bir keyfi yazma işlemiyle üzerine yazarak, saldırganın kontrol ettiği sahte bir fini_array'e işaret eden bir adresle ld.so'nun bu işaretçiye işaret etmesini sağlamak mümkündür, örneğin bir one gadget adresine.

/* 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, map->l_info[DT_FINI] değerinin üzerine yazılabilir ve sahte bir ElfW(Dyn) yapısına işaret edilebilir. Daha fazla bilgi için buraya bakabilirsiniz.

TLS-Depolama dtor_list üzerine yazma işlemi __run_exit_handlers içinde

Burada açıklandığı gibi, bir program return veya exit() ile çıkış yaparsa, __run_exit_handlers() fonksiyonunu çalıştıracaktır ve kayıtlı olan herhangi bir yıkıcı fonksiyonu çağıracaktır.

_run_exit_handlers() fonksiyonundan kod:

/* 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 ();

Kod __call_tls_dtors() fonksiyonundan:

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 bir kayıtlı fonksiyon için tls_dtor_list içindeki işaretçiyi cur->func'tan çözer ve cur->obj argümanı ile çağırır.

Bu GEF'in bu fork'u kullanılarak tls fonksiyonu ile, aslında dtor_list'in stack canary ve PTR_MANGLE cookie'ye çok yakın olduğu görülebilir. Bu nedenle, üzerine taşma olursa cookie ve stack canary'yi üzerine yazmak mümkün olacaktır. PTR_MANGLE cookie üzerine yazıldığında, PTR_DEMANLE fonksiyonunu atlayabilir çünkü 0x00 olarak ayarlanması, gerçek adresi elde etmek için kullanılan xor'un sadece yapılandırılmış adres olduğu anlamına gelir. Ardından, dtor_list üzerine yazarak, fonksiyon adresi ve argümanı ile birkaç fonksiyonu zincirleme mümkün olacaktır.

Son olarak, depolanan işaretçinin sadece cookie ile xor işlemine tabi tutulmayacağı, aynı zamanda 17 bit döndürüleceğine dikkat edin:

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

Yeni bir adres eklemeden önce bunu dikkate almanız gerekmektedir.

Örnek bir örneği orijinal gönderide bulun.

__run_exit_handlers içindeki diğer karışık işaretçiler

Bu teknik burada açıklanmıştır ve yine programın return veya exit() çağrısı yaparak çıkması ve dolayısıyla __run_exit_handlers() fonksiyonunun çağrılmasına bağlıdı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ğerine bağlı olarak, çağrılacak fonksiyonun adresi farklı bir yerde olacak, 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.

Hata ayıklama oturumunda GEF ile çalışırken gef> p initial komutu kullanılarak initial yapısı kontrol edilebilir.

Bunu kötüye kullanmak için ya PTR_MANGLEcookie'yi sızdırmanız ya da silmeniz ve ardından initial içindeki bir cxa girişini system('/bin/sh') ile üzerine yazmanız gerekir. Bu tekniğe ilişkin bir örneği orijinal teknik hakkındaki blog yazısında bulabilirsiniz.

Last updated