summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimo Röhling <roehling@debian.org>2023-01-02 21:12:53 +0100
committerTimo Röhling <roehling@debian.org>2023-01-02 21:12:53 +0100
commit0ba0716f87d77a4c526c151cf5126419f9b3fea7 (patch)
treed5fe97dce3887404b42e32052b68261e71a5d814
parent8e173f7e46585a5c2d37c9dd453244701cea9106 (diff)
New upstream version 4.9.0
-rw-r--r--.github/workflows/ci.yml30
-rw-r--r--CHANGELOG.md31
-rwxr-xr-xdocs/requirements.txt7
-rwxr-xr-xdocs/source/conf.py2
-rw-r--r--pwnlib/asm.py67
-rw-r--r--pwnlib/commandline/libcdb.py2
-rw-r--r--pwnlib/elf/corefile.py4
-rw-r--r--pwnlib/gdb.py2
-rw-r--r--pwnlib/rop/rop.py12
-rw-r--r--pwnlib/shellcraft/internal.py54
-rw-r--r--pwnlib/tubes/tube.py17
-rw-r--r--pwnlib/util/safeeval.py2
-rw-r--r--pwnlib/version.py2
-rwxr-xr-xsetup.py2
14 files changed, 118 insertions, 116 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index af7e8ad..2f6a2b2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -27,7 +27,7 @@ jobs:
key: ${{ matrix.os }}-cache-pip
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v1
+ uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
@@ -59,33 +59,9 @@ jobs:
binutils-s390x-linux-gnu \
binutils-sparc64-linux-gnu \
gcc-multilib \
- libc6-dbg
+ libc6-dbg \
+ elfutils
- - name: Cache for elfutils
- uses: actions/cache@v1
- id: cache-elfutils
- with:
- path: ~/.cache/elfutils
- key: ${{ matrix.os }}-cache-elfutils
-
- # Install newer elfutils version due to regression in 0.176 available in focal.
- - name: Install newer elfutils
- env:
- ELFUTILS_VERSION: 0.181
- run: |
- if [[ ! -d ~/.cache/elfutils/elfutils-${ELFUTILS_VERSION} ]]
- then
- wget -O /tmp/elfutils-${ELFUTILS_VERSION}.tar.bz2 https://sourceware.org/elfutils/ftp/${ELFUTILS_VERSION}/elfutils-${ELFUTILS_VERSION}.tar.bz2
- mkdir -p ~/.cache/elfutils && cd ~/.cache/elfutils
- tar -xf /tmp/elfutils-${ELFUTILS_VERSION}.tar.bz2
- cd elfutils-${ELFUTILS_VERSION}
- LDFLAGS=-Wl,-rpath=/usr/local/lib,--enable-new-dtags ./configure --disable-libdebuginfod --disable-debuginfod && make && sudo make install
- else
- cd ~/.cache/elfutils/elfutils-${ELFUTILS_VERSION}
- sudo make install
- fi
- eu-unstrip --version
-
- name: Install RPyC for GDB
run: |
sudo apt-get install -y python3-pip
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8073e3f..300c739 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,9 +9,10 @@ The table below shows which release corresponds to each branch, and what date th
| Version | Branch | Release Date |
| ---------------- | -------- | ---------------------- |
-| [4.10.0](#4100) | `dev` |
-| [4.9.0](#490) | `beta` |
-| [4.8.0](#480) | `stable` | Apr 21, 2022
+| [4.11.0](#4110) | `dev` |
+| [4.10.0](#4100) | `beta` |
+| [4.9.0](#490) | `stable` | Dec 29, 2022
+| [4.8.0](#480) | | Apr 21, 2022
| [4.7.1](#471) | | Apr 20, 2022
| [4.7.0](#470) | | Nov 15, 2021
| [4.6.0](#460) | | Jul 12, 2021
@@ -64,11 +65,25 @@ 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
-## 4.10.0 (`dev`)
+## 4.11.0 (`dev`)
-## 4.9.0 (`beta`)
+## 4.10.0 (`beta`)
+
+- [#2062][2062] make pwn cyclic -l work with entry larger than 4 bytes
+- [#2092][2092] shellcraft: dup() is now called dupio() consistently across all supported arches
+- [#2093][2093] setresuid() in shellcraft uses current euid by default
+- [#2125][2125] Allow tube.recvregex to return capture groups
+- [#2144][2144] Removes `p2align 2` `asm()` headers from `x86-32`, `x86-64` and `mips` architectures to avoid inconsistent instruction length when patching binaries
+
+[2062]: https://github.com/Gallopsled/pwntools/pull/2062
+[2092]: https://github.com/Gallopsled/pwntools/pull/2092
+[2093]: https://github.com/Gallopsled/pwntools/pull/2093
+[2125]: https://github.com/Gallopsled/pwntools/pull/2125
+[2144]: https://github.com/Gallopsled/pwntools/pull/2144
+
+## 4.9.0 (`stable`)
- [#1975][1975] Add libcdb commandline tool
- [#1979][1979] Add `js_escape()` and `js_unescape()` to `util.fiddling`
@@ -78,6 +93,8 @@ The table below shows which release corresponds to each branch, and what date th
- [#2033][2033] Quote file and core path in generated GDB script
- [#2035][2035] Change Buffer's parent class to object
- [#2037][2037] Allow SSH tunnel to be treated like a TCP socket (with 'raw=True')
+- [#2123][2123] Fix ROP without a writeable cache directory
+- [#2124][2124] Fix `tube.recvpred()` timeout argument
[1975]: https://github.com/Gallopsled/pwntools/pull/1975
[1979]: https://github.com/Gallopsled/pwntools/pull/1979
@@ -87,8 +104,10 @@ The table below shows which release corresponds to each branch, and what date th
[2033]: https://github.com/Gallopsled/pwntools/pull/2033
[2035]: https://github.com/Gallopsled/pwntools/pull/2035
[2037]: https://github.com/Gallopsled/pwntools/pull/2037
+[2123]: https://github.com/Gallopsled/pwntools/pull/2123
+[2124]: https://github.com/Gallopsled/pwntools/pull/2124
-## 4.8.0 (`stable`)
+## 4.8.0
- [#1922][1922] Fix logic in `wait_for_debugger`
- [#1828][1828] libcdb: Load debug info and unstrip libc binary
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 95762a6..b54a40f 100755
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -12,8 +12,7 @@ pypandoc
pyserial>=2.7
requests>=2.5.1
ropgadget>=5.3
-sphinx==1.8.6; python_version < '3.0.0'
-sphinx==4.5.0; python_version >= '3.0.0'
+sphinx==1.8.6; python_version<'3'
+sphinx>=4.5.0; python_version>='3'
sphinx_rtd_theme
-sphinxcontrib-napoleon
-sphinxcontrib-autoprogram<=0.1.5 \ No newline at end of file
+sphinxcontrib-autoprogram<=0.1.5
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 1ff254a..a07377a 100755
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -42,8 +42,8 @@ extensions = [
'sphinx.ext.coverage',
'sphinx.ext.todo',
'sphinx.ext.intersphinx',
+ 'sphinx.ext.napoleon',
'sphinxcontrib.autoprogram',
- 'sphinxcontrib.napoleon'
]
# Disable "info" logging directly to stdout by Sphinx
diff --git a/pwnlib/asm.py b/pwnlib/asm.py
index 6e9b9f5..7d59d76 100644
--- a/pwnlib/asm.py
+++ b/pwnlib/asm.py
@@ -136,8 +136,23 @@ Try installing binutils for this architecture:
%(instructions)s
""".strip() % locals())
+
+def check_binutils_version(util):
+ if util_versions[util]:
+ return util_versions[util]
+ result = subprocess.check_output([util, '--version','/dev/null'],
+ stderr=subprocess.STDOUT, universal_newlines=True)
+ if 'clang' in result:
+ log.warn_once('Your binutils is clang-based and may not work!\n'
+ 'Try installing with: https://docs.pwntools.com/en/stable/install/binutils.html\n'
+ 'Reported version: %r', result.strip())
+ version = re.search(r' (\d+\.\d+)', result).group(1)
+ util_versions[util] = version = tuple(map(int, version.split('.')))
+ return version
+
+
@LocalContext
-def which_binutils(util):
+def which_binutils(util, check_version=False):
"""
Finds a binutils in the PATH somewhere.
Expects that the utility is prefixed with the architecture name.
@@ -204,17 +219,23 @@ def which_binutils(util):
for pattern in patterns:
for dir in environ['PATH'].split(':'):
- res = sorted(glob(path.join(dir, pattern)))
- if res:
- return res[0]
+ for res in sorted(glob(path.join(dir, pattern))):
+ if check_version:
+ ver = check_binutils_version(res)
+ return res, ver
+ return res
# No dice!
print_binutils_instructions(util, context)
-checked_assembler_version = defaultdict(lambda: False)
+util_versions = defaultdict(tuple)
def _assembler():
- gas = which_binutils('as')
+ gas, version = which_binutils('as', check_version=True)
+ if version < (2, 19):
+ log.warn_once('Your binutils version is too old and may not work!\n'
+ 'Try updating with: https://docs.pwntools.com/en/stable/install/binutils.html\n'
+ 'Reported version: %r', version)
E = {
'big': '-EB',
@@ -246,25 +267,10 @@ def _assembler():
assembler = assemblers.get(context.arch, [gas])
- if not checked_assembler_version[gas]:
- checked_assembler_version[gas] = True
- result = subprocess.check_output([gas, '--version','/dev/null'],
- stderr=subprocess.STDOUT, universal_newlines=True)
- version = re.search(r' (\d+\.\d+)', result).group(1)
- if 'clang' in result:
- log.warn_once('Your binutils is clang version and may not work!\n'
- 'Try install with: https://docs.pwntools.com/en/stable/install/binutils.html\n'
- 'Reported Version: %r', result.strip())
- elif version < '2.19':
- log.warn_once('Your binutils version is too old and may not work!\n'
- 'Try updating with: https://docs.pwntools.com/en/stable/install/binutils.html\n'
- 'Reported Version: %r', result.strip())
-
-
return assembler
def _linker():
- ld = [which_binutils('ld')]
+ ld, _ = which_binutils('ld', check_version=True)
bfd = ['--oformat=' + _bfdname()]
E = {
@@ -276,7 +282,16 @@ def _linker():
'i386': ['-m', 'elf_i386'],
}.get(context.arch, [])
- return ld + bfd + [E] + arguments
+ return [ld] + bfd + [E] + arguments
+
+
+def _execstack(linker):
+ ldflags = ['-z', 'execstack']
+ version = util_versions[linker[0]]
+ if version >= (2, 39):
+ return ldflags + ['--no-warn-execstack', '--no-warn-rwx-segments']
+ return ldflags
+
def _objcopy():
return [which_binutils('objcopy')]
@@ -595,7 +610,7 @@ def make_elf(data,
_run(assembler + ['-o', step2, step1])
- linker_options = ['-z', 'execstack']
+ linker_options = _execstack(linker)
if vma is not None:
linker_options += ['--section-start=.shellcode=%#x' % vma,
'--entry=%#x' % vma]
@@ -689,7 +704,7 @@ def asm(shellcode, vma = 0, extract = True, shared = False):
shutil.copy(step2, step3)
if vma or not extract:
- ldflags = ['-z', 'execstack', '-o', step3, step2]
+ ldflags = _execstack(linker) + ['-o', step3, step2]
if vma:
ldflags += ['--section-start=.shellcode=%#x' % vma,
'--entry=%#x' % vma]
@@ -771,7 +786,7 @@ def disasm(data, vma = 0, byte = True, offset = True, instructions = True):
>>> print(disasm(unhex('4ff00500'), arch = 'thumb', bits=32))
0: f04f 0005 mov.w r0, #5
>>> print(disasm(unhex('656664676665400F18A4000000000051'), byte=0, arch='amd64'))
- 0: gs data16 fs data16 rex nop/reserved BYTE PTR gs:[eax+eax*1+0x0]
+ 0: gs data16 fs rex nop WORD PTR gs:[eax+eax*1+0x0]
f: push rcx
>>> print(disasm(unhex('01000000'), arch='sparc64'))
0: 01 00 00 00 nop
diff --git a/pwnlib/commandline/libcdb.py b/pwnlib/commandline/libcdb.py
index 81fd9f6..1555db2 100644
--- a/pwnlib/commandline/libcdb.py
+++ b/pwnlib/commandline/libcdb.py
@@ -4,7 +4,6 @@ from __future__ import division
from __future__ import print_function
import re
-import requests
import shutil
import sys
@@ -138,6 +137,7 @@ file_parser.add_argument(
common_symbols = ['dup2', 'printf', 'puts', 'read', 'system', 'write']
def find_libc(params):
+ import requests
url = "https://libc.rip/api/find"
result = requests.post(url, json=params, timeout=20)
log.debug('Request: %s', params)
diff --git a/pwnlib/elf/corefile.py b/pwnlib/elf/corefile.py
index 8290e85..7399762 100644
--- a/pwnlib/elf/corefile.py
+++ b/pwnlib/elf/corefile.py
@@ -445,7 +445,7 @@ class Corefile(ELF):
will be loaded.
>>> process('bash').corefile.libc # doctest: +ELLIPSIS
- Mapping('/.../libc-....so', start=0x..., stop=0x..., size=0x..., flags=..., page_offset=...)
+ Mapping('.../libc...so...', start=0x..., stop=0x..., size=0x..., flags=..., page_offset=...)
The corefile also contains a :attr:`.stack` property, which gives
us direct access to the stack contents. On Linux, the very top of the stack
@@ -759,7 +759,7 @@ class Corefile(ELF):
@property
def libc(self):
""":class:`Mapping`: First mapping for ``libc.so``"""
- expr = r'libc\b.*so$'
+ expr = r'^libc\b.*so(?:\.6)?$'
for m in self.mappings:
if not m.name:
diff --git a/pwnlib/gdb.py b/pwnlib/gdb.py
index 45019dc..39e2ce1 100644
--- a/pwnlib/gdb.py
+++ b/pwnlib/gdb.py
@@ -1329,7 +1329,7 @@ def version(program='gdb'):
Example:
- >>> (7,0) <= gdb.version() <= (12,0)
+ >>> (7,0) <= gdb.version() <= (19,0)
True
"""
program = misc.which(program)
diff --git a/pwnlib/rop/rop.py b/pwnlib/rop/rop.py
index 8238119..04e6d3c 100644
--- a/pwnlib/rop/rop.py
+++ b/pwnlib/rop/rop.py
@@ -1224,6 +1224,9 @@ class ROP(object):
def __get_cachefile_name(self, files):
"""Given an ELF or list of ELF objects, return a cache file for the set of files"""
+ if context.cache_dir is None:
+ return None
+
cachedir = os.path.join(context.cache_dir, 'rop-cache')
if not os.path.exists(cachedir):
os.mkdir(cachedir)
@@ -1240,12 +1243,14 @@ class ROP(object):
@staticmethod
def clear_cache():
"""Clears the ROP gadget cache"""
+ if context.cache_dir is None:
+ return
cachedir = os.path.join(context.cache_dir, 'rop-cache')
shutil.rmtree(cachedir)
def __cache_load(self, elf):
filename = self.__get_cachefile_name(elf)
- if not os.path.exists(filename):
+ if filename is None or not os.path.exists(filename):
return None
gadgets = eval(open(filename).read())
gadgets = {k - elf.load_addr + elf.address:v for k, v in gadgets.items()}
@@ -1253,8 +1258,11 @@ class ROP(object):
return gadgets
def __cache_save(self, elf, data):
+ filename = self.__get_cachefile_name(elf)
+ if filename is None:
+ return
data = {k + elf.load_addr - elf.address:v for k, v in data.items()}
- open(self.__get_cachefile_name(elf), 'w+').write(repr(data))
+ open(filename, 'w+').write(repr(data))
def __load(self):
"""Load all ROP gadgets for the selected ELF files"""
diff --git a/pwnlib/shellcraft/internal.py b/pwnlib/shellcraft/internal.py
index c772fa3..eee0dea 100644
--- a/pwnlib/shellcraft/internal.py
+++ b/pwnlib/shellcraft/internal.py
@@ -2,6 +2,7 @@ from __future__ import absolute_import
from __future__ import division
import os
+import sys
from pwnlib.context import context
@@ -109,47 +110,17 @@ def get_context_from_dirpath(directory):
return {'os': osys, 'arch': arch}
def make_function(funcname, filename, directory):
+ import functools
import inspect
path = os.path.join(directory, filename)
template = lookup_template(path)
- args, varargs, keywords, defaults = inspect.getargspec(template.module.render_body)
-
- defaults = defaults or []
-
- if len(defaults) < len(args) and args[0] == 'context':
- args.pop(0)
-
- args_used = args[:]
-
- for n, default in enumerate(defaults, len(args) - len(defaults)):
- args[n] = '%s = %r' % (args[n], default)
-
- if varargs:
- args.append('*' + varargs)
- args_used.append('*' + varargs)
-
- if keywords not in ['pageargs', None]:
- args.append('**' + keywords)
- args_used.append('**' + keywords)
-
- docstring = inspect.cleandoc(template.module.__doc__ or '')
- args = ', '.join(args)
- args_used = ', '.join(args_used)
local_ctx = get_context_from_dirpath(directory)
- # This is a slight hack to get the right signature for the function
- # It would be possible to simply create an (*args, **kwargs) wrapper,
- # but what would not have the right signature.
- # While we are at it, we insert the docstring too
- T = r'''
-def wrap(template, render_global):
- import pwnlib
- def %(funcname)s(%(args)s):
- %(docstring)r
+ def res(*args, **kwargs):
with render_global.go_inside() as was_inside:
- with pwnlib.context.context.local(**%(local_ctx)s):
- lines = template.render(%(args_used)s).split('\n')
+ with context.local(**local_ctx):
+ lines = template.render(*args, **kwargs).split('\n')
for i, line in enumerate(lines):
def islabelchar(c):
return c.isalnum() or c == '.' or c == '_'
@@ -168,19 +139,16 @@ def wrap(template, render_global):
return s
else:
return s + '\n'
- return %(funcname)s
-''' % locals()
-
- g = {}
- exec(T, g, g)
- wrap = g['wrap']
# Setting _relpath is a slight hack only used to get better documentation
- res = wrap(template, render_global)
res._relpath = path
res.__module__ = 'pwnlib.shellcraft.' + os.path.dirname(path).replace('/','.')
-
- import sys, functools
+ res.__name__ = res.__qualname__ = funcname
+ res.__doc__ = inspect.cleandoc(template.module.__doc__ or '')
+ if hasattr(inspect, 'signature'):
+ sig = inspect.signature(template.module.render_body)
+ sig = sig.replace(parameters=list(sig.parameters.values())[1:-1])
+ res.__signature__ = sig
@functools.wraps(res)
def function(*a):
diff --git a/pwnlib/tubes/tube.py b/pwnlib/tubes/tube.py
index d9b5dc8..f476cc4 100644
--- a/pwnlib/tubes/tube.py
+++ b/pwnlib/tubes/tube.py
@@ -195,14 +195,29 @@ class tube(Timeout, Logger):
Returns:
A bytes object containing bytes received from the socket,
or ``''`` if a timeout occurred while waiting.
+
+ Examples:
+
+ >>> t = tube()
+ >>> t.recv_raw = lambda n: b'abbbaccc'
+ >>> pred = lambda p: p.count(b'a') == 2
+ >>> t.recvpred(pred)
+ b'abbba'
+ >>> pred = lambda p: p.count(b'd') > 0
+ >>> t.recvpred(pred, timeout=0.05)
+ b''
"""
data = b''
with self.countdown(timeout):
while not pred(data):
+ if not self.countdown_active():
+ self.unrecv(data)
+ return b''
+
try:
- res = self.recv(1)
+ res = self.recv(1, timeout=timeout)
except Exception:
self.unrecv(data)
return b''
diff --git a/pwnlib/util/safeeval.py b/pwnlib/util/safeeval.py
index 0d0962c..47ed37f 100644
--- a/pwnlib/util/safeeval.py
+++ b/pwnlib/util/safeeval.py
@@ -6,6 +6,7 @@ _const_codes = [
'BUILD_CONST_KEY_MAP', 'BUILD_STRING',
'LOAD_CONST','RETURN_VALUE','STORE_SUBSCR', 'STORE_MAP',
'LIST_TO_TUPLE', 'LIST_EXTEND', 'SET_UPDATE', 'DICT_UPDATE', 'DICT_MERGE',
+ 'COPY', 'RESUME',
]
_expr_codes = _const_codes + [
@@ -15,6 +16,7 @@ _expr_codes = _const_codes + [
'BINARY_MODULO','BINARY_ADD','BINARY_SUBTRACT',
'BINARY_LSHIFT','BINARY_RSHIFT','BINARY_AND','BINARY_XOR',
'BINARY_OR',
+ 'BINARY_OP',
]
_values_codes = _expr_codes + ['LOAD_NAME']
diff --git a/pwnlib/version.py b/pwnlib/version.py
index 24ab87c..3161916 100644
--- a/pwnlib/version.py
+++ b/pwnlib/version.py
@@ -1 +1 @@
-__version__ = '4.9.0beta0'
+__version__ = '4.9.0'
diff --git a/setup.py b/setup.py
index ab1c9b2..cc29bc8 100755
--- a/setup.py
+++ b/setup.py
@@ -89,7 +89,7 @@ setup(
name = 'pwntools',
python_requires = '>=2.7',
packages = find_packages(),
- version = '4.9.0beta0',
+ version = '4.9.0',
data_files = [('pwntools-doc',
glob.glob('*.md') + glob.glob('*.txt')),
],