Release date: Sunday, July 31, 2022
Elf file uses to relocation dynamically linked functions. It is the core of ret2dlresolve attack: _dl_runtime_resolve(link_map, reloc_offset)
.
.rel.plt
.dynsym
.dynstr
.rel.plt
itemsBecause of resolve process according to the name of the symbol. Therefore, by changing the string corresponding to a function in the string table to the string corresponding to the target function. Howerver, .dynstr
and code are mapped together are read-only, similarly with .dynsym
and .rel.plt
.
However, if we can control the flow of program execution, then we can forge a suitable relocation offset. (This approach is cubersome)
.rel.plt
itemsIf we can modify the content in dynamic section, it is naturally easy to control the string corresponding to the symbol to be parsed.
link-map
Dynamic connectors primarily rely on link_map
to query related addresses when resolving symbolic addresses.
main.c
gcc -fno-stack-protector -m32 -z norelro -no-pie main.c -o main_norelro_32
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
.dynamic
section to a fake addr.read/gets... (corresponding function)
string with the system string./bin/sh
string at a specific location.read/gets... (corresponding function)
_dl_runtime_resolve
triggers function parsing.0CTF18 file
Because of Partial RELRO, .dynamic
section has read-only perm
obviously, it’s a trivial bof, but we don’t have any emit funcs to leak. No libc provided, so no offset calculation possible.
Stores a table called Relocation table
. Each entry maps to a symbol.
The type of these entries is Elf32_Rel
, which is defined as it follows. The size of one entry is 8 bytes.
Let's take a look at our table:
read@GLIBC_2.0
;0x0804a00c
;ELF32_R_SYM
or ELF32_R_TYPE
;According to the defined MACROS, ELF32_R_SYM(r_info) == 1
and ELF32_R_TYPE(r_info) == 7 (R_386_JUMP_SLOT)
. Keep in mind that R_SYM
is 1, we will use it later.
STRTAB is a simple table that stores the strings for symbols name.
This table holds relevant symbol information. Each entry is a Elf32_Sym
structure and its size is 16
bytes.
The first field, st_name
, gives the offset in STRTAB
where the name of the symbol begins. The other fields of this structure are not used in the exploit, so I will ignore them. The ELF32_R_SYM(r_info) == 1
variable (which we got from the JMPREL table) gives the index of the Elf32_Sym
in SYMTAB for the specified symbol. In this particular case, index is 1
. Let's analyze this entry.
Adding the first dword
from elf32_sym to STRTAB gives the address of the symbol name.
call read@plt
, the program read GOT val frrom (0x804a00c) and jmp back into PLT section.relog_arg
/rel_offset
) to stack.link_map
) and jmps to resolver.The process specified above is equivalent to the following function call: _dl_runtime_resolve (link_map , rel_offset
/relog_arg)
The rel_offset
gives the offset of the Elf32_Rel
in JMPREL table. Link_map
(0x804a004) is nothing but a list with all the loaded libraries.
_dl_runtime_resolve
uses this list to resolve the symbol. After relocating the symbol and its entry in SYMTAB populated, the initial call of read will be invoked. The pseudocode below summarize the process described until now:
Elf32_Rel
r_offset
writable (after resolving symbol write the actual address of function)r_info
high 24 bits
(r_info >> 8) * 16
point to fake Elf32_Sym
(16 is size of Elf32_Sym
)r_info
low 8 bits
0x07
(R_386_JMP_SLOT)Elf32_Sym
.dynstr + st_name
point to system
stringRead the fake Elf32_Rel
、Elf32_Sym
structures and ret2main to call _dl_runtime_resolve
.
plt0
We can calculate the reloc_arg
to make .rel.plt + reloc_arg
point to our fake structures and jump to plt0
, let it resolve symbol to system
.
After resolving the symbol, _dl_runtime_resolve
will call the function.
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
JMPREL (.rela.plt): It contains information used by the linker to perform relocations. It’s composed by 24 aligned Elf64_Rel structures.
According IDA, the offset of the write function in the symbol table is 2 (0x200000007h>>32).
DYNSYM (.dynsym): Contains a symbol table. It’s composed by 0x18 aligned Elf64_Sym structures. Every structure associates a symbolic name with a piece of code elsewhere in the binary.
https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/advanced-rop/ret2dlresolve/
research
pwn