Skip to content

Commit

Permalink
Release 3.11.0beta0
Browse files Browse the repository at this point in the history
  • Loading branch information
TethysSvensson committed Oct 25, 2017
2 parents d9f5a5b + cca1083 commit 712295f
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 27 deletions.
15 changes: 10 additions & 5 deletions CHANGELOG.md
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion pwnlib/dynelf.py
Expand Up @@ -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")
Expand Down
127 changes: 116 additions & 11 deletions pwnlib/elf/corefile.py
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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:
Expand All @@ -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)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {}

Expand Down Expand Up @@ -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
Expand Down
27 changes: 22 additions & 5 deletions pwnlib/elf/elf.py
Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion pwnlib/tubes/ssh.py
Expand Up @@ -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)


Expand Down
2 changes: 1 addition & 1 deletion pwnlib/version.py
@@ -1 +1 @@
__version__ = '3.11.0dev'
__version__ = '3.11.0beta0'
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -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')),
],
Expand Down
2 changes: 1 addition & 1 deletion travis/install.sh
Expand Up @@ -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
Expand Down

0 comments on commit 712295f

Please sign in to comment.