mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
227 lines
11 KiB
Markdown
227 lines
11 KiB
Markdown
# WWW2Exec - atexit(), TLS Berging & Ander gemanipuleerde Pointers
|
|
|
|
{{#include ../../banners/hacktricks-training.md}}
|
|
|
|
## **\_\_atexit Strukture**
|
|
|
|
> [!CAUTION]
|
|
> Vandag is dit baie **vreemd om dit te benut!**
|
|
|
|
**`atexit()`** is 'n funksie waaraan **ander funksies as parameters deurgegee word.** Hierdie **funksies** sal **uitgevoer** word wanneer 'n **`exit()`** of die **terugkeer** van die **main** uitgevoer word.\
|
|
As jy die **adres** van enige van hierdie **funksies** kan **wysig** om na 'n shellcode te verwys, sal jy **beheer** oor die **proses** verkry, maar dit is tans meer ingewikkeld.\
|
|
Tans is die **adresse na die funksies** wat uitgevoer moet word **versteek** agter verskeie strukture en uiteindelik is die adres waaraan dit verwys nie die adresse van die funksies nie, maar is **geënkripteer met XOR** en verskuiwings met 'n **willekeurige sleutel**. So tans is hierdie aanvalsvector **nie baie nuttig nie, ten minste op x86** en **x64_86**.\
|
|
Die **enkripsiefunksie** is **`PTR_MANGLE`**. **Ander argitekture** soos m68k, mips32, mips64, aarch64, arm, hppa... **implementeer nie die enkripsie** funksie nie omdat dit **diezelfde** teruggee as wat dit as invoer ontvang. So hierdie argitekture sou deur hierdie vektor aangeval kon word.
|
|
|
|
Jy kan 'n diepgaande verduideliking vind oor hoe dit werk in [https://m101.github.io/binholic/2017/05/20/notes-on-abusing-exit-handlers.html](https://m101.github.io/binholic/2017/05/20/notes-on-abusing-exit-handlers.html)
|
|
|
|
## link_map
|
|
|
|
Soos verduidelik [**in hierdie pos**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#2---targetting-ldso-link_map-structure), As die program verlaat deur `return` of `exit()` sal dit `__run_exit_handlers()` uitvoer wat geregistreerde vernietigers sal aanroep.
|
|
|
|
> [!CAUTION]
|
|
> As die program verlaat via **`_exit()`** funksie, sal dit die **`exit` syscall** aanroep en die uitgangshandelaars sal nie uitgevoer word nie. So, om te bevestig dat `__run_exit_handlers()` uitgevoer word, kan jy 'n breekpunt daarop stel.
|
|
|
|
Die belangrike kode is ([source](https://elixir.bootlin.com/glibc/glibc-2.32/source/elf/dl-fini.c#L131)):
|
|
```c
|
|
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
|
|
}
|
|
```
|
|
Let op hoe `map -> l_addr + fini_array -> d_un.d_ptr` gebruik word om die **posisie** van die **array van funksies om aan te roep** te **bereken**.
|
|
|
|
Daar is 'n **paar opsies**:
|
|
|
|
- Oorskryf die waarde van `map->l_addr` om dit na 'n **valse `fini_array`** met instruksies om arbitrêre kode uit te voer, te laat wys.
|
|
- Oorskryf `l_info[DT_FINI_ARRAY]` en `l_info[DT_FINI_ARRAYSZ]` inskrywings (wat meer of minder aaneengeskakeld in geheue is), om hulle **na 'n vervalste `Elf64_Dyn`** struktuur te laat wys wat weer **`array` na 'n geheue** gebied sal laat wys wat die aanvaller beheer.
|
|
- [**Hierdie skrywe**](https://github.com/nobodyisnobody/write-ups/tree/main/DanteCTF.2023/pwn/Sentence.To.Hell) oorskryf `l_info[DT_FINI_ARRAY]` met die adres van 'n beheerde geheue in `.bss` wat 'n valse `fini_array` bevat. Hierdie valse array bevat **eers 'n** [**one gadget**](../rop-return-oriented-programing/ret2lib/one-gadget.md) **adres** wat uitgevoer sal word en dan die **verskil** tussen die adres van hierdie **valse array** en die **waarde van `map->l_addr`** sodat `*array` na die valse array sal wys.
|
|
- Volgens die hoofpos van hierdie tegniek en [**hierdie skrywe**](https://activities.tjhsst.edu/csc/writeups/angstromctf-2021-wallstreet) laat ld.so 'n wysser op die stapel wat na die binêre `link_map` in ld.so wys. Met 'n arbitrêre skrywe is dit moontlik om dit oor te skryf en dit na 'n valse `fini_array` te laat wys wat deur die aanvaller beheer word met die adres na 'n [**one gadget**](../rop-return-oriented-programing/ret2lib/one-gadget.md) byvoorbeeld.
|
|
|
|
Volg die vorige kode kan jy 'n ander interessante afdeling met die kode vind:
|
|
```c
|
|
/* 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));
|
|
}
|
|
```
|
|
In hierdie geval sal dit moontlik wees om die waarde van `map->l_info[DT_FINI]` te oorskryf wat na 'n vervalste `ElfW(Dyn)` struktuur wys. Vind [**meer inligting hier**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#2---targetting-ldso-link_map-structure).
|
|
|
|
## TLS-Storage dtor_list oorskrywing in **`__run_exit_handlers`**
|
|
|
|
Soos [**hier verduidelik**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#5---code-execution-via-tls-storage-dtor_list-overwrite), as 'n program verlaat via `return` of `exit()`, sal dit **`__run_exit_handlers()`** uitvoer wat enige geregistreerde vernietigersfunksies sal aanroep.
|
|
|
|
Kode van `_run_exit_handlers()`:
|
|
```c
|
|
/* 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 ();
|
|
```
|
|
Kode van **`__call_tls_dtors()`**:
|
|
```c
|
|
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);
|
|
[...]
|
|
}
|
|
}
|
|
```
|
|
Vir elke geregistreerde funksie in **`tls_dtor_list`**, sal dit die pointer van **`cur->func`** demangle en dit aanroep met die argument **`cur->obj`**.
|
|
|
|
Met die **`tls`** funksie van hierdie [**fork van GEF**](https://github.com/bata24/gef), is dit moontlik om te sien dat die **`dtor_list`** eintlik baie **naby** die **stack canary** en **PTR_MANGLE cookie** is. So, met 'n oorgang op dit, sou dit moontlik wees om die **cookie** en die **stack canary** te **oorwrite**.\
|
|
Deur die PTR_MANGLE cookie te oorskry, sou dit moontlik wees om die **`PTR_DEMANLE` funksie** te **bypass** deur dit op 0x00 te stel, wat beteken dat die **`xor`** wat gebruik word om die werklike adres te kry net die adres is wat geconfigureer is. Dan, deur op die **`dtor_list`** te skryf, is dit moontlik om **verskeie funksies** te **ketting** met die funksie **adres** en sy **argument.**
|
|
|
|
Laastens, let daarop dat die gestoor pointer nie net met die cookie xored gaan word nie, maar ook 17 bits gedraai sal word:
|
|
```armasm
|
|
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
|
|
```
|
|
So jy moet dit in ag neem voordat jy 'n nuwe adres byvoeg.
|
|
|
|
Vind 'n voorbeeld in die [**oorspronklike pos**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#5---code-execution-via-tls-storage-dtor_list-overwrite).
|
|
|
|
## Ander gemanipuleerde punte in **`__run_exit_handlers`**
|
|
|
|
Hierdie tegniek is [**hier verduidelik**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#5---code-execution-via-tls-storage-dtor_list-overwrite) en hang weer af van die program **wat verlaat deur `return` of `exit()` aan te roep** sodat **`__run_exit_handlers()`** aangeroep word.
|
|
|
|
Kom ons kyk na meer kode van hierdie funksie:
|
|
```c
|
|
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);
|
|
```
|
|
Die veranderlike `f` verwys na die **`initial`** struktuur en afhangende van die waarde van `f->flavor` sal verskillende funksies aangeroep word.\
|
|
Afhangende van die waarde, sal die adres van die funksie om aan te roep in 'n ander plek wees, maar dit sal altyd **demangled** wees.
|
|
|
|
Boonop is dit in die opsies **`ef_on`** en **`ef_cxa`** ook moontlik om 'n **argument** te beheer.
|
|
|
|
Dit is moontlik om die **`initial` struktuur** in 'n foutopsporing sessie met GEF wat **`gef> p initial`** loop, te kontroleer.
|
|
|
|
Om dit te misbruik, moet jy ofwel die **leak of die `PTR_MANGLE`cookie** verwyder en dan 'n `cxa` inskrywing in initial met `system('/bin/sh')` oorskryf.\
|
|
Jy kan 'n voorbeeld hiervan in die [**oorspronklike blogpos oor die tegniek**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#6---code-execution-via-other-mangled-pointers-in-initial-structure) vind.
|
|
|
|
{{#include ../../banners/hacktricks-training.md}}
|