mirror of
https://github.com/Gallopsled/pwntools.git
synced 2026-11-09 10:09:36 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cca1083f0b | ||
|
|
9dacd1b5d9 | ||
|
|
74a5876c0d | ||
|
|
9d6b4881fe | ||
|
|
91025408a4 | ||
|
|
513682373e | ||
|
|
716c67d993 | ||
|
|
69f8cc2c47 | ||
|
|
ec98dc1246 | ||
|
|
7cd3c7b300 | ||
|
|
5831796f6f | ||
|
|
3e33180655 | ||
|
|
e09543c098 |
No files matched your search
+31
-4
@@ -9,9 +9,10 @@ The table below shows which release corresponds to each branch, and what date th
|
||||
|
||||
| Version | Branch | Release Date |
|
||||
| ---------------- | -------- | ---------------------- |
|
||||
| [3.11.0](#3110) | `dev` | Dec 2, 2017 (planned)
|
||||
| [3.10.0](#3100) | `beta` | Oct 21, 2017 (planned)
|
||||
| [3.9.2](#392) | `stable` | Oct 5, 2017
|
||||
| [3.12.0](#3120) | `dev` | Jan 13, 2018 (planned)
|
||||
| [3.11.0](#3110) | `beta` | Dec 2, 2017 (planned)
|
||||
| [3.10.0](#3100) | `stable` | Oct 25, 2017
|
||||
| [3.9.2](#392) | | Oct 5, 2017
|
||||
| [3.9.1](#391) | | Sep 28, 2017
|
||||
| [3.9.0](#390) | | Sep 11, 2017
|
||||
| [3.8.0](#380) | | Jul 29, 2017
|
||||
@@ -39,13 +40,39 @@ 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
|
||||
|
||||
To be released on Jan 13, 2018.
|
||||
|
||||
## 3.11.0
|
||||
|
||||
To be released on Dec 2, 2017.
|
||||
|
||||
- [#1044][1044] Enhancements to ROP
|
||||
+ Much better support for 64-bit Intel (amd64) ROP
|
||||
+ ROP gadget selection is optimized to favor multi-pops instead of multiple single-pop gadgets
|
||||
+ Added support for blacklisting byte values in ROP gadget addresses
|
||||
- [#1049][1049] Enhancements to `cyclic`
|
||||
+ `context` now has two additional attributes, `cyclic_alphabet` and `cyclic_length`, which correspond to the arguments `alphabet` and `n` to `cyclic()` and `cyclic_find()` and related routines.
|
||||
+ The motivation for this change is to allow setting the `alphabet` globally, so that any padding / patterns generated internally to pwntools can be controlled. The specific motivation is blacklisting values in ROP padding.
|
||||
- [#1052][1052] Enhancements for detecting `QEMU_LD_PREFIX` used by QEMU user-mode emulation for sysroots
|
||||
|
||||
[1044]: https://github.com/Gallopsled/pwntools/pull/1044
|
||||
[1049]: https://github.com/Gallopsled/pwntools/pull/1049
|
||||
[1052]: https://github.com/Gallopsled/pwntools/pull/1052
|
||||
|
||||
## 3.10.0
|
||||
|
||||
To be released on Oct 21, 2017.
|
||||
- [#1007][1007] Add support for setting a `gdbinit` file in the context
|
||||
- [#1055][1055] Fixes for `Corefile` stack parsing, speed up `ELF.string()`
|
||||
- [#1057][1057] Fix a variable name typo in `DynELF` logging which results in an exception being thrown
|
||||
- [#1058][1058] Fix an edge case in `ssh_process.exe`
|
||||
|
||||
[1007]: https://github.com/Gallopsled/pwntools/pull/1007
|
||||
[1055]: https://github.com/Gallopsled/pwntools/pull/1055
|
||||
[1057]: https://github.com/Gallopsled/pwntools/pull/1057
|
||||
[1058]: https://github.com/Gallopsled/pwntools/pull/1058
|
||||
|
||||
|
||||
## 3.9.2
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -150,7 +150,7 @@ class _Tls_DictStack(threading.local, _DictStack):
|
||||
|
||||
def _validator(validator):
|
||||
"""
|
||||
Validator that tis tightly coupled to the implementation
|
||||
Validator that is tightly coupled to the implementation
|
||||
of the classes here.
|
||||
|
||||
This expects that the object has a ._tls property which
|
||||
@@ -340,6 +340,7 @@ class ContextType(object):
|
||||
'delete_corefiles': False,
|
||||
'device': os.getenv('ANDROID_SERIAL', None) or None,
|
||||
'endian': 'little',
|
||||
'gdbinit': "",
|
||||
'kernel': None,
|
||||
'log_level': logging.INFO,
|
||||
'log_file': _devnull(),
|
||||
@@ -1231,6 +1232,23 @@ class ContextType(object):
|
||||
"""
|
||||
return bool(v)
|
||||
|
||||
|
||||
@_validator
|
||||
def gdbinit(self, value):
|
||||
"""Path to the gdbinit that is used when running GDB locally.
|
||||
|
||||
This is useful if you want pwntools-launched GDB to include some additional modules,
|
||||
like PEDA but you do not want to have GDB include them by default.
|
||||
|
||||
The setting will only apply when GDB is launched locally since remote hosts may not have
|
||||
the necessary requirements for the gdbinit.
|
||||
|
||||
If set to an empty string, GDB will use the default `~/.gdbinit`.
|
||||
|
||||
Default value is ``""``.
|
||||
"""
|
||||
return str(value)
|
||||
|
||||
#*************************************************************************
|
||||
# ALIASES
|
||||
#*************************************************************************
|
||||
|
||||
+1
-1
@@ -447,7 +447,7 @@ class DynELF(object):
|
||||
if debug:
|
||||
w.status("r_debug.linkmap")
|
||||
linkmap = self.leak.field(debug, r_debug.r_map)
|
||||
w.status("r_debug.linkmap %#x" % result)
|
||||
w.status("r_debug.linkmap %#x" % linkmap)
|
||||
|
||||
if not linkmap:
|
||||
w.failure("Could not find DT_PLTGOT or DT_DEBUG")
|
||||
|
||||
+116
-10
@@ -389,7 +389,7 @@ class Corefile(ELF):
|
||||
|
||||
>>> core.vdso.data[:4]
|
||||
'\x7fELF'
|
||||
>>> core.libc # docteset: +ELLIPSIS
|
||||
>>> core.libc # doctest: +ELLIPSIS
|
||||
Mapping('/lib/x86_64-linux-gnu/libc-...', ...)
|
||||
|
||||
The corefile also contains a :attr:`.Corefile.stack` property, which gives
|
||||
@@ -421,7 +421,7 @@ class Corefile(ELF):
|
||||
|
||||
>>> s = ssh('travis', 'example.pwnme')
|
||||
>>> _ = s.set_working_directory()
|
||||
>>> elf = ELF.from_assembly('int3')
|
||||
>>> elf = ELF.from_assembly(shellcraft.trap())
|
||||
>>> path = s.upload(elf.path)
|
||||
>>> _ =s.chmod('+x', path)
|
||||
>>> io = s.process(path)
|
||||
@@ -429,6 +429,46 @@ class Corefile(ELF):
|
||||
-1
|
||||
>>> io.corefile.signal == signal.SIGTRAP # doctest: +SKIP
|
||||
True
|
||||
|
||||
Tests:
|
||||
|
||||
These are extra tests not meant to serve as examples.
|
||||
|
||||
Corefile.getenv() works correctly, even if the environment variable's
|
||||
value contains embedded '='. Corefile is able to find the stack, even
|
||||
if the stack pointer doesn't point at the stack.
|
||||
|
||||
>>> elf = ELF.from_assembly(shellcraft.crash())
|
||||
>>> io = elf.process(env={'FOO': 'BAR=BAZ'})
|
||||
>>> io.wait()
|
||||
>>> core = io.corefile
|
||||
>>> core.getenv('FOO')
|
||||
'BAR=BAZ'
|
||||
>>> core.sp == 0
|
||||
True
|
||||
>>> core.sp in core.stack
|
||||
False
|
||||
|
||||
Corefile gracefully handles the stack being filled with garbage, including
|
||||
argc / argv / envp being overwritten.
|
||||
|
||||
>>> context.clear(arch='i386')
|
||||
>>> assembly = '''
|
||||
... LOOP:
|
||||
... mov dword ptr [esp], 0x41414141
|
||||
... pop eax
|
||||
... jmp LOOP
|
||||
... '''
|
||||
>>> elf = ELF.from_assembly(assembly)
|
||||
>>> io = elf.process()
|
||||
>>> io.wait()
|
||||
>>> core = io.corefile
|
||||
>>> core.argc, core.argv, core.env
|
||||
(0, [], {})
|
||||
>>> core.stack.data.endswith('AAAA')
|
||||
True
|
||||
>>> core.fault_addr == core.sp
|
||||
True
|
||||
"""
|
||||
|
||||
_fill_gaps = False
|
||||
@@ -454,6 +494,9 @@ class Corefile(ELF):
|
||||
#: variable.
|
||||
#:
|
||||
#: Note: Use with the :meth:`.ELF.string` method to extract them.
|
||||
#:
|
||||
#: Note: If FOO=BAR is in the environment, self.env['FOO'] is the
|
||||
#: address of the string "BAR\x00".
|
||||
self.env = {}
|
||||
|
||||
#: :class:`list`: List of addresses of arguments on the stack.
|
||||
@@ -664,7 +707,22 @@ class Corefile(ELF):
|
||||
|
||||
@property
|
||||
def signal(self):
|
||||
""":class:`int`: Signal which caused the core to be dumped."""
|
||||
""":class:`int`: Signal which caused the core to be dumped.
|
||||
|
||||
Example:
|
||||
|
||||
>>> elf = ELF.from_assembly(shellcraft.trap())
|
||||
>>> io = elf.process()
|
||||
>>> io.wait()
|
||||
>>> io.corefile.signal == signal.SIGTRAP
|
||||
True
|
||||
|
||||
>>> elf = ELF.from_assembly(shellcraft.crash())
|
||||
>>> io = elf.process()
|
||||
>>> io.wait()
|
||||
>>> io.corefile.signal == signal.SIGSEGV
|
||||
True
|
||||
"""
|
||||
if self.siginfo:
|
||||
return int(self.siginfo.si_signo)
|
||||
if self.prstatus:
|
||||
@@ -675,7 +733,17 @@ class Corefile(ELF):
|
||||
""":class:`int`: Address which generated the fault, for the signals
|
||||
SIGILL, SIGFPE, SIGSEGV, SIGBUS. This is only available in native
|
||||
core dumps created by the kernel. If the information is unavailable,
|
||||
this returns the address of the instruction pointer."""
|
||||
this returns the address of the instruction pointer.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
>>> elf = ELF.from_assembly('mov eax, 0xdeadbeef; jmp eax', arch='i386')
|
||||
>>> io = elf.process()
|
||||
>>> io.wait()
|
||||
>>> io.corefile.fault_addr == io.corefile.eax == 0xdeadbeef
|
||||
True
|
||||
"""
|
||||
if self.siginfo:
|
||||
return int(self.siginfo.sigfault_addr)
|
||||
|
||||
@@ -848,11 +916,31 @@ class Corefile(ELF):
|
||||
# Now we can fill in the environment
|
||||
env_pointer_data = stack[start_of_envp:p_last_env_addr+self.bytes]
|
||||
for pointer in unpack_many(env_pointer_data):
|
||||
end = stack.find('=', last_env_addr)
|
||||
|
||||
if pointer in stack and end in stack:
|
||||
name = stack[pointer:end]
|
||||
self.env[name] = pointer
|
||||
# If the stack is corrupted, the pointer will be outside of
|
||||
# the stack.
|
||||
if pointer not in stack:
|
||||
continue
|
||||
|
||||
try:
|
||||
name_value = self.string(pointer)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
name, value = name_value.split('=', 1)
|
||||
|
||||
# "end" points at the byte after the null terminator
|
||||
end = pointer + len(name_value) + 1
|
||||
|
||||
# Do not mark things as environment variables if they point
|
||||
# outside of the stack itself, or we had to cross into a different
|
||||
# mapping (after the stack) to read it.
|
||||
# This may occur when the entire stack is filled with non-NUL bytes,
|
||||
# and we NULL-terminate on a read failure in .string().
|
||||
if end not in stack:
|
||||
continue
|
||||
|
||||
self.env[name] = pointer + len(name) + len('=')
|
||||
|
||||
# May as well grab the arguments off the stack as well.
|
||||
# argc comes immediately before argv[0] on the stack, but
|
||||
@@ -904,15 +992,32 @@ class Corefile(ELF):
|
||||
|
||||
Returns:
|
||||
:class:`str`: The contents of the environment variable.
|
||||
|
||||
Example:
|
||||
|
||||
>>> elf = ELF.from_assembly(shellcraft.trap())
|
||||
>>> io = elf.process(env={'GREETING': 'Hello!'})
|
||||
>>> io.wait()
|
||||
>>> io.corefile.getenv('GREETING')
|
||||
'Hello!'
|
||||
"""
|
||||
if name not in self.env:
|
||||
log.error("Environment variable %r not set" % name)
|
||||
|
||||
return self.string(self.env[name]).split('=',1)[-1]
|
||||
return self.string(self.env[name])
|
||||
|
||||
@property
|
||||
def registers(self):
|
||||
""":class:`dict`: All available registers in the coredump."""
|
||||
""":class:`dict`: All available registers in the coredump.
|
||||
|
||||
Example:
|
||||
|
||||
>>> elf = ELF.from_assembly('mov eax, 0xdeadbeef;' + shellcraft.trap(), arch='i386')
|
||||
>>> io = elf.process()
|
||||
>>> io.wait()
|
||||
>>> io.corefile.registers['eax'] == 0xdeadbeef
|
||||
True
|
||||
"""
|
||||
if not self.prstatus:
|
||||
return {}
|
||||
|
||||
@@ -1020,6 +1125,7 @@ class CorefileFinder(object):
|
||||
new_path = 'core.%i' % core_pid
|
||||
if core_pid > 0 and new_path != self.core_path:
|
||||
write(new_path, self.read(self.core_path))
|
||||
self.unlink(self.core_path)
|
||||
self.core_path = new_path
|
||||
|
||||
# Check the PID
|
||||
|
||||
+22
-5
@@ -1686,16 +1686,33 @@ class ELF(ELFFile):
|
||||
return packing.unpack(self.read(address, context.bytes), *a, **kw)
|
||||
|
||||
def string(self, address):
|
||||
"""Reads a null-terminated string from the specified ``address``"""
|
||||
"""string(address) -> str
|
||||
|
||||
Reads a null-terminated string from the specified ``address``
|
||||
|
||||
Returns:
|
||||
A ``str`` with the string contents (NUL terminator is omitted),
|
||||
or an empty string if no NUL terminator could be found.
|
||||
"""
|
||||
data = ''
|
||||
while True:
|
||||
c = self.read(address, 1)
|
||||
read_size = 0x1000
|
||||
partial_page = address & 0xfff
|
||||
|
||||
if partial_page:
|
||||
read_size -= partial_page
|
||||
|
||||
c = self.read(address, read_size)
|
||||
|
||||
if not c:
|
||||
return ''
|
||||
if c == '\x00':
|
||||
return data
|
||||
|
||||
data += c
|
||||
address += 1
|
||||
|
||||
if '\x00' in c:
|
||||
return data[:data.index('\x00')]
|
||||
|
||||
address += len(c)
|
||||
|
||||
def flat(self, address, *a, **kw):
|
||||
"""Writes a full array of values to the specified address.
|
||||
|
||||
@@ -646,6 +646,10 @@ def attach(target, gdbscript = None, exe = None, need_ptrace_scope = True, gdb_a
|
||||
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:
|
||||
|
||||
+2
-1
@@ -341,7 +341,8 @@ class ssh_process(ssh_channel):
|
||||
libs = self.parent.libs(self.executable)
|
||||
|
||||
for lib in libs:
|
||||
if self.executable in lib:
|
||||
# Cannot just check "executable in lib", see issue #1047
|
||||
if lib.endswith(self.executable):
|
||||
return pwnlib.elf.elf.ELF(lib)
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__ = '3.9.2'
|
||||
__version__ = '3.10.0'
|
||||
@@ -86,7 +86,7 @@ except Exception as e:
|
||||
setup(
|
||||
name = 'pwntools',
|
||||
packages = find_packages(),
|
||||
version = '3.9.2',
|
||||
version = '3.10.0',
|
||||
data_files = [('',
|
||||
glob.glob('*.md') + glob.glob('*.txt')),
|
||||
],
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ setup_android_emulator()
|
||||
elif [[ -n "$TRAVIS_TAG" ]]; then
|
||||
echo "TRAVIS_TAG ($TRAVIS_TAG) indicates a new relase"
|
||||
echo "Forcing Android Emulator installation"
|
||||
elif (git log --stat "$TRAVIS_COMMIT_RANGE" | grep -iE "android|adb"); then
|
||||
elif (git log --stat "$TRAVIS_COMMIT_RANGE" | grep -iE "android|adb" | grep -v "commit "); then
|
||||
echo "Found Android-related commits, forcing Android Emulator installation"
|
||||
else
|
||||
# In order to avoid running the doctests that require the Android
|
||||
|
||||
Reference in new issue
Block a user