WWW2Exec - atexit(), TLS Storage & Other mangled Pointers
Reading time: 9 minutes
tip
Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
__atexit Structures
caution
Σήμερα είναι πολύ περίεργο να εκμεταλλευτείς αυτό!
atexit()
είναι μια συνάρτηση στην οποία άλλες συναρτήσεις περνιούνται ως παράμετροι. Αυτές οι συναρτήσεις θα εκτελούνται κατά την εκτέλεση μιας exit()
ή της επιστροφής της κύριας.
Αν μπορείς να τροποποιήσεις τη διεύθυνση οποιασδήποτε από αυτές τις συναρτήσεις ώστε να δείχνει σε ένα shellcode για παράδειγμα, θα κερδίσεις έλεγχο της διαδικασίας, αλλά αυτό είναι αυτή τη στιγμή πιο περίπλοκο.
Αυτή τη στιγμή οι διευθύνσεις στις συναρτήσεις που θα εκτελούνται είναι κρυμμένες πίσω από πολλές δομές και τελικά η διεύθυνση στην οποία δείχνει δεν είναι οι διευθύνσεις των συναρτήσεων, αλλά είναι κρυπτογραφημένες με XOR και μετατοπίσεις με μια τυχαία κλειδί. Έτσι, αυτή τη στιγμή αυτός ο επιθετικός παράγοντας είναι όχι πολύ χρήσιμος τουλάχιστον σε x86 και x64_86.
Η συνάρτηση κρυπτογράφησης είναι PTR_MANGLE
. Άλλες αρχιτεκτονικές όπως m68k, mips32, mips64, aarch64, arm, hppa... δεν υλοποιούν τη συνάρτηση κρυπτογράφησης γιατί επιστρέφει το ίδιο με αυτό που έλαβε ως είσοδο. Έτσι, αυτές οι αρχιτεκτονικές θα μπορούσαν να επιτεθούν μέσω αυτού του παραδείγματος.
Μπορείς να βρεις μια σε βάθος εξήγηση για το πώς λειτουργεί αυτό στο https://m101.github.io/binholic/2017/05/20/notes-on-abusing-exit-handlers.html
link_map
Όπως εξηγήθηκε σε αυτή την ανάρτηση, Αν το πρόγραμμα τερματίσει χρησιμοποιώντας return
ή exit()
θα εκτελέσει __run_exit_handlers()
που θα καλέσει τις καταχωρημένες καταστροφές.
caution
Αν το πρόγραμμα τερματίσει μέσω της _exit()
συνάρτησης, θα καλέσει την exit
syscall και οι χειριστές εξόδου δεν θα εκτελούνται. Έτσι, για να επιβεβαιώσεις ότι εκτελείται το __run_exit_handlers()
, μπορείς να ορίσεις ένα breakpoint σε αυτό.
Ο σημαντικός κώδικας είναι (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
χρησιμοποιείται για να υπολογίσει τη θέση του πίνακα συναρτήσεων που θα καλέσουμε.
Υπάρχουν μερικές επιλογές:
- Επαναγράψτε την τιμή του
map->l_addr
για να δείχνει σε ένα ψεύτικοfini_array
με οδηγίες για την εκτέλεση αυθαίρετου κώδικα. - Επαναγράψτε τις εγγραφές
l_info[DT_FINI_ARRAY]
καιl_info[DT_FINI_ARRAYSZ]
(οι οποίες είναι περισσότερο ή λιγότερο διαδοχικές στη μνήμη), ώστε να δείχνουν σε μια κατασκευασμένη δομήElf64_Dyn
που θα κάνει ξανά τονarray
να δείχνει σε μια ζώνη μνήμης που ελέγχεται από τον επιτιθέμενο. - Αυτή η αναφορά επαναγράφει το
l_info[DT_FINI_ARRAY]
με τη διεύθυνση μιας ελεγχόμενης μνήμης στο.bss
που περιέχει ένα ψεύτικοfini_array
. Αυτός ο ψεύτικος πίνακας περιέχει πρώτα μια διεύθυνση one gadget που θα εκτελεστεί και στη συνέχεια τη διαφορά μεταξύ της διεύθυνσης αυτού του ψεύτικου πίνακα και της τιμής τουmap->l_addr
ώστε το*array
να δείχνει στον ψεύτικο πίνακα. - Σύμφωνα με την κύρια ανάρτηση αυτής της τεχνικής και αυτή την αναφορά το ld.so αφήνει έναν δείκτη στη στοίβα που δείχνει στον δυαδικό
link_map
στο ld.so. Με μια αυθαίρετη εγγραφή είναι δυνατό να τον επαναγράψετε και να τον κάνετε να δείχνει σε ένα ψεύτικοfini_array
που ελέγχεται από τον επιτιθέμενο με τη διεύθυνση ενός one gadget για παράδειγμα.
Ακολουθώντας τον προηγούμενο κώδικα μπορείτε να βρείτε μια άλλη ενδιαφέρουσα ενότητα με τον κώδικα:
/* 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));
}
Σε αυτή την περίπτωση, θα ήταν δυνατό να επαναγραφεί η τιμή του map->l_info[DT_FINI]
που δείχνει σε μια πλαστή δομή ElfW(Dyn)
. Βρείτε περισσότερες πληροφορίες εδώ.
TLS-Storage dtor_list επαναγραφή στο __run_exit_handlers
Όπως εξηγείται εδώ, αν ένα πρόγραμμα τερματίσει μέσω return
ή exit()
, θα εκτελέσει __run_exit_handlers()
το οποίο θα καλέσει οποιαδήποτε συνάρτηση καταστροφέα έχει καταχωρηθεί.
Κώδικας από _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 ();
Κώδικας από __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);
[...]
}
}
Για κάθε καταχωρημένη συνάρτηση στη tls_dtor_list
, θα αποσυμπιέσει τον δείκτη από το cur->func
και θα την καλέσει με το επιχείρημα cur->obj
.
Χρησιμοποιώντας τη συνάρτηση tls
από αυτό το fork του GEF, είναι δυνατόν να δούμε ότι στην πραγματικότητα η dtor_list
είναι πολύ κοντά στο stack canary και το PTR_MANGLE cookie. Έτσι, με μια υπερχείλιση σε αυτό, θα ήταν δυνατό να επικαλυφθεί το cookie και το stack canary.
Επικαλύπτοντας το PTR_MANGLE cookie, θα ήταν δυνατό να παρακαμφθεί η συνάρτηση PTR_DEMANLE
ρυθμίζοντάς την σε 0x00, που σημαίνει ότι το xor
που χρησιμοποιείται για να αποκτήσει τη πραγματική διεύθυνση είναι απλώς η διεύθυνση που έχει ρυθμιστεί. Στη συνέχεια, γράφοντας στη dtor_list
είναι δυνατό να αλυσιδωθούν πολλές συναρτήσεις με τη διεύθυνση της συνάρτησης και το επιχείρημά της.
Τέλος, σημειώστε ότι ο αποθηκευμένος δείκτης δεν θα xored μόνο με το cookie αλλά και θα περιστραφεί 17 bits:
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
Πρέπει να λάβετε υπόψη αυτό πριν προσθέσετε μια νέα διεύθυνση.
Βρείτε ένα παράδειγμα στην αρχική ανάρτηση.
Άλλοι παραμορφωμένοι δείκτες στο __run_exit_handlers
Αυτή η τεχνική είναι εξηγημένη εδώ και εξαρτάται ξανά από το πρόγραμμα να τερματίζει καλώντας return
ή exit()
ώστε να κληθεί __run_exit_handlers()
.
Ας ελέγξουμε περισσότερος κώδικας αυτής της συνάρτησης:
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);
Η μεταβλητή f
δείχνει στη initial
δομή και ανάλογα με την τιμή του f->flavor
θα κληθούν διαφορετικές συναρτήσεις.
Ανάλογα με την τιμή, η διεύθυνση της συνάρτησης που θα κληθεί θα είναι σε διαφορετική θέση, αλλά θα είναι πάντα demangled.
Επιπλέον, στις επιλογές ef_on
και ef_cxa
είναι επίσης δυνατός ο έλεγχος ενός argument.
Είναι δυνατόν να ελέγξετε τη initial
δομή σε μια συνεδρία αποσφαλμάτωσης με το GEF να τρέχει gef> p initial
.
Για να εκμεταλλευτείτε αυτό, χρειάζεται είτε να leak ή να διαγράψετε το PTR_MANGLE
cookie και στη συνέχεια να αντικαταστήσετε μια είσοδο cxa
στην αρχική με system('/bin/sh')
.
Μπορείτε να βρείτε ένα παράδειγμα αυτού στο original blog post about the technique.
tip
Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.