mirror of
https://github.com/Gallopsled/pwntools.git
synced 2026-11-11 23:54:44 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c888a8cbe0 | ||
|
|
9f9d884790 | ||
|
|
39bc5808ea | ||
|
|
31f7c61c1f | ||
|
|
2a73356c78 | ||
|
|
25c84daa8a | ||
|
|
3ff7a9e940 | ||
|
|
e50b978398 | ||
|
|
35789287d8 | ||
|
|
7a1ab829eb | ||
|
|
899baec704 | ||
|
|
932cb57bf9 | ||
|
|
6a61617c73 | ||
|
|
ab8879afa3 | ||
|
|
515f678f2d | ||
|
|
c3afdd681d | ||
|
|
5d537a6189 | ||
|
|
9ba966ddba | ||
|
|
6ea5722f5b | ||
|
|
4e24f665b8 | ||
|
|
34cedc96d8 | ||
|
|
e8065440c2 | ||
|
|
de34906b4c | ||
|
|
e89a9b66ac | ||
|
|
64acece61b | ||
|
|
bb9004c298 |
No files matched your search
+28
-7
@@ -9,9 +9,10 @@ The table below shows which release corresponds to each branch, and what date th
|
||||
|
||||
| Version | Branch | Release Date |
|
||||
| ---------------- | -------- | ---------------------- |
|
||||
| [3.13.0](#3130) | `dev` | Mar 31, 2018 (planned)
|
||||
| [3.12.0](#3120) | `beta` | Feb 17, 2018 (planned)
|
||||
| [3.11.0](#3110) | `stable` | Jan 3, 2018
|
||||
| [3.14.0](#3130) | `dev` | Jun 12, 2018 (planned)
|
||||
| [3.13.0](#3130) | `beta` | Mar 31, 2018 (planned)
|
||||
| [3.12.0](#3120) | `stable` | Feb 22, 2018
|
||||
| [3.11.0](#3110) | | Jan 3, 2018
|
||||
| [3.10.0](#3100) | | Oct 25, 2017
|
||||
| [3.9.2](#392) | | Oct 5, 2017
|
||||
| [3.9.1](#391) | | Sep 28, 2017
|
||||
@@ -41,9 +42,31 @@ The table below shows which release corresponds to each branch, and what date th
|
||||
| [3.0.0](#300) | | Aug 20, 2016
|
||||
| [2.2.0](#220) | | Jan 5, 2015
|
||||
|
||||
## 3.12.0
|
||||
## 3.14.0
|
||||
|
||||
To be released on Feb 17, 2018.
|
||||
To be released on Jun 12, 2018.
|
||||
|
||||
## 3.13.0
|
||||
|
||||
To be released on Mar 31, 2018.
|
||||
|
||||
- [#1104][1104] Add `DynELF.dump()` for dumping remote ELF files
|
||||
- [#1101][1101] Set `context.os` via `context.binary`, useful for Android exploitation
|
||||
- [5fdc08][5fdc08] Work around broken `pidof` on Android
|
||||
- [63dfed][63dfed] Print warning when Corefile deletion fails instead of throwing an exception
|
||||
- [#1094][1094] Make hexdump output alignment more consistent
|
||||
- [#1096][1096] `flat()` and `fit()` are now the same function
|
||||
- [#1068][1068] Work around very old OpenSSL versions which don't have sha256 support *AND* don't exit with an error code when trying to use it
|
||||
|
||||
[1104]: https://github.com/Gallopsled/pwntools/pull/1104
|
||||
[1101]: https://github.com/Gallopsled/pwntools/pull/1101
|
||||
[1094]: https://github.com/Gallopsled/pwntools/pull/1094
|
||||
[1096]: https://github.com/Gallopsled/pwntools/pull/1096
|
||||
[1068]: https://github.com/Gallopsled/pwntools/pull/1068
|
||||
[5fdc08]: https://github.com/Gallopsled/pwntools/commit/5fdc08
|
||||
[63dfed]: https://github.com/Gallopsled/pwntools/commit/63dfed
|
||||
|
||||
## 3.12.0
|
||||
|
||||
- [#1083][1083] Better error messages for `gdb` when `LD_PRELOAD` is incorrect
|
||||
- [#1085][1085] Add support for extracting Android `BOOTLDR!` images
|
||||
@@ -59,7 +82,6 @@ To be released on Feb 17, 2018.
|
||||
[1067]: https://github.com/Gallopsled/pwntools/pull/1067
|
||||
[1063]: https://github.com/Gallopsled/pwntools/pull/1063
|
||||
|
||||
|
||||
## 3.11.0
|
||||
|
||||
- [#1044][1044] Enhancements to ROP
|
||||
@@ -95,7 +117,6 @@ To be released on Feb 17, 2018.
|
||||
[1057]: https://github.com/Gallopsled/pwntools/pull/1057
|
||||
[1058]: https://github.com/Gallopsled/pwntools/pull/1058
|
||||
|
||||
|
||||
## 3.9.2
|
||||
|
||||
- [#1043][1043] Do not attempt to populate the libraries used by statically-linked binaries
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||

|
||||
|
||||
[](https://docs.pwntools.com/)
|
||||
[](https://pypi.python.org/pypi/pwntools/)
|
||||
[](https://pypi.python.org/pypi/pwntools/)
|
||||
[](https://travis-ci.org/Gallopsled/pwntools)
|
||||
[](https://coveralls.io/github/Gallopsled/pwntools?branch=dev)
|
||||
[](https://twitter.com/pwntools)
|
||||
|
||||
@@ -24,6 +24,27 @@ sys.path.insert(0, os.path.abspath('../..'))
|
||||
|
||||
import pwnlib
|
||||
|
||||
# -- WORK-AROUNDS FOR DEPRECATION ----------------------------------------------
|
||||
# Deprecated
|
||||
# 1.6b1
|
||||
# sphinx.util.compat.Directive class is now deprecated.
|
||||
# Please use instead docutils.parsers.rst.Directive
|
||||
#
|
||||
# Pwntools Note:
|
||||
# Can't just "do the right thing" since we have dependencies that
|
||||
# are also affected by this, specifically sphinxcontrib.autoprogram
|
||||
try:
|
||||
import sphinx.util.compat
|
||||
except ImportError:
|
||||
import sys
|
||||
import types
|
||||
import sphinx.util
|
||||
import docutils.parsers.rst
|
||||
class compat(types.ModuleType):
|
||||
Directive = docutils.parsers.rst.Directive
|
||||
sphinx.util.compat = compat('sphinx.util.compat')
|
||||
sys.modules['sphinx.util.compat'] = sphinx.util.compat
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
.. testsetup:: *
|
||||
|
||||
from glob import glob
|
||||
from pwn import *
|
||||
|
||||
:mod:`pwnlib.elf.corefile` --- Core Files
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.. testsetup:: *
|
||||
|
||||
from pwn import *
|
||||
from pwnlib.tubes.server import server
|
||||
|
||||
:mod:`pwnlib.tubes.sock` --- Sockets
|
||||
===========================================================
|
||||
@@ -23,3 +24,9 @@
|
||||
.. autoclass:: pwnlib.tubes.listen.listen
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. automodule:: pwnlib.tubes.server
|
||||
|
||||
.. autoclass:: pwnlib.tubes.server.server
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -43,6 +43,7 @@ from pwnlib.tubes.listen import listen
|
||||
from pwnlib.tubes.process import process, PTY, PIPE, STDOUT
|
||||
from pwnlib.tubes.remote import remote, tcp, udp, connect
|
||||
from pwnlib.tubes.serialtube import serialtube
|
||||
from pwnlib.tubes.server import server
|
||||
from pwnlib.tubes.ssh import ssh
|
||||
from pwnlib.tubes.tube import tube
|
||||
from pwnlib.ui import *
|
||||
|
||||
@@ -124,8 +124,3 @@ linux_aarch64_sigreturn = SigreturnABI('sp', ['x8'], 16, 0)
|
||||
|
||||
windows_i386 = ABI('esp', [], 4, 0)
|
||||
windows_amd64 = ABI('rsp', ['rcx','rdx','r8','r9'], 32, 32)
|
||||
|
||||
# Fake ABIs used by SROP
|
||||
linux_i386_srop = ABI('esp', ['eax'], 4, 0)
|
||||
linux_amd64_srop = ABI('rsp', ['rax'], 4, 0)
|
||||
linux_arm_srop = ABI('sp', ['r7'], 4, 0)
|
||||
@@ -820,6 +820,7 @@ echo $PATH | while read -d: directory; do
|
||||
[ -x "$directory/{name}" ] || continue;
|
||||
echo -n "$directory/{name}\\x00";
|
||||
done
|
||||
[ -x "{name}" ] && echo -n "$PWD/{name}\\x00"
|
||||
'''.format(name=name)
|
||||
|
||||
which_cmd = which_cmd.strip()
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import ctypes
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
|
||||
class img_info(ctypes.Structure):
|
||||
_fields_ = [
|
||||
('name', ctypes.c_char * 64),
|
||||
('size', ctypes.c_uint32)
|
||||
]
|
||||
|
||||
class bootloader_images_header(ctypes.Structure):
|
||||
_fields_ = [
|
||||
('magic', ctypes.c_char * 8),
|
||||
('num_images', ctypes.c_uint32),
|
||||
('start_offset', ctypes.c_uint32),
|
||||
('bootldr_size', ctypes.c_uint32),
|
||||
]
|
||||
|
||||
def __init__(self, *a, **kw):
|
||||
super(bootloader_images_header, self).__init__(*a, **kw)
|
||||
if self.magic != self.MAGIC:
|
||||
raise ValueError("Incorrect magic (%r, expected %r)" % (self.magic, self.MAGIC))
|
||||
MAGIC = 'BOOTLDR!'
|
||||
|
||||
class BootloaderImage(object):
|
||||
def __init__(self, data):
|
||||
"""Android Bootloader image
|
||||
|
||||
Arguments:
|
||||
data(str): Binary data from the image file.
|
||||
"""
|
||||
self.data = data
|
||||
self.header = bootloader_images_header.from_buffer_copy(data)
|
||||
|
||||
imgarray = ctypes.ARRAY(img_info, self.header.num_images)
|
||||
self.img_info = imgarray.from_buffer_copy(data, ctypes.sizeof(self.header))
|
||||
|
||||
def extract(self, index_or_name):
|
||||
"""extract(index_or_name) -> bytes
|
||||
|
||||
Extract the contents of an image.
|
||||
|
||||
Arguments:
|
||||
index_or_name(str,int): Either an image index or name.
|
||||
|
||||
Returns:
|
||||
Contents of the image.
|
||||
"""
|
||||
if isinstance(index_or_name, int):
|
||||
index = index_or_name
|
||||
else:
|
||||
for i in range(len(self.img_info)):
|
||||
if self.img_info[i].name == index_or_name:
|
||||
index = i
|
||||
break
|
||||
else:
|
||||
raise ValueError("Invalid img name: %r" % index_or_name)
|
||||
|
||||
if index >= len(self.img_info):
|
||||
raise ValueError("index out of range (%s, max %s)" % (index, len(self.img_info)))
|
||||
|
||||
offset = self.header.start_offset
|
||||
|
||||
for i in range(index):
|
||||
offset += self.img_info[i].size
|
||||
|
||||
return self.data[offset:offset + self.img_info[index].size]
|
||||
|
||||
def extract_all(self, path):
|
||||
"""extract_all(path)
|
||||
|
||||
Extracts all images to the provided path. The filenames are taken
|
||||
from the image name, with '.img' appended.
|
||||
"""
|
||||
if not os.path.isdir(path):
|
||||
raise ValueError("%r does not exist or is not a directory" % path)
|
||||
|
||||
for img in self.img_info:
|
||||
imgpath = os.path.join(path, img.name + '.img')
|
||||
with open(imgpath, 'wb+') as f:
|
||||
data = self.extract(img.name)
|
||||
f.write(data)
|
||||
|
||||
def __str__(self):
|
||||
rv = []
|
||||
rv.append("Bootloader")
|
||||
rv.append(" Magic: %r" % self.header.magic)
|
||||
rv.append(" Offset: %#x" % self.header.start_offset)
|
||||
rv.append(" Size: %#x" % self.header.bootldr_size)
|
||||
rv.append(" Images: %s" % self.header.num_images)
|
||||
for img in self.img_info:
|
||||
rv.append(" Name: %s" % img.name)
|
||||
rv.append(" Size: %#x" % img.size)
|
||||
rv.append(" Data: %r..." % self.extract(img.name)[:32])
|
||||
return '\n'.join(rv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Easy sanity checking
|
||||
b = BootloaderImage(open(sys.argv[1]).read())
|
||||
print(b)
|
||||
@@ -30,13 +30,25 @@ parser.add_argument(
|
||||
help = 'The os/architecture/endianness/bits the shellcode will run in (default: linux/i386), choose from: %s' % common.choices,
|
||||
)
|
||||
parser.add_argument(
|
||||
'--exec', type=file, dest='executable',
|
||||
'--exec',
|
||||
|
||||
# NOTE: Type cannot be "file" because we may be referring to a remote
|
||||
# file, or a file on an Android device.
|
||||
type=str,
|
||||
|
||||
dest='executable',
|
||||
help='File to debug'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--process', metavar='PROCESS_NAME',
|
||||
help='Name of the process to attach to (e.g. "bash")'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--sysroot', metavar='SYSROOT',
|
||||
type=str,
|
||||
default='',
|
||||
help="GDB sysroot path"
|
||||
)
|
||||
|
||||
def main(args):
|
||||
gdbscript = ''
|
||||
@@ -47,23 +59,41 @@ def main(args):
|
||||
context.device = adb.wait_for_device()
|
||||
|
||||
if args.executable:
|
||||
context.binary = ELF(args.executable.name)
|
||||
target = context.binary.path
|
||||
if os.path.exists(args.executable):
|
||||
context.binary = ELF(args.executable)
|
||||
target = context.binary.path
|
||||
|
||||
# This path does nothing, but avoids the "print_usage()"
|
||||
# path below.
|
||||
elif context.os == 'android':
|
||||
target = args.executable
|
||||
elif args.pid:
|
||||
target = int(args.pid)
|
||||
elif args.process:
|
||||
if context.os == 'android':
|
||||
target = adb.pidof(process)
|
||||
target = adb.pidof(args.process)
|
||||
else:
|
||||
target = pidof(process)
|
||||
target = pidof(args.process)
|
||||
|
||||
# pidof() returns a list
|
||||
if not target:
|
||||
log.error("Could not find a PID for %r", args.process)
|
||||
target = target[0]
|
||||
else:
|
||||
parser.print_usage()
|
||||
return 1
|
||||
|
||||
if args.pid or args.process:
|
||||
gdb.attach(target, gdbscript=gdbscript)
|
||||
pid = gdb.attach(target, gdbscript=gdbscript, sysroot=args.sysroot)
|
||||
|
||||
# Since we spawned the gdbserver process, and process registers an
|
||||
# atexit handler to close itself, gdbserver will be terminated when
|
||||
# we exit. This will manifest as a "remote connected ended" or
|
||||
# similar error message. Hold it open for the user.
|
||||
log.info("GDB connection forwarding will terminate when you press enter")
|
||||
pause()
|
||||
else:
|
||||
gdb.debug(target, gdbscript=gdbscript).interactive()
|
||||
gdb.debug(target, gdbscript=gdbscript, sysroot=args.sysroot).interactive()
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnlib.commandline.common.main(__file__)
|
||||
+29
-21
@@ -753,30 +753,38 @@ class Corefile(ELF):
|
||||
>>> io.corefile.fault_addr == io.corefile.eax == 0xdeadbeef
|
||||
True
|
||||
"""
|
||||
fault_addr = 0
|
||||
if self.siginfo:
|
||||
fault_addr = int(self.siginfo.sigfault_addr)
|
||||
if not self.siginfo:
|
||||
return getattr(self, 'pc', 0)
|
||||
|
||||
# The fault_addr on AMD64 is zero if the crash occurs
|
||||
# after a "ret" instruction, if the "ret" would return
|
||||
# to an invalid address. We need to extract the address
|
||||
# manually, as a convenience.
|
||||
if fault_addr == 0 and self.arch == 'amd64' and self.pc and self.sp:
|
||||
try:
|
||||
code = self.read(self.pc, 1)
|
||||
if code != '\xc3':
|
||||
return fault_addr
|
||||
address = self.unpack(self.sp)
|
||||
return address
|
||||
except Exception:
|
||||
# Could not read $rsp or $rip
|
||||
pass
|
||||
fault_addr = int(self.siginfo.sigfault_addr)
|
||||
|
||||
return fault_addr
|
||||
# The fault_addr is zero if the crash occurs due to a
|
||||
# "protection fault", e.g. a dereference of 0x4141414141414141
|
||||
# because this is technically a kernel address.
|
||||
#
|
||||
# A protection fault does not set "fault_addr" in the siginfo.
|
||||
# (http://elixir.free-electrons.com/linux/v4.14-rc8/source/kernel/signal.c#L1052)
|
||||
#
|
||||
# Since a common use for corefiles is to spray the stack with a
|
||||
# cyclic pattern to find the offset to get control of $PC,
|
||||
# check for a "ret" instruction ("\xc3").
|
||||
#
|
||||
# If we find a RET at $PC, extract the "return address" from the
|
||||
# top of the stack.
|
||||
if fault_addr == 0 and self.siginfo.si_code == 0x80:
|
||||
try:
|
||||
code = self.read(self.pc, 1)
|
||||
RET = '\xc3'
|
||||
if code == RET:
|
||||
fault_addr = self.unpack(self.sp)
|
||||
except Exception:
|
||||
# Could not read $rsp or $rip
|
||||
pass
|
||||
|
||||
return fault_addr
|
||||
|
||||
# No embedded siginfo structure, so just return the
|
||||
# current instruction pointer.
|
||||
return getattr(self, 'pc', 0)
|
||||
|
||||
@property
|
||||
def _pc_register(self):
|
||||
@@ -819,8 +827,8 @@ class Corefile(ELF):
|
||||
fields = [
|
||||
repr(self.path),
|
||||
'%-10s %s' % ('Arch:', gnu_triplet),
|
||||
'%-10s %#x' % ('%s:' % self._pc_register.upper(), getattr(self, 'pc', 0)),
|
||||
'%-10s %#x' % ('%s:' % self._sp_register.upper(), getattr(self, 'sp', 0)),
|
||||
'%-10s %#x' % ('%s:' % self._pc_register.upper(), self.pc or 0),
|
||||
'%-10s %#x' % ('%s:' % self._sp_register.upper(), self.sp or 0),
|
||||
]
|
||||
|
||||
if self.exe and self.exe.name:
|
||||
|
||||
+7
-3
@@ -262,9 +262,13 @@ class ELF(ELFFile):
|
||||
self.bits = 64
|
||||
|
||||
# Is this a native binary? Should we be checking QEMU?
|
||||
with context.local(arch=self.arch):
|
||||
#: Whether this ELF should be able to run natively
|
||||
self.native = context.native
|
||||
try:
|
||||
with context.local(arch=self.arch):
|
||||
#: Whether this ELF should be able to run natively
|
||||
self.native = context.native
|
||||
except AttributeError:
|
||||
# The architecture may not be supported in pwntools
|
||||
self.native = False
|
||||
|
||||
self._address = 0
|
||||
if self.elftype != 'DYN':
|
||||
|
||||
+38
-9
@@ -229,7 +229,7 @@ def _gdbserver_args(pid=None, path=None, args=None, which=None):
|
||||
|
||||
orig_args = args
|
||||
|
||||
gdbserver_args = [gdbserver]
|
||||
gdbserver_args = [gdbserver, '--multi']
|
||||
if context.aslr:
|
||||
gdbserver_args += ['--no-disable-randomization']
|
||||
else:
|
||||
@@ -249,6 +249,12 @@ def _gdbserver_port(gdbserver, ssh):
|
||||
# Process /bin/bash created; pid = 14366
|
||||
# Listening on port 34816
|
||||
process_created = gdbserver.recvline()
|
||||
|
||||
if process_created.startswith('ERROR:'):
|
||||
raise ValueError(
|
||||
'Failed to spawn process under gdbserver. gdbserver error message: %s' % process_created
|
||||
)
|
||||
|
||||
gdbserver.pid = int(process_created.split()[-1], 0)
|
||||
|
||||
listening_on = ''
|
||||
@@ -287,7 +293,7 @@ def _get_runner(ssh=None):
|
||||
else: return tubes.process.process
|
||||
|
||||
@LocalContext
|
||||
def debug(args, gdbscript=None, exe=None, ssh=None, env=None, **kwargs):
|
||||
def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, **kwargs):
|
||||
"""debug(args) -> tube
|
||||
|
||||
Launch a GDB server with the specified command line,
|
||||
@@ -299,6 +305,8 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, **kwargs):
|
||||
exe(str): Path to the executable on disk
|
||||
env(dict): Environment to start the binary in
|
||||
ssh(:class:`.ssh`): Remote ssh session to use to launch the process.
|
||||
sysroot(str): Foreign-architecture sysroot, used for QEMU-emulated binaries
|
||||
and Android targets.
|
||||
|
||||
Returns:
|
||||
:class:`.process` or :class:`.ssh_channel`: A tube connected to the target process
|
||||
@@ -404,7 +412,6 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, **kwargs):
|
||||
|
||||
runner = _get_runner(ssh)
|
||||
which = _get_which(ssh)
|
||||
sysroot = None
|
||||
gdbscript = gdbscript or ''
|
||||
|
||||
if context.noptrace:
|
||||
@@ -416,11 +423,15 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, **kwargs):
|
||||
else:
|
||||
qemu_port = random.randint(1024, 65535)
|
||||
qemu_user = qemu.user_path()
|
||||
sysroot = qemu.ld_prefix(env)
|
||||
sysroot = sysroot or qemu.ld_prefix(env)
|
||||
if not qemu_user:
|
||||
log.error("Cannot debug %s binaries without appropriate QEMU binaries" % context.arch)
|
||||
args = [qemu_user, '-g', str(qemu_port)] + args
|
||||
|
||||
# Use a sane default sysroot for Android
|
||||
if not sysroot and context.os == 'android':
|
||||
sysroot = 'remote:/'
|
||||
|
||||
# Make sure gdbserver/qemu is installed
|
||||
if not which(args[0]):
|
||||
log.error("%s is not installed" % args[0])
|
||||
@@ -478,7 +489,7 @@ def binary():
|
||||
>>> gdb.binary() # doctest: +SKIP
|
||||
'/usr/bin/gdb'
|
||||
"""
|
||||
gdb = misc.which('gdb')
|
||||
gdb = misc.which('pwntools-gdb') or misc.which('gdb')
|
||||
|
||||
if not context.native:
|
||||
multiarch = misc.which('gdb-multiarch')
|
||||
@@ -508,6 +519,7 @@ def attach(target, gdbscript = None, exe = None, need_ptrace_scope = True, gdb_a
|
||||
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).
|
||||
@@ -607,6 +619,10 @@ def attach(target, gdbscript = None, exe = None, need_ptrace_scope = True, gdb_a
|
||||
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:
|
||||
@@ -671,7 +687,15 @@ def attach(target, gdbscript = None, exe = None, need_ptrace_scope = True, gdb_a
|
||||
exe = exe or target.executable
|
||||
elif isinstance(target, tuple) and len(target) == 2:
|
||||
host, port = target
|
||||
pre += 'target remote %s:%d\n' % (host, port)
|
||||
|
||||
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):
|
||||
@@ -699,7 +723,8 @@ def attach(target, gdbscript = None, exe = None, need_ptrace_scope = True, gdb_a
|
||||
log.error('could not find target process')
|
||||
|
||||
if exe:
|
||||
pre += 'file %s\n' % exe
|
||||
# The 'file' statement should go first
|
||||
pre = 'file %s\n%s' % (exe, pre)
|
||||
|
||||
cmd = binary()
|
||||
|
||||
@@ -731,7 +756,11 @@ def attach(target, gdbscript = None, exe = None, need_ptrace_scope = True, gdb_a
|
||||
gdbserver = runner(gdb_cmd)
|
||||
port = _gdbserver_port(gdbserver, None)
|
||||
host = context.adb_host
|
||||
pre += 'target remote %s:%i' % (context.adb_host, port)
|
||||
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 '')
|
||||
|
||||
@@ -759,7 +788,7 @@ def ssh_gdb(ssh, argv, gdbscript = None, arch = None, **kwargs):
|
||||
argv = [argv]
|
||||
|
||||
exe = argv[0]
|
||||
argv = ["gdbserver", "127.0.0.1:0"] + argv
|
||||
argv = ["gdbserver", "--multi", "127.0.0.1:0"] + argv
|
||||
|
||||
# Download the executable
|
||||
local_exe = os.path.basename(exe)
|
||||
|
||||
+9
-1
@@ -233,4 +233,12 @@ class Call(object):
|
||||
args.extend(map(repr, arg.values))
|
||||
else:
|
||||
args.append(arg)
|
||||
return '%s(%s)' % (self.name or fmt % self.target, ', '.join(map(str, args)))
|
||||
|
||||
name = self.name or fmt & self.target
|
||||
arg_str = []
|
||||
for arg in args:
|
||||
if isinstance(arg, (int,long)) and arg > 0x100:
|
||||
arg_str.append(hex(arg))
|
||||
else:
|
||||
arg_str.append(str(arg))
|
||||
return '%s(%s)' % (name, ', '.join(arg_str))
|
||||
+3
-1
@@ -105,7 +105,7 @@ Finally, let's build our ROP stack
|
||||
>>> rop.write(c.STDOUT_FILENO, binary.symbols['flag'], 8)
|
||||
>>> rop.exit()
|
||||
>>> print rop.dump()
|
||||
0x0000: 0x10000012 write(STDOUT_FILENO, 268435494, 8)
|
||||
0x0000: 0x10000012 write(STDOUT_FILENO, 0x10000026, 8)
|
||||
0x0004: 0x1000000e <adjust @0x18> add esp, 0x10; ret
|
||||
0x0008: 0x1 arg0
|
||||
0x000c: 0x10000026 flag
|
||||
@@ -1156,6 +1156,8 @@ class ROP(object):
|
||||
regs = set(regs or ())
|
||||
|
||||
for addr, gadget in self.gadgets.items():
|
||||
addr_bytes = set(pack(gadget.address))
|
||||
if addr_bytes & self._badchars: continue
|
||||
if gadget.insns[-1] != 'ret': continue
|
||||
if gadget.move < move: continue
|
||||
if not (regs <= set(gadget.regs)): continue
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<%
|
||||
from pwnlib import shellcraft
|
||||
%>
|
||||
<%page args="filename, mode='O_RDONLY'"/>
|
||||
<%page args="filename, flags='O_RDONLY', mode=0"/>
|
||||
<%docstring>
|
||||
Opens a file
|
||||
</%docstring>
|
||||
@@ -9,4 +9,4 @@ Opens a file
|
||||
AT_FDCWD=-100
|
||||
%>
|
||||
${shellcraft.pushstr(filename)}
|
||||
${shellcraft.syscall('SYS_openat', AT_FDCWD, 'sp', mode, 0)}
|
||||
${shellcraft.syscall('SYS_openat', AT_FDCWD, 'sp', flags, mode)}
|
||||
@@ -8,5 +8,5 @@ Opens a file and writes its contents to the specified file descriptor.
|
||||
</%docstring>
|
||||
|
||||
${pushstr(filename)}
|
||||
${syscall('SYS_open', 'rsp', 0, 'O_RDONLY')}
|
||||
${syscall('SYS_open', 'rsp', 'O_RDONLY', 0)}
|
||||
${syscall('SYS_sendfile', fd, 'rax', 0, 0x7fffffff)}
|
||||
@@ -10,7 +10,7 @@ Args: [sock (imm/reg) = rbp]
|
||||
after = common.label("after")
|
||||
%>
|
||||
|
||||
|
||||
/* dup() file descriptor ${sock} into stdin/stdout/stderr */
|
||||
${dup}:
|
||||
${amd64.mov('rbp', sock)}
|
||||
|
||||
|
||||
@@ -15,5 +15,5 @@ Example:
|
||||
|
||||
</%docstring>
|
||||
${arm.pushstr(filename)}
|
||||
${arm.linux.open('sp', 0, int(constants.O_RDONLY))}
|
||||
${arm.linux.open('sp', int(constants.O_RDONLY), 0)}
|
||||
${arm.linux.sendfile(fd, 'r0', 0, 0x7fffffff)}
|
||||
@@ -19,5 +19,5 @@ label = common.label("sendfile_loop")
|
||||
%>
|
||||
|
||||
${sc.pushstr(filename)}
|
||||
${sc.open('esp', 0, 'O_RDONLY')}
|
||||
${sc.open('esp', 'O_RDONLY', 0)}
|
||||
${sc.sendfile(fd, 'eax', 0, 0x7fffffff)}
|
||||
@@ -11,6 +11,7 @@ Args: [sock (imm/reg) = ebp]
|
||||
looplabel = common.label("loop")
|
||||
%>
|
||||
|
||||
/* dup() file descriptor ${sock} into stdin/stdout/stderr */
|
||||
${dup}:
|
||||
${mov('ebx', sock)}
|
||||
${mov('ecx', 3)}
|
||||
|
||||
@@ -22,5 +22,5 @@ label = common.label("sendfile_loop")
|
||||
%>
|
||||
|
||||
${mips.pushstr(filename)}
|
||||
${mips.open('$sp', 0, int(constants.O_RDONLY))}
|
||||
${mips.open('$sp', int(constants.O_RDONLY), 0)}
|
||||
${mips.sendfile(fd, '$v0', 0, 0x7fffffff)}
|
||||
@@ -20,6 +20,6 @@ label = common.label("sendfile_loop")
|
||||
%>
|
||||
|
||||
${thumb.pushstr(filename)}
|
||||
${thumb.linux.open('sp', 0, constants.O_RDONLY)}
|
||||
${thumb.linux.open('sp', constants.O_RDONLY, 0)}
|
||||
${thumb.mov('r5', 'r0')}
|
||||
${thumb.linux.sendfile(fd, 'r5', 0, 0x7fffffff)}
|
||||
@@ -9,6 +9,7 @@ Args: [sock (imm/reg) = r6]
|
||||
dup = common.label("dup")
|
||||
looplabel = common.label("loop")
|
||||
%>
|
||||
/* dup() file descriptor ${sock} into stdin/stdout/stderr */
|
||||
${dup}:
|
||||
${mov('r1', 2)}
|
||||
${mov('r7', 'SYS_dup2')}
|
||||
|
||||
@@ -15,8 +15,9 @@ from pwnlib.tubes import listen
|
||||
from pwnlib.tubes import process
|
||||
from pwnlib.tubes import remote
|
||||
from pwnlib.tubes import serialtube
|
||||
from pwnlib.tubes import server
|
||||
from pwnlib.tubes import sock
|
||||
from pwnlib.tubes import ssh
|
||||
from pwnlib.tubes import tube
|
||||
|
||||
__all__ = ['tube', 'sock', 'remote', 'listen', 'process', 'serialtube', 'ssh']
|
||||
__all__ = ['tube', 'sock', 'remote', 'listen', 'process', 'serialtube', 'server', 'ssh']
|
||||
@@ -565,7 +565,7 @@ class process(tube):
|
||||
#
|
||||
|
||||
# Create a duplicate so we can modify it safely
|
||||
env = dict(os.environ if env is None else env)
|
||||
env = (os.environ if env is None else env).copy()
|
||||
|
||||
for k,v in env.items():
|
||||
if not isinstance(k, (str, unicode)):
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import errno
|
||||
import socket
|
||||
import threading
|
||||
|
||||
from pwnlib.context import context
|
||||
from pwnlib.log import getLogger
|
||||
from pwnlib.tubes.sock import sock
|
||||
from pwnlib.tubes.remote import remote
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
class server(sock):
|
||||
r"""Creates an TCP or UDP-server to listen for connections. It supports
|
||||
both IPv4 and IPv6.
|
||||
|
||||
Arguments:
|
||||
port(int): The port to connect to.
|
||||
Defaults to a port auto-selected by the operating system.
|
||||
bindaddr(str): The address to bind to.
|
||||
Defaults to ``0.0.0.0`` / `::`.
|
||||
fam: The string "any", "ipv4" or "ipv6" or an integer to pass to :func:`socket.getaddrinfo`.
|
||||
typ: The string "tcp" or "udp" or an integer to pass to :func:`socket.getaddrinfo`.
|
||||
callback: A function to be started on incoming connections. It should take a :class:`pwnlib.tubes.remote` as its only argument.
|
||||
|
||||
Examples:
|
||||
|
||||
>>> s = server(8888)
|
||||
>>> client_conn = remote('localhost', s.lport)
|
||||
>>> server_conn = s.next_connection()
|
||||
>>> client_conn.sendline('Hello')
|
||||
>>> server_conn.recvline()
|
||||
'Hello\n'
|
||||
>>> def cb(r):
|
||||
... client_input = r.readline()
|
||||
... r.send(client_input[::-1])
|
||||
...
|
||||
>>> t = server(8889, callback=cb)
|
||||
>>> client_conn = remote('localhost', t.lport)
|
||||
>>> client_conn.sendline('callback')
|
||||
>>> client_conn.recv()
|
||||
'\nkcabllac'
|
||||
"""
|
||||
|
||||
#: Local port
|
||||
lport = 0
|
||||
|
||||
#: Local host
|
||||
lhost = None
|
||||
|
||||
#: Socket type (e.g. socket.SOCK_STREAM)
|
||||
type = None
|
||||
|
||||
#: Socket family
|
||||
family = None
|
||||
|
||||
#: Socket protocol
|
||||
protocol = None
|
||||
|
||||
#: Canonical name of the listening interface
|
||||
canonname = None
|
||||
|
||||
#: Sockaddr structure that is being listened on
|
||||
sockaddr = None
|
||||
|
||||
_accepter = None
|
||||
|
||||
def __init__(self, port=0, bindaddr = "0.0.0.0", fam = "any", typ = "tcp",
|
||||
callback = None, blocking = False, *args, **kwargs):
|
||||
super(server, self).__init__(*args, **kwargs)
|
||||
|
||||
port = int(port)
|
||||
fam = {socket.AF_INET: 'ipv4',
|
||||
socket.AF_INET6: 'ipv6'}.get(fam, fam)
|
||||
|
||||
fam = self._get_family(fam)
|
||||
typ = self._get_type(typ)
|
||||
|
||||
if fam == socket.AF_INET6 and bindaddr == '0.0.0.0':
|
||||
bindaddr = '::'
|
||||
|
||||
h = self.waitfor('Trying to bind to %s on port %d' % (bindaddr, port))
|
||||
|
||||
for res in socket.getaddrinfo(bindaddr, port, fam, typ, 0, socket.AI_PASSIVE):
|
||||
self.family, self.type, self.proto, self.canonname, self.sockaddr = res
|
||||
|
||||
if self.type not in [socket.SOCK_STREAM, socket.SOCK_DGRAM]:
|
||||
continue
|
||||
|
||||
h.status("Trying %s" % self.sockaddr[0])
|
||||
listen_sock = socket.socket(self.family, self.type, self.proto)
|
||||
listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
listen_sock.bind(self.sockaddr)
|
||||
self.lhost, self.lport = listen_sock.getsockname()[:2]
|
||||
if self.type == socket.SOCK_STREAM:
|
||||
listen_sock.listen(1)
|
||||
break
|
||||
else:
|
||||
h.failure()
|
||||
self.error("Could not bind to %s on port %d" % (bindaddr, port))
|
||||
|
||||
h.success()
|
||||
|
||||
self.sock = listen_sock
|
||||
self.connections_waiting = threading.Event()
|
||||
self.connections = []
|
||||
def accepter():
|
||||
while True:
|
||||
h = self.waitfor('Waiting for connections on %s:%s' % (self.lhost, self.lport))
|
||||
while True:
|
||||
try:
|
||||
if self.type == socket.SOCK_STREAM:
|
||||
sock, rhost = listen_sock.accept()
|
||||
else:
|
||||
data, rhost = listen_sock.recvfrom(4096)
|
||||
listen_sock.connect(rhost)
|
||||
sock = listen_sock
|
||||
sock.unrecv(data)
|
||||
sock.settimeout(self.timeout)
|
||||
break
|
||||
except socket.error as e:
|
||||
if e.errno == errno.EINTR:
|
||||
continue
|
||||
h.failure()
|
||||
self.exception("Socket failure while waiting for connection")
|
||||
sock = None
|
||||
return
|
||||
|
||||
self.rhost, self.rport = rhost[:2]
|
||||
r = remote(self.rhost, self.rport, sock = sock)
|
||||
h.success('Got connection from %s on port %d' % (self.rhost, self.rport))
|
||||
if callback:
|
||||
if not blocking:
|
||||
t = context.Thread(target = callback, args = (r,))
|
||||
t.daemon = True
|
||||
t.start()
|
||||
else:
|
||||
callback(r)
|
||||
else:
|
||||
self.connections.append(r)
|
||||
if not self.connections_waiting.is_set():
|
||||
self.connections_waiting.set()
|
||||
|
||||
self._accepter = context.Thread(target = accepter)
|
||||
self._accepter.daemon = True
|
||||
self._accepter.start()
|
||||
|
||||
def next_connection(self):
|
||||
if not self.connections_waiting.is_set():
|
||||
self.connections_waiting.wait()
|
||||
conn = self.connections.pop(0)
|
||||
if not self.connections:
|
||||
self.connections_waiting.clear()
|
||||
return conn
|
||||
|
||||
def close(self):
|
||||
# since `close` is scheduled to run on exit we must check that we got
|
||||
# a connection or the program will hang in the `join` call above
|
||||
if self._accepter and self._accepter.is_alive():
|
||||
return
|
||||
super(server, self).close()
|
||||
+4
-2
@@ -874,6 +874,8 @@ os.chdir(%(cwd)r)
|
||||
if env is not None:
|
||||
os.environ.clear()
|
||||
os.environ.update(env)
|
||||
else:
|
||||
env = os.environ
|
||||
|
||||
def is_exe(path):
|
||||
return os.path.isfile(path) and os.access(path, os.X_OK)
|
||||
@@ -961,7 +963,7 @@ except Exception:
|
||||
%(func_src)s
|
||||
apply(%(func_name)s, %(func_args)r)
|
||||
|
||||
os.execve(exe, argv, os.environ)
|
||||
os.execve(exe, argv, env)
|
||||
""" % locals()
|
||||
|
||||
script = script.strip()
|
||||
@@ -1302,7 +1304,7 @@ from ctypes import *; libc = CDLL('libc.so.6'); print(libc.getenv(%r))
|
||||
return misc.parse_ldd_output(data)
|
||||
|
||||
def _get_fingerprint(self, remote):
|
||||
cmd = '(openssl sha256 || sha256 || sha256sum) 2>/dev/null < '
|
||||
cmd = '(sha256 || sha256sum || openssl sha256) 2>/dev/null < '
|
||||
cmd = cmd + sh_string(remote)
|
||||
data, status = self.run_to_end(cmd)
|
||||
|
||||
|
||||
@@ -190,6 +190,8 @@ def run_in_new_terminal(command, terminal = None, args = None):
|
||||
variable), ``x-terminal-emulator`` is used.
|
||||
- If tmux is detected (by the presence of the ``$TMUX`` environment
|
||||
variable), a new pane will be opened.
|
||||
- If GNU Screen is detected (by the presence of the ``$STY`` environment
|
||||
variable), a new screen will be opened.
|
||||
|
||||
Arguments:
|
||||
command (str): The command to run.
|
||||
@@ -219,6 +221,9 @@ def run_in_new_terminal(command, terminal = None, args = None):
|
||||
elif 'TMUX' in os.environ and which('tmux'):
|
||||
terminal = 'tmux'
|
||||
args = ['splitw']
|
||||
elif 'STY' in os.environ and which('screen'):
|
||||
terminal = 'screen'
|
||||
args = ['-t','pwntools-gdb','bash','-c']
|
||||
|
||||
if not terminal:
|
||||
log.error('Could not find a terminal binary to use. Set context.terminal to your terminal.')
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__ = '3.11.0'
|
||||
__version__ = '3.12.0'
|
||||
Reference in new issue
Block a user