# WWW2Exec - .dtors & .fini\_array

<details>

<summary><strong>从零开始学习AWS黑客技术，成为专家</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>！</strong></summary>

其他支持HackTricks的方式：

* 如果您想看到您的**公司在HackTricks中做广告**或**下载PDF格式的HackTricks**，请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
* 获取[**官方PEASS & HackTricks周边产品**](https://peass.creator-spring.com)
* 探索[**PEASS家族**](https://opensea.io/collection/the-peass-family)，我们的独家[**NFTs**](https://opensea.io/collection/the-peass-family)收藏品
* **加入** 💬 [**Discord群**](https://discord.gg/hRep4RUj7f) 或 [**电报群**](https://t.me/peass) 或 **关注**我们的**Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks_live)**。**
* 通过向[**HackTricks**](https://github.com/carlospolop/hacktricks)和[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。

</details>

## .dtors

{% hint style="danger" %}
现在很**奇怪找到一个带有.dtors部分的二进制文件！**
{% endhint %}

析构函数是在程序结束之前（在`main`函数返回后）执行的函数。\
这些函数的地址存储在二进制文件的\*\*`.dtors`**部分中，因此，如果您设法**写入**一个**shellcode**的**地址**到**`__DTOR_END__`**，那么它将在程序结束之前被**执行\*\*。

使用以下命令获取此部分的地址：

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

通常你会在值`ffffffff`和`00000000`之间找到**DTOR**标记。所以如果你只看到这些值，意味着**没有任何函数注册**。因此，**覆盖**`00000000`的**地址**为**shellcode**的地址以执行它。

{% hint style="warning" %}
当然，你首先需要找到一个**存储shellcode的位置**，以便稍后调用它。
{% endhint %}

## **.fini\_array**

本质上，这是一个包含在程序完成之前将被调用的**函数的结构**，就像\*\*`.dtors`**一样。如果你可以通过**跳转到一个地址调用你的shellcode\*\*，或者在需要**返回到`main`以第二次利用漏洞**的情况下，这就变得很有趣。

```bash
objdump -s -j .fini_array ./greeting

./greeting:     file format elf32-i386

Contents of section .fini_array:
8049934 a0850408

#Put your address in 0x8049934
```

请注意，当从 **`.fini_array`** 中的一个函数执行时，它会移动到下一个函数，因此不会被多次执行（防止无限循环），但它也只会给你提供一个在这里放置的 **函数执行**。

请注意，`.fini_array` 中的条目按**相反**顺序调用，因此您可能希望从最后一个开始编写。

#### 无限循环

为了滥用 **`.fini_array`** 以获得无限循环，您可以[**检查这里做了什么**](https://guyinatuxedo.github.io/17-stack_pivot/insomnihack18_onewrite/index.html)**：** 如果 **`.fini_array`** 中至少有2个条目，您可以：

* 使用第一个写入来再次调用**易受攻击的任意写入函数**
* 然后，在由 **`__libc_csu_fini`** 存储的堆栈中计算 **返回地址**，并将 **`__libc_csu_fini`** 的地址放在那里
* 这将使 **`__libc_csu_fini`** 再次调用自身，执行 **`.fini_array`** 函数，再次调用易受攻击的 WWW 函数2次：一次用于**任意写入**，另一次用于再次覆盖堆栈上 **`__libc_csu_fini`** 的**返回地址**，以再次调用自身。

{% hint style="danger" %}
请注意，使用[**Full RELRO**](/binary-exploitation/common-binary-protections-and-bypasses/relro.md)**，** **`.fini_array`** 部分被设置为**只读**。
{% endhint %}

## link\_map

如[**此帖子**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#2---targetting-ldso-link_map-structure)中所解释的，如果程序使用 `return` 或 `exit()` 退出，它将运行 `__run_exit_handlers()`，该函数将调用已注册的析构函数。

{% hint style="danger" %}
如果程序通过 **`_exit()`** 函数退出，它将调用 **`exit` 系统调用**，退出处理程序将不会被执行。因此，要确认 `__run_exit_handlers()` 是否被执行，您可以在其上设置断点。
{% endhint %}

重要代码为（[源代码](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
}
```

注意`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`指向攻击者控制的内存区域\*\*。
* [**这篇文章**](https://github.com/nobodyisnobody/write-ups/tree/main/DanteCTF.2023/pwn/Sentence.To.Hell)用受控内存中`.bss`中的地址覆盖了`l_info[DT_FINI_ARRAY]`，其中包含一个伪造的`fini_array`。这个伪造数组首先包含一个[**one gadget**](/binary-exploitation/rop-return-oriented-programing/ret2lib/one-gadget.md)的地址，然后是这个**伪造数组**地址与`map->l_addr`值之间的**差异**，以便`*array`指向伪造数组。
* 根据这种技术的主要帖子和[**这篇文章**](https://activities.tjhsst.edu/csc/writeups/angstromctf-2021-wallstreet)，ld.so在栈上留下一个指向ld.so中的二进制`link_map`的指针。通过任意写入，可以覆盖它并使其指向由攻击者控制的伪造`fini_array`，其中包含一个[**one gadget**](/binary-exploitation/rop-return-oriented-programing/ret2lib/one-gadget.md)的地址，例如。

在前面的代码之后，您可以找到另一个有趣的部分，其中包含以下代码：

```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));
}
```

在这种情况下，可以覆盖`map->l_info[DT_FINI]`的值，指向一个伪造的`ElfW(Dyn)`结构。在[**这里找到更多信息**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#2---targetting-ldso-link_map-structure)。

## 在\*\*`__run_exit_handlers`\*\*中覆盖TLS存储dtor\_list

如[**这里解释的**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#5---code-execution-via-tls-storage-dtor_list-overwrite)，如果程序通过`return`或`exit()`退出，它将执行\*\*`__run_exit_handlers()`\*\*，该函数将调用任何已注册的析构函数。

来自`_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 ();
```

**`__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);
[...]
}
}
```

对于\*\*`tls_dtor_list`**中的每个注册函数，它将从**`cur->func`**中解码指针并使用参数**`cur->obj`\*\*调用它。

使用来自[**GEF的这个分支**](https://github.com/bata24/gef)中的\*\*`tls`**函数，可以看到**`dtor_list`**实际上非常**接近**栈保护和PTR\_MANGLE cookie\*\*。因此，通过对其进行溢出，可以\*\*覆盖**cookie**和**栈保护\*\*。\
覆盖PTR\_MANGLE cookie，将有可能**绕过`PTR_DEMANLE`函数**，因为将其设置为0x00，将意味着用于获取真实地址的\*\*`xor`**仅是配置的地址。然后，在**`dtor_list`**上写入，可以使用函数**地址**及其**参数**来**链接多个函数\*\*。

最后请注意，存储的指针不仅会与cookie进行异或运算，还会旋转17位：

```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
```

在添加新地址之前，您需要考虑这一点。

在[**原始帖子**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#5---code-execution-via-tls-storage-dtor_list-overwrite)中找一个例子。

## **`__run_exit_handlers`** 中的其他混淆指针

这种技术在[**这里解释**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#5---code-execution-via-tls-storage-dtor_list-overwrite)，再次取决于程序**通过调用 `return` 或 `exit()` 退出**，从而调用 **`__run_exit_handlers()`**。

让我们检查一下这个函数的更多代码：

```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);
```

变量`f`指向\*\*`initial`**结构，根据`f->flavor`的值不同，将调用不同的函数。**\
**根据数值不同，要调用的函数地址会在不同的位置，但它们始终会被**解码\*\*。

此外，在选项\*\*`ef_on`**和**`ef_cxa`**中，还可以控制一个**参数\*\*。

可以在使用GEF运行\*\*`gef> p initial`**的调试会话中检查**`initial`结构\*\*。

要利用这一点，您需要**泄漏或擦除`PTR_MANGLE`cookie**，然后用`system('/bin/sh')`覆盖`initial`中的`cxa`条目。\
您可以在[**有关该技术的原始博客文章**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#6---code-execution-via-other-mangled-pointers-in-initial-structure)中找到一个示例。

<details>

<summary><strong>从零开始学习AWS黑客技术，成为专家</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>！</strong></summary>

支持HackTricks的其他方式：

* 如果您想看到您的**公司在HackTricks中做广告**或**下载PDF格式的HackTricks**，请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
* 获取[**官方PEASS & HackTricks周边产品**](https://peass.creator-spring.com)
* 发现[**PEASS家族**](https://opensea.io/collection/the-peass-family)，我们独家的[NFTs](https://opensea.io/collection/the-peass-family)收藏品
* **加入** 💬 [**Discord群**](https://discord.gg/hRep4RUj7f) 或 [**电报群**](https://t.me/peass) 或在**Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks_live)**上关注**我们。
* 通过向[**HackTricks**](https://github.com/carlospolop/hacktricks)和[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hacktricks.xsx.tw/binary-exploitation/write-what-where-2-exec/www2exec-.dtors-and-.fini_array.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
