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 | |
| parent | 81e47a4dba4a44624ec56708c1655206ae19cbfc (diff) | |
Initial invocation of _dl_open in the RTLD
| -rw-r--r-- | README.md | 7 | ||||
| -rw-r--r-- | hypodermic/main.py | 4 | ||||
| -rw-r--r-- | hypodermic/process.py | 74 | ||||
| -rw-r--r-- | hypodermic/shellcode.py | 44 | ||||
| -rw-r--r-- | tests/test_process.py | 5 |
5 files changed, 126 insertions, 8 deletions
@@ -33,10 +33,9 @@ routines. This did not work, as the process of loading an ELF library into memory is far more complicated than calling mmap(2) on the file. The second iteration also involves injecting code into the inferior process, but -instead maps the Linux runtime linker into memory. This is difficult, as it -means mapping it the way the kernel would. This involves injecting auxiliary -vectors onto the stack in an attempt to trick it into loading the desired -libraries. +instead maps the Linux runtime linker into memory, if it is not already there, +and utilizes the internal _dl_open routine. This is difficult, as it means +mapping it the way the kernel would to ensure proper initialization of the RTLD. ## Important Resources 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) diff --git a/tests/test_process.py b/tests/test_process.py new file mode 100644 index 0000000..276f6f5 --- /dev/null +++ b/tests/test_process.py @@ -0,0 +1,5 @@ +from hypodermic.process import Process + + +def test_create_process(): + p = Process(path="/bin/ls") |