def __init__(self, text, **kwargs): self.arch = kwargs.get('arch') self.os = kwargs.get('os') self.text = text if not isinstance(text, str): pwn.die('Trying to create an AssemblerText class, but the text does not have type str.\nThe type is ' + str(type(text)) + ' with the value:\n' + repr(text)[:100])
def u(x, arch = None): """Unpacks a string into an integer based on the current context""" if arch == 'amd64': return u64(x) elif arch == 'i386': return u32(x) pwn.die('Architecture not set in the context while calling u(%s)' % repr(x))
def gadget(self, what, avoid=''): if what in self._gadget_cache: return self._gadget_cache[(what, avoid)] gs = [] err = 'Unknown gadget type: "%s"' % what if what == 'ret': gs = self._gadgets.get('popret', {}).get(0, []) elif what == 'leave': gs = self._gadgets.get('leave', []) elif what == 'popebp': gs = self._gadgets.get('popebp', []) elif what.startswith('pop'): if what.startswith('popret'): offset = what[6:] else: if what[-3:] == 'ret': what = what[:-3] offset = what[3:] if offset.isdigit(): offset = int(offset) elif offset == '': offset = 1 else: pwn.die(err) gs = self._gadgets.get('popret', {}).get(offset, []) else: pwn.die(err) for g in gs: gstr = pwn.pint(g) if all(c not in gstr for c in avoid): self._gadget_cache[(avoid, what)] = g return g
def p(x, arch = None): """Packs an integer into a string based on the current context""" if arch == 'amd64': return p64(x) elif arch == 'i386': return p32(x) pwn.die('Architecture not set in the context while calling p(%d)' % x)
def _download_to_cache(self, remote): self._initialize_sftp() fingerprint = self._get_fingerprint(remote) if fingerprint == None: import time local = os.path.normpath(remote) local = os.path.basename(local) local += time.strftime('-%Y-%m-d-%H:%M:%S') local = os.path.join(self._cachedir, local) self._download_raw(remote, local) return local local = self._get_cachefile(fingerprint) if self._verify_local_fingerprint(fingerprint): if not self.silent: pwn.log.success('Found %s in ssh cache' % remote) else: self._download_raw(remote, local) if not self._verify_local_fingerprint(fingerprint): pwn.die('Could not download file "%s"' % remote) return local
def p(x, arch=None): """Packs an integer into a string based on the current context""" if arch == 'amd64': return p64(x) elif arch == 'i386': return p32(x) pwn.die('Architecture not set in the context while calling p(%d)' % x)
def call(self, target, args = (), pivot = None): '''Irrelevant arguments should be marked by a None''' target_addr = self._resolve(target) if not target_addr: pwn.die('symbol {} not found'.format(target)) self._chain.append(('call', (target_addr, pivot, pwn.tuplify(args)))) return self
def _resolve(self, x): if x is None or isinstance(x, int): return x for y in [self.plt, self.symbols, self.sections]: if x in y: return y[x] die('Could not resolve `%s\'' % x)
def _parse_host(self, host): # Split off the optional authentication host_ = host.split('@', 1) if len(host_) == 1: auth, host_ = None, host_[0] else: auth, host_ = host_ # Parse the authentication if auth: auth_ = auth.split(':', 1) if len(auth_) == 1: self._user = auth_[0] else: self._user, self._password = auth_ # Parse the optional port host_ = host_.split(':', 1) if len(host_) == 1: self.host = host_[0] else: self.host, port = host_ if not (port and port.isdigit()): pwn.die('Port "%s" is not a number' % port) self._port = int(port)
def __init__(self, blob, **kwargs): self.arch = kwargs.get('arch') self.os = kwargs.get('os') self.blob = blob if not isinstance(blob, str): pwn.die('Trying to create an AssemblerBlob class, but the blob does not have type str.\nThe type is ' + str(type(blob)) + ' with the value:\n' + repr(blob)[:100])
def unbits(s, endian='big'): out = [] state = {'cur': ''} count = 0 def flush(): cur = state['cur'].ljust(8, '0') state['cur'] = '' if endian == 'little': out.append(chr(int(cur[::-1], 2))) elif endian == 'big': out.append(chr(int(cur, 2))) else: pwn.die('Wat (endian style)') for c in s: if c not in ['0', '1', 0, 1, True, False]: pwn.die('Unbits called with a funky argument') state['cur'] += '1' if c in ['1', 1, True] else '0' count += 1 if count == 8: count = 0 flush() if count: flush() return ''.join(out)
def __init__(self, file): waitfor('Loading ELF file `%s\'' % os.path.basename(file)) self.sections = {} self.symbols = {} self.plt = {} self.got = {} self.elfclass = None self._file_data = None if not (os.access(file, os.R_OK) and os.path.isfile(file)): die('File %s is not readable or does not exist' % file) self._file = file def check(f): if not (os.access(f, os.X_OK) and os.path.isfile(f)): die('Executable %s needed for readelf.py, please install binutils' % f) check(_READELF) check(_OBJDUMP) self._load_elfclass() self._load_sections() self._load_symbols() self._load_plt_got() succeeded()
def load_library(self, file, addr, relative_to=None): '''loads a library at an absolute address or relative to a known symbol''' import os syms = {} if not os.path.exists(file): if file in self.elf.libs: file = self.elf.libs[file] else: pwn.die('Could not load library, file %s does not exist.' % file) for k, v in pwn.elf.symbols(file).items(): if '@@' in k: k = k[:k.find('@@')] syms[k] = v offset = addr if relative_to: if relative_to not in syms: pwn.die( 'Could not load library relative to "%s" -- no such symbol', relative_to) offset -= syms[relative_to]['addr'] for k, v in syms.items(): self.symbols[k] = v['addr'] + offset
def flush(self, loaded_at = None): if loaded_at is not None: self._load_addr = loaded_at if self.elf.elfclass == 'ELF32': return self._generate32() else: pwn.die('Only 32bit ELF supported')
def _set_parent(self, other): if self.parent != None: pwn.die( 'Trying to set parent of "%s" to "%s", but parent was already "%s"' % (self, other, self.parent)) self.parent = other del Block._roots[self.name]
def gadget(self, what, avoid = ''): if what in self._gadget_cache: return self._gadget_cache[(what, avoid)] gs = [] err = 'Unknown gadget type: "%s"' % what if what == 'ret': gs = self._gadgets.get('popret', {}).get(0, []) elif what == 'leave': gs = self._gadgets.get('leave', []) elif what == 'popebp': gs = self._gadgets.get('popebp', []) elif what.startswith('pop'): if what.startswith('popret'): offset = what[6:] else: if what[-3:] == 'ret': what = what[:-3] offset = what[3:] if offset.isdigit(): offset = int(offset) elif offset == '': offset = 1 else: pwn.die(err) gs = self._gadgets.get('popret', {}).get(offset, []) else: pwn.die(err) for g in gs: gstr = pwn.pint(g) if all(c not in gstr for c in avoid): self._gadget_cache[(avoid, what)] = g return g
def unbits(s, endian = 'big'): out = [] state = {'cur': ''} count = 0 def flush(): cur = state['cur'].ljust(8, '0') state['cur'] = '' if endian == 'little': out.append(chr(int(cur[::-1], 2))) elif endian == 'big': out.append(chr(int(cur, 2))) else: pwn.die('Wat (endian style)') for c in s: if c not in ['0', '1', 0, 1, True, False]: pwn.die('Unbits called with a funky argument') state['cur'] += '1' if c in ['1', 1, True] else '0' count += 1 if count == 8: count = 0 flush() if count: flush() return ''.join(out)
def nasm_raw(code, checked=True, return_none=False, optimize='x'): with tempfile.NamedTemporaryFile(prefix='pwn', suffix='.asm') as tmp: tmp.write(code) tmp.flush() try: p = subprocess.Popen(_cmd(tmp.name, optimize), stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError, e: if e.errno == errno.ENOENT: pwn.die('nasm is not installed') else: raise ret = p.wait() if ret != 0: err = p.stderr.read() if return_none: return None elif checked: pwn.die('nasm could not compile file:\n' + err) else: raise Exception('nasm could not compile file:\n' + err) return p.stdout.read()
def __coerce__(self, other): if pwn.isint(other) and self._load_addr: return (self._load_addr, other) elif isinstance(other, str): return (self.flush(), other) else: pwn.die('Could not coerce ROP. Other value was: %r' % other)
def flush(self, loaded_at=None): if loaded_at is not None: self._load_addr = loaded_at if self.elf.elfclass == 'ELF32': return self._generate32() else: pwn.die('Only 32bit ELF supported')
def __coerce__ (self, other): if pwn.isint(other) and self._load_addr: return (self._load_addr, other) elif isinstance(other, str): return (self.flush(), other) else: pwn.die('Could not coerce ROP. Other value was: %r' % other)
def flush(): cur = state['cur'].ljust(8, '0') state['cur'] = '' if endian == 'little': out.append(chr(int(cur[::-1], 2))) elif endian == 'big': out.append(chr(int(cur, 2))) else: pwn.die('Wat (endian style)')
def section(self, name): if name in self.sections: self._load_data() sec = self.sections[name] offset = sec['offset'] size = sec['size'] return ''.join(self._data[offset:offset + size]) else: pwn.die('No section named %s' % name)
def section(self, name): if name in self.sections: self._load_data() sec = self.sections[name] offset = sec['offset'] size = sec['size'] return self._file_data[offset:offset + size] else: die('No section named %s' % name)
def set_name(self, name): if name == self.name: return if self.name in Block._roots: if name in Block._roots: pwn.die('A root block with the name "%s" already exicsts' % self.name) del Block._roots[self.name] Block._roots[name] = self self.name = name
def read(self, addr, numb): out = [] while numb > 0: data = self._read(addr, numb) if data is None: die('Offset %x does not live in any section' % addr) out.append(data) size = len(data) numb -= size addr += size return ''.join(out)
def _run(cmd): import subprocess, errno try: p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() exitcode = p.wait() except OSError, e: if e.errno == errno.ENOENT: pwn.die('%s is not installed' % cmd[0]) else: raise
def symbol(self, name): if name in self.symbols: sym = self.symbols[name] addr = sym['addr'] size = sym['size'] data = self._read(addr, size) if data is None: die('Symbol %s does not live in any section' % name) else: return data else: die('No symbol named %s' % name)
def fix(s): s_list = set([s]) for n in range(100): s = s.subs(Block.symbols) if s.is_integer: break if s in s_list: break if repr(s) > 200: pwn.die('The expression "%s" is misbehaving' % repr(s)) s_list.add(s) return s
def read_symbol(self, name): if name in self.symbols: sym = self.symbols[name] addr = sym['addr'] size = sym['size'] data = self.read(addr, size) if data is None: pwn.die('Symbol %s does not live in any section' % name) else: return data else: pwn.die('No symbol named %s' % name)
def attach_gdb(prog, execute = None, execute_file = None): pids = pwn.pidof(prog) if isinstance(prog, pwn.remote): pid = pids[0] if pid is None: pwn.die('Could not find remote process (%s:%d) on this machine' % prog.sock.getpeername()) elif isinstance(prog, str): if pids == []: pwn.die('No such process') pid = max(pids, key = pwn.proc_starttime) if len(pids) > 1: pwn.log.info('Attaching to youngest process (PID: %d) of %d' % (pid, len(pids))) attach_gdb_to_pid(pid, execute = execute, execute_file = execute_file)
def _initialize_sftp(self): import tempfile if self._supports_sftp: if self._sftp == None: self._sftp = self._client.open_sftp() self._cachedir = os.path.join(tempfile.gettempdir(), 'pwn-ssh-cache') if not os.path.isdir(self._cachedir): try: os.mkdir(self._cachedir) except: pwn.die('Could not create ssh cache dir: %s' % self._cachedir)
def attach_gdb_to_pid(pid, execute=None, execute_file=None): import os, tempfile if execute is not None and execute_file is not None: pwn.die('Both execute and execute_file can\'t be set') try: prog = pwn.proc_exe(pid) except OSError as e: pwn.die(e.strerror + ': ' + e.filename) if pwn.proc_tracer(pid) is not None: pwn.die('Program (pid: %d) is already being debugged' % pid) term = pwn.which('x-terminal-emulator') if term is None: term = os.getenv('COLORTERM') or os.getenv('TERM') if term is None: pwn.die('No environment variable named (COLOR)TERM') term = pwn.which(term) termpid = os.fork() if termpid == 0: argv = [term, '-e', 'gdb "%s" %d' % (prog, pid)] if execute: with tempfile.NamedTemporaryFile(prefix='pwn', suffix='.gdb') as tmp: tmp.write(execute) tmp.flush() argv[-1] += ' -x "%s"' % tmp.name os.execv(argv[0], argv) elif execute_file: argv[-1] += ' -x "%s"' % execute_file os.execv(argv[0], argv) else: os.execv(argv[0], argv) else: pwn.wait_for_debugger(pid)
def _generate32(self): out = [] chain = self._chain self._chain = [] p = p32 def garbage(): return self._garbage(4) def pargs(args): args = map(lambda a: garbage() if a is None else p(a), args) return args for i in range(len(chain)): type, link = chain[i] islast = i == len(chain) - 1 issndlast = i == len(chain) - 2 if type == 'raw': out += pargs(link) elif type == 'call': target, pivot, args = link out.append(p(target)) if len(args) > 0: if islast: out.append(garbage()) out += pargs(args) elif issndlast and chain[i + 1][0] == 'call' and \ len(chain[i + 1][1][2]) == 0: # the last target has no arguments, so go straight to it out.append(p(chain[i + 1][1][0])) out += pargs(args) break else: if pivot is None: # find suitable popret res = self._pivot(args) if res is None: die('Could not find gadget for pivoting %d arguments' % len(args)) pivot, size = res args = pargs(args) for _ in range(size - len(args)): args.append(garbage()) out.append(p(pivot)) out += args elif type == 'migrate': if not islast: die('Migrate must be last link in chain') esp, ebp = link gp = self._gadgets['popebp'] gl = self._gadgets['leave'] if len(gp) == 0 and len(gl) == 0: die('Could not find set-EBP and leave gadgets needed to migrate') gp = gp[0] gl = gl[0] if ebp is None: out += [p(gp), p(esp-4), p(gl)] else: out += [p(gp), p(esp), p(gl)] self.raw(p(ebp)) else: die('Unknown ROP-link type') return ''.join(out)
def __init__(self, content = None, wordsize = 4, name = None): self._content = [] self.wordsize = wordsize self.parent = None if name: self.name = name else: self.name = "block%d" % Block._block_count Block._block_count += 1 if self.name in Block._roots: pwn.die('A root block with the name "%s" already exicsts' % self.name) Block._roots[self.name] = self for o in pwn.concat_all([content]) if content != None else []: self._add(o)
def attach_gdb_to_pid(pid, execute = None, execute_file = None): if execute is not None and execute_file is not None: pwn.die('Both execute and execute_file can\'t be set') try: prog = pwn.proc_exe(pid) except OSError as e: pwn.die(e.strerror + ': ' + e.filename) if pwn.proc_tracer(pid) is not None: pwn.die('Program (pid: %d) is already being debugged' % pid) try: term = subprocess.check_output(['/usr/bin/which', 'x-terminal-emulator']).strip() except subprocess.CalledProcessError: term = '' if term == '': term = os.getenv('COLORTERM') or os.getenv('TERM') if term is None: pwn.die('No environment variable named (COLOR)TERM') term = subprocess.check_output(['/usr/bin/which', term]).strip() termpid = os.fork() if termpid == 0: argv = [term, '-e', 'gdb "%s" %d' % (prog, pid)] if execute: with tempfile.NamedTemporaryFile(prefix='pwn', suffix='.gdb') as tmp: tmp.write(execute) tmp.flush() argv[-1] += ' -x "%s"' % tmp.name os.execv(argv[0], argv) elif execute_file: argv[-1] += ' -x "%s"' % execute_file os.execv(argv[0], argv) else: os.execv(argv[0], argv) else: pwn.wait_for_debugger(pid)
def which(name, flags = os.X_OK, find_all = False): out = [] try: path = os.environ['PATH'] except KeyError: pwn.die('No PATH environment variable') for p in path.split(os.pathsep): p = os.path.join(p, name) if os.access(p, flags): out.append(p) if find_all: return out else: return out[0] if out else None
def __init__(self, *blocks, **kwargs): self.arch = kwargs.get('arch') self.os = kwargs.get('os') self.blocks = [] for b in pwn.concat_all(list(blocks)): if isinstance(b, AssemblerBlock): if self.os == None: self.os = b.os if self.arch == None: self.arch = b.arch if self.os != b.os and b.os != None: pwn.die('Trying to combine assembler blocks with different os: ' + self.os + ' and ' + b.os) if self.arch != b.arch and b.arch != None: pwn.die('Trying to combine assembler blocks with different archs: ' + self.arch + ' and ' + b.arch) if isinstance(b, AssemblerContainer): self.blocks.extend(b.blocks) elif isinstance(b, AssemblerBlock): self.blocks.append(b) elif isinstance(b, str): cast = kwargs.get('cast', 'blob') if cast == 'text': self.blocks.append(AssemblerText(b, **kwargs)) elif cast == 'blob': self.blocks.append(AssemblerBlob(b, **kwargs)) else: pwn.die('Invalid cast for AssemblerContainer') else: pwn.die('Trying to force something of type ' + str(type(b)) + ' into an assembler block. Its value is:\n' + repr(b)[:100])
def libs(self, remote, dir=None, rop=None): '''Downloads the libraries referred to by a file. This is done by running ldd on the remote server, parsing the output and downloading the relevant files. The dir argument specified where to download the files. This defaults to './$HOSTNAME' where $HOSTNAME is the hostname of the remote server. Set rop to a rop-object to update it's list of known libraries.''' libs = self._libs_remote(remote) if dir == None: dir = self.host dir = os.path.realpath(dir) res = {} seen = set([]) for lib, remote in libs.items(): if not remote or lib == 'linux': continue local = os.path.realpath( os.path.join(dir, '.' + os.path.sep + remote)) if not local.startswith(dir): pwn.warning('This seems fishy: %s' % remote) continue dir2 = os.path.dirname(local) if not os.path.exists(dir2): try: os.makedirs(dir2) except: pwn.die('Could not create dir: %s' % dir2) if remote not in seen: self.download(remote, local) seen.add(remote) res[lib] = local if rop: rop.extra_libs(res) return res
def __init__(self, content=None, wordsize=4, name=None): self._content = [] self.wordsize = wordsize self.parent = None if name: self.name = name else: self.name = "block%d" % Block._block_count Block._block_count += 1 if self.name in Block._roots: pwn.die('A root block with the name "%s" already exicsts' % self.name) Block._roots[self.name] = self for o in pwn.concat_all([content]) if content != None else []: self._add(o)
def attach_gdb(prog, execute=None, execute_file=None): pids = pwn.pidof(prog) if isinstance(prog, pwn.remote): pid = pids[0] if pid is None: pwn.die('Could not find remote process (%s:%d) on this machine' % prog.sock.getpeername()) elif isinstance(prog, str): if pids == []: pwn.die('No such process') pid = max(pids, key=pwn.proc_starttime) if len(pids) > 1: pwn.log.info('Attaching to youngest process (PID: %d) of %d' % (pid, len(pids))) attach_gdb_to_pid(pid, execute=execute, execute_file=execute_file)
def libs(self, remote, dir = None, rop = None): '''Downloads the libraries referred to by a file. This is done by running ldd on the remote server, parsing the output and downloading the relevant files. The dir argument specified where to download the files. This defaults to './$HOSTNAME' where $HOSTNAME is the hostname of the remote server. Set rop to a rop-object to update it's list of known libraries.''' libs = self._libs_remote(remote) if dir == None: dir = self.host dir = os.path.realpath(dir) res = {} seen = set([]) for lib, remote in libs.items(): if not remote or lib == 'linux': continue local = os.path.realpath(os.path.join(dir, '.' + os.path.sep + remote)) if not local.startswith(dir): pwn.warning('This seems fishy: %s' % remote) continue dir2 = os.path.dirname(local) if not os.path.exists(dir2): try: os.makedirs(dir2) except: pwn.die('Could not create dir: %s' % dir2) if remote not in seen: self.download(remote, local) seen.add(remote) res[lib] = local if rop: rop.extra_libs(res) return res
def wrapper(*args, **kwargs): with pwn.ExtraContext(kwargs) as kwargs: for k, vs in supported_context.items(): if kwargs[k] not in vs: pwn.die('Invalid context for ' + f.func_name + ': ' + k + '=' + str(kwargs[k]) + ' is not supported') r = shellcode_wrapper(f, args, kwargs, avoider) if kwargs.get('raw'): return r elif isinstance(r, AssemblerBlock): return r elif isinstance(r, (tuple, list)): kwargs['cast'] = 'blob' if blob else 'text' return AssemblerContainer(*r, **kwargs) elif not blob: return AssemblerText(r, **kwargs) else: return AssemblerBlob(r, **kwargs)