Permalink
Browse files

Release 3.11.0beta0

  • Loading branch information...
Idolf committed Oct 25, 2017
2 parents d9f5a5b + cca1083 commit 712295f3c52061ad27c22e8c6cfb194f5f902bc1
Showing with 155 additions and 27 deletions.
  1. +10 −5 CHANGELOG.md
  2. +1 −1 README.md
  3. +1 −1 pwnlib/dynelf.py
  4. +116 −11 pwnlib/elf/corefile.py
  5. +22 −5 pwnlib/elf/elf.py
  6. +2 −1 pwnlib/tubes/ssh.py
  7. +1 −1 pwnlib/version.py
  8. +1 −1 setup.py
  9. +1 −1 travis/install.sh
View
@@ -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,6 +40,10 @@ 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.
@@ -58,12 +63,12 @@ To be released on Dec 2, 2017.
## 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
View
@@ -2,7 +2,7 @@
![pwntools logo](https://github.com/Gallopsled/pwntools/blob/stable/docs/source/logo.png?raw=true)
[![Docs](https://readthedocs.org/projects/pwntools/badge/?version=stable)](https://docs.pwntools.com/)
[![PyPI](https://img.shields.io/badge/pypi-v3.9.2-green.svg?style=flat)](https://pypi.python.org/pypi/pwntools/)
[![PyPI](https://img.shields.io/badge/pypi-v3.10.0-green.svg?style=flat)](https://pypi.python.org/pypi/pwntools/)
[![Travis](https://travis-ci.org/Gallopsled/pwntools.svg)](https://travis-ci.org/Gallopsled/pwntools)
[![Coveralls](https://img.shields.io/coveralls/Gallopsled/pwntools/dev.svg)](https://coveralls.io/github/Gallopsled/pwntools?branch=dev)
[![Twitter](https://img.shields.io/badge/twitter-pwntools-4099FF.svg?style=flat)](https://twitter.com/pwntools)
View
@@ -447,7 +447,7 @@ def _find_linkmap(self, pltgot=None, debug=None):
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")
View
@@ -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)
@@ -438,6 +438,46 @@ class Corefile(ELF):
>>> io.wait()
>>> io.corefile.fault_addr
1234
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
@@ -463,6 +503,9 @@ def __init__(self, *a, **kw):
#: 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.
@@ -673,7 +716,22 @@ def ppid(self):
@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:
@@ -684,9 +742,18 @@ def fault_addr(self):
""":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."""
fault_addr = 0
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
"""
fault_addr = 0
if self.siginfo:
fault_addr = int(self.siginfo.sigfault_addr)
@@ -878,11 +945,31 @@ def _parse_stack(self):
# 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
@@ -934,15 +1021,32 @@ def getenv(self, name):
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 {}
@@ -1050,6 +1154,7 @@ def __init__(self, proc):
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
View
@@ -1692,16 +1692,33 @@ def unpack(self, address, *a, **kw):
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.
View
@@ -341,7 +341,8 @@ def elf(self):
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)
View
@@ -1 +1 @@
__version__ = '3.11.0dev'
__version__ = '3.11.0beta0'
View
@@ -86,7 +86,7 @@
setup(
name = 'pwntools',
packages = find_packages(),
version = '3.11.0dev',
version = '3.11.0beta0',
data_files = [('',
glob.glob('*.md') + glob.glob('*.txt')),
],
View
@@ -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

0 comments on commit 712295f

Please sign in to comment.