def recvline_endswith(self, delims, keepends=False, timeout=default): r"""recvline_endswith(delims, keepends=False, timeout=default) -> bytes Keep receiving lines until one is found that starts with one of `delims`. Returns the last line received. If the request is not satisfied before ``timeout`` seconds pass, all data is buffered and an empty string (``''``) is returned. See :meth:`recvline_startswith` for more details. Examples: >>> t = tube() >>> t.recv_raw = lambda n: b'Foo\nBar\nBaz\nKaboodle\n' >>> t.recvline_endswith(b'r') b'Bar' >>> t.recvline_endswith((b'a',b'b',b'c',b'd',b'e'), True) b'Kaboodle\n' >>> t.recvline_endswith(b'oodle') b'Kaboodle' """ # Convert string into singleton tupple if isinstance(delims, (bytes, six.text_type)): delims = (delims, ) delims = tuple( context._encode(delim) + self.newline for delim in delims) return self.recvline_pred(lambda line: any(map(line.endswith, delims)), keepends=keepends, timeout=timeout)
def recvline_regex(self, regex, exact=False, keepends=False, timeout=default): """recvline_regex(regex, exact=False, keepends=False, timeout=default) -> bytes Wrapper around :func:`recvline_pred`, which will return when a regex matches a line. By default :func:`re.RegexObject.search` is used, but if `exact` is set to True, then :func:`re.RegexObject.match` will be used instead. If the request is not satisfied before ``timeout`` seconds pass, all data is buffered and an empty string (``''``) is returned. """ if isinstance(regex, (bytes, six.text_type)): regex = context._encode(regex) regex = re.compile(regex) if exact: pred = regex.match else: pred = regex.search return self.recvline_pred(pred, keepends=keepends, timeout=timeout)
def __init__(self, elf, symbol, args, data_addr=None): self.elf = elf self.elf_load_address_fixup = self.elf.address - self.elf.load_addr self.strtab = elf.dynamic_value_by_tag( "DT_STRTAB") + self.elf_load_address_fixup self.symtab = elf.dynamic_value_by_tag( "DT_SYMTAB") + self.elf_load_address_fixup self.jmprel = elf.dynamic_value_by_tag( "DT_JMPREL") + self.elf_load_address_fixup self.versym = elf.dynamic_value_by_tag( "DT_VERSYM") + self.elf_load_address_fixup self.symbol = context._encode(symbol) self.args = args self.real_args = self._format_args() self.data_addr = data_addr if data_addr is not None else self._get_recommended_address( ) # Will be set when built self.reloc_index = -1 self.payload = b"" # PIE is untested, gcc forces FULL-RELRO when PIE is set if self.elf.pie and self.elf_load_address_fixup == 0: log.warning("WARNING: ELF is PIE but it has not base address") self._build()
def resolve(self, addr=None): """ Return a flat list of ``int`` or ``str`` objects which can be passed to :func:`.flat`. Arguments: addr(int): Address at which the data starts in memory. If :const:`None`, ``self.addr`` is used. """ if addr is None: addr = self.address with self.local(addr): self.address = addr rv = [None] * len(self.values) for i, value in enumerate(self.values): if isinstance(value, six.integer_types): rv[i] = value if isinstance(value, str): value = context._encode(value) if isinstance(value, (bytes, bytearray)): value += b'\x00' while len(value) % context.bytes: value += b'$' rv[i] = value if isinstance(value, Unresolved): rv[i] = value.address rv.extend(value.resolve()) return rv
def send(self, data): """send(data) Sends data. If log level ``DEBUG`` is enabled, also prints out the data received. If it is not possible to send anymore because of a closed connection, it raises ``exceptions.EOFError`` Examples: >>> def p(x): print(repr(x)) >>> t = tube() >>> t.send_raw = p >>> t.send(b'hello') b'hello' """ data = context._encode(data) if self.isEnabledFor(logging.DEBUG): self.debug('Sent %#x bytes:' % len(data)) if len(set(data)) == 1: self.indented('%r * %#x' % (data[0], len(data))) elif all(c in string.printable.encode() for c in data): for line in data.splitlines(True): self.indented(repr(line), level=logging.DEBUG) else: self.indented(fiddling.hexdump(data), level=logging.DEBUG) self.send_raw(data)
def gnu_hash(s): """gnu_hash(str) -> int Function used to generated GNU-style hashes for strings. """ s = bytearray(context._encode(s)) h = 5381 for c in s: h = h * 33 + c return h & 0xffffffff
def sysv_hash(symbol): """sysv_hash(str) -> int Function used to generate SYSV-style hashes for strings. """ h = 0 g = 0 for c in bytearray(context._encode(symbol)): h = (h << 4) + c g = h & 0xf0000000 h ^= (g >> 24) h &= ~g return h & 0xffffffff
def _dynamic_load_dynelf(self, libname): """_dynamic_load_dynelf(libname) -> DynELF Looks up information about a loaded library via the link map. Arguments: libname(str): Name of the library to resolve, or a substring (e.g. 'libc.so') Returns: A DynELF instance for the loaded library, or None. """ cur = self.link_map leak = self.leak LinkMap = { 32: elf.Elf32_Link_Map, 64: elf.Elf64_Link_Map }[self.elfclass] # make sure we rewind to the beginning! while leak.field(cur, LinkMap.l_prev): cur = leak.field(cur, LinkMap.l_prev) libname = context._encode(libname) while cur: self.status("link_map entry %#x" % cur) p_name = leak.field(cur, LinkMap.l_name) name = leak.s(p_name) if libname in name: break if name: self.status('Skipping %s' % name) cur = leak.field(cur, LinkMap.l_next) else: self.failure("Could not find library with name containing %r" % libname) return None libbase = leak.field(cur, LinkMap.l_addr) self.status("Resolved library %r at %#x" % (libname, libbase)) lib = DynELF(leak, libbase) lib._dynamic = leak.field(cur, LinkMap.l_ld) lib._waitfor = self._waitfor return lib
def sendline(self, line=b''): r"""sendline(data) Shorthand for ``t.send(data + t.newline)``. Examples: >>> def p(x): print(repr(x)) >>> t = tube() >>> t.send_raw = p >>> t.sendline(b'hello') b'hello\n' >>> t.newline = b'\r\n' >>> t.sendline(b'hello') b'hello\r\n' """ line = context._encode(line) self.send(line + self.newline)
def unrecv(self, data): """unrecv(data) Puts the specified data back at the beginning of the receive buffer. Examples: >>> t = tube() >>> t.recv_raw = lambda n: b'hello' >>> t.recv() b'hello' >>> t.recv() b'hello' >>> t.unrecv(b'world') >>> t.recv() b'world' >>> t.recv() b'hello' """ data = context._encode(data) self.buffer.unget(data)
def attach(target, gdbscript = None, exe = None, need_ptrace_scope = True, gdb_args = None, ssh = None, sysroot = None): """attach(target, gdbscript = None, exe = None, arch = None, ssh = None) -> None Start GDB in a new terminal and attach to `target`. Arguments: target: The target to attach to. gdbscript(:obj:`str` or :obj:`file`): GDB script to run after attaching. exe(str): The path of the target binary. arch(str): Architechture of the target binary. If `exe` known GDB will detect the architechture automatically (if it is supported). gdb_args(list): List of additional arguments to pass to GDB. sysroot(str): Foreign-architecture sysroot, used for QEMU-emulated binaries and Android targets. Returns: PID of the GDB process (or the window which it is running in). Notes: The ``target`` argument is very robust, and can be any of the following: :obj:`int` PID of a process :obj:`str` Process name. The youngest process is selected. :obj:`tuple` Host, port pair of a listening ``gdbserver`` :class:`.process` Process to connect to :class:`.sock` Connected socket. The executable on the other end of the connection is attached to. Can be any socket type, including :class:`.listen` or :class:`.remote`. :class:`.ssh_channel` Remote process spawned via :meth:`.ssh.process`. This will use the GDB installed on the remote machine. If a password is required to connect, the ``sshpass`` program must be installed. Examples: .. code-block:: python # Attach directly to pid 1234 gdb.attach(1234) .. code-block:: python # Attach to the youngest "bash" process gdb.attach('bash') .. code-block:: python # Start a process bash = process('bash') # Attach the debugger gdb.attach(bash, ''' set follow-fork-mode child break execve continue ''') # Interact with the process bash.sendline('whoami') .. code-block:: python # Start a forking server server = process(['socat', 'tcp-listen:1234,fork,reuseaddr', 'exec:/bin/sh']) # Connect to the server io = remote('localhost', 1234) # Connect the debugger to the server-spawned process gdb.attach(io, ''' break exit continue ''') # Talk to the spawned 'sh' io.sendline('exit') .. code-block:: python # Connect to the SSH server shell = ssh('bandit0', 'bandit.labs.overthewire.org', password='******', port=2220) # Start a process on the server cat = shell.process(['cat']) # Attach a debugger to it gdb.attach(cat, ''' break exit continue ''') # Cause `cat` to exit cat.close() """ if context.noptrace: log.warn_once("Skipping debug attach since context.noptrace==True") return # if gdbscript is a file object, then read it; we probably need to run some # more gdb script anyway if hasattr(gdbscript, 'read'): with gdbscript: gdbscript = gdbscript.read() # enable gdb.attach(p, 'continue') if gdbscript and not gdbscript.endswith('\n'): gdbscript += '\n' # Use a sane default sysroot for Android if not sysroot and context.os == 'android': sysroot = 'remote:/' # gdb script to run before `gdbscript` pre = '' if not context.native: pre += 'set endian %s\n' % context.endian pre += 'set architecture %s\n' % get_gdb_arch() if sysroot: pre += 'set sysroot %s\n' % sysroot if context.os == 'android': pre += 'set gnutarget ' + _bfdname() + '\n' # let's see if we can find a pid to attach to pid = None if isinstance(target, six.integer_types): # target is a pid, easy peasy pid = target elif isinstance(target, str): # pidof picks the youngest process pidof = proc.pidof if context.os == 'android': pidof = adb.pidof pids = pidof(target) if not pids: log.error('No such process: %s' % target) pid = pids[0] log.info('Attaching to youngest process "%s" (PID = %d)' % (target, pid)) elif isinstance(target, tubes.ssh.ssh_channel): if not target.pid: log.error("PID unknown for channel") shell = target.parent tmpfile = shell.mktemp() gdbscript = 'shell rm %s\n%s' % (tmpfile, gdbscript) shell.upload_data(context._encode(gdbscript), tmpfile) cmd = ['ssh', '-C', '-t', '-p', str(shell.port), '-l', shell.user, shell.host] if shell.password: if not misc.which('sshpass'): log.error("sshpass must be installed to debug ssh processes") cmd = ['sshpass', '-p', shell.password] + cmd if shell.keyfile: cmd += ['-i', shell.keyfile] cmd += ['gdb -q %r %s -x "%s"' % (target.executable, target.pid, tmpfile)] misc.run_in_new_terminal(' '.join(cmd)) return elif isinstance(target, tubes.sock.sock): pids = proc.pidof(target) if not pids: log.error('could not find remote process (%s:%d) on this machine' % target.sock.getpeername()) pid = pids[0] elif isinstance(target, tubes.process.process): pid = proc.pidof(target)[0] exe = exe or target.executable elif isinstance(target, tuple) and len(target) == 2: host, port = target if context.os != 'android': pre += 'target remote %s:%d\n' % (host, port) else: # Android debugging is done over gdbserver, which can't follow # new inferiors (tldr; follow-fork-mode child) unless it is run # in extended-remote mode. pre += 'target extended-remote %s:%d\n' % (host, port) pre += 'set detach-on-fork off\n' def findexe(): for spid in proc.pidof(target): sexe = proc.exe(spid) name = os.path.basename(sexe) # XXX: parse cmdline if name.startswith('qemu-') or name.startswith('gdbserver'): exe = proc.cmdline(spid)[-1] return os.path.join(proc.cwd(spid), exe) exe = exe or findexe() elif isinstance(target, elf.corefile.Corefile): pre += 'target core %s\n' % target.path else: log.error("don't know how to attach to target: %r" % target) # if we have a pid but no exe, just look it up in /proc/ if pid and not exe: exe_fn = proc.exe if context.os == 'android': exe_fn = adb.proc_exe exe = exe_fn(pid) if not pid and not exe: log.error('could not find target process') if exe: # The 'file' statement should go first pre = 'file "%s"\n%s' % (exe, pre) cmd = binary() if gdb_args: cmd += ' ' cmd += ' '.join(gdb_args) if context.gdbinit: cmd += ' -nh ' # ignore ~/.gdbinit cmd += ' -x %s ' % context.gdbinit # load custom gdbinit cmd += ' -q ' if exe and context.native: if not ssh and not os.path.isfile(exe): log.error('No such file: %s' % exe) cmd += ' "%s"' % exe if pid and not context.os == 'android': cmd += ' %d' % pid if context.os == 'android' and pid: runner = _get_runner() which = _get_which() gdb_cmd = _gdbserver_args(pid=pid, which=which) gdbserver = runner(gdb_cmd) port = _gdbserver_port(gdbserver, None) host = context.adb_host pre += 'target extended-remote %s:%i\n' % (context.adb_host, port) # gdbserver on Android sets 'detach-on-fork on' which breaks things # when you're trying to debug anything that forks. pre += 'set detach-on-fork off\n' gdbscript = pre + (gdbscript or '') if gdbscript: tmp = tempfile.NamedTemporaryFile(prefix = 'pwn', suffix = '.gdb', delete = False, mode = 'w+') log.debug('Wrote gdb script to %r\n%s' % (tmp.name, gdbscript)) gdbscript = 'shell rm %s\n%s' % (tmp.name, gdbscript) tmp.write(gdbscript) tmp.close() cmd += ' -x "%s"' % (tmp.name) log.info('running in new terminal: %s' % cmd) gdb_pid = misc.run_in_new_terminal(cmd) if pid and context.native: proc.wait_for_debugger(pid) return gdb_pid
def wrapperS(self, *a, **kw): return context._encode(func(self, *a, **kw))
def aux(args): for i, arg in enumerate(args): if isinstance(arg, (str, bytes)): args[i] = context._encode(args[i]) + b"\x00" elif isinstance(arg, (list, tuple)): aux(arg)
def cyclic_find(subseq, alphabet=None, n=None): """cyclic_find(subseq, alphabet = None, n = None) -> int Calculates the position of a substring into a De Bruijn sequence. .. todo: "Calculates" is an overstatement. It simply traverses the list. There exists better algorithms for this, but they depend on generating the De Bruijn sequence in another fashion. Somebody should look at it: https://www.sciencedirect.com/science/article/pii/S0012365X00001175 Arguments: subseq: The subsequence to look for. This can be a string, a list or an integer. If an integer is provided it will be packed as a little endian integer. alphabet: List or string to generate the sequence over. By default, uses :obj:`.context.cyclic_alphabet`. n(int): The length of subsequences that should be unique. By default, uses :obj:`.context.cyclic_size`. Examples: Let's generate an example cyclic pattern. >>> cyclic(16) b'aaaabaaacaaadaaa' Note that 'baaa' starts at offset 4. The `cyclic_find` routine shows us this: >>> cyclic_find(b'baaa') 4 The *default* length of a subsequence generated by `cyclic` is `4`. If a longer value is submitted, it is automatically truncated to four bytes. >>> cyclic_find(b'baaacaaa') 4 If you provided e.g. `n=8` to `cyclic` to generate larger subsequences, you must explicitly provide that argument. >>> cyclic_find(b'baaacaaa', n=8) 3515208 We can generate a large cyclic pattern, and grab a subset of it to check a deeper offset. >>> cyclic_find(cyclic(1000)[514:518]) 514 Instead of passing in the byte representation of the pattern, you can also pass in the integer value. Note that this is sensitive to the selected endianness via `context.endian`. >>> cyclic_find(0x61616162) 4 >>> cyclic_find(0x61616162, endian='big') 1 You can use anything for the cyclic pattern, including non-printable characters. >>> cyclic_find(0x00000000, alphabet=unhex('DEADBEEF00')) 621 """ if n is None: n = context.cyclic_size if isinstance(subseq, six.integer_types): subseq = packing.pack(subseq, bytes=n) subseq = context._encode(subseq) if len(subseq) != n: log.warn_once( "cyclic_find() expects %i-byte subsequences by default, you gave %r\n" "Unless you specified cyclic(..., n=%i), you probably just want the first 4 bytes.\n" "Truncating the data at 4 bytes. Specify cyclic_find(..., n=%i) to override this.", n, subseq, len(subseq), len(subseq)) subseq = subseq[:n] if alphabet is None: alphabet = context.cyclic_alphabet alphabet = context._encode(alphabet) if any(c not in alphabet for c in subseq): return -1 n = n or len(subseq) return _gen_find(subseq, de_bruijn(alphabet, n))
def lookup(self, symb=None, lib=None): """lookup(symb = None, lib = None) -> int Find the address of ``symbol``, which is found in ``lib``. Arguments: symb(str): Named routine to look up If omitted, the base address of the library will be returned. lib(str): Substring to match for the library name. If omitted, the current library is searched. If set to ``'libc'``, ``'libc.so'`` is assumed. Returns: Address of the named symbol, or :const:`None`. """ result = None if lib == 'libc': lib = 'libc.so' if symb: symb = context._encode(symb) # # Get a pretty name for the symbol to show the user # if symb and lib: pretty = '%r in %r' % (symb, lib) else: pretty = repr(symb or lib) if not pretty: self.failure("Must specify a library or symbol") self.waitfor('Resolving %s' % pretty) # # If we are loading from a different library, create # a DynELF instance for it. # if lib is not None: dynlib = self._dynamic_load_dynelf(lib) else: dynlib = self if dynlib is None: log.failure("Could not find %r" % lib) return None # # If we are resolving a symbol in the library, find it. # if symb and self.libcdb: # Try a quick lookup by build ID self.status("Trying lookup based on Build ID") build_id = dynlib._lookup_build_id(lib=lib) if build_id: log.info("Trying lookup based on Build ID: %s" % build_id) path = libcdb.search_by_build_id(build_id) if path: with context.local(log_level='error'): e = ELF(path) e.address = dynlib.libbase result = e.symbols[symb] if symb and not result: self.status("Trying remote lookup") result = dynlib._lookup(symb) if not symb: result = dynlib.libbase # # Did we win? # if result: self.success("%#x" % result) else: self.failure("Could not find %s" % pretty) return result