Compare commits

..
1 Commits
Author SHA1 Message Date
Mathias Svensson fef4483e22 Release 3.3.3 2017-01-10 23:35:11 +01:00
29 changed files with 235 additions and 506 deletions

No files matched your search

+1 -5
View File
@@ -13,7 +13,6 @@ addons:
- mksh
- zsh
- pandoc
- gdb
cache:
- pip
- directories:
@@ -23,14 +22,11 @@ cache:
python:
- "2.7"
before_install:
- echo $-
- source travis/install.sh
- source travis/ssh_setup.sh
- echo $-
install:
- pip install --upgrade pip
- pip install --upgrade --editable .
- pip install --upgrade --requirement docs/requirements.txt
- pip install --requirement docs/requirements.txt
before_script:
- PWNLIB_NOTERM=1 python -c 'from pwn import *; print pwnlib.term.term_mode'
- PWNLIB_NOTERM=1 python -c 'from pwn import *; print pwnlib.term.term_mode'
+8 -52
View File
@@ -9,13 +9,9 @@ The table below shows which release corresponds to each branch, and what date th
| Version | Branch | Release Date |
| ---------------- | -------- | ---------------------- |
| [3.6.0](#350) | `dev` | May 6, 2017 (planned)
| [3.5.0](#350) | `beta` | Mar 25, 2017 (planned)
| [3.4.1](#341) | `stable` | Feb 17, 2017
| [3.4.0](#340) | | Feb 13, 2017
| [3.3.4](#334) | | Jan 12, 2016
| [3.3.3](#333) | | Jan 10, 2016
| [3.3.2](#332) | | Jan 10, 2016
| [3.5.0](#350) | `dev` | Mar 18, 2017 (planned)
| [3.4.0](#340) | `beta` | Feb 4, 2017 (planned)
| [3.3.2](#332) | `stable` | Jan 10, 2016
| [3.3.1](#331) | | Jan 10, 2016
| [3.3.0](#330) | | Dec 24, 2016
| [3.2.1](#321) | | Dec 24, 2016
@@ -29,53 +25,13 @@ 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.6.0
To be released on May 6, 2017.
## 3.5.0
To be released on Mar 25, 2017.
## 3.4.1
- [#894][894] Fix a bug when using `gdb.debug()` over ssh.
- [e021f57][e021f57] Fix a bug ([#891][891]) in `rop` when needing to insert padding to fix alignment
[e021f57]: https://github.com/Gallopsled/pwntools/commit/e021f57
[894]: https://github.com/Gallopsled/pwntools/pull/894
[891]: https://github.com/Gallopsled/pwntools/issues/891
To be released on Mar 18, 2017.
## 3.4.0
- [#800][800] Add `shell=` option to `ssh.process()`
- [#806][806] Add `context.buffer_size` for fine-tuning `tube` performance
+ Also adds `buffer_fill_size=` argument for all tubes
- [b83a6c7][b83a6c7] Fix undocumented `process.leak` function
- [546061e][546061e] Modify `coredump_filter` of all spawned processes, so that core dumps are more complete
- [#809][809] Add several functions to `adb` (`unlink`, `mkdir`, `makedirs`, `isdir`, `exists`)
- [#817][817] Make disconnection detection more robust
[800]: https://github.com/Gallopsled/pwntools/pull/800
[806]: https://github.com/Gallopsled/pwntools/pull/806
[809]: https://github.com/Gallopsled/pwntools/pull/809
[817]: https://github.com/Gallopsled/pwntools/pull/817
[5d9792f]: https://github.com/Gallopsled/pwntools/commit/5d9792f
[b83a6c7]: https://github.com/Gallopsled/pwntools/commit/b83a6c7
[546061e]: https://github.com/Gallopsled/pwntools/commit/546061e
## 3.3.4
- [#850][850] and [#846][846] fix issues with `hexdump` and the `phd` command-line utility, when using pipes (e.g. `echo foo | phd`)
- [#852][852] Fixes register ordering in `regsort`
- [#853][853] Fixes the registers restored in `shellcraft.amd64.popad`
[846]: https://github.com/gallopsled/pwntools/pull/846
[850]: https://github.com/gallopsled/pwntools/pull/850
[852]: https://github.com/gallopsled/pwntools/pull/852
[853]: https://github.com/gallopsled/pwntools/pull/853
To be released on Feb 4, 2017.
## 3.3.3
@@ -185,7 +141,7 @@ Multiple bug fixes.
- [1e414af][1e414af] Added `connect()` alias for `remote()`
+ For example, `io=connect('google.com', 80)`
+ This also works with `tcp(...)` and `udp(...)` aliases
- [869ec42][869ec42] Added `ssh.read()` and `ssh.write()` aliases
- [869ec42][869ec42] Added `ssh.read()` and `ssh.write()` aiases
- [2af55c9][2af55c9] `AdbDevice` objects exposed via e.g. `adb.devices()` now offer scoped access to all `adb` module properties
+ It is now possible to e.g. `map(lambda d: d.process(['id']).recvall(), adb.devices())`
@@ -272,7 +228,7 @@ Android support via a new `adb` module, `context.device`, `context.adb_host`, an
- Added `context.log_file` setting for sending logs to a file. This can be set with the `LOG_FILE` magic command-line option.
- Added `context.noptrace` setting for disabling actions which require `ptrace` support. This is useful for turning all `gdb.debug` and `gdb.attach` options into no-ops, and can be set via the `NOPTRACE` magic command-line option.
- Added `context.proxy` which hooks all connections and sends them to a SOCKS4/SOCKS5. This can be set via the `PROXY` magic command-line option.
- Added `context.randomize` to control randomization of settings like XOR keys and register ordering (default off).
- Added `context.randomize` to control randommization of settings like XOR keys and register ordering (default off).
- Added `context.terminal` for setting how to launch commands in a new terminal.
#### DynELF and MemLeak Module
@@ -285,7 +241,7 @@ Android support via a new `adb` module, `context.device`, `context.adb_host`, an
#### Encoders Module
- Added a `pwnlib.encoders` module for assembled-shellcode encoders/decoders
- Includes position-independent basic XOR encoders
- Includes position-indepentent basic XOR encoders
- Includes position-independent delta encoders
- Includes non-position-independent alphanumeric encoders for Intel
- Includes position-independent alphanumeric encoders for ARM/Thumb
+2 -2
View File
@@ -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.4.1-green.svg?style=flat)](https://pypi.python.org/pypi/pwntools/)
[![PyPI](https://img.shields.io/badge/pypi-v3.3.3-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)
@@ -38,7 +38,7 @@ Most of the functionality of pwntools is self-contained and Python-only. You sh
```sh
apt-get update
apt-get install python2.7 python-pip python-dev git libssl-dev libffi-dev
apt-get install python2.7 python-pip python-dev git libssl-dev
pip install --upgrade pwntools
```
+1 -2
View File
@@ -24,8 +24,7 @@ pwntools is available as a ``pip`` package.
.. code-block:: bash
$ apt-get update
$ apt-get install python2.7 python-pip python-dev git libssl-dev libffi-dev
$ apt-get install python2.7 python2.7-dev python-pip
$ pip install --upgrade pwntools
Development
+4 -147
View File
@@ -599,150 +599,6 @@ def write(path, data=''):
misc.write(temp.name, data)
push(temp.name, path)
@context.quiet
@with_device
def mkdir(path):
"""Create a directory on the target device.
Note:
Silently succeeds if the directory already exists.
Arguments:
path(str): Directory to create.
Examples:
>>> adb.mkdir('/')
>>> path = '/data/local/tmp/mkdir_test'
>>> adb.exists(path)
False
>>> adb.mkdir(path)
>>> adb.exists(path)
True
>>> adb.mkdir('/init')
Traceback (most recent call last):
...
PwnlibException: mkdir failed for /init, File exists
"""
if not path.startswith('/'):
log.error("Must provide an absolute path: %r" % path)
with Client() as c:
st = c.stat(path)
# Don't re-create existing directories
if st and stat.S_ISDIR(st['mode']):
return
result = process(['mkdir', path]).recvall()
# Any output at all is an error
if result:
log.error(result)
@context.quiet
@with_device
def makedirs(path):
"""Create a directory and all parent directories on the target device.
Note:
Silently succeeds if the directory already exists.
Examples:
>>> adb.makedirs('/data/local/tmp/this/is/a/directory/heirarchy')
>>> adb.listdir('/data/local/tmp/this/is/a/directory')
['heirarchy']
"""
if path != '/':
makedirs(os.path.dirname(path))
mkdir(path)
@context.quiet
@with_device
def exists(path):
"""Return ``True`` if ``path`` exists on the target device.
Examples:
>>> adb.exists('/')
True
>>> adb.exists('/init')
True
>>> adb.exists('/does/not/exist')
False
"""
with Client() as c:
return bool(c.stat(path))
@context.quiet
@with_device
def isdir(path):
"""Return ``True`` if ``path`` is a on the target device.
Examples:
>>> adb.isdir('/')
True
>>> adb.isdir('/init')
False
>>> adb.isdir('/does/not/exist')
False
"""
with Client() as c:
st = c.stat(path)
return bool(st and stat.S_ISDIR(st['mode']))
@context.quiet
@with_device
def unlink(path, recursive=False):
"""Unlinks a file or directory on the target device.
Examples:
>>> adb.unlink("/does/not/exist")
Traceback (most recent call last):
...
PwnlibException: Could not unlink '/does/not/exist': Does not exist
>>> filename = '/data/local/tmp/unlink-test'
>>> adb.write(filename, 'hello')
>>> adb.exists(filename)
True
>>> adb.unlink(filename)
>>> adb.exists(filename)
False
>>> adb.mkdir(filename)
>>> adb.write(filename + '/contents', 'hello')
>>> adb.unlink(filename)
Traceback (most recent call last):
...
PwnlibException: Cannot delete non-empty directory '/data/local/tmp/unlink-test' without recursive=True
>>> adb.unlink(filename, recursive=True)
>>> adb.exists(filename)
False
"""
with Client() as c:
st = c.stat(path)
if not st:
log.error("Could not unlink %r: Does not exist" % path)
# If the directory is not empty, do not delete it
if isdir(path) and c.list(path) and not recursive:
log.error("Cannot delete non-empty directory %r without recursive=True" % path)
flags = '-rf' if recursive else '-r'
output = c.execute(['rm', flags, path]).recvall()
if output:
log.error(output)
@with_device
def process(argv, *a, **kw):
"""Execute a process on the device.
@@ -904,15 +760,16 @@ def listdir(directory='/'):
"""
return list(sorted(Client().list(directory)))
@with_device
def fastboot(args, *a, **kw):
"""Executes a fastboot command.
Returns:
The command output.
"""
argv = ['fastboot', '-s', str(context.device)] + list(args)
return tubes.process.process(argv, *a, **kw).recvall()
serial = context.device
if not serial:
log.error("Unknown device")
return tubes.process.process(['fastboot', '-s', serial] + list(args), **kw).recvall()
@with_device
def fingerprint():
+1 -1
View File
@@ -16,8 +16,8 @@ from . import scramble
from . import shellcraft
from . import unhex
from . import update
from ..context import context
from .common import parser
from ..context import context
commands = {
'asm': asm.main,
+1 -1
View File
@@ -21,7 +21,7 @@ p.add_argument('--pre', action='store_true', help='''
def main(a):
result = pwnlib.update.perform_check(prerelease=a.pre)
if a.install:
subprocess.check_call(result, shell=False)
subprocess.check_call(result, shell=True)
if __name__ == '__main__':
pwnlib.common.main(__file__)
+1 -10
View File
@@ -330,7 +330,6 @@ class ContextType(object):
'aslr': True,
'binary': None,
'bits': 32,
'buffer_size': 4096,
'device': os.getenv('ANDROID_SERIAL', None) or None,
'endian': 'little',
'kernel': None,
@@ -1147,15 +1146,6 @@ class ContextType(object):
return command
@_validator
def buffer_size(self, size):
"""Internal buffer size to use for ``tube`` objects.
This is not the maximum size of the buffer, but this is the amount of data
which is passed to each raw ``read`` syscall (or equivalent).
"""
return int(size)
@property
def cache_dir(self):
"""Directory used for caching data.
@@ -1195,6 +1185,7 @@ class ContextType(object):
return cache
#*************************************************************************
# ALIASES
#*************************************************************************
@@ -7,7 +7,6 @@ import re
import urllib
from bs4 import BeautifulSoup
from pwn import *
uas = set()
+5 -8
View File
@@ -226,7 +226,7 @@ def debug(args, execute=None, exe=None, ssh=None, env=None, **kwargs):
if not ssh and context.os == 'android':
host = context.adb_host
attach((host, port), exe=exe, execute=execute, need_ptrace_scope = False, ssh=ssh)
attach((host, port), exe=exe, execute=execute, need_ptrace_scope = False)
# gdbserver outputs a message when a client connects
garbage = gdbserver.recvline(timeout=1)
@@ -247,8 +247,8 @@ def get_gdb_arch():
@LocalContext
def attach(target, execute = None, exe = None, need_ptrace_scope = True, ssh = None):
"""attach(target, execute = None, exe = None, arch = None, ssh = None) -> None
def attach(target, execute = None, exe = None, need_ptrace_scope = True):
"""attach(target, execute = None, exe = None, arch = None) -> None
Start GDB in a new terminal and attach to `target`.
:func:`pwnlib.util.proc.pidof` is used to find the PID of `target` except
@@ -401,11 +401,8 @@ def attach(target, execute = None, exe = None, need_ptrace_scope = True, ssh = N
cmd += ' -q '
if exe and context.native:
if ssh:
ssh.download_file(exe)
exe = os.path.basename(exe)
if not os.path.isfile(exe):
log.error('No such file: %s' % exe)
log.error('no such file: %s' % exe)
cmd += ' "%s"' % exe
if pid and not context.os == 'android':
@@ -463,7 +460,7 @@ def ssh_gdb(ssh, process, execute = None, arch = None, **kwargs):
l = tubes.listen.listen(0)
forwardport = l.lport
attach(('127.0.0.1', forwardport), execute, local_exe, arch, ssh=ssh)
attach(('127.0.0.1', forwardport), execute, local_exe, arch)
l.wait_for_connection() <> ssh.connect_remote('127.0.0.1', gdbport)
return c
-11
View File
@@ -606,14 +606,3 @@ class MemLeak(object):
return result
return MemLeak(string_wrapper)
# Aliases for convenience
u64 = q
u32 = d
u16 = w
u8 = b
p64 = setq
p32 = setd
p16 = setw
p8 = setb
+1 -5
View File
@@ -224,8 +224,6 @@ def regsort(in_out, all_regs, tmp = None, xchg = True, randomize = None):
('mov', 'b', 'c'),
('mov', 'c', 'x'),
('mov', 'x', '1')]
>>> regsort({'a': 'b', 'b': 'c'}, ['a','b','c'], xchg=0)
[('mov', 'a', 'b'), ('mov', 'b', 'c')]
"""
if randomize is None:
randomize = context.randomize
@@ -335,9 +333,7 @@ def regsort(in_out, all_regs, tmp = None, xchg = True, randomize = None):
# If XCHG is expressly disabled, and there is no temporary register,
# try to see if there is any register which can be used as a temp
# register instead.
#
# If there are no cycles, there's no need for a temporary register.
if in_cycle and not (xchg or tmp):
if not (xchg or tmp):
for reg in in_out:
if not depends_on_cycle(reg, in_out, in_cycle):
tmp = reg
+1 -3
View File
@@ -478,9 +478,7 @@ class ROP(object):
"""
Generates padding to be inserted into the ROP stack.
"""
if count:
return cyclic.cyclic(offset + count)[-count:]
return ''
return cyclic.cyclic(offset + count)[-count:]
def describe(self, object):
"""
+2 -2
View File
@@ -6,8 +6,8 @@ in the same order.
pop rdi
pop rsi
pop rbp
pop rbx /* add rsp, 8 */
pop rbx
pop rsp
pop rbp
pop rdx
pop rcx
pop rax
-6
View File
@@ -22,7 +22,6 @@ import threading
import traceback
from . import termcap
from ..context import ContextType
settings = None
_graphics_mode = False
@@ -128,11 +127,6 @@ def init():
sys.stdout = Wrapper(sys.stdout)
if sys.stderr.isatty():
sys.stderr = Wrapper(sys.stderr)
console = ContextType.defaults['log_console']
if console.isatty():
ContextType.defaults['log_console'] = Wrapper(console)
# freeze all cells if an exception is thrown
orig_hook = sys.excepthook
def hook(*args):
+2 -21
View File
@@ -1,8 +1,5 @@
#!/usr/bin/env python2
from ..context import context
class Buffer(Exception):
"""
List of strings with some helper routines.
@@ -31,10 +28,10 @@ class Buffer(Exception):
The ``0th`` item in the buffer is the oldest item, and
will be received first.
"""
def __init__(self, buffer_fill_size = None):
def __init__(self):
self.data = [] # Buffer
self.size = 0 # Length
self.buffer_fill_size = buffer_fill_size
def __len__(self):
"""
@@ -172,19 +169,3 @@ class Buffer(Exception):
self.size -= len(data)
return data
def get_fill_size(self, size=None):
"""
Retrieves the default fill size for this buffer class.
Arguments:
size (int): (Optional) If set and not None, returns the size variable back.
Returns:
Fill size as integer if size == None, else size.
"""
if size is None:
size = self.buffer_fill_size
with context.local(buffer_size=size):
return context.buffer_size
+4 -2
View File
@@ -20,11 +20,13 @@ class listen(sock):
bindaddr(str): The address to bind to.
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`.
timeout: A positive number, None
"""
def __init__(self, port=0, bindaddr = "0.0.0.0",
fam = "any", typ = "tcp", *args, **kwargs):
super(listen, self).__init__(*args, **kwargs)
fam = "any", typ = "tcp",
timeout = Timeout.default, level = None):
super(listen, self).__init__(timeout, level = level)
port = int(port)
fam = {socket.AF_INET: 'ipv4',
+7 -6
View File
@@ -44,6 +44,8 @@ class process(tube):
Working directory. Uses the current working directory by default.
env(dict):
Environment variables. By default, inherits from Python's environment.
timeout(int):
Timeout to use on ``tube`` ``recv`` operations.
stdin(int):
File object or file descriptor number to use for ``stdin``.
By default, a pipe is used. A pty can be used instead by setting
@@ -204,9 +206,11 @@ class process(tube):
executable = None,
cwd = None,
env = None,
timeout = Timeout.default,
stdin = PIPE,
stdout = PTY,
stderr = STDOUT,
level = None,
close_fds = True,
preexec_fn = lambda: None,
raw = True,
@@ -214,11 +218,8 @@ class process(tube):
setuid = None,
where = 'local',
display = None,
alarm = None,
*args,
**kwargs
):
super(process, self).__init__(*args,**kwargs)
alarm = None):
super(process, self).__init__(timeout, level = level)
# Permit using context.binary
if argv is None:
@@ -793,7 +794,7 @@ class process(tube):
import pwnlib.elf.corefile
return pwnlib.elf.corefile.Core(filename)
def leak(self, address, count=1):
def leak(self, address, count=0):
"""Leaks memory within the process at the specified address.
Arguments:
+10 -6
View File
@@ -45,8 +45,8 @@ class remote(sock):
def __init__(self, host, port,
fam = "any", typ = "tcp",
ssl=False, sock=None, *args, **kwargs):
super(remote, self).__init__(*args, **kwargs)
timeout = Timeout.default, ssl=False, sock=None, level = None):
super(remote, self).__init__(timeout, level = level)
self.rport = int(port)
self.rhost = host
@@ -150,13 +150,17 @@ class remote(sock):
class tcp(remote):
__doc__ = remote.__doc__
def __init__(self, host, port, *a, **kw):
return super(tcp, self).__init__(host, port, typ="tcp", *a, **kw)
def __init__(self, host, port,
fam = "any", typ = "tcp",
timeout = Timeout.default, ssl=False, sock=None, level = None):
return super(tcp, self).__init__(host, port, fam, typ, timeout, ssl, sock, level)
class udp(remote):
__doc__ = remote.__doc__
def __init__(self, host, port, *a, **kw):
return super(udp, self).__init__(host, port, typ="udp", *a, **kw)
def __init__(self, host, port,
fam = "any", typ = "udp",
timeout = Timeout.default, ssl=False, sock=None, level = None):
return super(udp, self).__init__(host, port, fam, typ, timeout, ssl, sock, level)
class connect(remote):
__doc__ = remote.__doc__
+23 -14
View File
@@ -9,6 +9,7 @@ from . import tube
from .. import context
from .. import term
from ..log import getLogger
from ..timeout import Timeout
log = getLogger(__name__)
@@ -17,8 +18,10 @@ class serialtube(tube.tube):
self, port = None, baudrate = 115200,
convert_newlines = True,
bytesize = 8, parity='N', stopbits=1, xonxoff = False,
rtscts = False, dsrdtr = False, *a, **kw):
super(serialtube, self).__init__(*a, **kw)
rtscts = False, dsrdtr = False,
timeout = Timeout.default,
level = None):
super(serialtube, self).__init__(timeout, level = level)
if port is None:
if platform.system() == 'Darwin':
@@ -46,14 +49,21 @@ class serialtube(tube.tube):
if not self.conn:
raise EOFError
with self.countdown():
while self.conn and self.countdown_active():
data = self.conn.read(numb)
if self.timeout == None:
end = float('inf')
else:
end = time.time() + self.timeout
if data:
return data
while self.conn:
data = self.conn.read(numb)
if data:
return data
time.sleep(min(self.timeout, 0.1))
delta = end - time.time()
if delta <= 0:
break
else:
time.sleep(min(delta, 0.1))
return None
@@ -73,11 +83,10 @@ class serialtube(tube.tube):
pass
def can_recv_raw(self, timeout):
with self.countdown(timeout):
while self.conn and self.countdown_active():
if self.conn.inWaiting():
return True
time.sleep(min(self.timeout, 0.1))
end = time.time()
while time.time() < end:
if self.conn.inWaiting():
return True
return False
def connected_raw(self, direction):
@@ -90,7 +99,7 @@ class serialtube(tube.tube):
def fileno(self):
if not self.connected():
self.error("A closed serialtube does not have a file number")
self.error("A stopped program does not have a file number")
return self.conn.fileno()
+11 -79
View File
@@ -10,8 +10,8 @@ log = getLogger(__name__)
class sock(tube):
"""Methods available exclusively to sockets."""
def __init__(self, *args, **kwargs):
super(sock, self).__init__(*args, **kwargs)
def __init__(self, timeout, level = None):
super(sock, self).__init__(timeout, level = level)
self.closed = {"recv": False, "send": False}
# Overwritten for better usability
@@ -26,13 +26,13 @@ class sock(tube):
else:
return super(sock, self).recvall(timeout)
def recv_raw(self, numb, *a):
def recv_raw(self, numb):
if self.closed["recv"]:
raise EOFError
while True:
try:
data = self.sock.recv(numb, *a)
data = self.sock.recv(numb)
break
except socket.timeout:
return None
@@ -72,89 +72,21 @@ class sock(tube):
self.sock.settimeout(timeout)
def can_recv_raw(self, timeout):
"""
Tests:
>>> l = listen()
>>> r = remote('localhost', l.lport)
>>> r.can_recv_raw(timeout=0)
False
>>> l.send('a')
>>> r.can_recv_raw(timeout=1)
True
>>> r.recv()
'a'
>>> r.can_recv_raw(timeout=0)
False
>>> l.close()
>>> r.can_recv_raw(timeout=1)
False
>>> r.closed['recv']
True
"""
if not self.sock or self.closed["recv"]:
return False
# select() will tell us data is available at EOF
can_recv = select.select([self.sock], [], [], timeout) == ([self.sock], [], [])
if not can_recv:
return False
# Ensure there's actually data, not just EOF
try:
self.recv_raw(1, socket.MSG_PEEK)
except EOFError:
return False
return True
return select.select([self.sock], [], [], timeout) == ([self.sock], [], [])
def connected_raw(self, direction):
"""
Tests:
>>> l = listen()
>>> r = remote('localhost', l.lport)
>>> r.connected()
True
>>> l.close()
>>> time.sleep(1) # Avoid race condition
>>> r.connected()
False
"""
# If there's no socket, it's definitely closed
if not self.sock:
return False
# If we have noticed a connection close in a given direction before,
# return fast.
if self.closed.get(direction, False):
return False
# If a connection is closed in all manners, return fast
if all(self.closed.values()):
return False
# Use poll() to determine the connection state
want = {
'recv': select.POLLIN,
'send': select.POLLOUT,
'any': select.POLLIN | select.POLLOUT,
}[direction]
poll = select.poll()
poll.register(self, want | select.POLLHUP | select.POLLERR)
for fd, event in poll.poll(0):
if event & select.POLLHUP:
self.close()
return False
if event & select.POLLIN:
return True
if event & select.POLLOUT:
return True
return True
if direction == 'any':
return True
elif direction == 'recv':
return not self.closed['recv']
elif direction == 'send':
return not self.closed['send']
def close(self):
if not getattr(self, 'sock', None):
+18 -38
View File
@@ -59,7 +59,7 @@ class ssh_channel(sock):
#: Only valid when instantiated through :meth:`ssh.process`
pid = None
#: Executable of the procesks
#: Executable of the process
#: Only valid when instantiated through :meth:`ssh.process`
executable = None
@@ -67,8 +67,8 @@ class ssh_channel(sock):
#: Only valid when instantiated through :meth:`ssh.process`
argv = None
def __init__(self, parent, process = None, tty = False, wd = None, env = None, raw = True, *args, **kwargs):
super(ssh_channel, self).__init__(*args, **kwargs)
def __init__(self, parent, process = None, tty = False, wd = None, env = None, timeout = Timeout.default, level = 0, raw = True):
super(ssh_channel, self).__init__(timeout, level=level)
# keep the parent from being garbage collected in some cases
self.parent = parent
@@ -191,11 +191,11 @@ class ssh_channel(sock):
return self.returncode
def can_recv_raw(self, timeout):
with self.countdown(timeout):
while self.countdown_active():
if self.sock.recv_ready():
return True
time.sleep(min(self.timeout, 0.05))
end = time.time() + timeout
while time.time() < end:
if self.sock.recv_ready():
return True
time.sleep(0.05)
return False
def interactive(self, prompt = term.text.bold_red('$') + ' '):
@@ -366,8 +366,8 @@ class ssh_channel(sock):
return e
class ssh_connecter(sock):
def __init__(self, parent, host, port, *a, **kw):
super(ssh_connecter, self).__init__(*a, **kw)
def __init__(self, parent, host, port, timeout = Timeout.default, level = None):
super(ssh_connecter, self).__init__(timeout, level = level)
# keep the parent from being garbage collected in some cases
self.parent = parent
@@ -398,8 +398,8 @@ class ssh_connecter(sock):
class ssh_listener(sock):
def __init__(self, parent, bind_address, port, *a, **kw):
super(ssh_listener, self).__init__(*a, **kw)
def __init__(self, parent, bind_address, port, timeout = Timeout.default, level = None):
super(ssh_listener, self).__init__(timeout, level = level)
# keep the parent from being garbage collected in some cases
self.parent = parent
@@ -478,7 +478,8 @@ class ssh(Timeout, Logger):
def __init__(self, user, host, port = 22, password = None, key = None,
keyfile = None, proxy_command = None, proxy_sock = None,
level = None, cache = True, ssh_agent = False, *a, **kw):
timeout = Timeout.default, level = None, cache = True,
ssh_agent = False):
"""Creates a new ssh connection.
Arguments:
@@ -497,7 +498,7 @@ class ssh(Timeout, Logger):
NOTE: The proxy_command and proxy_sock arguments is only available if a
fairly new version of paramiko is used."""
super(ssh, self).__init__(*a, **kw)
super(ssh, self).__init__(timeout)
Logger.__init__(self)
if level is not None:
@@ -622,8 +623,7 @@ class ssh(Timeout, Logger):
return self.run(shell, tty, timeout = timeout)
def process(self, argv=None, executable=None, tty=True, cwd=None, env=None, timeout=Timeout.default, run=True,
stdin=0, stdout=1, stderr=2, preexec_fn=None, preexec_args=[], raw=True, aslr=None, setuid=None,
shell=False):
stdin=0, stdout=1, stderr=2, preexec_fn=None, preexec_args=[], raw=True, aslr=None, setuid=None):
r"""
Executes a process on the remote server, in the same fashion
as pwnlib.tubes.process.process.
@@ -680,8 +680,6 @@ class ssh(Timeout, Logger):
See ``pwnlib.tubes.process.process`` for more information.
setuid(bool):
See ``pwnlib.tubes.process.process`` for more information.
shell(bool):
Pass the command-line arguments to the shell.
Returns:
A new SSH channel, or a path to a script if ``run=False``.
@@ -739,9 +737,6 @@ class ssh(Timeout, Logger):
Traceback (most recent call last):
...
NameError: global name 'bar' is not defined
>>> s.process('echo hello', shell=True).recvall()
'hello\n'
"""
if not argv and not executable:
self.error("Must specify argv or executable")
@@ -755,11 +750,6 @@ class ssh(Timeout, Logger):
if not isinstance(argv, (list, tuple)):
self.error('argv must be a list or tuple')
if shell:
if len(argv) != 1:
self.error('Cannot provide more than 1 argument if shell=True')
argv = ['/bin/sh', '-c'] + argv
# Python doesn't like when an arg in argv contains '\x00'
# -> execve() arg 2 must contain only strings
for i, arg in enumerate(argv):
@@ -872,13 +862,6 @@ if not %(aslr)r:
resource.setrlimit(resource.RLIMIT_STACK, (-1, -1))
# Attempt to dump ALL core file regions
try:
with open('/proc/self/coredump_filter', 'w') as core_filter:
core_filter.write('0x3f\n')
except Exception:
pass
# Assume that the user would prefer to have core dumps.
resource.setrlimit(resource.RLIMIT_CORE, (-1, -1))
@@ -958,7 +941,7 @@ os.execve(exe, argv, os.environ)
return result
def system(self, process, tty = True, wd = None, env = None, timeout = None, raw = True):
def system(self, process, tty = True, wd = None, env = None, timeout = Timeout.default, raw = True):
r"""system(process, tty = True, wd = None, env = None, timeout = Timeout.default, raw = True) -> ssh_channel
Open a new channel with a specific process inside. If `tty` is True,
@@ -984,10 +967,7 @@ os.execve(exe, argv, os.environ)
if wd is None:
wd = self.cwd
if timeout is None:
timeout = self.timeout
return ssh_channel(self, process, tty, wd, env, timeout = timeout, level = self.level, raw = raw)
return ssh_channel(self, process, tty, wd, env, timeout, level = self.level, raw = raw)
#: Backward compatibility. Use :meth:`system`
run = system
+5 -7
View File
@@ -30,18 +30,18 @@ class tube(Timeout, Logger):
#: and related functions.
newline = '\n'
def __init__(self, timeout = default, level = None, *a, **kw):
def __init__(self, timeout = default, level = None):
super(tube, self).__init__(timeout)
Logger.__init__(self, None)
if level is not None:
self.setLevel(level)
self.buffer = Buffer(*a, **kw)
self.buffer = Buffer()
atexit.register(self.close)
# Functions based on functions from subclasses
def recv(self, numb = None, timeout = default):
def recv(self, numb = 4096, timeout = default):
r"""recv(numb = 4096, timeout = default) -> str
Receives up to `numb` bytes of data from the tube, and returns
@@ -72,7 +72,6 @@ class tube(Timeout, Logger):
[...] Received 0xc bytes:
'Hello, world'
"""
numb = self.buffer.get_fill_size(numb)
return self._recv(numb, timeout) or ''
def unrecv(self, data):
@@ -123,7 +122,7 @@ class tube(Timeout, Logger):
data = ''
with self.local(timeout):
data = self.recv_raw(self.buffer.get_fill_size())
data = self.recv_raw(4096)
if data and self.isEnabledFor(logging.DEBUG):
self.debug('Received %#x bytes:' % len(data))
@@ -142,13 +141,12 @@ class tube(Timeout, Logger):
return data
def _recv(self, numb = None, timeout = default):
def _recv(self, numb = 4096, timeout = default):
"""_recv(numb = 4096, timeout = default) -> str
Receives one chunk of from the internal buffer or from the OS if the
buffer is empty.
"""
numb = self.buffer.get_fill_size(numb)
data = ''
# No buffered data, could not put anything in the buffer
+22 -21
View File
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
import base64
import random
import os
import re
import string
import StringIO
@@ -11,7 +10,7 @@ from . import packing
from ..context import context
from ..log import getLogger
from ..term import text
from .cyclic import de_bruijn
from .cyclic import cyclic
from .cyclic import cyclic_find
log = getLogger(__name__)
@@ -560,15 +559,13 @@ default_style = {
}
cyclic_pregen = ''
de_bruijn_gen = de_bruijn()
def sequential_lines(a,b):
return (a+b) in cyclic_pregen
def update_cyclic_pregenerated(size):
global cyclic_pregen
while size > len(cyclic_pregen):
cyclic_pregen += de_bruijn_gen.next()
cyclic_pregen = cyclic(size)
def hexdump_iter(fd, width=16, skip=True, hexii=False, begin=0, style=None,
highlight=None, cyclic=False):
@@ -611,6 +608,22 @@ def hexdump_iter(fd, width=16, skip=True, hexii=False, begin=0, style=None,
spacer = ' '
marker = (style.get('marker') or (lambda s:s))('│')
# Total length of the input stream
total = 0
if hasattr(fd, 'len'):
total = fd.len
else:
# Save the current file offset
cur = fd.seek(0, os.SEEK_CUR)
# Determine the total size of the file
fd.seek(0, os.SEEK_END)
total = fd.tell()
# Restore the file offset
fd.seek(cur, os.SEEK_SET)
if hexii:
column_sep = ''
line_fmt = '%%(offset)08x %%(hexbytes)-%is│' % (len(column_sep)+(width*byte_width))
@@ -630,29 +643,17 @@ def hexdump_iter(fd, width=16, skip=True, hexii=False, begin=0, style=None,
return hbyte, abyte
cache = [style_byte(chr(b)) for b in range(256)]
if cyclic:
update_cyclic_pregenerated(total)
numb = 0
while True:
offset = begin + numb
# If a tube is passed in as fd, it will raise EOFError when it runs
# out of data, unlike a file or StringIO object, which return an empty
# string.
try:
chunk = fd.read(width)
except EOFError:
chunk = ''
# We have run out of data, exit the loop
chunk = fd.read(width)
if chunk == '':
break
# Advance the cursor by the number of bytes we actually read
numb += len(chunk)
# Update the cyclic pattern in case
if cyclic:
update_cyclic_pregenerated(numb)
# If this chunk is the same as the last unique chunk,
# use a '*' instead.
if skip and last_unique:
+61 -3
View File
@@ -2,14 +2,19 @@ import errno
import socket
import time
import psutil
from .. import tubes
from ..log import getLogger
try:
import psutil
_ok_import = True
except ImportError:
_ok_import = False
log = getLogger(__name__)
all_pids = psutil.pids
if _ok_import:
all_pids = psutil.pids
def pidof(target):
"""pidof(target) -> int list
@@ -300,3 +305,56 @@ def wait_for_debugger(pid):
while tracer(pid) is None:
time.sleep(0.01)
l.success()
if not _ok_import:
def _make_stub(func):
func.__doc__ = 'Stubbed out function, because psutil is not available.'
return func
@_make_stub
def all_pids():
log.error("Called stubbed-out function. Get psutil to work on your platform, then come back.")
@_make_stub
def pidof(target):
log.error("Called stubbed-out function. Get psutil to work on your platform, then come back.")
@_make_stub
def pid_by_name(name):
log.error("Called stubbed-out function. Get psutil to work on your platform, then come back.")
@_make_stub
def name(pid):
log.error("Called stubbed-out function. Get psutil to work on your platform, then come back.")
@_make_stub
def parent(pid):
log.error("Called stubbed-out function. Get psutil to work on your platform, then come back.")
@_make_stub
def children(ppid):
log.error("Called stubbed-out function. Get psutil to work on your platform, then come back.")
@_make_stub
def ancestors(pid):
log.error("Called stubbed-out function. Get psutil to work on your platform, then come back.")
@_make_stub
def descendants(pid):
log.error("Called stubbed-out function. Get psutil to work on your platform, then come back.")
@_make_stub
def exe(pid):
log.error("Called stubbed-out function. Get psutil to work on your platform, then come back.")
@_make_stub
def cwd(pid):
log.error("Called stubbed-out function. Get psutil to work on your platform, then come back.")
@_make_stub
def cmdline(pid):
log.error("Called stubbed-out function. Get psutil to work on your platform, then come back.")
@_make_stub
def starttime(pid):
log.error("Called stubbed-out function. Get psutil to work on your platform, then come back.")
+1 -1
View File
@@ -1 +1 @@
__version__ = '3.4.1'
__version__ = '3.3.3'
+6 -3
View File
@@ -54,8 +54,11 @@ install_requires = ['paramiko>=1.15.2',
'pysocks',
'python-dateutil',
'pypandoc',
'packaging',
'psutil>=3.3.0']
'packaging']
# This is a hack until somebody ports psutil to OpenBSD
if platform.system() != 'OpenBSD':
install_requires.append('psutil>=2.1.3')
# Check that the user has installed the Python development headers
PythonH = os.path.join(get_python_inc(), 'Python.h')
@@ -80,7 +83,7 @@ except ImportError:
setup(
name = 'pwntools',
packages = find_packages(),
version = '3.4.1',
version = '3.3.3',
data_files = [('',
glob.glob('*.md') + glob.glob('*.txt')),
],
+37 -47
View File
@@ -1,6 +1,5 @@
#!/usr/bin/env bash -e
set -e
local_deb_extract()
{
wget $1
@@ -9,54 +8,52 @@ local_deb_extract()
rm -f *.tar.* *deb*
}
install_deb()
get_binutils()
{
version=${2:-zesty}
package=$1
echo "Installing $package"
INDEX="http://packages.ubuntu.com/en/$version/amd64/$package/download"
URL=$(curl "$INDEX" | grep -Eo "https?://.*$package.*\.deb" | head -1)
local_deb_extract "$URL"
BINUTILS_PREFIX='https://launchpad.net/~pwntools/+archive/ubuntu/binutils/+files/binutils-'
BINUTILS_SUFFIX='-linux-gnu_2.22-6ubuntu1.1cross0.11pwntools12~precise_amd64.deb'
local_deb_extract "${BINUTILS_PREFIX}${1}${BINUTILS_SUFFIX}"
}
get_qemu()
{
echo "Installing qemu"
QEMU_INDEX='http://packages.ubuntu.com/en/yakkety/amd64/qemu-user-static/download'
QEMU_URL=$(curl "$QEMU_INDEX" | grep kernel.org | grep -Eo 'http.*\.deb')
local_deb_extract "$QEMU_URL"
}
setup_travis()
{
export PATH=$PWD/usr/bin:$PATH
export LD_LIBRARY_PATH=$PWD/usr/lib:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$PWD/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$PWD/usr/lib
# Install a more modern binutils, which is required for some of the tests
[[ -f usr/bin/objcopy ]] || install_deb binutils
# Install libbfd-multiarch and libopcodes-multiarch if not found in the cache
if [ ! -f usr/lib/libbfd-2.22-multiarch.so ];
then
# Install the multiarch binutils
local_deb_extract http://mirrors.mit.edu/ubuntu/pool/universe/b/binutils/binutils-multiarch_2.22-6ubuntu1_amd64.deb
pushd usr/lib
ln -sf libbfd-2.22-multiarch.so libbfd-2.22.so
ln -sf libopcodes-2.22-multiarch.so libopcodes-2.22.so
popd
fi
# Install/upgrade qemu
[[ -f usr/bin/qemu-arm-static ]] || install_deb qemu-user-static xenial
if ! (which qemu-arm-static && qemu-arm-static -version | grep 2.6.1); then
get_qemu
fi
# Install cross-binutils
[[ -f usr/bin/x86_64-linux-gnu-ar ]] || install_deb binutils-multiarch
[[ -f usr/bin/aarch64-linux-gnu-as ]] || install_deb binutils-aarch64-linux-gnu
[[ -f usr/bin/arm-linux-gnueabihf-as ]] || install_deb binutils-arm-linux-gnueabihf
[[ -f usr/bin/mips-linux-gnu-as ]] || install_deb binutils-mips-linux-gnu
[[ -f usr/bin/powerpc-linux-gnu-as ]] || install_deb binutils-powerpc-linux-gnu
# Install our custom binutils
which arm-linux-gnu-as || get_binutils arm
which mips-linux-gnu-as || get_binutils mips
which powerpc-linux-gnu-as || get_binutils powerpc
# Test that the installs worked
as --version
x86_64-linux-gnu-ar --version
aarch64-linux-gnu-as --version
arm-linux-gnueabihf-as --version
mips-linux-gnu-as --version
powerpc-linux-gnu-as --version
qemu-arm-static --version
# Force-install capstone because it's broken somehow
[[ -f usr/lib/libcapstone.so.3 ]] || install_deb libcapstone3
# Install a newer copy of GDB
if [[ ! -f usr/bin/gdb ]]; then
git clone --depth=1 https://github.com/zachriggle/pwntools-gdb-travis-ci.git
tar xf pwntools-gdb-travis-ci/gdb.tar.xz
which gdb
usr/bin/gdb --version
fi
which arm-linux-gnu-as
which mips-linux-gnu-as
which powerpc-linux-gnu-as
which qemu-arm-static
# Get rid of files we don't want cached
rm -rf usr/share
@@ -77,14 +74,8 @@ setup_android_emulator()
if [ -n "$TRAVIS" ]; then
if [ -z "$TRAVIS_COMMIT_RANGE" ]; then
echo "TRAVIS_COMMIT_RANGE is empty, forcing Android Emulator installation"
elif ! (git show "$TRAVIS_COMMIT_RANGE" >/dev/null) ; then
elif ! git show "$TRAVIS_COMMIT_RANGE" ; then
echo "TRAVIS_COMMIT_RANGE is invalid, forcing Android Emulator installation"
elif [[ "$TRAVIS_BRANCH" =~ "staging" ]]; then
echo "TRAVIS_BRANCH ($TRAVIS_BRANCH) indicates a branch we care about"
echo "Forcing Android Emulator installation"
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
echo "Found Android-related commits, forcing Android Emulator installation"
else
@@ -92,11 +83,9 @@ setup_android_emulator()
# emulator, while still leaving the code intact, we remove the
# RST file that Sphinx searches.
rm -f 'docs/source/adb.rst'
rm -f 'docs/source/protocols/adb.rst'
# However, the file needs to be present or else things break.
touch 'docs/source/adb.rst'
touch 'docs/source/protocols/adb.rst' || true
echo "Skipping Android emulator install, Android tests disabled."
return
@@ -193,4 +182,5 @@ elif [[ "$(uname)" == "Linux" ]]; then
setup_android_emulator
fi
set +e
dpkg -l
-2
View File
@@ -54,5 +54,3 @@ Host example.pwnme
EOF
ssh -o "StrictHostKeyChecking no" -vvvv [email protected] id
set +e