diff options
| author | jakob <jakob@memeware.net> | 2017-09-10 14:12:54 -0400 |
|---|---|---|
| committer | jakob <jakob@memeware.net> | 2017-09-10 14:12:54 -0400 |
| commit | d801868006f94acc6e3313332c55331710171172 (patch) | |
| tree | 9748047178f5503b198d52d07db75ded2e95c73c /hypodermic | |
| parent | 81e47a4dba4a44624ec56708c1655206ae19cbfc (diff) | |
Initial invocation of _dl_open in the RTLD
Diffstat (limited to 'hypodermic')
| -rw-r--r-- | hypodermic/main.py | 4 | ||||
| -rw-r--r-- | hypodermic/process.py | 74 | ||||
| -rw-r--r-- | hypodermic/shellcode.py | 44 |
3 files changed, 118 insertions, 4 deletions
diff --git a/hypodermic/main.py b/hypodermic/main.py index cb17d3b..3cbb047 100644 --- a/hypodermic/main.py +++ b/hypodermic/main.py @@ -108,8 +108,8 @@ def main(): if args.create: alert("Creating process at path '{}'...".format(args.create)) p = Process(path=args.create) - p.continue_until_haulted() else: alert("Attaching to process with pid {}...".format(args.attach)) p = Process(pid=args.attach) - p.continue_until_haulted() + print(p.dlopen("/usr/lib/libyggdrasil.so")) + p.continue_until_haulted() diff --git a/hypodermic/process.py b/hypodermic/process.py index e26978b..35b8c1d 100644 --- a/hypodermic/process.py +++ b/hypodermic/process.py @@ -18,11 +18,16 @@ """ctypes wrapper for ptrace.""" import ctypes -import os.path +import os import re +from elftools.elf.elffile import ELFFile +from elftools.elf.sections import SymbolTableSection + from hypodermic.memory import Region, maps -from hypodermic.shellcode import assemble, open_shellcode +from hypodermic.shellcode import (assemble, close_shellcode, dlopen_shellcode, + mmap_shellcode, munmap_shellcode, + open_shellcode) _AMD64_INDICES = { "r15": 0, @@ -436,6 +441,35 @@ class Process(object): else: self.run_code(munmap_shellcode(addr, size, arch="i386")) + def dlopen(self, path: str) -> int: + """Maps a shared object into the process address space. + + Args: + path (str): The path of the shared object to inject. + + Raises: + OSError: If the process does not have a usable RTLD. + + Returns: + The address at which the library was loaded. + """ + if not self.rtld: + raise OSError("Process does not have a usable RTLD") + + if self.arch == "x64": + old_rax = self.get_register("rax") + self.run_code(dlopen_shellcode(self.rtld_dl_open_addr, path), + preserve=["rax"]) + addr = self.get_register("rax") + self.set_register("rax", old_rax) + else: + old_eax = self.get_register("eax") + self.run_code(dlopen_shellcode(self.rtld_dl_open_addr, path, + arch="i386"), preserve=["eax"]) + addr = self.get_register("eax") + self.set_register("eax", old_eax) + return addr + def page_start(self, addr: int) -> int: return addr & ~(self.page_size - 1) @@ -467,6 +501,32 @@ class Process(object): return "x64" if self._isamd64 else "x86" @property + def rtld_dl_open_addr(self) -> int: + """Obtain the absolute address of _dl_open in main memory. + + Raises: + OSError: If either the process has no instance of the RTLD, + or the RTLD lacks sufficient symbols. + + Returns: + An integer containing the address. + """ + if self.rtld is None: + raise OSError("Process has no RTLD instance") + + with open(self.rtld.path, "rb") as rtld: + elf = ELFFile(rtld) + symtab = elf.get_section_by_name(".symtab") + + if not isinstance(symtab, SymbolTableSection): + raise OSError("RTLD has no usable symbol table") + + res = symtab.get_symbol_by_name("_dl_open") + if len(res) < 1: + raise OSError("RTLD has no _dl_open symbol") + return self.rtld.start + res[0].entry.st_value + + @property def maps(self) -> list: """Obtain the process' memory map. @@ -475,6 +535,16 @@ class Process(object): """ return maps(self.pid) + # TODO: Return something more detailed than the list of open fds. + @property + def fds(self) -> list: + """Obtain currently open file descriptors for the process. + + Returns: + A list of integer file descriptors. + """ + return [int(fd) for fd in os.listdir("/proc/{}/fd/".format(self.pid))] + @property def rtld(self) -> Region: """Obtain the base region of memory for the process' RTLD, if it diff --git a/hypodermic/shellcode.py b/hypodermic/shellcode.py index ceaf7a7..114a4bc 100644 --- a/hypodermic/shellcode.py +++ b/hypodermic/shellcode.py @@ -162,3 +162,47 @@ def munmap_shellcode(addr=0, size=0, arch="amd64"): " movl ${}, %ecx;" \ " int $0x80;".format(addr, size) return assemble(asm, arch) + + +# FIXME: Relative addressing is untested in i386. +def dlopen_shellcode(addr: int, path: str, arch="amd64"): + """Generates shellcode to invoke _dl_open in the RTLD. + + Args: + addr (int): The absolute address of _dl_open. + path (str): The path of the library to open. + + Returns: + The assembled shellcode, as a `bytes` object. + """ + if arch == "amd64": + asm = " jmp __path_end;" \ + "__path:" \ + " .asciz \"{}\";" \ + "__path_end:" \ + " leaq (%rip), %rdi;" \ + " subq $. - __path, %rdi;" \ + " movq $0x80000101, %rsi;" \ + " movq $0x00, %rdx;" \ + " movq $0x00, %rcx;" \ + " movq $0x00, %r8;" \ + " movq $0x00, %r9;" \ + " pushq $0x00;" \ + " callq ${};".format(path, addr) + else: + asm = " jmp __path_end;" \ + "__path:" \ + " .asciz \"{}\";" \ + "__path_end:" \ + " call $. + 5;" \ + " popl %ebx;" \ + " subl $. - 4 - __path, %ebx;" \ + " pushl %ebx;" \ + " pushl $0x80000101;" \ + " pushl $0x00;" \ + " pushl $0x00;" \ + " pushl $0x00;" \ + " pushl $0x00;" \ + " pushl $0x00;" \ + " calll ${};".format(path, addr) + return assemble(asm, arch) |