Compare commits

...
232 Commits
Author SHA1 Message Date
Peace-Maker f046fdd93e Release 4.12.0 2024-02-22 22:15:10 +01:00
Peace-Maker 4c0e08c2c8 Merge branch 'stable' into beta 2024-02-22 21:59:14 +01:00
eriklefflerandPeace-Maker 1b47ae1c20 Add RETURN_CONST as an allowed _const_code in safeeval (#2352)
* better error message when process creation fails in ssh.process

* add RETURN_CONST as an allowed _const_code in safeeval

* fix lint

* add to changelog

* fix changelog

* add couldn't find python warning

* Deduplicate error message code

---------

Co-authored-by: Peace-Maker <[email protected]>
2024-02-20 18:28:20 +00:00
peace-maker 2114692513 Fix readline omitting a trailing \n (#2349)
* Fix readline omitting a trailing \n

This is a regression from #2129 which was based on a bogus test which mixed `input` and `readline` calls.

Only `input` (and `raw_input`) are documented to strip the newline, but `readline` itself should include it.

Fixes #2342

* Update CHANGELOG
2024-02-16 23:43:19 +01:00
Peace-Maker b5e4ff137a Android: Install emulator in CI and switch to x86_64 platform 2024-02-16 23:11:12 +01:00
Arusekk 1b21f7a043 Release 4.12.0beta1 2023-11-14 20:15:36 +01:00
Arusekk 2e09b7dd91 Release 4.11.1 2023-11-14 20:01:02 +01:00
Arusekk d643a33233 Merge branch 'stable' into beta 2023-11-14 19:47:35 +01:00
peace-maker 8d6dc0b121 Python 2: Fix installing from source (#2298)
* Python 2: Fix installing from source

`python2.7 -m pip install git+https://github.com...`  doesn't install
any packages.

The auto-discovery doesn't work for the old setuptools version used in
Python 2.
https://setuptools.pypa.io/en/latest/userguide/package_discovery.html#flat-layout

* Use Python 3 commandline tools in Docker images

Since the Python 2 version was installed first, the Python 3 install
didn't touch the existing shell script wrapper files.
2023-11-14 19:44:05 +01:00
Arusekk 2f58fe7ae5 Merge branch 'stable' into beta 2023-11-01 23:44:00 +01:00
Arusekk 64d52b09f3 shellcraft.aarch64: Fix atexit SEGV in loader (#2294)
Fixes #2289
2023-11-01 22:26:43 +00:00
Arusekk 8cf3ecdc5b Merge branch 'stable' into beta 2023-10-29 16:30:08 +01:00
Kexuan Shen 0c1121d32c FIX: Generated shebang with path to python invalid if path contains spaces (#2285)
* modify script shebang to accept path with white space

* add changelog for 2271
2023-10-29 14:38:57 +00:00
peace-makerandArusekk dafd42b2c3 Fix tube.clean_and_log not logging buffered data (#2272)
* Fix tube.clean_and_log not logging buffered data

Only new data received after the `clean_and_log` call is printed while
data already received and buffered earlier is not.
Print the buffered data first before waiting for more so we don't miss
anything.

* Only show buffered data if DEBUG is off

* Update CHANGELOG.md

---------

Co-authored-by: Arusekk <[email protected]>
2023-10-29 14:05:16 +00:00
Tanix 3ff37a8b7e Fix _countdown_handler not invoking timeout_change; Fix value is value < 0 (#2287) 2023-10-29 11:23:31 +00:00
Arusekk e9f73bf440 Merge branch 'stable' into beta 2023-10-01 13:48:12 +02:00
Gordiig 045b8c2186 FIX: Getting right amount of data for search fix (#2281) 2023-10-01 13:47:30 +02:00
Peace-Maker 01e6970971 Release 4.12.0beta0 2023-09-16 00:20:18 +02:00
Peace-Maker 6aaaf84586 Release 4.11.0 2023-09-15 23:16:19 +02:00
92cd96999f Allow empty argv in ssh.process() (#2217) (#2234)
* Fix bug at ssh.py:process() - empty argv[0] Error

Before this, process.py relied on `os.execve` which disallows an empty
argv or argv[0] == "".
This commit replaces `os.execve` with  `ctypes.CDLL(None).execve` to
call the C-Library function directly which allows an empty argv.

* Add #2225 to stable changelog

* Better ctypes syntax

* Add error message if cytpes.execve fails.

* Updata CHANGELOG.md

* ssh.py: python2 compatibility for os.environb

* Add check that "=" not in misc.normalize_argv_env

This check checks prevents the use of "=" in the
key of an environment variable, which is generally
impossible.

* ssh.process: Seperate cases for empty argv[0]

This commit seperates the cases for an empty argv[0]
from normal cases.

* ssh.py delete leftover comment

---------

Co-authored-by: Youheng Lü <[email protected]>
Co-authored-by: Arusekk <[email protected]>
Co-authored-by: peace-maker <[email protected]>
2023-09-15 01:10:14 +02:00
Hannes WeissteinerandHannes Weissteiner 8b86cc3ba6 Allow creating custom templates in user-config directory (#2257)
* Allow creating custom templates in user-config directory

* Update changelog

* add cmdline parameter for template

* direct template file selection, added more help

* added template path to help string

* remove testing template

* allow crash on invalid template file

* python2 compatibility

* improved documentation for template

---------

Co-authored-by: Hannes Weissteiner <[email protected]>
2023-09-13 18:03:29 +02:00
dependabot[bot] 20f07795f0 Bump actions/checkout from 3 to 4 (#2265)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [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/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  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>
2023-09-12 19:00:26 +02:00
Peace-Maker d12d2ea78c Merge branch 'beta' into dev 2023-09-12 13:37:43 +02:00
Peace-Maker 2d998a4082 Merge branch 'stable' into beta 2023-09-12 13:15:04 +02:00
peace-maker 8be72238b0 Rename wd parameter to cwd in ssh.system and ssh.run_to_end (#2251)
The current working directory should be called the same everywhere.
The `wd` argument is deprecated and `cwd` takes precedence.
2023-09-12 09:37:27 +02:00
peace-maker d762fe0c31 Fix corefile module after pyelftools update (#2261)
* Fix corefile module after pyelftools update

The `bytes2str` internal method was moved while Python 2 support was
removed in pyelftools 0.3.0.
Just use our own python aware decoder wrapper.

Fixes #2260

* Pin pyelftools < 0.3 for Python 2

* Fix pyelftools version number scheme

* Update CHANGELOG
2023-09-12 09:27:40 +02:00
Arusekk e608a46490 Merge branch 'beta' into dev 2023-08-20 20:54:18 +02:00
Arusekk fa2d47753b Merge branch 'stable' into beta 2023-08-20 20:53:42 +02:00
Arusekk 6ff74256d8 plt: remove stale MIPS workaround (#2256)
Fixes #2042
2023-08-20 18:49:35 +00:00
peace-maker 340b0bf0bc Only run docker workflow on upstream repo (#2252)
Don't run it in forks which can't push to the dockerhub.
2023-08-02 01:34:17 +02:00
Furkan 2e5a33cfea Remove unused and broken rop.find_stack_adjustment (#2249)
* Added missing return to rop.find_stack_adjustment

* Removed ROP.find_stack_adjustment()
2023-07-31 21:44:37 +02:00
peace-maker b8b29e1cc7 Fix pushing docker images on manual workflow run (#2250)
* Fix pushing docker images on manual workflow run

Running the docker workflow manually would associate the run with the
`dev` branch. The generated tags would all point to the `dev` tag, so
tag the image manually to the correct branch.

* Fix building ci image

It's called `python2.7` instead of `python`.

* Add riscv64 binutils to base image
2023-07-30 17:49:54 +02:00
dependabot[bot] ff178aad6a Bump actions/download-artifact from 2 to 3 (#2247)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  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>
2023-07-30 15:34:24 +02:00
dependabot[bot] 8c8e49cc10 Bump actions/upload-artifact from 2 to 3 (#2246)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  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>
2023-07-30 15:33:47 +02:00
dependabot[bot] 0b0f786002 Bump actions/checkout from 2 to 3 (#2248)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [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/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  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>
2023-07-30 09:38:24 +02:00
Arusekk 9ba65eb414 Merge branch 'beta' into dev 2023-07-29 22:31:37 +02:00
Arusekk cf53c4b219 Merge branch 'stable' into beta 2023-07-29 22:31:12 +02:00
peace-maker a0c9da07ac Fix ssh.process not setting ssh_process.cwd (#2241)
* Fix ssh.process not setting ssh_process.cwd

The cwd wasn't passed to the ssh_process created in ssh.process,
so the ssh_process.cwd attribute was never set.

This passes the cwd along now.
```
s = ssh(..)
io = s.process("pwd", cwd="/tmp")
io.cwd # "."
io.recvall() # b"/tmp\n"
```

* Update changelog
2023-07-29 22:29:21 +02:00
peace-maker 5b783feabd Keep Github Actions up-to-date with Dependabot (#2238)
Check every week if there are newer versions of the Github Actions we use.
2023-07-29 22:16:08 +02:00
Furkan 5589e15051 Decoded data from recv_all in packages to a string (#2237)
* Decoded data from recv_all in packages to a string

* Added sources for UTF-8 usage in Android

* Updated adb source
2023-07-29 21:23:27 +02:00
peace-maker 40c1ef5890 Do not overwrite global bytes in examples (#2240) 2023-07-28 15:50:18 +02:00
Arusekk e5af2bc3a3 Fix format string badbytes inconsistency (#1895)
* Fix format string badbytes inconsistency

Fixes #1888

* fmtstr: Make sure initial atom is considered
2023-07-28 15:49:00 +02:00
Furkan 6fa13de917 Added MAP_ADD opcode to list of opcodes in safeeval.py (#2243) 2023-07-28 14:45:22 +02:00
Arusekk 216e3edcde ci: stabilize coverage (#2235)
* ci: stabilize coverage

* fix cache dirs
2023-07-27 16:41:05 +02:00
peace-maker c334d46026 Publish Docker images through CI (#2236)
Rebuild the images on push to the respective branch.
2023-07-27 14:41:10 +02:00
peace-makerandArusekk 3e4e8b7055 Add --libc libc.so argument to pwn template (#2212)
* Add ELF.set_runpath()

Shells out to the `patchelf` tool to patch the ELF's RUNPATH. This lets the dynamic loader look for needed shared libraries in the given path first before the system libraries when running the binary.

* Add ELF.set_interpreter()

Shells out to the `patchelf` tool to patch the ELF's PT_INTERP segment. This allows to change the ld.so used when running the binary.

* Add convenience wrapper to set runpath & interpreter

A helper function to patch the ELF such that it uses the dynamic loader and other libraries in the given folder.

* Add method to download libraries matching a libc

Download the matching libraries for the given libc binary and cache them in a local directory using `libcdb.download_libraries()`. The libraries are looked up using libc.rip and fetched from the official package repositories if available.

Only .deb and .pkg.tar.* packages are currently supported (Debian/Ubuntu, Arch).

* Add --libc argument to pwnup template

This generates code into the template which allows you to run the binary using the given libc.

The foreign libc is used by default, but you can choose to run the binary against your system's local libc using the `LOCAL_LIBC` command line argument when executing the exploit script.

* Work around Python 2 tarfile not supporting xz

* Fix crash when libc wasn't found on libc.rip

* Update README

* Use subprocess.check_output instead of pwnlib.process

* Special case Python 2 instead of 3

* Only catch Exceptions instead of everything

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

* Check launchpad.net for Ubuntu libcs

This mimics the way io12/pwninit obtains the ld.so.
If the download from libc.rip fails, try launchpad.net.

* Hide comment in template when --quiet

* Please confused pylint in PY2 context

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

---------

Co-authored-by: Arusekk <[email protected]>
2023-07-26 13:58:32 +02:00
Arusekk 0696c5cb4f shellcraft/*/freebsd: match linux in switching cs (#2232)
* shellcraft/*/freebsd: match linux in switching cs

* shellcraft: note trashed registers in switching cs
2023-07-24 19:34:12 +02:00
Arusekk ac855ab179 shellcraft/i386: optimize stackhunter (#2231)
* shellcraft/i386: optimize stackhunter

* shellcraft/i386: add+opt long-gone stackhunter_helper

* shellcraft/i386: concise doctests
2023-07-24 19:33:26 +02:00
Arusekk 42c0798077 Merge branch 'beta' into dev 2023-07-17 19:26:41 +02:00
Arusekk cc3b28b23c Merge branch 'stable' into beta 2023-07-17 19:26:27 +02:00
Arusekk d69e294d54 py2: fix long hex in shellcraft etc. (#2230) 2023-07-17 17:10:15 +02:00
d679252ba9 Fix ret2csu with PIE binaries (#2158)
* fix indentation

* Allow ret2csu to find non-pie binaries

* ret2csu: fix detection logic

---------

Co-authored-by: peace-maker <[email protected]>
Co-authored-by: Arusekk <[email protected]>
2023-07-17 10:08:43 +00:00
whokilleddbandDebjeet Banerjee 94fc5d2769 Update ssh.py (#1946)
Two minor changes were made in the commit:
- `which` was replaced with `command -v`
- Minor bug fix

## Why bother with `which` ?
- `POSIX` compatible
-  `which` is an external process you're launching for doing very little (meaning builtins like hash, type or command are way cheaper), you can also rely on the builtins to actually do what you want, while the effects of external commands can easily vary from system to system.
- Many operating systems have a which that doesn't even set an exit status, meaning the if which foo won't even work there and will always report that foo exists, even if it doesn't (note that some POSIX shells appear to do this for hash too).
- Many operating systems make which do custom and evil stuff like change the output or even hook into the package manager.

Reference [here](https://stackoverflow.com/a/677212)

Co-authored-by: Debjeet Banerjee <[email protected]>
2023-07-16 20:44:59 +02:00
Furkan fbe88c06db Improve error handling of serial-tube constructor (#2228)
* Resolved issue #1411 abt serial-tube misbehaving.

* Removed redundant docstrings
2023-07-15 12:41:10 +02:00
Disconnect3d dd1edaa6c9 shellcraft: more explicit sleep.asm docstring (#2226)
This commit adds information that the sleep shellcraft function does not check if it returned as part of an interrupt and it does not retry the syscall if this occured.
2023-07-12 00:24:54 +02:00
peace-maker b23251d05b Fix Python 2 editable user install (#2224)
This appears to be fixed in setuptools 62, but Python 2 is stuck at
version 44. Use a workaround which overrides the disabled user installs
temporarily.

This fixes our develop Dockerfile too!
2023-07-10 08:53:15 +02:00
peace-maker 83e19216ab Don't change log level for Corefile._parse_stack() (#2222)
* Don't change log level for Corefile._parse_stack()

If we don't want to see the log, don't print it. There are no spammy
debug logs anymore.

Fixes #1666

* Fix doctest
2023-07-09 22:02:44 +02:00
ArusekkandPeace-Maker 1399c7a969 Shellcraft stackargs (#2219)
From: Peace-Maker <[email protected]>

* Shellcraft: Fix spilling syscall args to the stack

There was a typo in the generated syscall template causing only the last
stack argument to be generated.

$ shellcraft -f asm mips.linux.sendto 3 0x123456 0x100 0 0xabcdefff 0x10
The 0xabcdefff argument was missing.

* Shellcraft: Generate 6 syscall arguments for unknown functions

There are syscalls with 6 arguments like mmap and sendto which couldn't
be called with all 6 arguments set.

$ shellcraft -f asm mips.linux.mmap2 0 0x1000
'PROT_READ | PROT_WRITE | PROT_EXEC' 'MAP_PRIVATE | MAP_ANONYMOUS' -1 0

* Shellcraft: Fix typo in error message

The `syscalls` variable is only present in the generate.py, not the
generated template.

* Regenerate syscall templates

* MIPS shellcraft: Push last two syscall arguments to the stack

The mips.linux.syscall template was only handling 4 syscall arguments
and silently discarded the remaining ones. Push the arguments to the stack
instead.

$ shellcraft -f asm mips.linux.syscall SYS_sendto 3 0x123456 0x100 0 0xabcdefff 0x10

Fixes #2153

* Fix mips syscall doctest

* Update CHANGELOG

Co-authored-by: Peace-Maker <[email protected]>
2023-07-09 22:00:54 +02:00
peace-makerandArusekk 8d104851ad Add shellcraft.sleep template wrapping SYS_nanosleep (#2221)
* Add shellcraft.sleep template wrapping SYS_nanosleep

Accepts the time in seconds as a float argument and calls SYS_nanosleep.

Fixes #1428

* Update CHANGELOG

* Use nanosleep wrapper

Co-authored-by: Arusekk <[email protected]>
2023-07-09 21:27:53 +02:00
peace-maker 9c83fa1099 Remove Travis CI Badge (#2220)
We don't use Travis CI anymore.

Closes #1740
2023-07-09 20:36:26 +02:00
Arusekk f1d5afe328 Merge branch 'beta' into dev 2023-07-09 15:25:06 +02:00
LY 05783e98c9 Change temp path in binutils install script (#2097)
Use $TMPDIR first. If $TMPDIR is unset/empty, use /tmp
(AFAIK most distros don't set $TMPDIR by default)
2023-07-09 15:21:40 +02:00
Arusekk d90cfd8b50 Merge branch 'stable' into beta 2023-07-09 15:13:11 +02:00
Youheng Lü c397626530 Fix bug at ssh.py:get() with relative path (#2214)
* Fix bug at ssh.py:get() with relative path

* Add doctest for bug at ssh.py:get()

* doctest for download and download_file
* remove os.path.join, since _download_raw already seem
  handle remote paths

* Add #2214 to stable changelog

* Fix Doctest for ssh.py to not be dependent on user dir

* Update CHANGELOG.md
2023-07-09 12:57:17 +00:00
peace-maker 9f072e3d0b Fix BytesWarning in ssh.interactive() (#2218)
When the current working directory was changed, `interactive()` changes
into that directory using `cd X` which wasn't encoded.
2023-07-09 12:30:04 +00:00
Arusekk 7547518e7c Merge branch 'beta' into dev 2023-07-09 12:57:50 +02:00
Arusekk edb93986ef Merge branch 'stable' into beta 2023-07-09 12:57:04 +02:00
peace-maker 8c62a58a18 Add pathlib2 to install_requirements for python_version < 3.4 (#2205)
The dynamic addition of the dependency which was used previously wasn't
included in the published egg on pypi.
2023-07-09 10:50:20 +00:00
zt20xx 96a8db0243 Update elf.py (#2211) 2023-07-09 10:36:39 +02:00
peace-makerandArusekk 8b4cacf8b8 Fix typo in ssh trying to access non-existent log.Progress.error (#2200)
* Fix typo in ssh trying to access log.Progress.error

That function doesn't exist and .failure() is used everywhere else.

* Use log.error instead of manually raising

The log.Progress logger doesn't print the exception when exiting the scope.

---------

Co-authored-by: Arusekk <[email protected]>
2023-07-03 19:29:39 +02:00
Arusekk 5f15b53ee6 Merge branch 'beta' into dev 2023-07-02 22:16:47 +02:00
peace-maker 7d4b80fc48 Fix stable changelog in beta (#2213) 2023-07-02 19:59:07 +00:00
maple b73e74f1ff Replace isSet with is_set as the former is deprecated in 3.10+ (#2208)
`threading.Event.isSet` is deprecated here: https://github.com/python/cpython/commit/9825bdfbd5c966abf1f1b7264992d722a94c9613
2023-06-26 13:50:11 +02:00
Arusekk 909d5833cd Merge branch 'beta' into dev 2023-06-26 01:45:34 +02:00
Arusekk b9cc118a15 Clean up CI workflow 2023-06-26 01:44:54 +02:00
Arusekk 1f63615085 Merge branch 'stable' into beta 2023-06-24 02:20:19 +02:00
peace-maker 23ef84e45a Fix Python 2 CI (#2210)
* Fix Python 2 CI

* Fix python symlink related tests
2023-06-24 02:16:53 +02:00
91be8af121 Add -p (--prefix) and -s (--separator) arguments to hex command (#2117)
* Add formats for hex

* Add formats for hex coverage

Relates to #2117. Adding coverage as requested.

* Use util.lists.group

* Update CHANGELOG

* Fix Python 2 support

* Simplify hex formatting

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

---------

Co-authored-by: marcan2020 <[email protected]>
Co-authored-by: carydrew <[email protected]>
Co-authored-by: Peace-Maker <[email protected]>
Co-authored-by: Arusekk <[email protected]>
2023-06-04 22:23:11 +02:00
peace-maker 61bce7f6cf Fix remote and listen in sagemath (#2202)
* Fix `remote` and `listen` in sagemath

sage's Integer doesn't seem to inherit from python's base int.

```
----> 1 listen(Integer(1337))
OSError: Int or String expected
```

Fixes #2201

* Update CHANGELOG
2023-06-02 16:57:00 +02:00
Arusekk c195c14b13 Merge branch 'beta' into dev 2023-06-01 11:40:37 +02:00
Arusekk 17fa832443 Merge branch 'stable' into beta 2023-06-01 11:40:03 +02:00
peace-maker c72886a9b9 Fix 4.11 beta changelog (#2198)
It included the changes in stable again.
2023-06-01 01:56:59 +02:00
Arusekk e09112b7d4 Merge branch 'beta' into dev 2023-05-21 16:38:42 +02:00
Arusekk f8fd03c2c2 Do not pollute PyPI with half-handed packages 2023-05-21 16:38:09 +02:00
Arusekk d590a0b77f Begin working on 4.12.0 2023-05-21 16:16:11 +02:00
Arusekk f0ffaee217 Release 4.11.0beta0 2023-05-21 15:56:09 +02:00
Arusekk 9dfc4d7ee4 Release 4.10.0 2023-05-21 15:46:14 +02:00
Arusekk cb5b8af5f3 Merge branch 'beta' into dev 2023-05-21 15:18:06 +02:00
Arusekk 78196db658 Merge branch 'stable' into beta 2023-05-21 15:07:18 +02:00
peace-maker d76c9fc704 Fix typo in mips mov shellcraft template (#2195) 2023-05-21 13:01:48 +00:00
Jonathan KellerandArusekk 8f122c3e00 Fix encoding & escaping in ssh.upload_dir (#2165)
remote_tar is a `bytes`, so the string interpolation resulted in the
command `cd ... && tar -xzf b'/tmp/...tar.gz`. It also would have failed
had either path contained spaces.

To fix this, make everything a bytes and use proper escaping.

Co-authored-by: Arusekk <[email protected]>
2023-05-21 12:51:03 +00:00
peace-makerandArusekk 123f68f096 Fix highlight of unaligned nullbyte in disasm cmd (#2182)
"1001" would highlight the "00" even though it's not a null byte in the
input. Group the bytes correctly to look for real null bytes and
newlines only ignoring accidental matches of adjacent bytes.

Co-authored-by: Arusekk <[email protected]>
2023-05-21 12:42:05 +00:00
James R T 5a84bbf775 Force ELF.path to be of type string (#2174)
This commit forces the `path` class variable of the `ELF` class to be of
type string, even if a byte string argument is passed. This also makes
the implementation consistent with the documentation, which states that
`ELF.path` is of type `str`.

Fixes #2166

Cherry-picked from 60af3a26d0

Signed-off-by: James Raphael Tiovalen <[email protected]>
2023-05-21 14:23:22 +02:00
James R T 60af3a26d0 Force ELF.path to be of type string (#2174)
This commit forces the `path` class variable of the `ELF` class to be of
type string, even if a byte string argument is passed. This also makes
the implementation consistent with the documentation, which states that
`ELF.path` is of type `str`.

Fixes #2166

Signed-off-by: James Raphael Tiovalen <[email protected]>
2023-05-21 14:20:38 +02:00
Arusekk 060746e274 pypi: Use trusted publishing (OIDC) (#2194)
Recently introduced [auth mechanism][1] makes it possible to not keep
any long-lived tokens in GH secrets, reducing attack surface.

[1]: https://blog.pypi.org/posts/2023-04-20-introducing-trusted-publishers/

Cherry-picked from 74c37037e2
2023-05-21 14:02:13 +02:00
peace-maker 0499631429 Fix failing libcdb.unstrip_libc tests (#2196)
* Fix libcdb.unstrip_libc tests

The old debugsymbols seem to have been pruned.
Update hashes to recent Ubuntu 22.04 libcs.

* Switch wget test to httpbingo.org

httpbin.org is very slow to respond.

Cherry-picked from 1998ff0920
2023-05-21 14:02:13 +02:00
Arusekk 74c37037e2 pypi: Use trusted publishing (OIDC) (#2194)
Recently introduced [auth mechanism][1] makes it possible to not keep
any long-lived tokens in GH secrets, reducing attack surface.

[1]: https://blog.pypi.org/posts/2023-04-20-introducing-trusted-publishers/
2023-05-21 13:39:01 +02:00
peace-maker 58222ccf8b Handle context.newline correctly in tube.interactive() (#2129)
* Handle `context.newline` correctly in `tube.interactive()`

The `context.newline` or `self.newline` variable isn't obeyed when typing in interactive mode. It is used when sending and receiving lines through `tube.sendline` though, causing a mismatch.

The receiving end of the `tube.interactive()` already has handling of newlines, but the sending side does not.

Example:
```python
from pwn import *
io = process('cat')
io.newline = b'\r\n'
io.sendline(b'auto')
io.interactive()
```

```
$ python testinteractive.py DEBUG
[x] Starting local process '/usr/bin/cat' argv=[b'cat']
[+] Starting local process '/usr/bin/cat' argv=[b'cat'] : pid 19060
[DEBUG] Sent 0x6 bytes:
    b'auto\r\n'
[*] Switching to interactive mode
[DEBUG] Received 0x6 bytes:
    b'auto\r\n'
auto
$ test
[DEBUG] Sent 0x5 bytes:
    b'test\n'
[DEBUG] Received 0x5 bytes:
    b'test\n'
test
```

The expected outcome would be to send `b'test\r\n'.

The same problem arises in non-term mode (`PWNLIB_NOTERM=1`), where stdin is read in binary mode causing the OS line seperators to come through. Correctly replacing them with the `context.newline` setting allows to use the interactive input on windows hosts as well, without constantly sending `\r\n`.

* Update CHANGELOG.md

* Only replace newlines in TTYs

Don't mess with the line endings when piping data through.

* Always send bytes immediately on receive

Only replace newlines in a tty if NOTERM wasn't explicitly set or
the `newline` setting was set.

* Don't return a newline after term.readline lines

The builtin `input()` doesn't include the newline.

* Remove bogous newline from CHANGELOG
2023-05-18 22:32:08 +02:00
peace-maker 1998ff0920 Fix failing libcdb.unstrip_libc tests (#2196)
* Fix libcdb.unstrip_libc tests

The old debugsymbols seem to have been pruned.
Update hashes to recent Ubuntu 22.04 libcs.

* Switch wget test to httpbingo.org

httpbin.org is very slow to respond.
2023-05-18 17:59:16 +02:00
yuvalpo ab60471266 ELF.nx and ELF.execstack enhancements (#2186)
* ELF.nx and ELF.execstack enhancements

* Update CHANGELOG.md

* Detect NX state whenever possible when GNU_STACK is missing
2023-05-15 14:16:19 +02:00
ElouanFiore d16da4f8a8 Fixing a typo (Procedure Linkate Table -> Procedure Linkage Table) (#2192) 2023-05-13 11:11:45 +02:00
Arusekk 731f4a9219 Fix readthedocs hopefully (#2191)
* Fix readthedocs hopefully

* Make colored_traceback an optional dep

* Add python-dateutil to docs req

* Add more docs reqs
2023-05-13 11:09:17 +02:00
mthangandArusekk 83e3b5bbaa Fmtstr no dollar payload (#2185)
* added feature to the fmtstr module, now able to generate payload without the dollar syntax.

* made some improvements in the no_dollar feature implemented

* reduced different lines

* removed some blank lines and debug stuff

* (fix) missing check for no_dollar option

* (fix) updatet argument type for documentation

* updated CHANGELOG.md

* (fix) typo in the CHANGELOG.md version

* Update pwnlib/fmtstr.py

suggested change, dont need comparison in calling the make_payload_dollar

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

* Update pwnlib/fmtstr.py

suggestion; better use a non null byte thing to fill the values used by the %c to pad before writing.

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

* (fix) minor fixes, typos on comments

* update pwnlib/fmtstr.py : suggestion from Arusekk, made improvements to save some bytes in the generation of the payload

* added tests to fmtstr_payload with no dollar flag

* update CHANGELOG.md with correct version

---------

Co-authored-by: Arusekk <[email protected]>
2023-04-30 21:01:14 +02:00
Arusekk c1ed0e6f10 Declarative setup (#2179)
* Move most settings to pyproject.toml

* py2 minimal compat

* Make it not crash nor include spurious files

* Use PEP517

* Update workflows

* Limit workflows

Closes #1587
2023-04-18 22:33:01 +02:00
Arusekk ef3bb56be9 ci: leverage stock Android SDK present on GHA (#2175)
* ci: leverage built-in Android SDK of GHA runners

* android: more robust NDK detection
2023-04-18 15:12:17 +02:00
peace-maker 1a1124cf46 Fix debugging and assembly for RISCV (#2180)
* Fix debugging and assembly for RISCV

They are broken since the riscv architecture was split into riscv32
and riscv64 internally in #2177.

* Always look for riscv32. riscv64 or riscv toolchains

* Look for riscv64 for riscv64 first
2023-04-11 16:40:33 +02:00
XeonacidandArusekk fbf2727ac6 Determine elf arch by e_machine and bits (#2177)
* Determine elf arch by e_machine and bits

Separate 32/64 for riscv and mips
Trim some trailing space

* Fix missing comma

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

* Add CHANGELOG

* unicorn: Use fixed version

* Cosmetic enhancements

---------

Co-authored-by: Arusekk <[email protected]>
2023-04-09 02:18:59 +02:00
Arusekk a51a06a36f ci: Update CI workflow YAML devops engineering 2023-04-08 00:37:47 +02:00
peace-makerandArusekk aa48227781 Add search for libc binary by leaked function addresses (#2103)
* libcdb: Add option to search by function offsets

When you're able to leak addresses of the libc library, use `libcdb.search_by_symbol_offsets()` to find and download the matching libc library from https://libc.rip.

If there are multiple matches, the user is prompted to select one interactively. The selection can be saved in the code for future executions of the script.

Fixes #1867

* libcdb: Handle multiple results when looking up by hash

Sometimes the same library appears to be indexed multiple times (see 0b52d2e713d243f0f65d808fcd3fbe372bb3cd32). Handle that situation by selecting the first in the list, since they should all be identical given the same hash value.

* Update CHANGELOG

* Fix off-by-one when pre-selecting a libc

* Do .json() only once

---------

Co-authored-by: Arusekk <[email protected]>
2023-03-22 12:26:39 +01:00
Minei3oat 1461b486ab Fix documentation of tube.recvall (#2163) 2023-03-22 12:14:18 +01:00
Ivan Komarov 98778d2277 Fix a couple of typos in the comments for unpack*() functions (#2167) 2023-02-06 13:33:12 +01:00
Liam Siegel ba7c7e5545 Noticed a small typo and corrected the word "whith." (#2157)
This is only a cosmetic change to an incorrect word in the documentation.
2023-01-14 12:57:57 +01:00
Yulian 60e91b7fd6 Fix typo and comment consistency (#2115) 2023-01-12 15:12:10 +01:00
36ad53b4ff Fixes for download_dir (#2151)
* Fixes for download_dir.

* Adds support to use SFTP if available

* Update pwnlib/tubes/ssh.py

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

* Removal of unnecessary parens after 'if' keyword

Co-authored-by: Juuso Käenmäki <[email protected]>
Co-authored-by: Arusekk <[email protected]>
2023-01-10 07:17:53 +01:00
peace-maker 44d514119e Add basic shellcode for Win64 to shellcraft (#2154)
* Add initial set of shellcode to run WinExec("cmd.exe")

* Remove nullbyte from getexport

* Generate docs for windows shellcode
2023-01-08 22:51:58 +01:00
Jusb3andJuuso Käenmäki 55c304328a Changes _get_fingerprint to accept variety of output formats (#2152)
Co-authored-by: Juuso Käenmäki <[email protected]>
2023-01-06 18:32:26 +01:00
Arusekk 39ef958bfe Begin working on 4.11.0 2022-12-29 14:42:14 +01:00
Arusekk 082c55dd78 Release 4.10.0beta0 2022-12-29 14:11:52 +01:00
Arusekk c45e92d78b Release 4.9.0 2022-12-29 14:08:51 +01:00
Tobias JostandArusekk 7673ce8ea2 Make template respect args.EXE (#2111)
* Make template respect args.EXE

To be able to execute a script using an executable with another name and/or location than specified at template generation, `args.EXE` will be used as path for ELF file loading, only using the preconfigured value when no arg was supplied

* Update CHANGELOG.md

* FIXUP: properly test for EXE arg

* Style fixes and revert changelog

Co-authored-by: Arusekk <[email protected]>
2022-12-29 13:16:04 +01:00
Patryk KrawaczyńskiandArusekk 6a9f78ac46 Upgrade to Ubuntu Focal 20.04 LTS (#2118)
* Update Dockerfile

* Move to Ubuntu Jammy 22.04 and keep python2.7

* Apparently Debian intentionally broke ensurepip

Co-authored-by: Arusekk <[email protected]>
2022-12-29 13:08:23 +01:00
peace-makerandArusekk 48d76ed0af Support adding lists in ROP.raw() (#2128)
* Support adding lists in `ROP.raw()`

Allow to add multiple raw entries on a rop chain using an array instead of multiple calls to `raw()`.

```py
rop = ROP('/bin/ls')
# before
rop.raw(1)
rop.raw(2)
rop.raw(rop.ret)

# after
rop.raw([1, 2, rop.ret])
```

Fixes #2017

* Update CHANGELOG.md

* Update pwnlib/rop/rop.py

Co-authored-by: Arusekk <[email protected]>
2022-12-29 02:19:50 +01:00
Johnson 838e9ec947 Support FinishBreakpoint for GDB API (#2114)
* Support FinishBreakpoint for GDB API

* Evaluate args outside FinishBreakpoint callbacks
2022-12-29 02:17:50 +01:00
Arusekk 826f4f39da Create SECURITY.md
Closes #2015
2022-12-29 01:58:19 +01:00
peace-maker 188632cd96 Fix docker image building and local testing (#2146)
The base image was missing the patchelf utility and the develop image
didn't update the apt cache. That caused building the image to fail when
pulling the outdated pwntools:base image from dockerhub.
The docker Makefile was using an invalid `-depth 1` argument which appear
to have been replaced by `-mindepth` and `-maxdepth` arguments recently.

The testing container is based on the develop image's Dockerfile which
copies new files into the container. Grab them from the appropriate folder.
pwndbg was installed twice when running the testing container for the
doctests.
2022-12-29 00:55:39 +01:00
Ian Laird e5407603f0 Added arch and bits specific flag to asm call from elf wrapper (#2137) 2022-12-29 00:21:34 +01:00
peace-maker f1693e3005 Support non-bytes argument in packing._need_text() (#2127)
* Support non-bytes argument to `packing._need_text()`

Allow to pass everything to `_need_text` and be sure to get a string back. This allows to log any object without the need for a format string by calling `repr` on it.

```py
# before:
log.info('%r', 5)
# now
log.info(5)
```

Fixes #2018
2022-12-29 00:19:35 +01:00
clubby789 4d81cb5d3f Reduce Pylint CI false positives (#2142) 2022-12-29 00:15:11 +01:00
Arusekk fa08f5a7bf Fix spurious AttributeError on None stdio
Fixes #2036
Closes #2102
2022-12-29 00:08:14 +01:00
Arusekk 69132c66e3 Fix CI after careless merging of #2144
This is why CI should always be green.
Make it green, dear contributor, because the maintainers might merge
something without checking if it is green.  Please.
2022-12-29 00:08:14 +01:00
Arusekk 74cf13eb58 Update README.md
The old API was a hack apparently, but it looks like GitHub Actions slowly gets less and less sketchy over time, and even goes as far as providing well-designed APIs.
Ref. https://github.com/badges/shields/issues/8671
2022-12-29 00:03:41 +01:00
PeterD1524 448914a5aa Fix logging in wait_for_debugger (#2143) 2022-12-28 23:14:58 +01:00
Disconnect3d 2f21f1bedb Fix PwntoolsLexer number matching (#2139)
This commit fixes a small bug with PwntoolsLexer where it matched negative numbers incorrectly in Arm assembly.

See https://github.com/pwndbg/pwndbg/pull/1367 for more information
2022-12-28 23:13:19 +01:00
BinSong 165452a0ab fix a bug in ret2dlresolve when pie is enabled. (#2136) 2022-12-28 23:12:46 +01:00
ThatOnePizzaandArusekk b771fc560b Fix asm() instruction consistency (#2144)
* sets p2align header to 0 for x86-32 x86-64 and thumb

* updates changelog

* adds PR link to changelog

* Update pwnlib/asm.py

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

Co-authored-by: Arusekk <[email protected]>
2022-12-28 23:12:23 +01:00
Arusekk 21131bd5e8 Merge branch 'beta' into dev 2022-12-28 22:34:59 +01:00
Arusekk a76c7b0ca1 Defer import of optional dependency 2022-12-28 22:29:50 +01:00
Arusekk b8fad78d3e Fix binutils version checking and RWX warnings
Fixes #2140
Closes #2098
Closes #2141
2022-12-28 22:19:35 +01:00
Arusekk 8f42bef2b1 Use modern inspect.signature
While at it, drop the hard-to-maintain code.
We lose the signature in py2, but its support is second-class anyway.

Fixes #2145
Closes #2148
2022-12-28 22:13:55 +01:00
Arusekk 020143f0f9 Compatibility with python 3.11 (#2070)
Closes #2057
Closes #2052
2022-12-28 21:46:26 +01:00
Arusekk 416c33ed00 New toolchain, new doctests 2022-12-28 21:46:26 +01:00
Arusekk 3884d606b7 ci: Use default elfutils from 22.04 2022-12-28 21:19:42 +01:00
Arusekk 4628627943 Bump setup-python GH action 2022-12-28 20:15:06 +01:00
Arusekk d86ae5794a Merge branch 'stable' into beta 2022-12-28 19:43:01 +01:00
minaminao 9e065adae8 fix: str -> bytes in doc (#2133) 2022-12-02 23:04:40 +01:00
Arusekk 0a7c0b62a7 Make term resistant against SIGWINCH storm 2022-11-06 17:39:28 +01:00
peace-maker 67d49a693d Fix tube.recvpred() timout argument (#2124)
* Fix tube.recvpred() timout argument

The `timeout` argument wasn't implemented correctly causing the function to hang instead of timing out.

* Update CHANGELOG.md
2022-11-01 19:21:37 +01:00
peace-maker 4ddb702ba7 Mention need for listen.wait_for_connection() in docs (#2126)
You have to call `listen.wait_for_connection()` before using the listen socket or you face raceconditions with the thread accepting the client connection.

Fixes #2100
2022-10-27 14:39:25 +02:00
peace-maker 30f55d33f4 Allow tube.recvregex to return capture groups (#2125)
* Allow tube.recvregex to return capture groups

When wanting to extract a value from the received data, one had to receive up until the value and the value itself in two steps without carving the data out of a large blob like this:

```
io.recvuntil(b'The address is: ')
address = int(io.recvline(), 16)
```

or use a regex twice:

```
pattern = re.compile(rb'The address is: (0x[0-9a-fA-F]+)')
data = io.recvregex(pattern)
address = int(pattern.search(data).group(1), 16)
```

This patch allows to pass `capture=True` to `tube.recvregex()` to return a `re.Match` object instead of the raw bytes.

```
addr = int(io.recvregex(rb'The address is: (0x[0-9a-fA-F]+)', capture=True).group(1), 16)
```

This allows for slicker data extraction!

Fixes #1962

* Update CHANGELOG.md
2022-10-27 14:37:23 +02:00
peace-maker a7cb54931c Fix ROP without a writeable cache directory (#2123)
* Fix ROP without a writeable cache directory

`context.cache_dir` can be None, but the ROP class assumed it would always be a valid path and crash. Disable rop-cache if we can't save it.

Fixes #2072

* Update CHANGELOG.md
2022-10-27 14:33:54 +02:00
锋临 dc4108d6f2 Fix boundary judgment error (#2116) 2022-10-23 09:37:20 +02:00
peace-maker 7d1dae1071 Update libcdb debuginfod default URL to federating server (#2101)
* Switch to federated debuginfod service

elfutils.org offers a federating proxy server which forwards the requests to all other debuginfod servers of the different linux distributions.

https://sourceware.org/elfutils/Debuginfod.html

Use that instead by default to increase our successrate and potentially include new servers automatically once the distro sets one up.

* Use DEBUGINFOD_URLS envvar for server selection

The `DEBUGINFOD_URLS` environment variable is what you use for your normal debuginfod setup to tell it where to look for debug symbols.

Parse it as well and prepend it to the list of servers to try to allow for dynamic adjusting of the target servers.
2022-09-25 14:56:19 +02:00
锋临 302a4ef4ed Fix format string badbytes not working (#2113)
* Fix format string badbytes not working

* update boundary conditions
2022-09-24 12:10:24 +02:00
Aidan W 6457c33280 Spelling mistake fix (#2104) 2022-08-30 10:10:33 +02:00
Arusekk 87e37ee121 setresuid: generic implementation for all arches (#2093)
* setresuid: generic implementation for all arches

Closes #1324

* Update changelog
2022-08-15 12:41:25 +02:00
Arusekk e53c7f7350 shellcraft: consistently name dupio and optimize (#2092)
* shellcraft: consistently name dupio and optimize

* Update changelog
2022-08-08 23:10:10 +02:00
Gustaf Blomqvist a5c0809f38 Do not skip chars after non-printable characters
Closes #2094
2022-08-08 23:00:30 +02:00
01df19d360 Stable: ret2csu: fix a silent failure when rdx is populated from r14 (#2063)
* Fix for overwritten env when parsing core file

* warn_once message when dealing with overwritten pointer to env variable in core file

* Check for sha256 and sha256sum before trying openssl sha256 as "openssl sha256" doesn't return an error code in some old versions of openssl (OpenSSL 0.9.8g 19 Oct 2007 at least) that doesn't support sha256.

* ret2csu: fix a silent failure when rdx is populated from r14.
One such binary can be found at https://github.com/nobodyisnobody/write-ups/tree/main/FCSC.2022/pwn/Deflation

* Clean up the code a bit

Co-authored-by: Zach Riggle <[email protected]>
Co-authored-by: Arusekk <[email protected]>
2022-08-07 17:02:13 +02:00
Lasne OlivierandArusekk e63e685bc7 make pwn cyclic -l work with entry larger than 4 bytes (#2062)
* make pwn cyclic -l work with entry larger than 4 bytes

* Do not assume py3, do not assume little-endian

* Update changelog

* pack all bytes instead

Co-authored-by: Arusekk <[email protected]>
2022-08-07 16:47:37 +02:00
LYandArusekk 002ac9e03e Change prefix in install script to $PREFIX (#2079)
* Change prefix in install script to $PREFIX

On special envrionments like Termux, /usr/local is not the proper path.

* Change prefix in binutils install script to $PREFIX

* PREFIX not everywhere

Co-authored-by: Arusekk <[email protected]>
2022-08-07 12:41:29 +02:00
Arusekk b825e977f3 shellcraft: fix and optimize itoa
Fixes #2073
2022-08-07 10:56:17 +02:00
Arusekk 0533ba01c8 Make setRegisters more robust
Closes #1683
2022-08-07 10:31:33 +02:00
Carlos Silva f084699341 Don't pass basename to coredumpctl (#1879)
coredumpctl saves the coredumps with full path to the binary. Passing only the basename makes it fail to find the coredump when it is actually there.

Closes #1879
2022-08-06 19:47:49 +02:00
Florian Kothmeier 955b6d4cbd Enable sysroot parameter for native gdb.attach() (#2084) 2022-08-06 19:34:37 +02:00
Zhi-Qiang ZhouandArusekk 1b5a00cad9 code style (add abstractmethod) (#2069)
Co-authored-by: Arusekk <[email protected]>
2022-08-06 18:40:04 +02:00
aviyae a7b362e1c0 gdb: fix #1493 (#2083)
Fix wrong branch taken when trying to debug non native arch via ssh
(resulting qemu_port being referenced without being set).

This is done by setting the condition in line 571 to be the same
as in line 534

Signed-off-by: Aviya Erenfeld <[email protected]>
2022-08-06 18:25:44 +02:00
S1lv3rGh0st eb67d55f0d Fix typo (#2080)
For issue #2074
2022-08-06 17:44:44 +02:00
Arusekk 807f534c24 Compatibility with python 3.11 (#2070)
Closes #2057
Closes #2052
2022-06-17 21:50:47 +02:00
Arusekk 11f34c3bc5 Follow-up after #2061 which broke CI 2022-06-04 21:52:37 +02:00
u91o dbf066b89c Fix links (#2061) 2022-05-18 16:00:28 +02:00
u91o 45551f4bc4 Fix dash build (#2059) 2022-05-18 16:00:08 +02:00
Arusekk 7f5866ab8d Begin working on 4.10.0 2022-04-21 01:23:52 +02:00
Arusekk e450f6c247 Release 4.9.0beta0 2022-04-21 01:14:48 +02:00
Arusekk 493a3e3d92 Release 4.8.0 2022-04-21 01:05:15 +02:00
Arusekk 3e4d4a2115 Allow non-numeric ports with remote() 2022-04-21 00:51:54 +02:00
Arusekk b3b6d52bdd Clean up usages of getattr/__getattr__ (#2028) 2022-04-21 00:38:24 +02:00
sourqueandLMS57 6ba46d4eec Add the 'raw' option to SSH shell initialization (#2037)
* Add the 'raw' option to SSH shell initialization

This permits the use of the SSH shell as an arbitrary TCP tunnel by
bypassing the startup environmental checks.

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

* Add changelog items

* Add assignment to self variable

Co-authored-by: LMS57 <[email protected]>
2022-04-21 00:37:25 +02:00
PaulandArusekk 5af2d92100 Quote file and core path in generated GDB script (#2033)
* Quote file and core path in generated GDB script

* Update changelog

Co-authored-by: Arusekk <[email protected]>
2022-04-21 00:36:03 +02:00
3fadc67327 clean ioerror (#2039)
* clean ioerror

* enforce stricter checks on broken pipe

* Apply suggestions from code review

Co-authored-by: caprinux <[email protected]>
Co-authored-by: Arusekk <[email protected]>
2022-04-21 00:32:40 +02:00
peace-maker 91c1b20c7b Detect Windows Terminal and split gdb into new pane (#2056)
Split the terminal window similar to tmux debugging experience when we're running in Windows Terminal instead of the plain old console host.
2022-04-20 23:46:42 +02:00
Justin StevenandArusekk 0351219903 Add js_escape and js_unescape (#1979)
* Add js_escape and js_unescape

* Add entry for js packing functions

* Use hardcoded data for doctest

* Fix js_escape data iteration for Python 2

* Move js packing functions to util.fiddling

* Apply suggestions from code review

Co-authored-by: Arusekk <[email protected]>
2022-04-20 17:49:00 +02:00
peace-makerandArusekk 4b067d2ec5 Add libcdb commandline tool (#1975)
* Add libcdb commandline tool

Allows to dump information about a local libc binary or lookup matching libc versions based on a build hash or symbol offsets (querying https://libc.rip).

It's possible to download and unstrip a matching libc binary from the command line.

* Fix Python 2 compatibility

* Remove main_arena heuristic

* Update CHANGELOG

Co-authored-by: Arusekk <[email protected]>
2022-04-20 16:35:36 +02:00
Heap Crash d9f9e7ca6f Add documentation on the rop.register=foo assignment option (#1924)
* Add documentation on the rop.register=foo assignment option

* Add looking up ROP gadgets by name as well, and manual ROP construction
2022-04-20 16:34:30 +02:00
Emanuel HauptandArusekk 9e55d8f4c8 Add badge packaging status badge (#2054)
* Add badge packaging status badge

Add a repology badge to show packaging status.

* Update README.md

Use shields.io everywhere

Co-authored-by: Arusekk <[email protected]>
2022-04-20 15:56:37 +02:00
Arusekk 97a760a0dd Merge branch 'beta' into dev 2022-04-20 15:47:08 +02:00
Arusekk f491bc6a22 Merge branch 'stable' into beta 2022-04-20 15:46:34 +02:00
Arusekk cde1b89615 Release 4.7.1 2022-04-20 15:37:20 +02:00
zup 17fc0aeaad fix double decoding of bytestring when logging (#2051) 2022-04-20 13:20:40 +00:00
maybe-sybr c93bdfeed8 fix: Fallback to temp dir if cache does not exist (#1784)
This fixes a misbehaviour in environments where `XDG_CACHE_DIR` is not
set and `~/.cache` does not exist, e.g. in the docker image. We simply
attempt to create a temporary directory which will be registered with
`atexit` to be deleted on exit using `shutil.rmtree()`. A Python 3
approach for future would be to use a `TemporaryDirectory` instead which
would get cleaned up when the held object gets dropped out of the TLS
stack.

It is still possible for the `context.cache_dir` to be `None` if the
default directory exists but is not writable, which means some code
which uses this property is liable to blow up in such a circumstance.
2022-04-20 12:09:41 +00:00
TestzeroandT3stzer0 4fd08b0b00 fix packing error (#2040)
Co-authored-by: T3stzer0 <[email protected]>
2022-04-20 12:00:35 +00:00
Spencer Pogorzelski 8dd566b9e9 Fix env command not found in gdb.debug (#2007) 2022-04-20 11:47:57 +00:00
Sebastiano MarianiandArusekk fca2929de0 Fix download_dir method when using an ssh tube (#1961)
Signed-off-by: Sebastiano Mariani <[email protected]>
Co-authored-by: Arusekk <[email protected]>
2022-04-20 10:51:21 +00:00
lucasg d67bdbc550 [asm] pwn.disam can return <text+0x0> instead of <text> (#1912)
Unit test:

```
(pwntools_mips_bug) lucasg@home:/tmp/pwntools$ git show
commit 78c6b8c2a9 (HEAD -> dev, origin/dev, origin/HEAD)
Merge: e3a5ac58 d7139997
Author: heapcrash <[email protected]>
Date:   Mon May 31 16:06:34 2021 -0500

    Merge branch 'beta' into dev

(pwntools_mips_bug) lucasg@home:/tmp/pwntools$ python
Python 3.7.3 (default, Jan 22 2021, 20:04:44)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pwn, binascii
>>> print(pwn.disasm(binascii.unhexlify("000000000d0000000000000000"), vma=0x00000000, arch="mips"))
   0:   00000000        nop
   4:   0000000d        break
   8:   00000000        nop
        ...
>>> print(pwn.disasm(binascii.unhexlify("000000000d0000000000000000"), vma=0x80000000, arch="mips"))
[ERROR] Could not find .text in objdump output:

    /tmp/pwn-disasm-77tu42pr/step2:     file format elf32-tradlittlemips

    Disassembly of section .text:

    80000000 <.text+0x0>:
    80000000:   00000000        nop
    80000004:   0000000d        break
    80000008:   00000000        nop
        ...
[ERROR] An error occurred while disassembling:
    b'\x00\x00\x00\x00\r\x00\x00\x00\x00\x00\x00\x00\x00'
    Traceback (most recent call last):
      File "/tmp/pwntools/pwnlib/asm.py", line 814, in disasm
        log.error('Could not find .text in objdump output:\n%s' % output0)
      File "/tmp/pwntools/pwnlib/log.py", line 424, in error
        raise PwnlibException(message % args)
    pwnlib.exception.PwnlibException: Could not find .text in objdump output:

    /tmp/pwn-disasm-77tu42pr/step2:     file format elf32-tradlittlemips

    Disassembly of section .text:

    80000000 <.text+0x0>:
    80000000:   00000000        nop
    80000004:   0000000d        break
    80000008:   00000000        nop
        ...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/tmp/pwntools/pwnlib/context/__init__.py", line 1524, in setter
    return function(*a, **kw)
  File "/tmp/pwntools/pwnlib/asm.py", line 818, in disasm
    log.exception("An error occurred while disassembling:\n%s" % data)
  File "/tmp/pwntools/pwnlib/asm.py", line 814, in disasm
    log.error('Could not find .text in objdump output:\n%s' % output0)
  File "/tmp/pwntools/pwnlib/log.py", line 424, in error
    raise PwnlibException(message % args)
pwnlib.exception.PwnlibException: Could not find .text in objdump output:

/tmp/pwn-disasm-77tu42pr/step2:     file format elf32-tradlittlemips

Disassembly of section .text:

80000000 <.text+0x0>:
80000000:       00000000        nop
80000004:       0000000d        break
80000008:       00000000        nop
        ...
```
2022-04-20 10:25:58 +00:00
Alan YeeandArusekk c3894c24de Make ELF symbol dict accept bytes (#1973)
DynElf symbol lookup uses bytes as key

Fixes #1951

Co-authored-by: Arusekk <[email protected]>
2022-04-20 09:48:13 +00:00
Arusekk 208b11df69 Avoid name clash of plain and android-related test 2022-04-20 11:14:45 +02:00
Will 0a860bd964 Fix incorrect disasm output (#2031)
Use objdump --wide option to ensure we don't have new lines in large instructions (>7 bytes).
2022-04-20 08:58:08 +00:00
0x3c3e 15efc3f1dc Cast int target pid to str (#2012) 2022-04-20 08:24:06 +00:00
gogo2464 2d0f03b66c Update Sphinx in order to fix jinja2 deprecated dependency. (#2053) 2022-04-20 08:44:02 +02:00
Justin Steven d7e9d313a2 Fix hexdump docstring. s should by bytes. (#1982) 2022-04-20 00:27:25 +02:00
Roderick 7a246e7887 fix Gallopsled/pwntools#2044 (#2045)
Specify the distro name when pop up a debugging window in wsl
2022-04-20 00:16:10 +02:00
QuqqU c04dbdb9e9 Correct a return type in Docstring (#2055) 2022-04-19 23:25:45 +02:00
Arusekk fc372d17a2 rop: Improve density (#2048)
When the last gadget has no arguments and the gadget before has stack
arguments, put the last gadget directly in its return address slot
(instead of a stack adjustment gadget).
2022-04-19 23:25:23 +02:00
gogo2464 79986d8262 Update Sphinx in order to fix jinja2 deprecated dependency. (#2053) 2022-04-13 17:49:01 +02:00
tkmikan ef698d4562 change Buffer's parent class to object (#2035)
* change Buffer's parent class to object

* update CHANGELOG.md
2022-02-15 17:31:01 +01:00
Swapnadeep Som 1238fc359e Fix UPX packer identifier (#1947)
Fixes #1855
Closes #1947
2022-02-01 11:53:12 +01:00
Arusekk 79d150061c Merge branch 'beta' into dev 2022-02-01 11:09:35 +01:00
peace-maker 2198b74df9 Fix CI after Groovy Gorilla went away for libc unstrip test (#2025)
* Fix CI after Groovy Gorilla went away for libc unstrip test

Build elfutils 0.181 from source since we can't use builds
from a newer ubuntu version anymore.

* Install python wheels in CI
2022-02-01 11:07:39 +01:00
TomasGl bf37947a99 Use a more reliable way to split the KDE konsole (#2026)
* Use a more reliable way to split the KDE konsole

* Changed xml parsing to pure parsing

* Update CHANGELOG.md
2022-02-01 11:01:31 +01:00
peace-maker 1fe9b3e693 Fix ELF.libc_start_main_return with glibc 2.34 (#2027)
* Fix ELF.libc_start_main_return with glibc 2.34

The actual call to `main` was split out into a new `__libc_start_call_main` function in glibc 2.34.

Update the heuristic to find main's return address by looking into all direct calls in `__libc_start_main` until the old heuristic matches.

The other direct calls could be e.g. `atexit` which doesn't call `exit`.

* Update CHANGELOG.md
2022-01-31 18:41:42 +01:00
peace-maker 762591bf35 Fix CI after Groovy Gorilla went away for libc unstrip test (#2025)
* Fix CI after Groovy Gorilla went away for libc unstrip test

Build elfutils 0.181 from source since we can't use builds
from a newer ubuntu version anymore.

* Install python wheels in CI
2022-01-28 11:58:23 +01:00
TomasGl 04f401c70e Support KDE Konsole in run_in_new_terminal function (#2023)
* Support KDE Konsole in run_in_new_terminal function

* Rewrite '.stdout.read()' to '.communicate()[0]'
2022-01-26 00:07:20 +01:00
jammychiou1 84b51fc1a5 Update filepointer.py (#2022)
Fix typo in documentation: "defination" to "definition"
2022-01-23 22:37:58 +01:00
tkmikan 1573957cc8 fix tube's debug output of same byte compression (#2011)
* fix tube's debug output of same byte compression

* Update CHANGELOG.md

* move code to `log.maybe_hexdump`

* update doc string
2022-01-08 13:22:38 +01:00
Arusekk 038e50fa0c Replace reserved argument names in syscalls (#1998)
Just stick to the replaced names everywhere.
This actually only broke linkat(2) and symlinkat(2),
but it touches many len -> length renames as well
for the sake of consistency.
Fixes #1964
2022-01-04 11:05:08 +01:00
Disconnect3d 58d6aea217 Add arch alias: x86-64 -> amd64 (#1967)
While we have an alias for "x86_64" some software (like Pwndbg) uses the "x86-64" string.
2021-11-21 23:54:24 +01:00
Arusekk 4c78e188ce Begin working on 4.9.0 2021-11-15 12:49:32 +01:00
Arusekk 9d8060496c Release 4.8.0beta0 2021-11-15 12:44:33 +01:00
Arusekk 7ea1f1d492 Merge branch 'beta' into dev 2021-11-15 12:25:01 +01:00
Arusekk da8bf2667d Fix CI failures (#1997)
Ubuntu, why are you doing this to us?
I seriously mean that Ubuntu is the most silly distro.

Like, they probably updated their docker (or VM) images last week,
then Github got it, and we started getting failures.
They totally changed the behaviour of that 'apport' thing,
and it now drops core files in /var/lib/apport/coredump
instead of the current working directory.

Why is that even... ugh.  Nevermind.  It works now, at least.
But for how long?
2021-11-15 12:14:18 +01:00
Arusekk a37bce2bdc Display nice error message on unavailable ret2csu (#1991)
Fixes #1941
2021-11-14 15:20:59 +01:00
Arusekk 3dd0b24cc3 Add cat2 to shellcraft (#1995)
* Introduce shellcraft.linux.cat2 to avoid sendfile

Fixes #1871

* Switch CAT_PROC_MAPS_EXIT to shellcraft.linux.cat2
2021-11-14 15:20:43 +01:00
Arusekk a8c8c28f00 Fix CI failures (#1994)
* Try to fix build failures by updating sphinx

See sphinx-doc/sphinx#9811

* Does not work with py2, so pin old docutils
2021-11-11 17:53:45 +01:00
Justin Steven 315a462603 Make cyclic_find work with large int values (#1981)
* Make cyclic_find work with large int values

* Add entry for cyclic_find() large int fix

* Tweak cyclic_find() large int warning
2021-11-11 14:59:05 +01:00
ElJeffeandArusekk ddeddd455e Fix: TypeError: can't concat str to bytes (#1953)
* Fix: TypeError: can't concat str to bytes

sh_string returns a string, but two lines try to concat it with bytes, which throws a TypeError.  Either convert them all to bytes or all to string.

* Update to use packing._need_bytes

I just followed the format from surrounding conversions.  Hopefully that works correctly.

* Update pwnlib/tubes/ssh.py

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

* Conversion to bytes

* Going for higher level conversion

Co-authored-by: Arusekk <[email protected]>
2021-11-01 00:35:10 +01:00
Alan Yee d3140ed983 Using native features of built-in functions over built-in operations config.py (#1980)
Use lazy logging and use second argument of `.get()` instead of relying on `or`
2021-11-01 00:33:32 +01:00
Arusekk fc250a33a1 Add missing copyright notice to ARM alphanumeric (#1955)
Closes #1954
2021-10-07 09:33:34 +02:00
peace-makerandArusekk 92911cbabd libcdb: Load debug info and unstrip libc binary (#1828)
* libcdb: Load debug info and unstrip libc binary

The debug info is obtained from a debuginfod server maintained by systemtap.org. elfutils is used to unstrip the binary and merge the debug info back.

Try to unstrip every libc binary downloaded by the libcdb module by default. #1818

* Install elfutils on CI

* Update CHANGELOG.md

* Add more tests

* Fix negative test

* Raise an exception when eu-unstrip fails

This will hopefully get logged during doctests.

* Install elfutils 0.181 from groovy

Use APT Pinning to select the newer elfutils version from groovy instead of the buggy 0.176 version available in focal.

* Fix missing import of requests

pylint?

Co-authored-by: Arusekk <[email protected]>
2021-10-03 10:51:30 +02:00
Alan Yee a9715d8c74 Use lazy logging instead of native string interpolation (#1972)
https://docs.python.org/3/howto/logging.html#logging-variable-data
2021-10-01 07:48:57 +02:00
Darin Mao bd12d1874f Fix KB, MB, GB globals (#1926) 2021-07-29 19:17:17 +02:00
clubby789 11b8537d17 Fix validation of log level name for Python 3 (#1939)
* Fix validation of log level name for Python 3

* Add changelog

* Reorder changelog and use try/except
2021-07-29 19:16:36 +02:00
Connor NelsonandArusekk f7283bf2eb Fix logic in wait_for_debugger (#1922)
* Fix logic in `wait_for_debugger`

* Update CHANGELOG.md

* Fix CHANGELOG.md

* Fix CHANGELOG.md

Co-authored-by: Arusekk <[email protected]>
2021-07-15 11:42:42 +02:00
Arusekk d5d7b8385c Begin working on 4.8.0 2021-07-12 23:06:17 +02:00
627 changed files with 6731 additions and 3603 deletions

No files matched your search

-12
View File
@@ -1,12 +0,0 @@
[run]
branch = True
parallel = True
concurrency = thread,multiprocessing
omit =
*/constants/*
source =
pwn
pwnlib
~/.cache/.pwntools-cache-2.7/
~/.cache/.pwntools-cache-3.8/
disable_warnings = module-not-imported
+8
View File
@@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
# Check for updates to GitHub Actions every week
interval: "weekly"
+7 -8
View File
@@ -3,7 +3,7 @@ name: Android Tests
on: [push, pull_request]
jobs:
test:
android-test:
strategy:
matrix:
python-version: [3.8]
@@ -11,17 +11,17 @@ jobs:
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Cache for pip
uses: actions/cache@v1
uses: actions/cache@v3
id: cache-pip
with:
path: ~/.cache/pip
key: ${{ matrix.os }}-cache-pip
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
@@ -33,14 +33,13 @@ jobs:
qemu-user-static \
binutils-aarch64-linux-gnu \
binutils-arm-linux-gnueabihf \
libc6-dbg \
openjdk-8-jre-headless
libc6-dbg
- name: Install Android AVD
run: |
USER=travis source travis/setup_avd.sh
source travis/setup_avd_fast.sh
sed -i 's/skip_android = True/skip_android = False/' docs/source/conf.py
set | egrep '^(ANDROID|PATH)' >.android.env
set | grep ^PATH >.android.env
- name: Install dependencies
run: |
+6 -2
View File
@@ -1,5 +1,9 @@
name: Changelog
on: [pull_request]
on:
pull_request:
paths:
- pwnlib/**
- pwn/**
env:
GITHUB_BASE: origin/${{ github.event.pull_request.base.ref }}
@@ -11,7 +15,7 @@ jobs:
runs-on: ${{ matrix.os }}
timeout-minutes: 5
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
fetch-depth: 0
+74 -29
View File
@@ -5,14 +5,14 @@ jobs:
test:
strategy:
matrix:
python-version: [2.7, 3.8]
python_version: ['2.7', '3.10']
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-depth: 20
- name: Git History
run: |
@@ -20,16 +20,29 @@ jobs:
git log --oneline --graph -10
- name: Cache for pip
uses: actions/cache@v1
uses: actions/cache@v3
id: cache-pip
with:
path: ~/.cache/pip
key: ${{ matrix.os }}-cache-pip
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
- name: Set up Python ${{ matrix.python_version }}
if: matrix.python_version != '2.7'
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
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
- name: Verify tag against version
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
@@ -49,7 +62,8 @@ jobs:
sudo apt-get update
sudo apt-get install -y --no-install-recommends -o Acquire::Retries=3 \
ash bash-static dash ksh mksh zsh \
pandoc gdb gdbserver socat sshpass \
python3-rpyc \
gdb gdbserver socat \
binutils-multiarch qemu-user-static \
binutils-aarch64-linux-gnu \
binutils-arm-linux-gnueabihf \
@@ -59,13 +73,10 @@ jobs:
binutils-s390x-linux-gnu \
binutils-sparc64-linux-gnu \
gcc-multilib \
libc6-dbg
- name: Install RPyC for GDB
run: |
sudo apt-get install -y python3-pip
/usr/bin/python3 -m pip install rpyc
libc6-dbg \
elfutils \
patchelf
- name: Testing Corefiles
run: |
ulimit -a
@@ -81,18 +92,19 @@ jobs:
- name: Install dependencies
run: |
pip install --upgrade pip
pip install --upgrade wheel build
pip install --upgrade flake8 appdirs
python setup.py egg_info
pip install --upgrade --editable .
- name: Sanity checks
run: PWNLIB_NOTERM=1 python -bb -c 'from pwn import *; print(pwnlib.term.term_mode)'
- name: Install documentation dependencies
run: pip install -r docs/requirements.txt
run: pip install -r docs/requirements.txt
- name: Manually install non-broken Unicorn
run: pip install unicorn==1.0.2rc3
if: matrix.python_version == '2.7'
run: pip install unicorn==2.0.0rc7
- name: Disable yama ptrace_scope
run: |
@@ -140,6 +152,11 @@ jobs:
pwn disasm --color ff3424c3ebfe
pwn asm -f hex nop
pwn hex ABCD
pwn hex ABCD --separator ' '
pwn hex ABCD --prefix '\x'
pwn hex ABCD -p '0x' -s ' '
pwn hex abcd
pwn unhex 4141 4141
@@ -156,27 +173,55 @@ jobs:
pwn constgrep ^MAP_ 0
pwn constgrep -e O_RDWR
- name: Build source and wheel distributions
run: |
python setup.py sdist
python setup.py bdist_wheel --universal
pwn libcdb file /lib/x86_64-linux-gnu/libc.so.6
pwn libcdb lookup puts 5f0 __libc_start_main_ret d0a
pwn libcdb hash b229d1da1e161f95e839cf90cded5f719e5de308
- uses: actions/upload-artifact@v2
- name: Build source and wheel distributions
if: matrix.python_version > '2.7'
run: |
python -m build
- uses: actions/upload-artifact@v3
with:
name: packages
path: dist/
- uses: actions/upload-artifact@v3
with:
name: coverage
path: .coverage*
upload-coverage:
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 20
- uses: actions/download-artifact@v3
with:
name: coverage
path: .
- name: Install coveralls
run: |
pip install tomli coveralls
- name: Upload coverage to coveralls.io
run: |
coverage combine
COVERALLS_REPO_TOKEN=PP20MEgztXIQJJTguQwe2jeCh6Bm4lkbv coveralls
staging-merge:
runs-on: ubuntu-latest
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@v2
- uses: actions/checkout@v4
with:
fetch-depth: 20
- name: Push changes to protected branch
@@ -189,22 +234,22 @@ jobs:
git push origin "$TARGET"
git push origin --delete "$BRANCH"
pypi:
runs-on: ubuntu-latest
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
permissions:
id-token: write
needs: test
steps:
- name: Download artifacts
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
with:
name: packages
path: dist
- name: Publish package
uses: pypa/gh-action-pypi-publish@v1.1.0
with:
user: __token__
password: ${{ secrets.pypi_password }}
uses: pypa/gh-action-pypi-publish@release/v1
- if: failure()
run: ls -R
+64
View File
@@ -0,0 +1,64 @@
name: Publish Docker images
on:
push:
branches:
- dev
- beta
- stable
workflow_dispatch:
jobs:
publish:
runs-on: ubuntu-latest
if: github.repository_owner == 'Gallopsled'
steps:
# Required for subdirectories in Git context
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Build and push base image
uses: docker/build-push-action@v4
if: github.event_name == 'workflow_dispatch'
with:
context: "{{defaultContext}}:extra/docker/base"
push: true
tags: pwntools/pwntools:base
- name: Build and push stable image
uses: docker/build-push-action@v4
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/stable')
with:
context: "{{defaultContext}}:extra/docker/stable"
push: true
tags: pwntools/pwntools:stable
- name: Build and push beta image
uses: docker/build-push-action@v4
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/beta')
with:
context: "{{defaultContext}}:extra/docker/beta"
push: true
tags: pwntools/pwntools:beta
- name: Build and push dev image
uses: docker/build-push-action@v4
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/dev')
with:
context: "{{defaultContext}}:extra/docker/dev"
push: true
tags: |
pwntools/pwntools:dev
pwntools/pwntools:latest
- name: Build and push ci image
uses: docker/build-push-action@v4
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/dev')
with:
context: "{{defaultContext}}:travis/docker"
push: true
tags: pwntools/pwntools:ci
+3 -3
View File
@@ -10,16 +10,16 @@ jobs:
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Cache for pip
uses: actions/cache@v1
uses: actions/cache@v3
id: cache-pip
with:
path: ~/.cache/pip
key: ${{ matrix.os }}-cache-pip
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
+4 -2
View File
@@ -1,5 +1,7 @@
name: Check for merge markers
on: [pull_request]
on:
pull_request:
types: [synchronize]
env:
GITHUB_BASE: origin/${{ github.event.pull_request.base.ref }}
@@ -11,7 +13,7 @@ jobs:
runs-on: ${{ matrix.os }}
timeout-minutes: 5
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
fetch-depth: 0
+6 -6
View File
@@ -10,16 +10,16 @@ jobs:
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Cache for pip
uses: actions/cache@v1
uses: actions/cache@v3
id: cache-pip
with:
path: ~/.cache/pip
key: ${{ matrix.os }}-cache-pip
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
@@ -27,11 +27,11 @@ jobs:
run: |
set -x
pip install pylint
pip install --upgrade -r requirements.txt
pylint --exit-zero --errors-only pwnlib > current.txt
pip install --upgrade -e .
pylint --exit-zero --errors-only pwnlib -f parseable | cut -d ' ' -f2- > current.txt
git fetch origin
git checkout origin/"$GITHUB_BASE_REF"
pylint --exit-zero --errors-only pwnlib > base.txt
pylint --exit-zero --errors-only pwnlib -f parseable | cut -d ' ' -f2- > base.txt
if diff base.txt current.txt | grep '>'; then
false
fi
+1
View File
@@ -21,3 +21,4 @@ venv
.idea
__pycache__
!.github
.DS_Store
+12
View File
@@ -0,0 +1,12 @@
# https://docs.readthedocs.io/en/stable/config-file/v2.html
version: 2
build:
os: ubuntu-22.04
tools:
python: "3"
python:
install:
- requirements: docs/requirements.txt
-66
View File
@@ -1,66 +0,0 @@
language: python
addons:
apt:
packages:
- lib32stdc++6
- ash
- bash
- dash
- ksh
- mksh
- zsh
- pandoc
- gdb
- socat
- sshpass
- binutils
- qemu-user-static
- binutils-multiarch
- binutils-aarch64-linux-gnu
- binutils-arm-linux-gnueabihf
- binutils-mips-linux-gnu
- binutils-msp430
- binutils-powerpc-linux-gnu
- binutils-s390x-linux-gnu
- binutils-sparc64-linux-gnu
- bash-static
- python3-pip
cache:
- pip
- directories:
- usr
- /home/travis/virtualenv/python2.7.15/lib/python2.7/site-packages/
- /home/travis/virtualenv/python2.7.15/bin/
- /home/travis/virtualenv/python3.8.*/lib/python3.8/site-packages/
- /home/travis/virtualenv/python3.8.*/bin/
- docs/build/doctest/.doctrees
python:
- "2.7"
- "3.8"
before_install:
- echo $-
- source travis/install.sh
- source travis/ssh_setup.sh
- echo $-
install:
- /usr/bin/python3 -m pip install rpyc # for GDB
- pip install --upgrade pip
- pip install --upgrade flake8 appdirs # https://github.com/ActiveState/appdirs/issues/89#issuecomment-282326570
- python setup.py egg_info # pypa/pip#6275
- pip install --upgrade --editable .
- pip install --upgrade --requirement docs/requirements.txt
before_script:
- PWNLIB_NOTERM=1 python -c 'from pwn import *; print(pwnlib.term.term_mode)'
- PWNLIB_NOTERM=1 python -c 'from pwn import *; print(pwnlib.term.term_mode)'
- PWNLIB_NOTERM=1 python -c 'from pwn import *; print(pwnlib.term.term_mode)'
- PWNLIB_NOTERM=1 python -c 'from pwn import *; print(pwnlib.term.term_mode)'
- sudo sh -c "echo 0 > /proc/sys/kernel/yama/ptrace_scope"
script:
# stop the build if there are Python syntax errors or undefined names
- flake8 . --count --select=E9,F63,F7 --show-source --statistics --exclude=android-?dk # TODO: Add F82
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
- PWNLIB_NOTERM=1 coverage run -m sphinx -b doctest docs/source docs/build/doctest
- coverage combine
after_success:
- coveralls
- source travis/update_demo.sh
+183 -6
View File
@@ -9,9 +9,16 @@ The table below shows which release corresponds to each branch, and what date th
| Version | Branch | Release Date |
| ---------------- | -------- | ---------------------- |
| [4.9.0](#490) | `dev` |
| [4.8.0](#480) | `beta` |
| [4.7.0](#470) | `stable` | Nov 15, 2021
| [4.14.0](#4140-dev) | `dev` |
| [4.13.0](#4130-beta) | `beta` |
| [4.12.0](#4120-stable) | `stable` | Feb 22, 2024
| [4.11.1](#4111) | | Nov 14, 2023
| [4.11.0](#4110) | | Sep 15, 2023
| [4.10.0](#4100) | | May 21, 2023
| [4.9.0](#490) | | Dec 29, 2022
| [4.8.0](#480) | | Apr 21, 2022
| [4.7.1](#471) | | Apr 20, 2022
| [4.7.0](#470) | | Nov 15, 2021
| [4.6.0](#460) | | Jul 12, 2021
| [4.5.1](#451) | | May 30, 2021
| [4.5.0](#450) | | Apr 30, 2021
@@ -62,11 +69,164 @@ 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.9.0 (`dev`)
## 4.14.0 (`dev`)
## 4.13.0 (`beta`)
## 4.8.0 (`beta`)
- [#2242][2242] Term module revamp: activating special handling of terminal only when necessary
- [#2277][2277] elf: Resolve more relocations into GOT entries
- [#2281][2281] FIX: Getting right amount of data for search fix
- [#2293][2293] Add x86 CET status to checksec output
- [#1763][1763] Allow to add to the existing environment in `process` instead of replacing it
- [#2307][2307] Fix `pwn libcdb file` crashing if "/bin/sh" string was not found
- [#2309][2309] Detect challenge binary and libc in `pwn template`
- [#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
- [#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
- [#2339][2339] Fix: Allow setting attributes on gdb Breakpoints
- [#2323][2323] Retry failed lookups after one week in libcdb
- [#2325][2325] Match against local system libc first in libcdb
- [#2336][2336] Add `ELF.stripped` and `ELF.debuginfo` properties
- [#2161][2161] Add basic support for darwin shellcraft/asm/disasm/run_shellcode/run_assembly
- [#2161][2161] Fix freebsd amd64 SyscallABI
- [#2160][2161] Fix invalid shellcraft.mov on arm64
- [#2284][2161] Fix invalid shellcraft.pushstr_array on arm64
- [#2345][2345] Fix pwn constgrep when it matches a non-constant type
- [#2338][2338] Fix: follow symlink for libs on ssh connection
- [#2341][2341] Launch GDB correctly in iTerm on Mac
- [#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
[2242]: https://github.com/Gallopsled/pwntools/pull/2242
[2277]: https://github.com/Gallopsled/pwntools/pull/2277
[2281]: https://github.com/Gallopsled/pwntools/pull/2281
[2293]: https://github.com/Gallopsled/pwntools/pull/2293
[1763]: https://github.com/Gallopsled/pwntools/pull/1763
[2307]: https://github.com/Gallopsled/pwntools/pull/2307
[2309]: https://github.com/Gallopsled/pwntools/pull/2309
[2308]: https://github.com/Gallopsled/pwntools/pull/2308
[2279]: https://github.com/Gallopsled/pwntools/pull/2279
[2310]: https://github.com/Gallopsled/pwntools/pull/2310
[2335]: https://github.com/Gallopsled/pwntools/pull/2335
[2334]: https://github.com/Gallopsled/pwntools/pull/2334
[2328]: https://github.com/Gallopsled/pwntools/pull/2328
[2189]: https://github.com/Gallopsled/pwntools/pull/2189
[2339]: https://github.com/Gallopsled/pwntools/pull/2339
[2323]: https://github.com/Gallopsled/pwntools/pull/2323
[2325]: https://github.com/Gallopsled/pwntools/pull/2325
[2336]: https://github.com/Gallopsled/pwntools/pull/2336
[2161]: https://github.com/Gallopsled/pwntools/pull/2161
[2345]: https://github.com/Gallopsled/pwntools/pull/2345
[2338]: https://github.com/Gallopsled/pwntools/pull/2338
[2341]: https://github.com/Gallopsled/pwntools/pull/2341
[2268]: https://github.com/Gallopsled/pwntools/pull/2268
[2347]: https://github.com/Gallopsled/pwntools/pull/2347
[2233]: https://github.com/Gallopsled/pwntools/pull/2233
## 4.12.0 (`stable`)
- [#2202][2202] Fix `remote` and `listen` in sagemath
- [#2117][2117] Add -p (--prefix) and -s (--separator) arguments to `hex` command
- [#2221][2221] Add shellcraft.sleep template wrapping SYS_nanosleep
- [#2219][2219] Fix passing arguments on the stack in shellcraft syscall template
- [#2212][2212] Add `--libc libc.so` argument to `pwn template` command
- [#2257][2257] Allow creation of custom templates for `pwn template` command
- [#2225][2225] Allow empty argv in ssh.process()
- [#2349][2349] Fix term.readline omitting a trailing \n
- [#2352][2352] add `RETURN_CONST` as an allowed `_const_code` in safeeval
[2202]: https://github.com/Gallopsled/pwntools/pull/2202
[2117]: https://github.com/Gallopsled/pwntools/pull/2117
[2221]: https://github.com/Gallopsled/pwntools/pull/2221
[2219]: https://github.com/Gallopsled/pwntools/pull/2219
[2212]: https://github.com/Gallopsled/pwntools/pull/2212
[2257]: https://github.com/Gallopsled/pwntools/pull/2257
[2225]: https://github.com/Gallopsled/pwntools/pull/2225
[2349]: https://github.com/Gallopsled/pwntools/pull/2349
[2352]: https://github.com/Gallopsled/pwntools/pull/2352
## 4.11.1
- [#2271][2271] FIX: Generated shebang with path to python invalid if path contains spaces
- [#2272][2272] Fix `tube.clean_and_log` not logging buffered data
- [#2281][2281] FIX: Getting right amount of data for search fix
- [#2287][2287] Fix `_countdown_handler` not invoking `timeout_change`
- [#2294][2294] Fix atexit SEGV in aarch64 loader
[2271]: https://github.com/Gallopsled/pwntools/pull/2271
[2272]: https://github.com/Gallopsled/pwntools/pull/2272
[2281]: https://github.com/Gallopsled/pwntools/pull/2281
[2287]: https://github.com/Gallopsled/pwntools/pull/2287
[2294]: https://github.com/Gallopsled/pwntools/pull/2294
## 4.11.0
- [#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`
- [#2129][2129] Handle `context.newline` correctly when typing in `tube.interactive()`
- [#2214][2214] Fix bug at ssh.py:`download` and `download_file` with relative paths
- [#2241][2241] Fix ssh.process not setting ssh_process.cwd attribute
- [#2261][2261] Fix corefile module after pyelftools update
[2185]: https://github.com/Gallopsled/pwntools/pull/2185
[2103]: https://github.com/Gallopsled/pwntools/pull/2103
[2177]: https://github.com/Gallopsled/pwntools/pull/2177
[2186]: https://github.com/Gallopsled/pwntools/pull/2186
[2129]: https://github.com/Gallopsled/pwntools/pull/2129
[2214]: https://github.com/Gallopsled/pwntools/pull/2214
[2241]: https://github.com/Gallopsled/pwntools/pull/2241
[2261]: https://github.com/Gallopsled/pwntools/pull/2261
## 4.10.0
In memoriam — [Zach Riggle][zach] — long time contributor and maintainer of Pwntools.
- [#2062][2062] make pwn cyclic -l work with entry larger than 4 bytes
- [#2092][2092] shellcraft: dup() is now called dupio() consistently across all supported arches
- [#2093][2093] setresuid() in shellcraft uses current euid by default
- [#2125][2125] Allow tube.recvregex to return capture groups
- [#2144][2144] Removes `p2align 2` `asm()` headers from `x86-32`, `x86-64` and `mips` architectures to avoid inconsistent instruction length when patching binaries
[2062]: https://github.com/Gallopsled/pwntools/pull/2062
[2092]: https://github.com/Gallopsled/pwntools/pull/2092
[2093]: https://github.com/Gallopsled/pwntools/pull/2093
[2125]: https://github.com/Gallopsled/pwntools/pull/2125
[2144]: https://github.com/Gallopsled/pwntools/pull/2144
[zach]: https://github.com/zachriggle
## 4.9.0
- [#1975][1975] Add libcdb commandline tool
- [#1979][1979] Add `js_escape()` and `js_unescape()` to `util.fiddling`
- [#2011][2011] Fix tube's debug output of same byte compression
- [#2023][2023] Support KDE Konsole in run_in_new_terminal function
- [#2027][2027] Fix ELF.libc_start_main_return with glibc 2.34
- [#2033][2033] Quote file and core path in generated GDB script
- [#2035][2035] Change Buffer's parent class to object
- [#2037][2037] Allow SSH tunnel to be treated like a TCP socket (with 'raw=True')
- [#2123][2123] Fix ROP without a writeable cache directory
- [#2124][2124] Fix `tube.recvpred()` timeout argument
[1975]: https://github.com/Gallopsled/pwntools/pull/1975
[1979]: https://github.com/Gallopsled/pwntools/pull/1979
[2011]: https://github.com/Gallopsled/pwntools/pull/2011
[2023]: https://github.com/Gallopsled/pwntools/pull/2023
[2027]: https://github.com/Gallopsled/pwntools/pull/2027
[2033]: https://github.com/Gallopsled/pwntools/pull/2033
[2035]: https://github.com/Gallopsled/pwntools/pull/2035
[2037]: https://github.com/Gallopsled/pwntools/pull/2037
[2123]: https://github.com/Gallopsled/pwntools/pull/2123
[2124]: https://github.com/Gallopsled/pwntools/pull/2124
## 4.8.0
- [#1922][1922] Fix logic in `wait_for_debugger`
- [#1828][1828] libcdb: Load debug info and unstrip libc binary
@@ -78,7 +238,24 @@ The table below shows which release corresponds to each branch, and what date th
[1939]: https://github.com/Gallopsled/pwntools/pull/1939
[1981]: https://github.com/Gallopsled/pwntools/pull/1981
## 4.7.0 (`stable`)
## 4.7.1
- [#1784][1784] Use temporary cache directory when persistent cache cannot be used
- [#1973][1973] ELF symbols can be looked up by bytes values
- several bugfixes ([#2012][2012], [#2031][2031], [#1912][1912], [#1961][1961],
[#2007][2007], [#2040][2040], [#2051][2051])
[1784]: https://github.com/Gallopsled/pwntools/pull/1784
[1912]: https://github.com/Gallopsled/pwntools/pull/1912
[1961]: https://github.com/Gallopsled/pwntools/pull/1961
[1973]: https://github.com/Gallopsled/pwntools/pull/1973
[2007]: https://github.com/Gallopsled/pwntools/pull/2007
[2012]: https://github.com/Gallopsled/pwntools/pull/2012
[2031]: https://github.com/Gallopsled/pwntools/pull/2031
[2040]: https://github.com/Gallopsled/pwntools/pull/2040
[2051]: https://github.com/Gallopsled/pwntools/pull/2051
## 4.7.0
- [#1733][1733] Update libc headers -> more syscalls available!
- [#1876][1876] add `self.message` and change `sys.exc_type` to `sys.exec_info()` in PwnlibException
+3 -1
View File
@@ -1,7 +1,9 @@
graft build
graft examples
graft extra
graft travis
include *.md *.txt *.sh *.yml MANIFEST.in
recursive-include docs *.rst *.png Makefile *.py *.txt
recursive-include pwnlib *.py *.asm *.rst *.md *.txt *.sh __doc__ *.mako
recursive-include pwn *.py *.asm *.rst *.md *.txt *.sh
recursive-exclude *.pyc
global-exclude *.pyc
+2 -2
View File
@@ -3,10 +3,10 @@
[![PyPI](https://img.shields.io/pypi/v/pwntools?style=flat)](https://pypi.python.org/pypi/pwntools/)
[![Docs](https://readthedocs.org/projects/pwntools/badge/?version=stable)](https://docs.pwntools.com/)
[![Travis](https://img.shields.io/travis/Gallopsled/pwntools/dev?logo=Travis)](https://travis-ci.org/Gallopsled/pwntools)
[![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/Gallopsled/pwntools/Continuous%20Integration/dev?logo=GitHub)](https://github.com/Gallopsled/pwntools/actions?query=workflow%3A%22Continuous+Integration%22+branch%3Adev)
[![GitHub Workflow Status (dev)](https://img.shields.io/github/actions/workflow/status/Gallopsled/pwntools/ci.yml?branch=dev&logo=GitHub)](https://github.com/Gallopsled/pwntools/actions/workflows/ci.yml?query=branch%3Adev)
[![Coveralls](https://img.shields.io/coveralls/github/Gallopsled/pwntools/dev?logo=coveralls)](https://coveralls.io/github/Gallopsled/pwntools?branch=dev)
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](http://choosealicense.com/licenses/mit/)
[![Packaging status](https://img.shields.io/repology/repositories/python:pwntools)](https://repology.org/project/python:pwntools/versions)
[![Discord](https://img.shields.io/discord/809590285687980052?label=Discord&style=plastic)](https://discord.gg/96VA2zvjCB)
[![Twitter](https://img.shields.io/twitter/follow/Pwntools)](https://twitter.com/pwntools)
+32
View File
@@ -0,0 +1,32 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------------- | ------------------ |
| latest dev | :white_check_mark: |
| latest beta | :white_check_mark: |
| latest stable | :white_check_mark: |
| anything else | :x: |
## Reporting a Vulnerability
The aim of pwntools is exploiting software vulnerabilities, which is an unusual position, but it nevertheless can have its own security issues.
Especially that an attacker (=re-victim) is usually not prepared to be attacked back (by the re-attacker).
The first question to ask yourself is: is this an actual vulnerability?
- can it be triggered by a re-attacker (malicious honeypot pretending to be a vulnerable service)?
- does it impact the attacker (=re-victim)?
- is it serious?
* *availability: medium* means *at least* exhausting RAM or disk space of the attacker (=re-victim)
* *confidentiality: medium* means *at least* reading the filesystem of the attacker (=re-victim)
* *integrity: medium* means *at least* performing uncontrolled actions or data corruption on behalf of the attacker (=re-victim)
* if crucial for some sophisticated exploit chain, it is always serious
* `safe_eval` bypasses **are** serious.
* an example of what was **kind of** serious: [#1732](https://github.com/Gallopsled/pwntools/pull/1732)
- can it be fixed without compromising on Pwntools' usability?
If at least one of the answers is no, then this is NOT a vulnerability, so just file a bug report or feature request, without the weird confidential disclosure dance.
Just e-mail the maintainers. Arusekk is the one that is currently the most excited to fix vulnerabilities.
Or create a CTF task! Prove a point the good old hacker way!
+2 -3
View File
@@ -6,7 +6,6 @@ SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = build
DASHBUILD = ./dashbuild.py
TAR = tar
# Internal variables.
@@ -145,8 +144,8 @@ gettext:
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
dash:
$(DASHBUILD) source $(BUILDDIR)/dash
$(TAR) --exclude='.DS_Store' -cvzf $(BUILDDIR)/dash/pwntools.tgz -C $(BUILDDIR)/dash pwntools.docset
$(SPHINXBUILD) -b html -d $(BUILDDIR)/doctrees -t dash source $(BUILDDIR)/html
doc2dash $(BUILDDIR)/html -d $(BUILDDIR)/dash -n pwntools -f -I index.html
@echo "Build finished. The Dash docset is in $(BUILDDIR)/dash."
changes:
-99
View File
@@ -1,99 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Helper to build Dash docset from sphinx source files.
#
# Dash docsets can be read by various applications:
#
# Dash OS X, iOS https://kapeli.com
# Zeal Linux, Windows https://zealdocs.org
# Velocity Windows http://velocity.silverlakesoftware.com
# LovelyDocs Android http://lovelydocs.io
# dasht POSIX https://github.com/sunaku/dasht
# Helm Dash emacs https://github.com/areina/helm-dash
#
import argparse
import doc2dash.__main__
import os, os.path
import sphinx
import sqlite3
import sys
sys.path.append(os.path.abspath(os.path.join('..', 'pwnlib')))
import version
def main(args):
"""Generate a Dash docset from Sphinx source files."""
srcdir = args.srcdir
dstdir = args.dstdir
name = args.name
if not os.path.exists(dstdir):
os.makedirs(dstdir)
# Generate HTML without indices.
sphinx.build_main([ "sphinx-build", "-b", "html", "-d", os.path.join(dstdir, "doctrees"), \
"-t", "dash", srcdir, os.path.join(dstdir, "html") ])
# Convert to docset.
try:
doc2dash.__main__.main.main( \
[ os.path.join(dstdir, "html"), "-d", dstdir, "-n", name, \
"-f", "-I", "index.html"], "doc2dash", False)
except SystemExit as e:
pass
# Insert a link to the online version.
online = args.online
if online is not None and online != "":
url = online.replace("@VERSION", args.version)
with open(os.path.join(dstdir, name+".docset", "Contents", "Info.plist"), "r+") as f_info:
pl = f_info.read()
pl = pl.replace("</dict>", \
"\t<key>DashDocSetFallbackURL</key>\n\t<string>%s</string>\n</dict>" % url)
f_info.seek(0)
f_info.write(pl)
f_info.truncate()
# Modify the CSS to hide the menu included in the HTML.
with open(os.path.join(dstdir, name+".docset", "Contents", "Resources", "Documents", "_static", "css", "theme.css"), "r+") as f_css:
css = f_css.read()
css = css.replace( \
'@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}',\
'@media screen {.wy-body-for-nav{background:#fcfcfc}' )
css = css.replace( \
'@media screen and (max-width: 480px)', \
'@media screen ')
f_css.seek(0)
f_css.write(css)
f_css.truncate()
# Modify the index
db_conn = sqlite3.connect(os.path.join(dstdir, name+".docset", "Contents", "Resources", "docSet.dsidx"))
try:
db_conn.execute('INSERT INTO "searchIndex" ("name","type","path") VALUES ' \
'("1 Contents", "Guide", "index.html"), ' \
'("2 About pwntools", "Guide", "about.html"), ' \
'("3 Installation", "Guide", "install.html"), ' \
'("4 Getting Started", "Guide", "intro.html"), ' \
'("5 Globals (pwn)", "Guide", "globals.html"), ' \
'("6 Command Line Tools", "Guide", "commandline.html")')
db_conn.execute('DELETE FROM "searchIndex" WHERE "type" = "Module" AND ("name" = "pwn" OR "name" = "pwnlib")')
db_conn.commit()
finally:
db_conn.close()
return 0
parser = argparse.ArgumentParser()
parser.add_argument("--name", help="docset name", default="pwntools")
parser.add_argument("--online", help="URL for online docs", default="https://pwntools.readthedocs.org/en/@VERSION/")
parser.add_argument("--version", help="pwntools version", default=version.__version__)
parser.add_argument("srcdir", help="Source directory containing .rst files")
parser.add_argument("dstdir", help="Destination and working directory")
main(parser.parse_args())
+7 -3
View File
@@ -1,7 +1,9 @@
capstone
coveralls
coverage[toml]
python-dateutil
doc2dash
docutils<0.18
intervaltree
isort
mako>=1.0.0
paramiko>=1.15.2
@@ -10,9 +12,11 @@ pyelftools>=0.2.3
pygments>=2.0
pypandoc
pyserial>=2.7
pysocks
psutil
requests>=2.5.1
ropgadget>=5.3
sphinx<3.4
sphinx==1.8.6; python_version<'3'
sphinx>=4.5.0; python_version>='3'
sphinx_rtd_theme
sphinxcontrib-napoleon
sphinxcontrib-autoprogram<=0.1.5
+5 -4
View File
@@ -42,8 +42,8 @@ extensions = [
'sphinx.ext.coverage',
'sphinx.ext.todo',
'sphinx.ext.intersphinx',
'sphinx.ext.napoleon',
'sphinxcontrib.autoprogram',
'sphinxcontrib.napoleon'
]
# Disable "info" logging directly to stdout by Sphinx
@@ -377,9 +377,10 @@ if build_dash:
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
if not on_rtd: # only import and set the theme if we're building docs locally
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
import alabaster
html_theme = 'alabaster'
html_theme_path = [alabaster.get_path()]
html_theme_options = { 'nosidebar' : True }
# otherwise, readthedocs.org uses their theme by default, so no need to specify it
+1
View File
@@ -3,6 +3,7 @@
from pwn import *
from glob import glob
from pwnlib.elf.maps import CAT_PROC_MAPS_EXIT
import shutil
:mod:`pwnlib.elf.elf` --- ELF Files
===========================================================
+1 -1
View File
@@ -13,7 +13,7 @@ readthedocs_. It comes in three primary flavors:
- Dev_
.. _readthedocs: https://readthedocs.org
.. _docs.pwntools.com: https://docs.pwntools.com
.. _docs.pwntools.com: https://docs.pwntools.com/en/latest
.. _Stable: https://docs.pwntools.com/en/stable
.. _Beta: https://docs.pwntools.com/en/beta
.. _Dev: https://docs.pwntools.com/en/dev
+5 -5
View File
@@ -5,7 +5,7 @@ Assembly of foreign architectures (e.g. assembling Sparc shellcode on
Mac OS X) requires cross-compiled versions of ``binutils`` to be
installed. We've made this process as smooth as we can.
In these examples, replace ``$ARCH`` with your target architecture (e.g., arm, mips64, vax, etc.).
In these examples, replace ``$ARCH`` with your target architecture (e.g., arm, aarch64, mips64, vax, etc.).
Building `binutils` from source takes about 60 seconds on a modern 8-core machine.
@@ -33,7 +33,7 @@ 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.
After installing `brew <http://brew.sh>`__, grab the appropriate
After installing `brew <https://brew.sh>`__, grab the appropriate
recipe from our `binutils
repo <https://github.com/Gallopsled/pwntools-binutils/>`__.
@@ -51,10 +51,10 @@ OSes, ``binutils`` is simple to build by hand.
#!/usr/bin/env bash
V=2.25 # Binutils Version
V=2.38 # Binutils Version
ARCH=arm # Target architecture
cd /tmp
cd ${TMPDIR:-/tmp}
wget -nc https://ftp.gnu.org/gnu/binutils/binutils-$V.tar.gz
wget -nc https://ftp.gnu.org/gnu/binutils/binutils-$V.tar.gz.sig
@@ -70,7 +70,7 @@ OSes, ``binutils`` is simple to build by hand.
export AS=as
../binutils-$V/configure \
--prefix=/usr/local \
--prefix=${PREFIX:-/usr/local} \
--target=$ARCH-unknown-linux-gnu \
--disable-static \
--disable-multilib \
+1
View File
@@ -15,6 +15,7 @@
from pwnlib.tubes.process import process
from pwnlib import shellcraft
from pwnlib.util.misc import which
import pwnlib.data
context.clear()
+6
View File
@@ -17,3 +17,9 @@
.. automodule:: pwnlib.shellcraft.amd64.linux
:members:
:mod:`pwnlib.shellcraft.amd64.windows`
---------------------------------------
.. automodule:: pwnlib.shellcraft.amd64.windows
:members:
+18 -12
View File
@@ -11,18 +11,24 @@ Solution:
"""
from pwn import *
from multiprocessing import Process
os.system('''((
echo prefix sometext ;
echo prefix someothertext ;
echo here comes the flag ;
echo LostInTheInterTubes
) | nc -l 1337) &
''')
def submit_data():
with context.quiet:
with listen(1337) as io:
io.wait_for_connection()
io.sendline(b'prefix sometext')
io.sendline(b'prefix someothertext')
io.sendline(b'here comes the flag')
io.sendline(b'LostInTheInterTubes')
r = remote('localhost', 1337)
atexit.register(r.clean_and_log)
if __name__ == '__main__':
p = Process(target=submit_data)
p.start()
while True:
line = r.recvline()
print(re.findall(r'^prefix (\S+)$', line)[0])
r = remote('localhost', 1337)
atexit.register(r.clean_and_log)
while True:
line = r.recvline()
print(re.findall(br'^prefix (\S+)$', line)[0])
+3 -3
View File
@@ -1,4 +1,4 @@
subdirs=$(shell find . -type d -depth 1 | xargs basename)
subdirs=$(shell find . -mindepth 1 -maxdepth 1 -type d | xargs -n1 basename)
tags=base stable beta dev
ROOT=$(shell git rev-parse --show-toplevel)
CMD ?= zsh
@@ -7,7 +7,7 @@ ifneq ($(HISTFILE),)
MOUNT_HISTFILE=--mount type=bind,source="$(HISTFILE)",target=/home/pwntools/.history
endif
all: $(subdirs) $(tags)
all: $(tags) $(subdirs)
tags: $(tags)
@@ -36,4 +36,4 @@ clean:
FORCE:
.PHONY: all $(subdirs) $(makefiles) $(tags)
.PHONY: all $(subdirs) $(makefiles) $(tags)
+16 -12
View File
@@ -3,28 +3,30 @@
# Based on Ubuntu
############################################################
FROM ubuntu:bionic
FROM ubuntu:jammy
MAINTAINER Maintainer Gallopsled et al.
env DEBIAN_FRONTEND=noninteractive
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 \
&& apt-get install locales \
&& locale-gen en_US.UTF-8 \
&& apt-get install -y \
sudo \
locales \
build-essential \
elfutils \
git \
libssl-dev \
libffi-dev \
python \
python-pip \
python-dev \
python2.7 \
python2.7-dev \
python2-pip-whl \
python3 \
python3-pip \
python3-dev \
python-is-python3 \
qemu-user-static \
binutils-arm-linux-gnueabihf \
binutils-aarch64-linux-gnu \
@@ -33,15 +35,17 @@ RUN apt-get update \
binutils-powerpc-linux-gnu \
binutils-powerpc64-linux-gnu \
binutils-sparc64-linux-gnu \
binutils-riscv64-linux-gnu \
tmux \
&& pip install --upgrade pip \
&& python -m pip install --upgrade pwntools \
&& pip3 install --upgrade pip \
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 \
&& PWNLIB_NOTERM=1 pwn update \
&& apt-get install -y sudo \
&& useradd -m pwntools \
&& passwd --delete --unlock pwntools \
&& echo "pwntools ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers.d/pwntools
USER pwntools
WORKDIR /home/pwntools
+2 -2
View File
@@ -1,7 +1,7 @@
FROM pwntools/pwntools:stable
USER root
RUN pip install --upgrade git+https://github.com/Gallopsled/pwntools@beta
RUN pip3 install --upgrade git+https://github.com/Gallopsled/pwntools@beta
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 PWNLIB_NOTERM=1 pwn update
USER pwntools
+2 -2
View File
@@ -1,7 +1,7 @@
FROM pwntools/pwntools:stable
USER root
RUN pip install --upgrade git+https://github.com/Gallopsled/pwntools@dev
RUN pip3 install --upgrade git+https://github.com/Gallopsled/pwntools@dev
RUN python2.7 -m pip install --upgrade git+https://github.com/Gallopsled/pwntools@dev \
&& python3 -m pip install --force-reinstall --upgrade git+https://github.com/Gallopsled/pwntools@dev
RUN PWNLIB_NOTERM=1 pwn update
USER pwntools
+15 -15
View File
@@ -5,8 +5,8 @@ ENV HISTFILE=/home/pwntools/.history
# Uninstall existing versions of pwntools
USER root
RUN python -m pip uninstall -q -y pwntools \
&& python3 -m pip uninstall -q -y pwntools
RUN python2.7 -m pip uninstall -q -y pwntools \
&& python3 -m pip uninstall -q -y pwntools
# Switch back to the pwntools user from here forward
USER pwntools
@@ -18,20 +18,23 @@ 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 \
&& python -m pip install --upgrade --editable pwntools \
&& python2.7 -m pip install --upgrade --editable pwntools \
&& python3 -m pip install --upgrade --editable pwntools \
&& PWNLIB_NOTERM=1 pwn version
# Requirements for running the tests
RUN python -m pip install --upgrade --requirement pwntools/docs/requirements.txt \
&& python3 -m pip install --upgrade --requirement pwntools/docs/requirements.txt
RUN python2.7 -m pip install --upgrade --requirement pwntools/docs/requirements.txt \
&& python3 -m pip install --upgrade --requirement pwntools/docs/requirements.txt
# Python niceties for debugging
RUN python -m pip install -U ipython ipdb \
&& python3 -m pip install -U ipython ipdb
RUN python2.7 -m pip install -U ipython ipdb \
&& python3 -m pip install -U ipython ipdb
# Dependencies from .travis.yml addons -> apt -> packages
RUN sudo apt-get install -y \
ARG DEBIAN_FRONTEND=noninteractive
ENV TZ="UTC"
RUN sudo apt-get update && sudo -E apt-get install -y \
tzdata \
ash \
bash \
bash-static \
@@ -41,7 +44,7 @@ RUN sudo apt-get install -y \
dash \
gcc \
gcc-multilib \
gdb \
gdb \
ksh \
lib32stdc++6 \
libc6-dev-i386 \
@@ -51,12 +54,9 @@ RUN sudo apt-get install -y \
socat \
sshpass \
vim \
zsh
zsh \
# Misc useful things when developing
RUN sudo apt-get install -y \
curl \
ipython \
ipython3 \
lsb-release \
ssh \
@@ -64,7 +64,7 @@ RUN sudo apt-get install -y \
wget
# Use zsh by default
RUN sudo chsh -s /bin/zsh pwntools
RUN sudo -E chsh -s /bin/zsh pwntools
# Get and install prezto
RUN git clone --recursive https://github.com/sorin-ionescu/prezto.git .zprezto
@@ -83,4 +83,4 @@ ADD 10-import.py /home/pwntools/.ipython/profile_default/startup
ADD ipython_config.py /home/pwntools/.ipython/profile_default
# Do not require password for sudo
RUN echo "pwntools ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/travis
RUN echo "pwntools ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/travis
+2 -2
View File
@@ -1,7 +1,7 @@
FROM pwntools/pwntools:base
USER root
RUN pip install --upgrade git+https://github.com/Gallopsled/pwntools@stable
RUN pip3 install --upgrade git+https://github.com/Gallopsled/pwntools@stable
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 PWNLIB_NOTERM=1 pwn update
USER pwntools
+6 -2
View File
@@ -16,7 +16,6 @@ import tempfile
import threading
import time
import colored_traceback
from pprint import pprint
import pwnlib
@@ -85,7 +84,12 @@ info = log.info
debug = log.debug
success = log.success
colored_traceback.add_hook()
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('_')]
+23 -11
View File
@@ -66,6 +66,7 @@ from pwnlib import tubes
from pwnlib.context import LocalContext
from pwnlib.context import context
from pwnlib.device import Device
from pwnlib.exception import PwnlibException
from pwnlib.log import getLogger
from pwnlib.protocols.adb import AdbClient
from pwnlib.util.packing import _decode
@@ -122,7 +123,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_armv7', model='sdk ...phone armv7', device='generic')
AdbDevice(serial='emulator-5554', type='device', port='emulator', product='sdk_...phone_...', model='...', device='generic...')
>>> device.port
'emulator'
"""
@@ -252,13 +253,13 @@ class AdbDevice(Device):
>>> device = adb.wait_for_device()
>>> device.arch
'arm'
'amd64'
>>> device.bits
32
64
>>> device.os
'android'
>>> device.product # doctest: +ELLIPSIS
'sdk_...phone_armv7'
'sdk_...phone_...'
>>> device.serial
'emulator-5554'
"""
@@ -384,6 +385,9 @@ class AdbDevice(Device):
>>> adb.getprop(property) == device.getprop(property)
True
"""
if name.startswith('_'):
raise AttributeError(name)
with context.local(device=self):
g = globals()
@@ -1361,14 +1365,18 @@ def compile(source):
>>> filename = adb.compile(temp)
>>> sent = adb.push(filename, "/data/local/tmp")
>>> adb.process(sent).recvall() # doctest: +ELLIPSIS
b'... /system/bin/linker\n...'
b'... /system/lib64/libc.so\n...'
"""
ndk_build = misc.which('ndk-build')
if not ndk_build:
# Ensure that we can find the NDK.
ndk = os.environ.get('NDK', None)
if ndk is None:
for envvar in ('NDK', 'ANDROID_NDK', 'ANDROID_NDK_ROOT',
'ANDROID_NDK_HOME', 'ANDROID_NDK_LATEST_HOME'):
ndk = os.environ.get(envvar)
if ndk is not None:
break
else:
log.error('$NDK must be set to the Android NDK directory')
ndk_build = os.path.join(ndk, 'ndk-build')
@@ -1483,8 +1491,9 @@ class Partitions(object):
@context.quietfunc
def by_name_dir(self):
try:
return next(find('/dev/block/platform','by-name'))
except StopIteration:
with context.local(log_level=logging.FATAL):
return next(find('/dev/block/platform','by-name'))
except (StopIteration, PwnlibException):
return '/dev/block'
@context.quietfunc
@@ -1513,7 +1522,7 @@ class Partitions(object):
return iter(names)
def __getattr__(self, attr):
if attr.startswith("_"):
if attr.startswith('_'):
raise AttributeError(attr)
for name in self:
@@ -1584,7 +1593,10 @@ def uninstall(package, *arguments):
@context.quietfunc
def packages():
"""Returns a list of packages installed on the system"""
packages = process(['pm', 'list', 'packages']).recvall()
# Decodes the received bytes as UTF-8 per:
# https://developer.android.com/reference/java/nio/charset/Charset#defaultCharset()
# where it is specified that UTF-8 is the default charset for Android.
packages = process(['pm', 'list', 'packages']).recvall().decode('utf-8')
return [line.split('package:', 1)[-1] for line in packages.splitlines()]
@context.quietfunc
+4 -4
View File
@@ -46,7 +46,7 @@ class BootImage(object):
self.kernel = self.data[PAGE:PAGE+self.kernel_size]
def __getattr__(self, name):
value = getattr(self.header, name, None)
if value is not None:
return value
return getattr(super(BootImage, self), name)
if name.startswith('_'):
raise AttributeError(name)
return getattr(self.header, name)
+5 -4
View File
@@ -114,10 +114,11 @@ class BootloaderImage(object):
return '\n'.join(rv)
def __getattr__(self, name):
value = getattr(self.header, name, None)
if value is not None:
return value
return getattr(super(BootImage, self), name)
if name.startswith('_'):
raise AttributeError(name)
return getattr(self.header, name)
if __name__ == '__main__':
# Easy sanity checking
+2
View File
@@ -62,6 +62,8 @@ from pwnlib.context import context
class PwnlibArgs(collections.defaultdict):
def __getattr__(self, attr):
if attr.startswith('_'):
raise AttributeError(attr)
return self[attr]
args = PwnlibArgs(str)
+67 -35
View File
@@ -136,8 +136,23 @@ Try installing binutils for this architecture:
%(instructions)s
""".strip() % locals())
def check_binutils_version(util):
if util_versions[util]:
return util_versions[util]
result = subprocess.check_output([util, '--version','/dev/null'],
stderr=subprocess.STDOUT, universal_newlines=True)
if 'clang' in result:
log.warn_once('Your binutils is clang-based and may not work!\n'
'Try installing with: https://docs.pwntools.com/en/stable/install/binutils.html\n'
'Reported version: %r', result.strip())
version = re.search(r' (\d+\.\d+)', result).group(1)
util_versions[util] = version = tuple(map(int, version.split('.')))
return version
@LocalContext
def which_binutils(util):
def which_binutils(util, check_version=False):
"""
Finds a binutils in the PATH somewhere.
Expects that the utility is prefixed with the architecture name.
@@ -170,6 +185,8 @@ def which_binutils(util):
'mips64': ['mips'],
'powerpc64': ['powerpc'],
'sparc64': ['sparc'],
'riscv32': ['riscv32', 'riscv64', 'riscv'],
'riscv64': ['riscv64', 'riscv32', 'riscv'],
}.get(arch, [])
# If one of the candidate architectures matches the native
@@ -204,17 +221,23 @@ def which_binutils(util):
for pattern in patterns:
for dir in environ['PATH'].split(':'):
res = sorted(glob(path.join(dir, pattern)))
if res:
return res[0]
for res in sorted(glob(path.join(dir, pattern))):
if check_version:
ver = check_binutils_version(res)
return res, ver
return res
# No dice!
print_binutils_instructions(util, context)
checked_assembler_version = defaultdict(lambda: False)
util_versions = defaultdict(tuple)
def _assembler():
gas = which_binutils('as')
gas, version = which_binutils('as', check_version=True)
if version < (2, 19):
log.warn_once('Your binutils version is too old and may not work!\n'
'Try updating with: https://docs.pwntools.com/en/stable/install/binutils.html\n'
'Reported version: %r', version)
E = {
'big': '-EB',
@@ -241,30 +264,19 @@ def _assembler():
'powerpc64': [gas, '-m%s' % context.endianness, '-mppc%s' % context.bits],
# ia64 only accepts -mbe or -mle
'ia64': [gas, '-m%ce' % context.endianness[0]]
'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'],
}
assembler = assemblers.get(context.arch, [gas])
if not checked_assembler_version[gas]:
checked_assembler_version[gas] = True
result = subprocess.check_output([gas, '--version','/dev/null'],
stderr=subprocess.STDOUT, universal_newlines=True)
version = re.search(r' (\d+\.\d+)', result).group(1)
if 'clang' in result:
log.warn_once('Your binutils is clang version and may not work!\n'
'Try install with: https://docs.pwntools.com/en/stable/install/binutils.html\n'
'Reported Version: %r', result.strip())
elif version < '2.19':
log.warn_once('Your binutils version is too old and may not work!\n'
'Try updating with: https://docs.pwntools.com/en/stable/install/binutils.html\n'
'Reported Version: %r', result.strip())
return assembler
def _linker():
ld = [which_binutils('ld')]
ld, _ = which_binutils('ld', check_version=True)
bfd = ['--oformat=' + _bfdname()]
E = {
@@ -276,7 +288,16 @@ def _linker():
'i386': ['-m', 'elf_i386'],
}.get(context.arch, [])
return ld + bfd + [E] + arguments
return [ld] + bfd + [E] + arguments
def _execstack(linker):
ldflags = ['-z', 'execstack']
version = util_versions[linker[0]]
if version >= (2, 39):
return ldflags + ['--no-warn-execstack', '--no-warn-rwx-segments']
return ldflags
def _objcopy():
return [which_binutils('objcopy')]
@@ -305,20 +326,23 @@ def _arch_header():
prefix = ['.section .shellcode,"awx"',
'.global _start',
'.global __start',
'.p2align 2',
'_start:',
'__start:']
headers = {
'i386' : ['.intel_syntax noprefix'],
'amd64' : ['.intel_syntax noprefix'],
'i386' : ['.intel_syntax noprefix', '.p2align 0'],
'amd64' : ['.intel_syntax noprefix', '.p2align 0'],
'arm' : ['.syntax unified',
'.arch armv7-a',
'.arm'],
'.arm',
'.p2align 2'],
'thumb' : ['.syntax unified',
'.arch armv7-a',
'.thumb'],
'.thumb',
'.p2align 2'
],
'mips' : ['.set mips2',
'.set noreorder',
'.p2align 2'
],
}
@@ -344,7 +368,8 @@ def _bfdname():
'msp430' : 'elf32-msp430',
'powerpc' : 'elf32-powerpc',
'powerpc64' : 'elf64-powerpc',
'riscv' : 'elf%d-%sriscv' % (context.bits, E),
'riscv32' : 'elf%d-%sriscv' % (context.bits, E),
'riscv64' : 'elf%d-%sriscv' % (context.bits, E),
'vax' : 'elf32-vax',
's390' : 'elf%d-s390' % context.bits,
'sparc' : 'elf32-sparc',
@@ -367,6 +392,8 @@ def _bfdarch():
'powerpc64': 'powerpc',
'sparc64': 'sparc',
'thumb': 'arm',
'riscv32': 'riscv',
'riscv64': 'riscv',
}
if arch in convert:
@@ -595,7 +622,7 @@ def make_elf(data,
_run(assembler + ['-o', step2, step1])
linker_options = ['-z', 'execstack']
linker_options = _execstack(linker)
if vma is not None:
linker_options += ['--section-start=.shellcode=%#x' % vma,
'--entry=%#x' % vma]
@@ -689,7 +716,7 @@ def asm(shellcode, vma = 0, extract = True, shared = False):
shutil.copy(step2, step3)
if vma or not extract:
ldflags = ['-z', 'execstack', '-o', step3, step2]
ldflags = _execstack(linker) + ['-o', step3, step2]
if vma:
ldflags += ['--section-start=.shellcode=%#x' % vma,
'--entry=%#x' % vma]
@@ -771,7 +798,7 @@ def disasm(data, vma = 0, byte = True, offset = True, instructions = True):
>>> print(disasm(unhex('4ff00500'), arch = 'thumb', bits=32))
0: f04f 0005 mov.w r0, #5
>>> print(disasm(unhex('656664676665400F18A4000000000051'), byte=0, arch='amd64'))
0: gs data16 fs data16 rex nop/reserved BYTE PTR gs:[eax+eax*1+0x0]
0: gs data16 fs rex nop WORD PTR gs:[eax+eax*1+0x0]
f: push rcx
>>> print(disasm(unhex('01000000'), arch='sparc64'))
0: 01 00 00 00 nop
@@ -779,6 +806,11 @@ def disasm(data, vma = 0, byte = True, offset = True, instructions = True):
0: 60 00 00 00 nop
>>> print(disasm(unhex('00000000'), arch='mips64'))
0: 00000000 nop
>>> print(disasm(unhex('48b84141414141414100c3'), arch='amd64'))
0: 48 b8 41 41 41 41 41 41 41 00 movabs rax, 0x41414141414141
a: c3 ret
>>> print(disasm(unhex('00000000'), vma=0x80000000, arch='mips'))
80000000: 00000000 nop
"""
result = ''
@@ -790,7 +822,7 @@ def disasm(data, vma = 0, byte = True, offset = True, instructions = True):
bfdarch = _bfdarch()
bfdname = _bfdname()
objdump = _objdump() + ['-d', '--adjust-vma', str(vma), '-b', bfdname]
objdump = _objdump() + ['-w', '-d', '--adjust-vma', str(vma), '-b', bfdname]
objcopy = _objcopy() + [
'-I', 'binary',
'-O', bfdname,
@@ -815,7 +847,7 @@ def disasm(data, vma = 0, byte = True, offset = True, instructions = True):
_run(objcopy + [step1, step2])
output0 = _run(objdump + [step2])
output1 = output0.split('<.text>:\n')
output1 = re.split(r'<\.text(?:\+0x0)?>:\n', output0, flags=re.MULTILINE)
if len(output1) != 2:
log.error('Could not find .text in objdump output:\n%s' % output0)
+5 -2
View File
@@ -72,13 +72,16 @@ def main(args):
try:
pat = int(pat, 0)
pat = pack(pat, 'all')
except ValueError:
pass
pat = flat(pat, bytes=args.length)
if len(pat) != subsize:
log.critical('Subpattern must be %d bytes' % subsize)
if len(pat) < subsize:
log.critical('Subpattern must be at least %d bytes' % subsize)
sys.exit(1)
else:
pat = pat[:subsize]
if not all(c in alphabet for c in pat):
log.critical('Pattern contains characters not present in the alphabet')
+2 -2
View File
@@ -82,9 +82,9 @@ def main(args):
instrs = disasm(dat, vma=safeeval.const(args.address), byte=False, offset=False)
# instrs = highlight(instrs, PwntoolsLexer(), TerminalFormatter())
highlight_bytes = lambda t: ''.join(map(lambda x: x.replace('00', text.red('00')).replace('0a', text.red('0a')), group(2, t)))
for o,b,i in zip(*map(str.splitlines, (offsets, bytes, instrs))):
b = b.replace('00', text.red('00'))
b = b.replace('0a', text.red('0a'))
b = ' '.join(highlight_bytes(bb) for bb in b.split(' '))
i = highlight(i.strip(), PwntoolsLexer(), TerminalFormatter()).strip()
i = i.replace(',',', ')
+27 -2
View File
@@ -7,6 +7,7 @@ import sys
from pwnlib.commandline import common
from pwnlib.util.fiddling import enhex
from pwnlib.util.lists import group
parser = common.parser_commands.add_parser(
'hex',
@@ -16,14 +17,38 @@ parser = common.parser_commands.add_parser(
parser.add_argument('data', nargs='*',
help='Data to convert into hex')
parser.add_argument(
'-p', '--prefix',
metavar = 'prefix',
type = str,
default = '',
help = 'Insert a prefix before each byte',
)
parser.add_argument(
'-s', '--separator',
metavar = 'separator',
type = str,
default = '',
help = 'Add a separator between each byte',
)
def format_hex(hex_string, prefix, separator):
return separator.join([prefix + x for x in group(2, hex_string)])
def main(args):
if not args.data:
print(enhex(getattr(sys.stdin, 'buffer', sys.stdin).read()))
encoded = enhex(getattr(sys.stdin, 'buffer', sys.stdin).read())
else:
data = ' '.join(args.data)
if not hasattr(data, 'decode'):
data = data.encode('utf-8', 'surrogateescape')
print(enhex(data))
encoded = enhex(data)
if args.prefix or args.separator:
encoded = format_hex(encoded, args.prefix, args.separator)
print(encoded)
if __name__ == '__main__':
common.main(__file__)
+247
View File
@@ -0,0 +1,247 @@
#!/usr/bin/env python
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import re
import shutil
import sys
import pwnlib.args
pwnlib.args.free_form = False
from pwn import *
from pwnlib.commandline import common
parser = common.parser_commands.add_parser(
'libcdb',
help = 'Print various information about a libc binary',
description = 'Print various information about a libc binary'
)
libc_commands = parser.add_subparsers(
dest = 'libc_command'
)
lookup_parser = libc_commands.add_parser(
'lookup',
help = 'Lookup a libc version by function offsets',
description = 'Lookup a libc version by function offsets'
)
lookup_parser.add_argument(
'symbol_offset_pairs',
metavar = 'symbol_offset_pairs',
nargs = '+',
help = 'Symbol and offset pairs to lookup matching libc version. Can be any number of pairs to narrow the search. Example: "read 3e0 write 520"'
)
lookup_parser.add_argument(
'--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',
dest = 'unstrip',
help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'
)
hash_parser = libc_commands.add_parser(
'hash',
help = 'Display information of a libc version given an unique hash',
description = 'Display information of a libc version given an unique hash'
)
hash_parser.add_argument(
'hash_value',
metavar = 'hash_value',
nargs = '+',
help = 'Hex encoded hash value'
)
hash_parser.add_argument(
'-t', '--hash_type',
nargs = '?',
type = str,
choices = ['id', 'buildid', 'md5', 'sha1', 'sha256'],
default = 'buildid',
help = 'The type of the provided hash value. Supported hashtypes: id, buildid, md5, sha1, sha256'
)
hash_parser.add_argument(
'--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',
dest = 'unstrip',
help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'
)
file_parser = libc_commands.add_parser(
'file',
help = 'Dump information about a libc binary',
description = 'Dump information about a libc binary'
)
file_parser.add_argument(
'files',
metavar = 'files',
nargs = '+',
help = 'Libc binary to dump'
)
file_parser.add_argument(
'-s', '--symbols',
metavar = 'symbols',
nargs = '*',
help = 'List of symbol offsets to dump in addition to the common ones'
)
file_parser.add_argument(
'-o', '--offset',
metavar = 'offset',
type = str,
help = 'Display all offsets relative to this symbol'
)
file_parser.add_argument(
'--unstrip',
action = 'store_true',
default = False,
help = 'Attempt to unstrip the libc binary inplace with debug symbols from a debuginfod server'
)
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):
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'])
log.indented('\t%-20s %s', text.green('SHA1:'), libc['sha1'])
log.indented('\t%-20s %s', text.green('SHA256:'), libc['sha256'])
log.indented('\t%s', text.green('Symbols:'))
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 translate_offset(offs, args, exe):
if args.offset:
if args.offset not in exe.symbols:
log.info_once('offset symbol %s not found. ignoring.', args.offset)
return offs
return offs - exe.symbols[args.offset]
return offs
def collect_synthetic_symbols(exe):
available_symbols = ['str_bin_sh']
exe.symbols['str_bin_sh'] = next(exe.search(b'/bin/sh\x00'))
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
available_symbols.append('__libc_start_main_ret')
return available_symbols
def main(args):
if len(sys.argv) < 3:
parser.print_usage()
sys.exit()
if args.libc_command == 'lookup':
pairs = args.symbol_offset_pairs
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})
for libc in matched_libcs:
handle_remote_libc(args, libc)
elif args.libc_command == 'hash':
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)
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)
exe = ELF(file, checksec=False)
log.info('%s', text.red(os.path.basename(file)))
libc_version = re.search(b'libc[ -](\d+\.\d+)', exe.data)
if libc_version:
log.indented('%-20s %s', text.green('Version:'), libc_version.group(1).decode())
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))
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
+2
View File
@@ -14,6 +14,7 @@ from pwnlib.commandline import elfdiff
from pwnlib.commandline import elfpatch
from pwnlib.commandline import errno
from pwnlib.commandline import hex
from pwnlib.commandline import libcdb
from pwnlib.commandline import phd
from pwnlib.commandline import pwnstrip
from pwnlib.commandline import scramble
@@ -37,6 +38,7 @@ commands = {
'elfpatch': elfpatch.main,
'errno': errno.main,
'hex': hex.main,
'libcdb': libcdb.main,
'phd': phd.main,
'pwnstrip': pwnstrip.main,
'scramble': scramble.main,
+21 -3
View File
@@ -5,7 +5,7 @@ from __future__ import division
from pwn import *
from pwnlib.commandline import common
from mako.lookup import TemplateLookup
from mako.lookup import TemplateLookup, Template
parser = common.parser_commands.add_parser(
'template',
@@ -13,18 +13,29 @@ parser = common.parser_commands.add_parser(
description = 'Generate an exploit template'
)
# change path to hardcoded one when building the documentation
printable_data_path = "pwnlib/data" if 'sphinx' in sys.modules else pwnlib.data.path
parser.add_argument('exe', nargs='?', help='Target binary')
parser.add_argument('--host', help='Remote host / SSH server')
parser.add_argument('--port', help='Remote port / SSH port', type=int)
parser.add_argument('--user', help='SSH Username')
parser.add_argument('--pass', '--password', help='SSH Password', dest='password')
parser.add_argument('--libc', help='Path to libc binary to use')
parser.add_argument('--path', help='Remote path of file on SSH server')
parser.add_argument('--quiet', help='Less verbose template comments', action='store_true')
parser.add_argument('--color', help='Print the output in color', choices=['never', 'always', 'auto'], default='auto')
parser.add_argument('--template', help='Path to a custom template. Tries to use \'~/.config/pwntools/templates/pwnup.mako\', if it exists. '
'Check \'%s\' for the default template shipped with pwntools.' %
os.path.join(printable_data_path, "templates", "pwnup.mako"))
def main(args):
lookup = TemplateLookup(
directories = [os.path.join(pwnlib.data.path, 'templates')],
directories = [
os.path.expanduser('~/.config/pwntools/templates/'),
os.path.join(pwnlib.data.path, 'templates')
],
module_directory = None
)
@@ -47,12 +58,18 @@ def main(args):
if not args.exe:
args.exe = os.path.basename(args.path)
template = lookup.get_template('pwnup.mako')
if args.template:
template = Template(filename=args.template) # Failing on invalid file is ok
else:
template = lookup.get_template('pwnup.mako')
output = template.render(args.exe,
args.host,
args.port,
args.user,
args.password,
args.libc,
args.path,
args.quiet)
@@ -75,3 +92,4 @@ def main(args):
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
+3 -3
View File
@@ -55,8 +55,8 @@ def initialize():
from pwnlib.log import getLogger
log = getLogger(__name__)
xdg_config_home = (os.environ.get('XDG_CONFIG_HOME') or
os.path.expanduser("~/.config"))
xdg_config_home = os.environ.get('XDG_CONFIG_HOME',
os.path.expanduser("~/.config"))
c = configparser.ConfigParser()
c.read(['/etc/pwn.conf',
@@ -65,7 +65,7 @@ def initialize():
for section in c.sections():
if section not in registered_configs:
log.warn("Unknown configuration section %r" % section)
log.warn("Unknown configuration section %r", section)
continue
settings = dict(c.items(section))
registered_configs[section](settings)
+79 -23
View File
@@ -6,17 +6,20 @@ contexts work properly and as expected.
from __future__ import absolute_import
from __future__ import division
import atexit
import collections
import errno
import functools
import logging
import os
import os.path
import platform
import shutil
import six
import socket
import stat
import string
import subprocess
import sys
import tempfile
import threading
import time
@@ -345,6 +348,10 @@ class ContextType(object):
'binary': None,
'bits': 32,
'buffer_size': 4096,
'cache_dir_base': os.environ.get(
'XDG_CACHE_HOME',
os.path.join(os.path.expanduser('~'), '.cache')
),
'cyclic_alphabet': string.ascii_lowercase.encode(),
'cyclic_size': 4,
'delete_corefiles': False,
@@ -397,7 +404,8 @@ class ContextType(object):
'msp430': little_16,
'powerpc': big_32,
'powerpc64': big_64,
'riscv': little_32,
'riscv32': little_32,
'riscv64': little_64,
's390': big_32,
'sparc': big_32,
'sparc64': big_64,
@@ -762,12 +770,15 @@ class ContextType(object):
# We have to make sure that x86_64 appears before x86 for this to work correctly.
transform = [('ppc64', 'powerpc64'),
('ppc', 'powerpc'),
('x86-64', 'amd64'),
('x86_64', 'amd64'),
('x86', 'i386'),
('i686', 'i386'),
('armv7l', 'arm'),
('armeabi', 'arm'),
('arm64', 'aarch64')]
('arm64', 'aarch64'),
('rv32', 'riscv32'),
('rv64', 'riscv64')]
for k, v in transform:
if arch.startswith(k):
arch = v
@@ -972,7 +983,10 @@ class ContextType(object):
except AttributeError: pass
# Otherwise, fail
level_names = filter(lambda x: isinstance(x,str), logging._levelNames)
try:
level_names = logging._levelToName.values()
except AttributeError:
level_names = filter(lambda x: isinstance(x,str), logging._levelNames)
permitted = sorted(level_names)
raise AttributeError('log_level must be an integer or one of %r' % permitted)
@@ -1269,6 +1283,21 @@ class ContextType(object):
"""
return int(size)
@_validator
def cache_dir_base(self, new_base):
"""Base directory to use for caching content.
Changing this to a different value will clear the `cache_dir` path
stored in TLS since a new path will need to be generated to respect the
new `cache_dir_base` value.
"""
if new_base != self.cache_dir_base:
del self._tls["cache_dir"]
if os.access(new_base, os.F_OK) and not os.access(new_base, os.W_OK):
raise OSError(errno.EPERM, "Cache base dir is not writable")
return new_base
@property
def cache_dir(self):
"""Directory used for caching data.
@@ -1282,32 +1311,59 @@ class ContextType(object):
>>> cache_dir is not None
True
>>> os.chmod(cache_dir, 0o000)
>>> del context._tls['cache_dir']
>>> context.cache_dir is None
True
>>> os.chmod(cache_dir, 0o755)
>>> cache_dir == context.cache_dir
True
"""
xdg_cache_home = os.environ.get('XDG_CACHE_HOME') or \
os.path.join(os.path.expanduser('~'), '.cache')
try:
# If the TLS already has a cache directory path, we return it
# without any futher checks since it must have been valid when it
# was set and if that has changed, hiding the TOCTOU here would be
# potentially confusing
return self._tls["cache_dir"]
except KeyError:
pass
if not os.access(xdg_cache_home, os.W_OK):
# Attempt to create a Python version specific cache dir and its parents
cache_dirname = '.pwntools-cache-%d.%d' % sys.version_info[:2]
cache_dirpath = os.path.join(self.cache_dir_base, cache_dirname)
try:
os.makedirs(cache_dirpath)
except OSError as exc:
# If we failed for any reason other than the cache directory
# already existing then we'll fall back to a temporary directory
# object which doesn't respect the `cache_dir_base`
if exc.errno != errno.EEXIST:
try:
cache_dirpath = tempfile.mkdtemp(prefix=".pwntools-tmp")
except IOError:
# This implies no good candidates for temporary files so we
# have to return `None`
return None
else:
# Ensure the temporary cache dir is cleaned up on exit. A
# `TemporaryDirectory` would do this better upon garbage
# collection but this is necessary for Python 2 support.
atexit.register(shutil.rmtree, cache_dirpath)
# By this time we have a cache directory which exists but we don't know
# if it is actually writable. Some wargames e.g. pwnable.kr have
# created dummy directories which cannot be modified by the user
# account (owned by root).
if os.access(cache_dirpath, os.W_OK):
# Stash this in TLS for later reuse
self._tls["cache_dir"] = cache_dirpath
return cache_dirpath
else:
return None
cache = os.path.join(xdg_cache_home, '.pwntools-cache-%d.%d' % sys.version_info[:2])
if not os.path.exists(cache):
try:
os.mkdir(cache)
except OSError:
return None
# Some wargames e.g. pwnable.kr have created dummy directories
# which cannot be modified by the user account (owned by root).
if not os.access(cache, os.W_OK):
return None
return cache
@cache_dir.setter
def cache_dir(self, v):
if os.access(v, os.W_OK):
# Stash this in TLS for later reuse
self._tls["cache_dir"] = v
@_validator
def delete_corefiles(self, v):
@@ -1394,7 +1450,7 @@ class ContextType(object):
from pwnlib.tubes.ssh import ssh
if not isinstance(shell, ssh):
raise AttributeError("context.ssh_session must be an ssh tube")
raise AttributeError("context.ssh_session must be an ssh tube")
return shell
+56 -44
View File
@@ -1,58 +1,59 @@
# This file was generated using the command:
# curl -s 'http://reveng.sourceforge.net/crc-catalogue/all.htm#crc.legend' | sed -nr -e 's@<H3><A NAME="(crc\.cat[^"]*).*@\1@p' -e 's@.*<CODE>(width.*)</CODE>.*@\1@p' | sed '$!N;s/\n/ /' > crcsums.txt
# curl -s 'https://reveng.sourceforge.io/crc-catalogue/all.htm' | sed -nr -e 's@.*?<A NAME="(crc\.cat[^"]*)">C.*@\1@p' -e 's@.*<CODE>(width=.*)</CODE>.*@\1@p' | sed '$!N;s/\n/ /'
#
# At the time of download, that page was available under the GPLv3 license, which can found here:
# https://www.gnu.org/licenses/gpl.html
#
crc.cat.crc-3-gsm width=3 poly=0x3 init=0x0 refin=false refout=false xorout=0x7 check=0x4 residue=0x2 name="CRC-3/GSM"
crc.cat.crc-3-rohc width=3 poly=0x3 init=0x7 refin=true refout=true xorout=0x0 check=0x6 residue=0x0 name="CRC-3/ROHC"
crc.cat-bits.3 width=3 poly=0x3 init=0x0 refin=false refout=false xorout=0x7 check=0x4 residue=0x2 name="CRC-3/GSM"
crc.cat-bits.4 width=4 poly=0x3 init=0xf refin=false refout=false xorout=0xf check=0xb residue=0x2 name="CRC-4/INTERLAKEN"
crc.cat.crc-4-itu width=4 poly=0x3 init=0x0 refin=true refout=true xorout=0x0 check=0x7 residue=0x0 name="CRC-4/ITU"
crc.cat-bits.5 width=5 poly=0x09 init=0x09 refin=false refout=false xorout=0x00 check=0x00 residue=0x00 name="CRC-5/EPC"
crc.cat.crc-5-itu width=5 poly=0x15 init=0x00 refin=true refout=true xorout=0x00 check=0x07 residue=0x00 name="CRC-5/ITU"
crc.cat.crc-4-g-704 width=4 poly=0x3 init=0x0 refin=true refout=true xorout=0x0 check=0x7 residue=0x0 name="CRC-4/G-704"
crc.cat.crc-4-interlaken width=4 poly=0x3 init=0xf refin=false refout=false xorout=0xf check=0xb residue=0x2 name="CRC-4/INTERLAKEN"
crc.cat.crc-5-epc-c1g2 width=5 poly=0x09 init=0x09 refin=false refout=false xorout=0x00 check=0x00 residue=0x00 name="CRC-5/EPC-C1G2"
crc.cat.crc-5-g-704 width=5 poly=0x15 init=0x00 refin=true refout=true xorout=0x00 check=0x07 residue=0x00 name="CRC-5/G-704"
crc.cat.crc-5-usb width=5 poly=0x05 init=0x1f refin=true refout=true xorout=0x1f check=0x19 residue=0x06 name="CRC-5/USB"
crc.cat-bits.6 width=6 poly=0x27 init=0x3f refin=false refout=false xorout=0x00 check=0x0d residue=0x00 name="CRC-6/CDMA2000-A"
crc.cat.crc-6-cdma2000-a width=6 poly=0x27 init=0x3f refin=false refout=false xorout=0x00 check=0x0d residue=0x00 name="CRC-6/CDMA2000-A"
crc.cat.crc-6-cdma2000-b width=6 poly=0x07 init=0x3f refin=false refout=false xorout=0x00 check=0x3b residue=0x00 name="CRC-6/CDMA2000-B"
crc.cat.crc-6-darc width=6 poly=0x19 init=0x00 refin=true refout=true xorout=0x00 check=0x26 residue=0x00 name="CRC-6/DARC"
crc.cat.crc-6-g-704 width=6 poly=0x03 init=0x00 refin=true refout=true xorout=0x00 check=0x06 residue=0x00 name="CRC-6/G-704"
crc.cat.crc-6-gsm width=6 poly=0x2f init=0x00 refin=false refout=false xorout=0x3f check=0x13 residue=0x3a name="CRC-6/GSM"
crc.cat.crc-6-itu width=6 poly=0x03 init=0x00 refin=true refout=true xorout=0x00 check=0x06 residue=0x00 name="CRC-6/ITU"
crc.cat-bits.7 width=7 poly=0x09 init=0x00 refin=false refout=false xorout=0x00 check=0x75 residue=0x00 name="CRC-7"
crc.cat.crc-7-mmc width=7 poly=0x09 init=0x00 refin=false refout=false xorout=0x00 check=0x75 residue=0x00 name="CRC-7/MMC"
crc.cat.crc-7-rohc width=7 poly=0x4f init=0x7f refin=true refout=true xorout=0x00 check=0x53 residue=0x00 name="CRC-7/ROHC"
crc.cat.crc-7-umts width=7 poly=0x45 init=0x00 refin=false refout=false xorout=0x00 check=0x61 residue=0x00 name="CRC-7/UMTS"
crc.cat-bits.8 width=8 poly=0x07 init=0x00 refin=false refout=false xorout=0x00 check=0xf4 residue=0x00 name="CRC-8"
crc.cat.crc-8-autosar width=8 poly=0x2f init=0xff refin=false refout=false xorout=0xff check=0xdf residue=0x42 name="CRC-8/AUTOSAR"
crc.cat.crc-8-bluetooth width=8 poly=0xa7 init=0x00 refin=true refout=true xorout=0x00 check=0x26 residue=0x00 name="CRC-8/BLUETOOTH"
crc.cat.crc-8-cdma2000 width=8 poly=0x9b init=0xff refin=false refout=false xorout=0x00 check=0xda residue=0x00 name="CRC-8/CDMA2000"
crc.cat.crc-8-darc width=8 poly=0x39 init=0x00 refin=true refout=true xorout=0x00 check=0x15 residue=0x00 name="CRC-8/DARC"
crc.cat.crc-8-dvb-s2 width=8 poly=0xd5 init=0x00 refin=false refout=false xorout=0x00 check=0xbc residue=0x00 name="CRC-8/DVB-S2"
crc.cat.crc-8-ebu width=8 poly=0x1d init=0xff refin=true refout=true xorout=0x00 check=0x97 residue=0x00 name="CRC-8/EBU"
crc.cat.crc-8-gsm-a width=8 poly=0x1d init=0x00 refin=false refout=false xorout=0x00 check=0x37 residue=0x00 name="CRC-8/GSM-A"
crc.cat.crc-8-gsm-b width=8 poly=0x49 init=0x00 refin=false refout=false xorout=0xff check=0x94 residue=0x53 name="CRC-8/GSM-B"
crc.cat.crc-8-hitag width=8 poly=0x1d init=0xff refin=false refout=false xorout=0x00 check=0xb4 residue=0x00 name="CRC-8/HITAG"
crc.cat.crc-8-i-432-1 width=8 poly=0x07 init=0x00 refin=false refout=false xorout=0x55 check=0xa1 residue=0xac name="CRC-8/I-432-1"
crc.cat.crc-8-i-code width=8 poly=0x1d init=0xfd refin=false refout=false xorout=0x00 check=0x7e residue=0x00 name="CRC-8/I-CODE"
crc.cat.crc-8-itu width=8 poly=0x07 init=0x00 refin=false refout=false xorout=0x55 check=0xa1 residue=0xac name="CRC-8/ITU"
crc.cat.crc-8-lte width=8 poly=0x9b init=0x00 refin=false refout=false xorout=0x00 check=0xea residue=0x00 name="CRC-8/LTE"
crc.cat.crc-8-maxim width=8 poly=0x31 init=0x00 refin=true refout=true xorout=0x00 check=0xa1 residue=0x00 name="CRC-8/MAXIM"
crc.cat.crc-8-maxim-dow width=8 poly=0x31 init=0x00 refin=true refout=true xorout=0x00 check=0xa1 residue=0x00 name="CRC-8/MAXIM-DOW"
crc.cat.crc-8-mifare-mad width=8 poly=0x1d init=0xc7 refin=false refout=false xorout=0x00 check=0x99 residue=0x00 name="CRC-8/MIFARE-MAD"
crc.cat.crc-8-nrsc-5 width=8 poly=0x31 init=0xff refin=false refout=false xorout=0x00 check=0xf7 residue=0x00 name="CRC-8/NRSC-5"
crc.cat.crc-8-opensafety width=8 poly=0x2f init=0x00 refin=false refout=false xorout=0x00 check=0x3e residue=0x00 name="CRC-8/OPENSAFETY"
crc.cat.crc-8-rohc width=8 poly=0x07 init=0xff refin=true refout=true xorout=0x00 check=0xd0 residue=0x00 name="CRC-8/ROHC"
crc.cat.crc-8-sae-j1850 width=8 poly=0x1d init=0xff refin=false refout=false xorout=0xff check=0x4b residue=0xc4 name="CRC-8/SAE-J1850"
crc.cat.crc-8-wdcma width=8 poly=0x9b init=0x00 refin=true refout=true xorout=0x00 check=0x25 residue=0x00 name="CRC-8/WCDMA"
crc.cat-bits.10 width=10 poly=0x233 init=0x000 refin=false refout=false xorout=0x000 check=0x199 residue=0x000 name="CRC-10"
crc.cat.crc-8-smbus width=8 poly=0x07 init=0x00 refin=false refout=false xorout=0x00 check=0xf4 residue=0x00 name="CRC-8/SMBUS"
crc.cat.crc-8-tech-3250 width=8 poly=0x1d init=0xff refin=true refout=true xorout=0x00 check=0x97 residue=0x00 name="CRC-8/TECH-3250"
crc.cat.crc-8-wcdma width=8 poly=0x9b init=0x00 refin=true refout=true xorout=0x00 check=0x25 residue=0x00 name="CRC-8/WCDMA"
crc.cat.crc-10-atm width=10 poly=0x233 init=0x000 refin=false refout=false xorout=0x000 check=0x199 residue=0x000 name="CRC-10/ATM"
crc.cat.crc-10-cdma2000 width=10 poly=0x3d9 init=0x3ff refin=false refout=false xorout=0x000 check=0x233 residue=0x000 name="CRC-10/CDMA2000"
crc.cat.crc-10-gsm width=10 poly=0x175 init=0x000 refin=false refout=false xorout=0x3ff check=0x12a residue=0x0c6 name="CRC-10/GSM"
crc.cat-bits.11 width=11 poly=0x385 init=0x01a refin=false refout=false xorout=0x000 check=0x5a3 residue=0x000 name="CRC-11"
crc.cat.crc-11-flexray width=11 poly=0x385 init=0x01a refin=false refout=false xorout=0x000 check=0x5a3 residue=0x000 name="CRC-11/FLEXRAY"
crc.cat.crc-11-umts width=11 poly=0x307 init=0x000 refin=false refout=false xorout=0x000 check=0x061 residue=0x000 name="CRC-11/UMTS"
crc.cat-bits.12 width=12 poly=0xf13 init=0xfff refin=false refout=false xorout=0x000 check=0xd4d residue=0x000 name="CRC-12/CDMA2000"
crc.cat.crc-12-cdma2000 width=12 poly=0xf13 init=0xfff refin=false refout=false xorout=0x000 check=0xd4d residue=0x000 name="CRC-12/CDMA2000"
crc.cat.crc-12-dect width=12 poly=0x80f init=0x000 refin=false refout=false xorout=0x000 check=0xf5b residue=0x000 name="CRC-12/DECT"
crc.cat.crc-12-gsm width=12 poly=0xd31 init=0x000 refin=false refout=false xorout=0xfff check=0xb34 residue=0x178 name="CRC-12/GSM"
crc.cat.crc-12-umts width=12 poly=0x80f init=0x000 refin=false refout=true xorout=0x000 check=0xdaf residue=0x000 name="CRC-12/UMTS"
crc.cat-bits.13 width=13 poly=0x1cf5 init=0x0000 refin=false refout=false xorout=0x0000 check=0x04fa residue=0x0000 name="CRC-13/BBC"
crc.cat-bits.14 width=14 poly=0x0805 init=0x0000 refin=true refout=true xorout=0x0000 check=0x082d residue=0x0000 name="CRC-14/DARC"
crc.cat.crc-13-bbc width=13 poly=0x1cf5 init=0x0000 refin=false refout=false xorout=0x0000 check=0x04fa residue=0x0000 name="CRC-13/BBC"
crc.cat.crc-14-darc width=14 poly=0x0805 init=0x0000 refin=true refout=true xorout=0x0000 check=0x082d residue=0x0000 name="CRC-14/DARC"
crc.cat.crc-14-gsm width=14 poly=0x202d init=0x0000 refin=false refout=false xorout=0x3fff check=0x30ae residue=0x031e name="CRC-14/GSM"
crc.cat-bits.15 width=15 poly=0x4599 init=0x0000 refin=false refout=false xorout=0x0000 check=0x059e residue=0x0000 name="CRC-15"
crc.cat.crc-15-can width=15 poly=0x4599 init=0x0000 refin=false refout=false xorout=0x0000 check=0x059e residue=0x0000 name="CRC-15/CAN"
crc.cat.crc-15-mpt1327 width=15 poly=0x6815 init=0x0000 refin=false refout=false xorout=0x0001 check=0x2566 residue=0x6815 name="CRC-15/MPT1327"
crc.cat-bits.16 width=16 poly=0x8005 init=0x0000 refin=true refout=true xorout=0x0000 check=0xbb3d residue=0x0000 name="ARC"
crc.cat.crc-16-aug-ccitt width=16 poly=0x1021 init=0x1d0f refin=false refout=false xorout=0x0000 check=0xe5cc residue=0x0000 name="CRC-16/AUG-CCITT"
crc.cat.crc-16-buypass width=16 poly=0x8005 init=0x0000 refin=false refout=false xorout=0x0000 check=0xfee8 residue=0x0000 name="CRC-16/BUYPASS"
crc.cat.crc-16-ccitt-false width=16 poly=0x1021 init=0xffff refin=false refout=false xorout=0x0000 check=0x29b1 residue=0x0000 name="CRC-16/CCITT-FALSE"
crc.cat.crc-16-arc width=16 poly=0x8005 init=0x0000 refin=true refout=true xorout=0x0000 check=0xbb3d residue=0x0000 name="CRC-16/ARC"
crc.cat.crc-16-cdma2000 width=16 poly=0xc867 init=0xffff refin=false refout=false xorout=0x0000 check=0x4c06 residue=0x0000 name="CRC-16/CDMA2000"
crc.cat.crc-16-cms width=16 poly=0x8005 init=0xffff refin=false refout=false xorout=0x0000 check=0xaee7 residue=0x0000 name="CRC-16/CMS"
crc.cat.crc-16-dds-110 width=16 poly=0x8005 init=0x800d refin=false refout=false xorout=0x0000 check=0x9ecf residue=0x0000 name="CRC-16/DDS-110"
@@ -62,44 +63,55 @@ crc.cat.crc-16-dnp width=16 poly=0x3d65 init=0x0000 refin=true refout=true
crc.cat.crc-16-en-13757 width=16 poly=0x3d65 init=0x0000 refin=false refout=false xorout=0xffff check=0xc2b7 residue=0xa366 name="CRC-16/EN-13757"
crc.cat.crc-16-genibus width=16 poly=0x1021 init=0xffff refin=false refout=false xorout=0xffff check=0xd64e residue=0x1d0f name="CRC-16/GENIBUS"
crc.cat.crc-16-gsm width=16 poly=0x1021 init=0x0000 refin=false refout=false xorout=0xffff check=0xce3c residue=0x1d0f name="CRC-16/GSM"
crc.cat.crc-16-ibm-3740 width=16 poly=0x1021 init=0xffff refin=false refout=false xorout=0x0000 check=0x29b1 residue=0x0000 name="CRC-16/IBM-3740"
crc.cat.crc-16-ibm-sdlc width=16 poly=0x1021 init=0xffff refin=true refout=true xorout=0xffff check=0x906e residue=0xf0b8 name="CRC-16/IBM-SDLC"
crc.cat.crc-16-iso-iec-14443-3-a width=16 poly=0x1021 init=0xc6c6 refin=true refout=true xorout=0x0000 check=0xbf05 residue=0x0000 name="CRC-16/ISO-IEC-14443-3-A"
crc.cat.crc-16-kermit width=16 poly=0x1021 init=0x0000 refin=true refout=true xorout=0x0000 check=0x2189 residue=0x0000 name="CRC-16/KERMIT"
crc.cat.crc-16-lj1200 width=16 poly=0x6f63 init=0x0000 refin=false refout=false xorout=0x0000 check=0xbdf4 residue=0x0000 name="CRC-16/LJ1200"
crc.cat.crc-16-maxim width=16 poly=0x8005 init=0x0000 refin=true refout=true xorout=0xffff check=0x44c2 residue=0xb001 name="CRC-16/MAXIM"
crc.cat.crc-16-m17 width=16 poly=0x5935 init=0xffff refin=false refout=false xorout=0x0000 check=0x772b residue=0x0000 name="CRC-16/M17"
crc.cat.crc-16-maxim-dow width=16 poly=0x8005 init=0x0000 refin=true refout=true xorout=0xffff check=0x44c2 residue=0xb001 name="CRC-16/MAXIM-DOW"
crc.cat.crc-16-mcrf4xx width=16 poly=0x1021 init=0xffff refin=true refout=true xorout=0x0000 check=0x6f91 residue=0x0000 name="CRC-16/MCRF4XX"
crc.cat.crc-16-modbus width=16 poly=0x8005 init=0xffff refin=true refout=true xorout=0x0000 check=0x4b37 residue=0x0000 name="CRC-16/MODBUS"
crc.cat.crc-16-nrsc-5 width=16 poly=0x080b init=0xffff refin=true refout=true xorout=0x0000 check=0xa066 residue=0x0000 name="CRC-16/NRSC-5"
crc.cat.crc-16-opensafety-a width=16 poly=0x5935 init=0x0000 refin=false refout=false xorout=0x0000 check=0x5d38 residue=0x0000 name="CRC-16/OPENSAFETY-A"
crc.cat.crc-16-opensafety-a width=16 poly=0x755b init=0x0000 refin=false refout=false xorout=0x0000 check=0x20fe residue=0x0000 name="CRC-16/OPENSAFETY-B"
crc.cat.crc-16-opensafety-b width=16 poly=0x755b init=0x0000 refin=false refout=false xorout=0x0000 check=0x20fe residue=0x0000 name="CRC-16/OPENSAFETY-B"
crc.cat.crc-16-profibus width=16 poly=0x1dcf init=0xffff refin=false refout=false xorout=0xffff check=0xa819 residue=0xe394 name="CRC-16/PROFIBUS"
crc.cat.crc-16-riello width=16 poly=0x1021 init=0xb2aa refin=true refout=true xorout=0x0000 check=0x63d0 residue=0x0000 name="CRC-16/RIELLO"
crc.cat.crc-16-spi-fujitsu width=16 poly=0x1021 init=0x1d0f refin=false refout=false xorout=0x0000 check=0xe5cc residue=0x0000 name="CRC-16/SPI-FUJITSU"
crc.cat.crc-16-t10-dif width=16 poly=0x8bb7 init=0x0000 refin=false refout=false xorout=0x0000 check=0xd0db residue=0x0000 name="CRC-16/T10-DIF"
crc.cat.crc-16-teledisk width=16 poly=0xa097 init=0x0000 refin=false refout=false xorout=0x0000 check=0x0fb3 residue=0x0000 name="CRC-16/TELEDISK"
crc.cat.crc-16-tms37157 width=16 poly=0x1021 init=0x89ec refin=true refout=true xorout=0x0000 check=0x26b1 residue=0x0000 name="CRC-16/TMS37157"
crc.cat.crc-16-umts width=16 poly=0x8005 init=0x0000 refin=false refout=false xorout=0x0000 check=0xfee8 residue=0x0000 name="CRC-16/UMTS"
crc.cat.crc-16-usb width=16 poly=0x8005 init=0xffff refin=true refout=true xorout=0xffff check=0xb4c8 residue=0xb001 name="CRC-16/USB"
crc.cat.crc-a width=16 poly=0x1021 init=0xc6c6 refin=true refout=true xorout=0x0000 check=0xbf05 residue=0x0000 name="CRC-A"
crc.cat.kermit width=16 poly=0x1021 init=0x0000 refin=true refout=true xorout=0x0000 check=0x2189 residue=0x0000 name="KERMIT"
crc.cat.modbus width=16 poly=0x8005 init=0xffff refin=true refout=true xorout=0x0000 check=0x4b37 residue=0x0000 name="MODBUS"
crc.cat.x-25 width=16 poly=0x1021 init=0xffff refin=true refout=true xorout=0xffff check=0x906e residue=0xf0b8 name="X-25"
crc.cat.xmodem width=16 poly=0x1021 init=0x0000 refin=false refout=false xorout=0x0000 check=0x31c3 residue=0x0000 name="XMODEM"
crc.cat-bits.24 width=24 poly=0x864cfb init=0xb704ce refin=false refout=false xorout=0x000000 check=0x21cf02 residue=0x000000 name="CRC-24"
crc.cat.crc-16-xmodem width=16 poly=0x1021 init=0x0000 refin=false refout=false xorout=0x0000 check=0x31c3 residue=0x0000 name="CRC-16/XMODEM"
crc.cat.crc-17-can-fd width=17 poly=0x1685b init=0x00000 refin=false refout=false xorout=0x00000 check=0x04f03 residue=0x00000 name="CRC-17/CAN-FD"
crc.cat.crc-21-can-fd width=21 poly=0x102899 init=0x000000 refin=false refout=false xorout=0x000000 check=0x0ed841 residue=0x000000 name="CRC-21/CAN-FD"
crc.cat.crc-24-ble width=24 poly=0x00065b init=0x555555 refin=true refout=true xorout=0x000000 check=0xc25a56 residue=0x000000 name="CRC-24/BLE"
crc.cat.crc-24-flexray-a width=24 poly=0x5d6dcb init=0xfedcba refin=false refout=false xorout=0x000000 check=0x7979bd residue=0x000000 name="CRC-24/FLEXRAY-A"
crc.cat.crc-24-flexray-b width=24 poly=0x5d6dcb init=0xabcdef refin=false refout=false xorout=0x000000 check=0x1f23b8 residue=0x000000 name="CRC-24/FLEXRAY-B"
crc.cat.crc-24-interlaken width=24 poly=0x328b63 init=0xffffff refin=false refout=false xorout=0xffffff check=0xb4f3e6 residue=0x144e63 name="CRC-24/INTERLAKEN"
crc.cat.crc-24-lte-a width=24 poly=0x864cfb init=0x000000 refin=false refout=false xorout=0x000000 check=0xcde703 residue=0x000000 name="CRC-24/LTE-A"
crc.cat.crc-24-lte-b width=24 poly=0x800063 init=0x000000 refin=false refout=false xorout=0x000000 check=0x23ef52 residue=0x000000 name="CRC-24/LTE-B"
crc.cat-bits.30 width=30 poly=0x2030b9c7 init=0x3fffffff refin=false refout=false xorout=0x3fffffff check=0x04c34abf residue=0x34efa55a name="CRC-30/CDMA"
crc.cat-bits.31 width=31 poly=0x04c11db7 init=0x7fffffff refin=false refout=false xorout=0x7fffffff check=0x0ce9e46c residue=0x4eaf26f1 name="CRC-31/PHILIPS"
crc.cat-bits.32 width=32 poly=0x04c11db7 init=0xffffffff refin=true refout=true xorout=0xffffffff check=0xcbf43926 residue=0xdebb20e3 name="CRC-32"
crc.cat.crc-24-openpgp width=24 poly=0x864cfb init=0xb704ce refin=false refout=false xorout=0x000000 check=0x21cf02 residue=0x000000 name="CRC-24/OPENPGP"
crc.cat.crc-24-os-9 width=24 poly=0x800063 init=0xffffff refin=false refout=false xorout=0xffffff check=0x200fa5 residue=0x800fe3 name="CRC-24/OS-9"
crc.cat.crc-30-cdma width=30 poly=0x2030b9c7 init=0x3fffffff refin=false refout=false xorout=0x3fffffff check=0x04c34abf residue=0x34efa55a name="CRC-30/CDMA"
crc.cat.crc-31-philips width=31 poly=0x04c11db7 init=0x7fffffff refin=false refout=false xorout=0x7fffffff check=0x0ce9e46c residue=0x4eaf26f1 name="CRC-31/PHILIPS"
crc.cat.crc-32-aixm width=32 poly=0x814141ab init=0x00000000 refin=false refout=false xorout=0x00000000 check=0x3010bf7f residue=0x00000000 name="CRC-32/AIXM"
crc.cat.crc-32-autosar width=32 poly=0xf4acfb13 init=0xffffffff refin=true refout=true xorout=0xffffffff check=0x1697d06a residue=0x904cddbf name="CRC-32/AUTOSAR"
crc.cat.crc-32-base91-d width=32 poly=0xa833982b init=0xffffffff refin=true refout=true xorout=0xffffffff check=0x87315576 residue=0x45270551 name="CRC-32/BASE91-D"
crc.cat.crc-32-bzip2 width=32 poly=0x04c11db7 init=0xffffffff refin=false refout=false xorout=0xffffffff check=0xfc891918 residue=0xc704dd7b name="CRC-32/BZIP2"
crc.cat.crc-32c width=32 poly=0x1edc6f41 init=0xffffffff refin=true refout=true xorout=0xffffffff check=0xe3069283 residue=0xb798b438 name="CRC-32C"
crc.cat.crc-32d width=32 poly=0xa833982b init=0xffffffff refin=true refout=true xorout=0xffffffff check=0x87315576 residue=0x45270551 name="CRC-32D"
crc.cat.crc-32-cd-rom-edc width=32 poly=0x8001801b init=0x00000000 refin=true refout=true xorout=0x00000000 check=0x6ec2edc4 residue=0x00000000 name="CRC-32/CD-ROM-EDC"
crc.cat.crc-32-cksum width=32 poly=0x04c11db7 init=0x00000000 refin=false refout=false xorout=0xffffffff check=0x765e7680 residue=0xc704dd7b name="CRC-32/CKSUM"
crc.cat.crc-32-iscsi width=32 poly=0x1edc6f41 init=0xffffffff refin=true refout=true xorout=0xffffffff check=0xe3069283 residue=0xb798b438 name="CRC-32/ISCSI"
crc.cat.crc-32-iso-hdlc width=32 poly=0x04c11db7 init=0xffffffff refin=true refout=true xorout=0xffffffff check=0xcbf43926 residue=0xdebb20e3 name="CRC-32/ISO-HDLC"
crc.cat.crc-32-jamcrc width=32 poly=0x04c11db7 init=0xffffffff refin=true refout=true xorout=0x00000000 check=0x340bc6d9 residue=0x00000000 name="CRC-32/JAMCRC"
crc.cat.crc-32-mef width=32 poly=0x741b8cd7 init=0xffffffff refin=true refout=true xorout=0x00000000 check=0xd2c22f51 residue=0x00000000 name="CRC-32/MEF"
crc.cat.crc-32-mpeg-2 width=32 poly=0x04c11db7 init=0xffffffff refin=false refout=false xorout=0x00000000 check=0x0376e6e7 residue=0x00000000 name="CRC-32/MPEG-2"
crc.cat.crc-32-posix width=32 poly=0x04c11db7 init=0x00000000 refin=false refout=false xorout=0xffffffff check=0x765e7680 residue=0xc704dd7b name="CRC-32/POSIX"
crc.cat.crc-32q width=32 poly=0x814141ab init=0x00000000 refin=false refout=false xorout=0x00000000 check=0x3010bf7f residue=0x00000000 name="CRC-32Q"
crc.cat.jamcrc width=32 poly=0x04c11db7 init=0xffffffff refin=true refout=true xorout=0x00000000 check=0x340bc6d9 residue=0x00000000 name="JAMCRC"
crc.cat.xfer width=32 poly=0x000000af init=0x00000000 refin=false refout=false xorout=0x00000000 check=0xbd0be338 residue=0x00000000 name="XFER"
crc.cat-bits.40 width=40 poly=0x0004820009 init=0x0000000000 refin=false refout=false xorout=0xffffffffff check=0xd4164fc646 residue=0xc4ff8071ff name="CRC-40/GSM"
crc.cat-bits.64 width=64 poly=0x42f0e1eba9ea3693 init=0x0000000000000000 refin=false refout=false xorout=0x0000000000000000 check=0x6c40df5f0b497347 residue=0x0000000000000000 name="CRC-64"
crc.cat.crc-32-xfer width=32 poly=0x000000af init=0x00000000 refin=false refout=false xorout=0x00000000 check=0xbd0be338 residue=0x00000000 name="CRC-32/XFER"
crc.cat.crc-40-gsm width=40 poly=0x0004820009 init=0x0000000000 refin=false refout=false xorout=0xffffffffff check=0xd4164fc646 residue=0xc4ff8071ff name="CRC-40/GSM"
crc.cat.crc-64-ecma-182 width=64 poly=0x42f0e1eba9ea3693 init=0x0000000000000000 refin=false refout=false xorout=0x0000000000000000 check=0x6c40df5f0b497347 residue=0x0000000000000000 name="CRC-64/ECMA-182"
crc.cat.crc-64-go-iso width=64 poly=0x000000000000001b init=0xffffffffffffffff refin=true refout=true xorout=0xffffffffffffffff check=0xb90956c775a41001 residue=0x5300000000000000 name="CRC-64/GO-ISO"
crc.cat.crc-64-ms width=64 poly=0x259c84cba6426349 init=0xffffffffffffffff refin=true refout=true xorout=0x0000000000000000 check=0x75d4b74f024eceea residue=0x0000000000000000 name="CRC-64/MS"
crc.cat.crc-64-we width=64 poly=0x42f0e1eba9ea3693 init=0xffffffffffffffff refin=false refout=false xorout=0xffffffffffffffff check=0x62ec59e3f1a4f00a residue=0xfcacbebd5931a992 name="CRC-64/WE"
crc.cat.crc-64-xz width=64 poly=0x42f0e1eba9ea3693 init=0xffffffffffffffff refin=true refout=true xorout=0xffffffffffffffff check=0x995dc9bbdf1939fa residue=0x49958c9abd7d353f name="CRC-64/XZ"
crc.cat-bits.82 width=82 poly=0x0308c0111011401440411 init=0x000000000000000000000 refin=true refout=true xorout=0x000000000000000000000 check=0x09ea83f625023801fd612 residue=0x000000000000000000000 name="CRC-82/DARC"
crc.cat.crc-82-darc width=82 poly=0x0308c0111011401440411 init=0x000000000000000000000 refin=true refout=true xorout=0x000000000000000000000 check=0x09ea83f625023801fd612 residue=0x000000000000000000000 name="CRC-82/DARC"
+39 -30
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
from __future__ import division
import argparse
import keyword
@@ -72,7 +72,7 @@ CALL = """
target = regs[index]
register_arguments[target] = arg
elif arg is not None:
stack_arguments[index] = arg
stack_arguments[name] = arg
# The argument is not a register. It is a string value, and we
# are expecting a string value
@@ -100,7 +100,7 @@ CALL = """
target = regs[index]
register_arguments[target] = arg
elif arg is not None:
stack_arguments[target] = arg
stack_arguments[name] = arg
# Some syscalls have different names on various architectures.
# Determine which syscall number to use for the current architecture.
@@ -108,7 +108,7 @@ CALL = """
if hasattr(pwnlib.constants, syscall):
break
else:
raise Exception("Could not locate any syscalls: %r" % syscalls)
raise Exception("Could not locate any syscalls: %r" % {syscalls!r})
%>
/* {name}(${{', '.join(syscall_repr)}}) */
%for name, arg in string_arguments.items():
@@ -137,6 +137,7 @@ def can_be_string(arg):
if arg.type == 'void' and arg.derefcnt == 1:
return True
def can_be_array(arg):
if arg.type == 'char' and arg.derefcnt == 2:
return True
@@ -160,11 +161,13 @@ def fix_bad_arg_names(func, arg):
def get_arg_default(arg):
return 0
def fix_rt_syscall_name(name):
if name.startswith('rt_'):
return name[3:]
return name
def fix_syscall_names(name):
# Do not use old_mmap
if name == 'SYS_mmap':
@@ -180,6 +183,7 @@ def main(target):
with context.local(arch=arch):
generate_one(target)
def generate_one(target):
SYSCALL_NAMES = [c for c in dir(constants) if c.startswith('SYS_')]
@@ -214,46 +218,50 @@ def generate_one(target):
# Set up the argument string for Mako
argument_names = []
argument_names_ = []
argument_defaults = []
#
for arg in function.args:
argname = fix_bad_arg_names(function, arg)
default = get_arg_default(arg)
# Mako is unable to use *vararg and *kwarg, so we just stub in
# a whole bunch of additional arguments.
if argname == 'vararg':
for j in range(5):
argname = 'vararg_%i' % j
argument_names.append(argname)
argument_defaults.append('%s=%s' % (argname, None))
break
argument_names.append(argname)
argument_defaults.append('%s=%s' % (argname, default))
arguments_default_values = ', '.join(argument_defaults)
arguments_comma_separated = ', '.join(argument_names)
string_arguments = []
array_arguments = []
arg_docs = []
#
for arg in function.args:
argname_ = fix_bad_arg_names(function, arg)
argname = argname_.rstrip('_')
default = get_arg_default(arg)
if can_be_array(arg):
array_arguments.append(arg.name)
array_arguments.append(argname)
if can_be_string(arg):
string_arguments.append(arg.name)
string_arguments.append(argname)
argname = arg.name
argtype = str(arg.type) + ('*' * arg.derefcnt)
arg_docs.append(
' {argname}({argtype}): {argname}'.format(argname=argname,
argtype=argtype))
' {argname_}({argtype}): {argname}'.format(
argname_=argname_,
argname=argname,
argtype=argtype,
))
# Mako is unable to use *vararg and *kwarg, so we just stub in
# a whole bunch of additional arguments.
if argname == 'vararg':
for j in range(6):
argname = 'vararg_%i' % j
argument_names.append(argname)
argument_names_.append(argname)
argument_defaults.append('%s=%s' % (argname, None))
break
argument_names.append(argname)
argument_names_.append(argname_)
argument_defaults.append('%s=%s' % (argname_, default))
arguments_default_values = ', '.join(argument_defaults)
arguments_comma_separated = ', '.join(argument_names_)
return_type = str(function.type) + ('*' * function.derefcnt)
arg_docs = '\n'.join(arg_docs)
@@ -282,6 +290,7 @@ def generate_one(target):
with open(os.path.join(target, name + '.asm'), 'wt') as f:
f.write('\n'.join(map(str.strip, lines)) + '\n')
if __name__ == '__main__':
p = argparse.ArgumentParser()
p.add_argument('target_directory')
+33 -3
View File
@@ -1,4 +1,4 @@
<%page args="binary, host=None, port=None, user=None, password=None, remote_path=None, quiet=False"/>\
<%page args="binary, host=None, port=None, user=None, password=None, libc=None, remote_path=None, quiet=False"/>\
<%
import os
import sys
@@ -31,6 +31,7 @@ elif host and not port:
remote_path = remote_path or exe
password = password or 'secret1234'
binary_repr = repr(binary)
libc_repr = repr(libc)
%>\
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
@@ -44,7 +45,7 @@ from pwn import *
# Set up pwntools for the correct architecture
%endif
%if ctx.binary:
exe = context.binary = ELF(${binary_repr})
exe = context.binary = ELF(args.EXE or ${binary_repr})
<% binary_repr = 'exe.path' %>
%else:
context.update(arch='i386')
@@ -58,7 +59,7 @@ exe = ${binary_repr}
# for all created processes...
# ./exploit.py DEBUG NOASLR
%if host or port or user:
# ./exploit.py GDB HOST=example.com PORT=4141
# ./exploit.py GDB HOST=example.com PORT=4141 EXE=/tmp/executable
%endif
%endif
%if host:
@@ -83,6 +84,35 @@ if not args.LOCAL:
shell.set_working_directory(symlink=True)
%endif
%if libc:
%if not quiet:
# Use the specified remote libc version unless explicitly told to use the
# local system version with the `LOCAL_LIBC` argument.
# ./exploit.py LOCAL LOCAL_LIBC
%endif
if args.LOCAL_LIBC:
libc = exe.libc
%if host:
elif args.LOCAL:
%else:
else:
%endif
library_path = libcdb.download_libraries(${libc_repr})
if library_path:
%if ctx.binary:
exe = context.binary = ELF.patch_custom_libraries(${binary_repr}, library_path)
%else:
exe = ELF.patch_custom_libraries(exe, library_path)
%endif
libc = exe.libc
else:
libc = ELF(${libc_repr})
%if host:
else:
libc = ELF(${libc_repr})
%endif
%endif
%if host:
def start_local(argv=[], *a, **kw):
'''Execute the target binary locally'''
@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
"""Script for downloading lists of user agent strings."""
from __future__ import division
import os
@@ -43,7 +43,7 @@ with log.waitfor('Fetching from from http://www.user-agents.org') as l:
l.success()
with log.waitfor('Parsing list') as l:
for item in soup.__getattr__('user-agents'):
for item in getattr(soup, 'user-agents'):
if item.name == 'user-agent':
ua = item.select('string')[0].string.strip()
try:
+8 -9
View File
@@ -15,7 +15,7 @@ Example
# leaks at least one byte at that address.
def leak(address):
data = p.read(address, 4)
log.debug("%#x => %s" % (address, enhex(data or '')))
log.debug("%#x => %s", address, enhex(data or ''))
return data
# For the sake of this example, let's say that we
@@ -62,8 +62,7 @@ from pwnlib.elf import constants
from pwnlib.log import getLogger
from pwnlib.memleak import MemLeak
from pwnlib.util.fiddling import enhex
from pwnlib.util.packing import unpack, _need_bytes
from pwnlib.util.web import wget
from pwnlib.util.packing import _need_bytes
log = getLogger(__name__)
sizeof = ctypes.sizeof
@@ -141,10 +140,10 @@ class DynELF(object):
.. _.got.plt: https://refspecs.linuxbase.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/specialsections.html
.. _DYNAMIC: http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#dynamic_section
.. _SYSV: https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html#hash
.. _GNU: https://blogs.oracle.com/ali/entry/gnu_hash_elf_sections
.. _GNU: https://blogs.oracle.com/solaris/post/gnu-hash-elf-sections
.. _DT_DEBUG: https://reverseengineering.stackexchange.com/questions/6525/elf-link-map-when-linked-as-relro
.. _link map: https://sourceware.org/git/?p=glibc.git;a=blob;f=elf/link.h;h=eaca8028e45a859ac280301a6e955a14eed1b887;hb=HEAD#l84
.. _DT_PLTGOT: http://refspecs.linuxfoundation.org/ELF/zSeries/lzsabi0_zSeries/x2251.html
.. _DT_PLTGOT: https://refspecs.linuxfoundation.org/ELF/zSeries/lzsabi0_zSeries/x2251.html
'''
def __init__(self, leak, pointer=None, elf=None, libcdb=True):
@@ -562,7 +561,7 @@ class DynELF(object):
else: dynlib = self
if dynlib is None:
log.failure("Could not find %r" % lib)
log.failure("Could not find %r", lib)
return None
#
@@ -573,7 +572,7 @@ class DynELF(object):
self.status("Trying lookup based on Build ID")
build_id = dynlib._lookup_build_id(lib=lib)
if build_id:
log.info("Trying lookup based on Build ID: %s" % build_id)
log.info("Trying lookup based on Build ID: %s", build_id)
path = libcdb.search_by_build_id(build_id)
if path:
with context.local(log_level='error'):
@@ -615,7 +614,7 @@ class DynELF(object):
addr = leak.field(cur, LinkMap.l_addr)
cur = leak.field(cur, LinkMap.l_next)
log.debug('Found %r @ %#x' % (name, addr))
log.debug('Found %r @ %#x', name, addr)
self._bases[name] = addr
@@ -770,7 +769,7 @@ class DynELF(object):
structure.
Again, Oracle has good documentation.
https://blogs.oracle.com/ali/entry/gnu_hash_elf_sections
https://blogs.oracle.com/solaris/post/gnu-hash-elf-sections
You can force an ELF to use this type of symbol table by compiling
with 'gcc -Wl,--hash-style=gnu'
+16 -18
View File
@@ -76,7 +76,6 @@ import tempfile
from io import BytesIO, StringIO
import elftools
from elftools.common.py3compat import bytes2str
from elftools.common.utils import roundup
from elftools.common.utils import struct_parse
from elftools.construct import CString
@@ -94,6 +93,7 @@ from pwnlib.util.fiddling import enhex
from pwnlib.util.fiddling import unhex
from pwnlib.util.misc import read
from pwnlib.util.misc import write
from pwnlib.util.packing import _decode
from pwnlib.util.packing import pack
from pwnlib.util.packing import unpack_many
@@ -134,12 +134,13 @@ def iter_notes(self):
self.stream.seek(offset)
# n_namesz is 4-byte aligned.
disk_namesz = roundup(note['n_namesz'], 2)
note['n_name'] = bytes2str(
CString('').parse(self.stream.read(disk_namesz)))
offset += disk_namesz
with context.local(encoding='latin-1'):
note['n_name'] = _decode(
CString('').parse(self.stream.read(disk_namesz)))
offset += disk_namesz
desc_data = bytes2str(self.stream.read(note['n_descsz']))
note['n_desc'] = desc_data
desc_data = _decode(self.stream.read(note['n_descsz']))
note['n_desc'] = desc_data
offset += roundup(note['n_descsz'], 2)
note['n_size'] = offset - note['n_offset']
yield note
@@ -445,7 +446,7 @@ class Corefile(ELF):
will be loaded.
>>> process('bash').corefile.libc # doctest: +ELLIPSIS
Mapping('/.../libc-....so', start=0x..., stop=0x..., size=0x..., flags=..., page_offset=...)
Mapping('.../libc...so...', start=0x..., stop=0x..., size=0x..., flags=..., page_offset=...)
The corefile also contains a :attr:`.stack` property, which gives
us direct access to the stack contents. On Linux, the very top of the stack
@@ -530,7 +531,6 @@ class Corefile(ELF):
>>> io = elf.process()
>>> io.wait(2)
>>> core = io.corefile
[!] End of the stack is corrupted, skipping stack parsing (got: 41414141)
>>> core.argc, core.argv, core.env
(0, [], {})
>>> core.stack.data.endswith(b'AAAA')
@@ -666,7 +666,7 @@ class Corefile(ELF):
log.warn('Could not find the stack!')
self.stack = None
with context.local(bytes=self.bytes, log_level='warn'):
with context.local(bytes=self.bytes):
try:
self._parse_stack()
except ValueError:
@@ -759,7 +759,7 @@ class Corefile(ELF):
@property
def libc(self):
""":class:`Mapping`: First mapping for ``libc.so``"""
expr = r'libc\b.*so$'
expr = r'^libc\b.*so(?:\.6)?$'
for m in self.mappings:
if not m.name:
@@ -1184,14 +1184,13 @@ class Corefile(ELF):
pwnlib.gdb.attach(self, exe=self.exe.path)
def __getattr__(self, attribute):
if self.prstatus:
if hasattr(self.prstatus, attribute):
return getattr(self.prstatus, attribute)
if attribute.startswith('_') or not self.prstatus:
raise AttributeError(attribute)
if hasattr(self.prstatus.pr_reg, attribute):
return getattr(self.prstatus.pr_reg, attribute)
if hasattr(self.prstatus, attribute):
return getattr(self.prstatus, attribute)
return super(Corefile, self).__getattribute__(attribute)
return getattr(self.prstatus.pr_reg, attribute)
# Override routines which don't make sense for Corefiles
def _populate_got(*a): pass
@@ -1407,9 +1406,8 @@ class CorefileFinder(object):
"coredumpctl",
"dump",
"--output=%s" % filename,
# Filter coredump by pid and filename
# Filter coredump by pid
str(self.pid),
self.basename,
],
stdout=open(os.devnull, 'w'),
stderr=subprocess.STDOUT,
+2 -1
View File
@@ -604,7 +604,8 @@ class user_regs_struct_aarch64(ctypes.Structure):
def __getattr__(self, name):
if name.startswith('r'):
name = 'x' + name[1:]
return getattr(name) & 0xffffffff
return getattr(self, name) & 0xffffffff
raise AttributeError(name)
class elf_prstatus_i386(ctypes.Structure):
_fields_ = generate_prstatus_common(32, user_regs_struct_i386)
+460 -132
View File
@@ -49,7 +49,6 @@ from six import BytesIO
from collections import namedtuple
from elftools.elf.constants import E_FLAGS
from elftools.elf.constants import P_FLAGS
from elftools.elf.constants import SHN_INDICES
from elftools.elf.descriptions import describe_e_type
@@ -83,7 +82,7 @@ from pwnlib.tubes.process import process
from pwnlib.util import misc
from pwnlib.util import packing
from pwnlib.util.fiddling import unhex
from pwnlib.util.misc import align, align_down
from pwnlib.util.misc import align, align_down, which
from pwnlib.util.sh_string import sh_string
log = getLogger(__name__)
@@ -157,6 +156,12 @@ class dotdict(dict):
>>> x.bar.baz
4
"""
def __missing__(self, name):
if isinstance(name, (bytes, bytearray)):
name = packing._decode(name)
return self[name]
raise KeyError(name)
def __getattr__(self, name):
if name in self:
return self[name]
@@ -167,8 +172,7 @@ class dotdict(dict):
if subkeys:
return dotdict(subkeys)
return getattr(super(dotdict, self), name)
raise AttributeError(name)
class ELF(ELFFile):
"""Encapsulates information about an ELF file.
@@ -220,14 +224,7 @@ class ELF(ELFFile):
super(ELF,self).__init__(self.mmap)
#: :class:`str`: Path to the file
self.path = os.path.abspath(path)
#: :class:`str`: Architecture of the file (e.g. ``'i386'``, ``'arm'``).
#:
#: See: :attr:`.ContextType.arch`
self.arch = self.get_machine_arch()
if isinstance(self.arch, (bytes, six.text_type)):
self.arch = self.arch.lower()
self.path = packing._need_text(os.path.abspath(path))
#: :class:`dotdict` of ``name`` to ``address`` for all symbols in the ELF
self.symbols = dotdict()
@@ -235,7 +232,7 @@ class ELF(ELFFile):
#: :class:`dotdict` of ``name`` to ``address`` for all Global Offset Table (GOT) entries
self.got = dotdict()
#: :class:`dotdict` of ``name`` to ``address`` for all Procedure Linkate Table (PLT) entries
#: :class:`dotdict` of ``name`` to ``address`` for all Procedure Linkage Table (PLT) entries
self.plt = dotdict()
#: :class:`dotdict` of ``name`` to :class:`.Function` for each function in the ELF
@@ -263,17 +260,12 @@ class ELF(ELFFile):
#: :class:`int`: Pointer width, in bytes
self.bytes = self.bits // 8
if self.arch == 'mips':
mask = lambda a, b: a & b == b
flags = self.header['e_flags']
if mask(flags, E_FLAGS.EF_MIPS_ARCH_32) \
or mask(flags, E_FLAGS.EF_MIPS_ARCH_32R2):
pass
elif mask(flags, E_FLAGS.EF_MIPS_ARCH_64) \
or mask(flags, E_FLAGS.EF_MIPS_ARCH_64R2):
self.arch = 'mips64'
self.bits = 64
#: :class:`str`: Architecture of the file (e.g. ``'i386'``, ``'arm'``).
#:
#: See: :attr:`.ContextType.arch`
self.arch = self.get_machine_arch()
if isinstance(self.arch, (bytes, six.text_type)):
self.arch = self.arch.lower()
self._sections = None
self._segments = None
@@ -466,18 +458,21 @@ class ELF(ELFFile):
def get_machine_arch(self):
return {
'EM_X86_64': 'amd64',
'EM_386' :'i386',
'EM_486': 'i386',
'EM_ARM': 'arm',
'EM_AARCH64': 'aarch64',
'EM_MIPS': 'mips',
'EM_PPC': 'powerpc',
'EM_PPC64': 'powerpc64',
'EM_SPARC32PLUS': 'sparc',
'EM_SPARCV9': 'sparc64',
'EM_IA_64': 'ia64'
}.get(self['e_machine'], self['e_machine'])
('EM_X86_64', 64): 'amd64',
('EM_386', 32): 'i386',
('EM_486', 32): 'i386',
('EM_ARM', 32): 'arm',
('EM_AARCH64', 64): 'aarch64',
('EM_MIPS', 32): 'mips',
('EM_MIPS', 64): 'mips64',
('EM_PPC', 32): 'powerpc',
('EM_PPC64', 64): 'powerpc64',
('EM_SPARC32PLUS', 32): 'sparc',
('EM_SPARCV9', 64): 'sparc64',
('EM_IA_64', 64): 'ia64',
('EM_RISCV', 32): 'riscv32',
('EM_RISCV', 64): 'riscv64',
}.get((self['e_machine'], self.bits), self['e_machine'])
@property
def entry(self):
@@ -1056,10 +1051,10 @@ class ELF(ELFFile):
return
banner = self.string(self.symbols.linux_banner)
# convert banner into a utf-8 string since re.search does not accept bytes anymore
banner = banner.decode('utf-8')
# 'Linux version 3.18.31-gd0846ecc
regex = r'Linux version (\S+)'
match = re.search(regex, banner)
@@ -1076,11 +1071,17 @@ class ELF(ELFFile):
@property
def libc_start_main_return(self):
"""
Try to find the return address from main into __libc_start_main.
The heuristic to find the call to the function pointer of main is
to list all calls inside __libc_start_main, find the call to exit
after the call to main and select the previous call.
""":class:`int`: Address of the return address into __libc_start_main from main.
>>> bash = ELF(which('bash'))
>>> libc = bash.libc
>>> libc.libc_start_main_return > 0
True
Try to find the return address from main into __libc_start_main.
The heuristic to find the call to the function pointer of main is
to list all calls inside __libc_start_main, find the call to exit
after the call to main and select the previous call.
"""
if '__libc_start_main' not in self.functions:
return 0
@@ -1103,19 +1104,44 @@ class ELF(ELFFile):
else:
log.error('Unsupported architecture %s in ELF.libc_start_main_return', self.arch)
return 0
code = self.disasm(self.symbols['__libc_start_main'], self.functions['__libc_start_main'].size)
exit_addr = hex(self.symbols['exit'])
lines = code.split('\n')
calls = [(index, line) for index, line in enumerate(lines) if set(line.split()) & call_instructions]
exit_calls = [index for index, line in enumerate(calls) if exit_addr in line[1]]
if len(exit_calls) != 1:
return 0
call_to_main = calls[exit_calls[0] - 1]
return_from_main = lines[call_to_main[0] + call_return_offset].lstrip()
return_from_main = int(return_from_main[ : return_from_main.index(':') ], 16)
return return_from_main
lines = self.functions['__libc_start_main'].disasm().split('\n')
exit_addr = hex(self.symbols['exit'])
calls = [(index, line) for index, line in enumerate(lines) if set(line.split()) & call_instructions]
def find_ret_main_addr(lines, calls):
exit_calls = [index for index, line in enumerate(calls) if exit_addr in line[1]]
if len(exit_calls) != 1:
return 0
call_to_main = calls[exit_calls[0] - 1]
return_from_main = lines[call_to_main[0] + call_return_offset].lstrip()
return_from_main = int(return_from_main[ : return_from_main.index(':') ], 16)
return return_from_main
# Starting with glibc-2.34 calling `main` is split out into `__libc_start_call_main`
ret_addr = find_ret_main_addr(lines, calls)
# Pre glibc-2.34 case - `main` is called directly
if ret_addr:
return ret_addr
# `__libc_start_main` -> `__libc_start_call_main` -> `main`
# Find a direct call which calls `exit` once. That's probably `__libc_start_call_main`.
direct_call_pattern = re.compile(r'['+r'|'.join(call_instructions)+r']\s+(0x[0-9a-zA-Z]+)')
for line in calls:
match = direct_call_pattern.search(line[1])
if not match:
continue
target_addr = int(match.group(1), 0)
# `__libc_start_call_main` is usually smaller than `__libc_start_main`, so
# we might disassemble a bit too much, but it's a good dynamic estimate.
callee_lines = self.disasm(target_addr, self.functions['__libc_start_main'].size).split('\n')
callee_calls = [(index, line) for index, line in enumerate(callee_lines) if set(line.split()) & call_instructions]
ret_addr = find_ret_main_addr(callee_lines, callee_calls)
if ret_addr:
return ret_addr
return 0
def search(self, needle, writable = False, executable = False):
"""search(needle, writable = False, executable = False) -> generator
@@ -1129,7 +1155,7 @@ class ELF(ELFFile):
won't work.
Arguments:
needle(str): String to search for.
needle(bytes): String to search for.
writable(bool): Search only writable sections.
executable(bool): Search only executable sections.
@@ -1165,13 +1191,14 @@ class ELF(ELFFile):
segments = self.executable_segments
else:
segments = self.segments
needle = packing._need_bytes(needle)
for seg in segments:
addr = seg.header.p_vaddr
memsz = seg.header.p_memsz
zeroed = memsz - seg.header.p_filesz
filesz = seg.header.p_filesz
zeroed = memsz - filesz
offset = seg.header.p_offset
data = self.mmap[offset:offset+memsz]
data = self.mmap[offset:offset+filesz]
data += b'\x00' * zeroed
offset = 0
while True:
@@ -1310,7 +1337,7 @@ class ELF(ELFFile):
count(int): Number of bytes to read
Returns:
A :class:`str` object, or :const:`None`.
A :class:`bytes` object, or :const:`None`.
Examples:
The simplest example is just to read the ELF header.
@@ -1471,7 +1498,7 @@ class ELF(ELFFile):
@property
def data(self):
""":class:`str`: Raw data of the ELF file.
""":class:`bytes`: Raw data of the ELF file.
See:
:meth:`get_data`
@@ -1499,7 +1526,7 @@ class ELF(ELFFile):
This modifies the ELF in-place.
The resulting binary can be saved with :meth:`.ELF.save`
"""
binary = asm(assembly, vma=address)
binary = asm(assembly, vma=address, arch=self.arch, endian=self.endian, bits=self.bits)
self.write(address, binary)
def bss(self, offset=0):
@@ -1617,7 +1644,7 @@ class ELF(ELFFile):
.. _page 81: https://refspecs.linuxbase.org/elf/elf.pdf#page=81
.. _DT_BIND_NOW: https://refspecs.linuxbase.org/elf/elf.pdf#page=81
.. _PT_GNU_RELRO: https://refspecs.linuxbase.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic.html#PROGHEADER
.. _DF_BIND_NOW: http://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html#df_bind_now
.. _DF_BIND_NOW: https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html#df_bind_now
>>> path = pwnlib.data.elf.relro.path
>>> for test in glob(os.path.join(path, 'test-*')):
@@ -1649,61 +1676,244 @@ class ELF(ELFFile):
Specifically, we are checking for ``READ_IMPLIES_EXEC`` being set
by the kernel, as a result of honoring ``PT_GNU_STACK`` in the kernel.
The **Linux kernel** directly honors ``PT_GNU_STACK`` to `mark the
stack as executable.`__
``READ_IMPLIES_EXEC`` is set, according to a set of architecture specific
rules, that depend on the CPU features, and the presence of ``PT_GNU_STACK``.
.. __: https://github.com/torvalds/linux/blob/v4.9/fs/binfmt_elf.c#L784-L789
Unfortunately, :class:`ELF` is not context-aware, so it's not always possible
to determine whether the process of a binary that's missing ``PT_GNU_STACK``
will have NX or not.
The rules are as follows:
+-----------+--------------+---------------------------+------------------------------------------------+----------+
| ELF arch | linux | GNU_STACK | other | NX |
+===========+==============+===========================+================================================+==========+
| i386 | < 5.8 | non-exec | | enabled |
| | [#x86_5.7]_ +---------------------------+------------------------------------------------+----------+
| | | exec / missing | | disabled |
| +--------------+---------------------------+------------------------------------------------+----------+
| | >= 5.8 | exec / non-exec | | enabled |
| | [#x86_5.8]_ +---------------------------+------------------------------------------------+----------+
| | | missing | | disabled |
+-----------+--------------+---------------------------+------------------------------------------------+----------+
| amd64 | < 5.8 | non-exec | | enabled |
| | [#x86_5.7]_ +---------------------------+------------------------------------------------+----------+
| | | exec / missing | | disabled |
| +--------------+---------------------------+------------------------------------------------+----------+
| | >= 5.8 | exec / non-exec / missing | | enabled |
| | [#x86_5.8]_ | | | |
+-----------+--------------+---------------------------+------------------------------------------------+----------+
| arm | < 5.8 | non-exec* | | enabled |
| | [#arm_5.7]_ +---------------------------+------------------------------------------------+----------+
| | | exec / missing | | disabled |
| +--------------+---------------------------+------------------------------------------------+----------+
| | >= 5.8 | exec / non-exec* | | enabled |
| | [#arm_5.8]_ +---------------------------+------------------------------------------------+----------+
| | | missing | | disabled |
+-----------+--------------+---------------------------+------------------------------------------------+----------+
| mips | < 5.18 | non-exec* | | enabled |
| | [#mips_5.17]_+---------------------------+------------------------------------------------+----------+
| | | exec / missing | | disabled |
| +--------------+---------------------------+------------------------------------------------+----------+
| | >= 5.18 | exec / non-exec* | | enabled |
| | [#mips_5.18]_+---------------------------+------------------------------------------------+----------+
| | | missing | | disabled |
+-----------+--------------+---------------------------+------------------------------------------------+----------+
| powerpc | [#powerpc]_ | non-exec / exec | | enabled |
| | +---------------------------+------------------------------------------------+----------+
| | | missing | | disabled |
+-----------+--------------+---------------------------+------------------------------------------------+----------+
| powerpc64 | [#powerpc]_ | exec / non-exec / missing | | enabled |
+-----------+--------------+---------------------------+------------------------------------------------+----------+
| ia64 | [#ia64]_ | non-exec | | enabled |
| | +---------------------------+------------------------------------------------+----------+
| | | exec / missing | e_flags & EF_IA_64_LINUX_EXECUTABLE_STACK == 0 | enabled |
| | + +------------------------------------------------+----------+
| | | | e_flags & EF_IA_64_LINUX_EXECUTABLE_STACK != 0 | disabled |
+-----------+--------------+---------------------------+------------------------------------------------+----------+
| the rest | [#the_rest]_ | exec / non-exec / missing | | enabled |
+-----------+--------------+---------------------------+------------------------------------------------+----------+
\\* Hardware limitations are ignored.
If ``READ_IMPLIES_EXEC`` is set, then `all readable pages are executable`__.
.. __: https://github.com/torvalds/linux/blob/v6.3/fs/binfmt_elf.c#L1008-L1009
.. code-block:: c
if (elf_read_implies_exec(loc->elf_ex, executable_stack))
current->personality |= READ_IMPLIES_EXEC;
.. [#x86_5.7]
`source <https://github.com/torvalds/linux/blob/v5.7/arch/x86/include/asm/elf.h#L285-L286>`__
.. code-block:: c
#define elf_read_implies_exec(ex, executable_stack) \\
(executable_stack != EXSTACK_DISABLE_X)
.. [#x86_5.8]
`source <https://github.com/torvalds/linux/blob/v5.8/arch/x86/include/asm/elf.h#L305-L306>`__
.. code-block:: c
#define elf_read_implies_exec(ex, executable_stack) \\
(mmap_is_ia32() && executable_stack == EXSTACK_DEFAULT)
`mmap_is_ia32()`__:
.. __: https://github.com/torvalds/linux/blob/v5.8/arch/x86/include/asm/elf.h#L318-L321
.. code-block:: c
/*
* True on X86_32 or when emulating IA32 on X86_64
*/
static inline int mmap_is_ia32(void)
.. [#arm_5.7]
`source <https://github.com/torvalds/linux/blob/v5.7/arch/arm/kernel/elf.c#L85-L92>`__
.. code-block:: c
int arm_elf_read_implies_exec(int executable_stack)
{
if (executable_stack != EXSTACK_DISABLE_X)
return 1;
if (cpu_architecture() < CPU_ARCH_ARMv6)
return 1;
return 0;
}
.. [#arm_5.8]
`source <https://github.com/torvalds/linux/blob/v5.8/arch/arm/kernel/elf.c#L104-L111>`__
.. code-block:: c
int arm_elf_read_implies_exec(int executable_stack)
{
if (executable_stack == EXSTACK_DEFAULT)
return 1;
if (cpu_architecture() < CPU_ARCH_ARMv6)
return 1;
return 0;
}
.. [#mips_5.17]
`source <https://github.com/torvalds/linux/blob/v5.17/arch/mips/kernel/elf.c#L329-L342>`__
.. code-block:: c
int mips_elf_read_implies_exec(void *elf_ex, int exstack)
{
if (exstack != EXSTACK_DISABLE_X) {
/* The binary doesn't request a non-executable stack */
return 1;
}
if (!cpu_has_rixi) {
/* The CPU doesn't support non-executable memory */
return 1;
}
return 0;
}
.. [#mips_5.18]
`source <https://github.com/torvalds/linux/blob/v5.18/arch/mips/kernel/elf.c#L329-L336>`__
.. code-block:: c
int mips_elf_read_implies_exec(void *elf_ex, int exstack)
{
/*
* Set READ_IMPLIES_EXEC only on non-NX systems that
* do not request a specific state via PT_GNU_STACK.
*/
return (!cpu_has_rixi && exstack == EXSTACK_DEFAULT);
}
.. [#powerpc]
`source <https://github.com/torvalds/linux/blob/v6.3/arch/powerpc/include/asm/elf.h#L82-L108>`__
.. code-block:: c
#ifdef __powerpc64__
/* stripped */
# define elf_read_implies_exec(ex, exec_stk) (is_32bit_task() ? \\
(exec_stk == EXSTACK_DEFAULT) : 0)
#else
# define elf_read_implies_exec(ex, exec_stk) (exec_stk == EXSTACK_DEFAULT)
#endif /* __powerpc64__ */
.. [#ia64]
`source <https://github.com/torvalds/linux/blob/v6.3/arch/ia64/include/asm/elf.h#L203-L204>`__
.. code-block:: c
#define elf_read_implies_exec(ex, executable_stack) \\
((executable_stack!=EXSTACK_DISABLE_X) && ((ex).e_flags & EF_IA_64_LINUX_EXECUTABLE_STACK) != 0)
EF_IA_64_LINUX_EXECUTABLE_STACK__:
.. __: https://github.com/torvalds/linux/blob/v6.3/arch/ia64/include/asm/elf.h#L33
.. code-block:: c
#define EF_IA_64_LINUX_EXECUTABLE_STACK 0x1 /* is stack (& heap) executable by default? */
.. [#the_rest]
`source <https://github.com/torvalds/linux/blob/v6.3/include/linux/elf.h#L13>`__
.. code-block:: c
# define elf_read_implies_exec(ex, have_pt_gnu_stack) 0
"""
if not self.executable:
return True
exec_bit = None
for seg in self.iter_segments_by_type('GNU_STACK'):
exec_bit = bool(seg.header.p_flags & P_FLAGS.PF_X)
non_exec = exec_bit is False
missing = exec_bit is None
EF_IA_64_LINUX_EXECUTABLE_STACK = 1
if self.arch in ['i386', 'arm', 'aarch64', 'mips', 'mips64']:
if non_exec:
return True
elif missing:
return False
return None
elif self.arch == 'amd64':
return True if non_exec else None
elif self.arch == 'powerpc':
return not missing
elif self.arch == 'powerpc64':
return True
elif self.arch == 'ia64':
if non_exec:
return True
return not bool(self['e_flags'] & EF_IA_64_LINUX_EXECUTABLE_STACK)
return True
@property
def execstack(self):
""":class:`bool`: Whether dynamically loading the current binary will make the stack executable.
This is based on the presence of a program header ``PT_GNU_STACK``,
its setting, and the default stack permissions for the architecture.
If ``PT_GNU_STACK`` is persent, the stack permissions are `set according to it`__:
.. __: https://github.com/bminor/glibc/blob/glibc-2.37/elf/dl-load.c#L1218-L1220
.. code-block:: c
case PT_GNU_STACK:
if (elf_ppnt->p_flags & PF_X)
executable_stack = EXSTACK_ENABLE_X;
else
executable_stack = EXSTACK_DISABLE_X;
break;
stack_flags = ph->p_flags;
break;
Additionally, it then sets ``read_implies_exec``, so that `all readable pages
are executable`__.
Else, the stack permissions are set according to the architecture defaults
as `defined by`__ ``DEFAULT_STACK_PERMS``:
.. __: https://github.com/torvalds/linux/blob/v4.9/fs/binfmt_elf.c#L849-L850
.. code-block:: c
if (elf_read_implies_exec(loc->elf_ex, executable_stack))
current->personality |= READ_IMPLIES_EXEC;
"""
if not self.executable:
return True
for seg in self.iter_segments_by_type('GNU_STACK'):
return not bool(seg.header.p_flags & P_FLAGS.PF_X)
# If you NULL out the PT_GNU_STACK section via ELF.disable_nx(),
# everything is executable.
return False
@property
def execstack(self):
""":class:`bool`: Whether the current binary uses an executable stack.
This is based on the presence of a program header PT_GNU_STACK_
being present, and its setting.
``PT_GNU_STACK``
The p_flags member specifies the permissions on the segment
containing the stack and is used to indicate whether the stack
should be executable. The absense of this header indicates
that the stack will be executable.
In particular, if the header is missing the stack is executable.
If the header is present, it may **explicitly** mark that the stack is
executable.
This is only somewhat accurate. When using the GNU Linker, it usees
DEFAULT_STACK_PERMS_ to decide whether a lack of ``PT_GNU_STACK``
should mark the stack as executable:
.. __: https://github.com/bminor/glibc/blob/glibc-2.37/elf/dl-load.c#L1093-L1096
.. code-block:: c
@@ -1718,29 +1928,25 @@ class ELF(ELFFile):
::
$ git grep '#define DEFAULT_STACK_PERMS' | grep -v PF_X
sysdeps/aarch64/stackinfo.h:31:#define DEFAULT_STACK_PERMS (PF_R|PF_W)
sysdeps/nios2/stackinfo.h:31:#define DEFAULT_STACK_PERMS (PF_R|PF_W)
sysdeps/tile/stackinfo.h:31:#define DEFAULT_STACK_PERMS (PF_R|PF_W)
.. _PT_GNU_STACK: https://refspecs.linuxbase.org/LSB_3.0.0/LSB-PDA/LSB-PDA/progheader.html
.. _DEFAULT_STACK_PERMS: https://github.com/bminor/glibc/blob/glibc-2.25/elf/dl-load.c#L1036-L1038
sysdeps/aarch64/stackinfo.h: #define DEFAULT_STACK_PERMS (PF_R|PF_W)
sysdeps/arc/stackinfo.h: #define DEFAULT_STACK_PERMS (PF_R|PF_W)
sysdeps/csky/stackinfo.h: #define DEFAULT_STACK_PERMS (PF_R|PF_W)
sysdeps/ia64/stackinfo.h: #define DEFAULT_STACK_PERMS (PF_R|PF_W)
sysdeps/loongarch/stackinfo.h: #define DEFAULT_STACK_PERMS (PF_R | PF_W)
sysdeps/nios2/stackinfo.h: #define DEFAULT_STACK_PERMS (PF_R|PF_W)
sysdeps/riscv/stackinfo.h: #define DEFAULT_STACK_PERMS (PF_R | PF_W)
"""
# Dynamic objects do not have the ability to change the executable state of the stack.
if not self.executable:
return False
# If NX is completely off for the process, the stack is executable.
if not self.nx:
return True
# If the ``PT_GNU_STACK`` program header is preset, use it's premissions.
for seg in self.iter_segments_by_type('GNU_STACK'):
return bool(seg.header.p_flags & P_FLAGS.PF_X)
# If the ``PT_GNU_STACK`` program header is missing, then use the
# default rules. Only AArch64 gets a non-executable stack by default.
for _ in self.iter_segments_by_type('GNU_STACK'):
break
else:
return self.arch != 'aarch64'
return False
# default rules. Out of the supported architectures, only AArch64,
# IA-64, and RISC-V get a non-executable stack by default.
return self.arch not in ['aarch64', 'ia64', 'riscv32', 'riscv64']
@property
def canary(self):
@@ -1753,7 +1959,7 @@ class ELF(ELFFile):
@property
def packed(self):
""":class:`bool`: Whether the current binary is packed with UPX."""
return b'UPX!' in self.get_data()
return b'UPX!' in self.get_data()[:0xFF]
@property
def pie(self):
@@ -1815,6 +2021,7 @@ class ELF(ELFFile):
"NX:".ljust(10) + {
True: green("NX enabled"),
False: red("NX disabled"),
None: yellow("NX unknown - GNU_STACK missing"),
}[self.nx],
"PIE:".ljust(10) + {
True: green("PIE enabled"),
@@ -1823,7 +2030,7 @@ class ELF(ELFFile):
])
# Execstack may be a thing, even with NX enabled, because of glibc
if self.execstack and self.nx:
if self.execstack and self.nx is not False:
res.append("Stack:".ljust(10) + red("Executable"))
# Are there any RWX areas in the binary?
@@ -1879,7 +2086,7 @@ class ELF(ELFFile):
@property
def buildid(self):
""":class:`str`: GNU Build ID embedded into the binary"""
""":class:`bytes`: GNU Build ID embedded into the binary"""
section = self.get_section_by_name('.note.gnu.build-id')
if section:
return section.data()[16:]
@@ -2035,6 +2242,127 @@ class ELF(ELFFile):
if self.mmap[offset:offset+4] == PT_GNU_STACK:
self.mmap[offset:offset+4] = b'\x00' * 4
self.save()
# Invalidate the cached segments, ``PT_GNU_STACK`` was removed.
self._segments = None
return
log.error("Could not find PT_GNU_STACK, stack should already be executable")
@staticmethod
def set_runpath(exepath, runpath):
r"""set_runpath(str, str) -> ELF
Patches the RUNPATH of the ELF to the given path using the `patchelf utility <https://github.com/NixOS/patchelf>`_.
The dynamic loader will look for any needed shared libraries in the given path first,
before trying the system library paths. This is useful to run a binary with a different
libc binary.
Arguments:
exepath(str): Path to the binary to patch.
runpath(str): Path containing the needed libraries.
Returns:
A new ELF instance is returned after patching the binary with the external ``patchelf`` tool.
Example:
>>> tmpdir = tempfile.mkdtemp()
>>> ls_path = os.path.join(tmpdir, 'ls')
>>> _ = shutil.copy(which('ls'), ls_path)
>>> e = ELF.set_runpath(ls_path, './libs')
>>> e.runpath == b'./libs'
True
"""
if not which('patchelf'):
log.error('"patchelf" tool not installed. See https://github.com/NixOS/patchelf')
return None
try:
subprocess.check_output(['patchelf', '--set-rpath', runpath, exepath], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
log.failure('Patching RUNPATH failed (%d): %r', e.returncode, e.stdout)
return ELF(exepath, checksec=False)
@staticmethod
def set_interpreter(exepath, interpreter_path):
r"""set_interpreter(str, str) -> ELF
Patches the interpreter of the ELF to the given binary using the `patchelf utility <https://github.com/NixOS/patchelf>`_.
When running the binary, the new interpreter will be used to load the ELF.
Arguments:
exepath(str): Path to the binary to patch.
interpreter_path(str): Path to the ld.so dynamic loader.
Returns:
A new ELF instance is returned after patching the binary with the external ``patchelf`` tool.
Example:
>>> tmpdir = tempfile.mkdtemp()
>>> ls_path = os.path.join(tmpdir, 'ls')
>>> _ = shutil.copy(which('ls'), ls_path)
>>> e = ELF.set_interpreter(ls_path, '/tmp/correct_ld.so')
>>> e.linker == b'/tmp/correct_ld.so'
True
"""
# patch the interpreter
if not which('patchelf'):
log.error('"patchelf" tool not installed. See https://github.com/NixOS/patchelf')
return None
try:
subprocess.check_output(['patchelf', '--set-interpreter', interpreter_path, exepath], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
log.failure('Patching interpreter failed (%d): %r', e.returncode, e.stdout)
return ELF(exepath, checksec=False)
@staticmethod
def patch_custom_libraries(exe_path, custom_library_path, create_copy=True, suffix='_remotelibc'):
r"""patch_custom_libraries(str, str, bool, str) -> ELF
Looks for the interpreter binary in the given path and patches the binary to use
it if available. Also patches the RUNPATH to the given path using the `patchelf utility <https://github.com/NixOS/patchelf>`_.
Arguments:
exe_path(str): Path to the binary to patch.
custom_library_path(str): Path to a folder containing the libraries.
create_copy(bool): Create a copy of the binary and apply the patches to the copy.
suffix(str): Suffix to append to the filename when creating the copy to patch.
Returns:
A new ELF instance is returned after patching the binary with the external ``patchelf`` tool.
Example:
>>> tmpdir = tempfile.mkdtemp()
>>> linker_path = os.path.join(tmpdir, 'ld-mock.so')
>>> write(linker_path, b'loader')
>>> ls_path = os.path.join(tmpdir, 'ls')
>>> _ = shutil.copy(which('ls'), ls_path)
>>> e = ELF.patch_custom_libraries(ls_path, tmpdir)
>>> e.runpath.decode() == tmpdir
True
>>> e.linker.decode() == linker_path
True
"""
if not which('patchelf'):
log.error('"patchelf" tool not installed. See https://github.com/NixOS/patchelf')
return None
# Create a copy of the ELF to patch instead of the original file.
if create_copy:
import shutil
patched_path = exe_path + suffix
shutil.copy2(exe_path, patched_path)
exe_path = patched_path
# Set interpreter in ELF to the one in the library path.
interpreter_name = [filename for filename in os.listdir(custom_library_path) if filename.startswith('ld-')]
if interpreter_name:
interpreter_path = os.path.realpath(os.path.join(custom_library_path, interpreter_name[0]))
ELF.set_interpreter(exe_path, interpreter_path)
else:
log.warn("Couldn't find ld.so in library path. Interpreter not set.")
# Set RUNPATH to library path in order to find other libraries.
return ELF.set_runpath(exe_path, custom_library_path)
+3 -12
View File
@@ -64,8 +64,11 @@ def prepare_unicorn_and_context(elf, got, address, data):
'arm': U.UC_ARCH_ARM,
'i386': U.UC_ARCH_X86,
'mips': U.UC_ARCH_MIPS,
'mips64': U.UC_ARCH_MIPS,
# 'powerpc': U.UC_ARCH_PPC, <-- Not actually supported
'thumb': U.UC_ARCH_ARM,
'riscv32': U.UC_ARCH_RISCV,
'riscv64': U.UC_ARCH_RISCV,
}.get(elf.arch, None)
if arch is None:
@@ -119,18 +122,6 @@ def prepare_unicorn_and_context(elf, got, address, data):
uc.mem_write(got, p_magic)
# Separately, Unicorn is apparently unable to hook unmapped memory
# accesses on MIPS. So we also have to map the page that contains
# the magic address.
start = magic_addr & (~0xfff)
try:
uc.mem_map(start, 0x1000)
except Exception:
# Ignore double-mapping
pass
trap = packing.p32(0x34000000, endian=elf.endian)
uc.mem_write(magic_addr, trap)
return uc, uc.context_save()
@@ -1,3 +1,5 @@
# Copyright (c) 2013 Pratik Kumar Sahu, Nagendra Chowdary, Anish Mathuria
# Ported to Python by Gallopsled
from __future__ import division
# +------------------------------------------------------------------------+
@@ -1,3 +1,5 @@
# Copyright (c) 2013 Pratik Kumar Sahu, Nagendra Chowdary, Anish Mathuria
# Ported to Python by Gallopsled
from __future__ import absolute_import
from __future__ import division
@@ -1,3 +1,5 @@
# Copyright (c) 2013 Pratik Kumar Sahu, Nagendra Chowdary, Anish Mathuria
# Ported to Python by Gallopsled
from __future__ import division
from __future__ import absolute_import
@@ -1,3 +1,5 @@
# Copyright (c) 2013 Pratik Kumar Sahu, Nagendra Chowdary, Anish Mathuria
# Ported to Python by Gallopsled
from __future__ import absolute_import
from __future__ import division
@@ -1,3 +1,5 @@
# Copyright (c) 2013 Pratik Kumar Sahu, Nagendra Chowdary, Anish Mathuria
# Ported to Python by Gallopsled
from __future__ import division
import os
+2 -2
View File
@@ -21,10 +21,10 @@ class AsciiShellcodeEncoder(Encoder):
executes (on the stack)
The original paper this encoder is based on:
http://julianor.tripod.com/bc/bypass-msb.txt
https://julianor.tripod.com/bc/bypass-msb.txt
A more visual explanation as well as an implementation in C:
https://github.com/VincentDary/PolyAsciiShellGen/blob/master/README.md#mechanism
https://vincentdary.github.io/blog-posts/polyasciishellgen-caezar-ascii-shellcode-generator/index.html#22-mechanism
"""
def __init__(self, slop=20, max_subs=4):
+10 -14
View File
@@ -125,7 +125,7 @@ class FileStructure(object):
>>> len(fileStr)
224
The defination for __repr__ orders the structure members and displays then in a dictionary format. It's useful when viewing a structure objet in python/IPython shell
The definition for __repr__ orders the structure members and displays then in a dictionary format. It's useful when viewing a structure objet in python/IPython shell
>>> q=FileStructure(0xdeadbeef)
>>> q
@@ -176,24 +176,20 @@ class FileStructure(object):
def __repr__(self):
structure=[]
for i in self.vars_:
structure.append(" %s: %s" % (i,hex(self.__getattr__(i))))
structure.append(" %s: %#x" % (i, getattr(self, i)))
return "{"+ "\n".join(structure)+"}"
def __getattr__(self,item):
if item in FileStructure.__dict__ or item in self.vars_:
return object.__getattribute__(self,item)
log.error("Unknown variable %r" % item)
def __len__(self):
return len(bytes(self))
def __bytes__(self):
structure = b''
for val in self.vars_:
if isinstance(self.__getattr__(val), bytes):
structure += self.__getattr__(val).ljust(context.bytes, b'\x00')
if isinstance(getattr(self, val), bytes):
structure += getattr(self, val).ljust(context.bytes, b'\x00')
else:
structure += pack(self.__getattr__(val), self.length[val]*8)
if self.length[val] > 0:
structure += pack(getattr(self, val), self.length[val]*8)
return structure
def struntil(self,v):
@@ -218,10 +214,10 @@ class FileStructure(object):
return b''
structure = b''
for val in self.vars_:
if isinstance(self.__getattr__(val), bytes):
structure += self.__getattr__(val).ljust(context.bytes, b'\x00')
if isinstance(getattr(self, val), bytes):
structure += getattr(self, val).ljust(context.bytes, b'\x00')
else:
structure += pack(self.__getattr__(val), self.length[val]*8)
structure += pack(getattr(self, val), self.length[val]*8)
if val == v:
break
return structure[:-1]
@@ -313,7 +309,7 @@ class FileStructure(object):
def orange(self,io_list_all,vtable):
r"""
Perform a House of Orange (https://github.com/shellphish/how2heap/blob/master/glibc_2.25/house_of_orange.c), provided you have libc leaks.
Perform a House of Orange (https://github.com/shellphish/how2heap/blob/master/glibc_2.23/house_of_orange.c), provided you have libc leaks.
Arguments:
io_list_all(int)
+89 -17
View File
@@ -285,9 +285,15 @@ def make_atoms_simple(address, data, badbytes=frozenset()):
This function is simple and does not try to minimize the number of atoms. For example, if there are no
bad bytes, it simply returns one atom for each byte:
>>> pwnlib.fmtstr.make_atoms_simple(0x0, b"abc", set())
[AtomWrite(start=0, size=1, integer=0x61, mask=0xff), AtomWrite(start=1, size=1, integer=0x62, mask=0xff), AtomWrite(start=2, size=1, integer=0x63, mask=0xff)]
>>> pwnlib.fmtstr.make_atoms_simple(0x0, b"abc", set())
[AtomWrite(start=0, size=1, integer=0x61, mask=0xff), AtomWrite(start=1, size=1, integer=0x62, mask=0xff), AtomWrite(start=2, size=1, integer=0x63, mask=0xff)]
If there are bad bytes, it will try to bypass by skipping addresses containing bad bytes, otherwise a
RuntimeError will be raised:
>>> pwnlib.fmtstr.make_atoms_simple(0x61, b'abc', b'\x62')
[AtomWrite(start=97, size=2, integer=0x6261, mask=0xffff), AtomWrite(start=99, size=1, integer=0x63, mask=0xff)]
>>> pwnlib.fmtstr.make_atoms_simple(0x61, b'a'*0x10, b'\x62\x63\x64\x65\x66\x67\x68')
[AtomWrite(start=97, size=8, integer=0x6161616161616161, mask=0xffffffffffffffff), AtomWrite(start=105, size=1, integer=0x61, mask=0xff), AtomWrite(start=106, size=1, integer=0x61, mask=0xff), AtomWrite(start=107, size=1, integer=0x61, mask=0xff), AtomWrite(start=108, size=1, integer=0x61, mask=0xff), AtomWrite(start=109, size=1, integer=0x61, mask=0xff), AtomWrite(start=110, size=1, integer=0x61, mask=0xff), AtomWrite(start=111, size=1, integer=0x61, mask=0xff), AtomWrite(start=112, size=1, integer=0x61, mask=0xff)]
"""
data = bytearray(data)
if not badbytes:
@@ -298,14 +304,15 @@ def make_atoms_simple(address, data, badbytes=frozenset()):
i = 0
out = []
end = address + len(data)
while i < len(data):
candidate = AtomWrite(address + i, 1, data[i])
while candidate.end < len(data) and any(x in badbytes for x in pack(candidate.end)):
while candidate.end < end and any(x in badbytes for x in pack(candidate.end)):
candidate = candidate.union(AtomWrite(candidate.end, 1, data[i + candidate.size]))
sz = min([s for s in SPECIFIER if s >= candidate.size] + [float("inf")])
if candidate.start + sz > len(data):
raise RuntimeError("impossible to avoid badbytes starting after offset %d (address %x)" % (i, i + address))
if candidate.start + sz > end:
raise RuntimeError("impossible to avoid badbytes starting after offset %d (address %#x)" % (i, i + address))
i += candidate.size
candidate = candidate.union(AtomWrite(candidate.end, sz - candidate.size, 0, 0))
out.append(candidate)
@@ -492,7 +499,7 @@ def merge_atoms_overlapping(atoms, sz, szmax, numbwritten, overflows):
# the best write is the one which sets the largest number of target
# bytes correctly
candidate = AtomWrite(atom.start, 0, 0)
best = (0, None)
best = (atom.size, idx, atom)
for nextidx, nextatom in enumerate(atoms[idx:], idx):
# if there is no atom immediately following the current candidate
# that we haven't written yet, stop
@@ -521,6 +528,8 @@ def merge_atoms_overlapping(atoms, sz, szmax, numbwritten, overflows):
_, nextidx, best_candidate = best
numbwritten_here += best_candidate.compute_padding(numbwritten_here)
if numbwritten_here > maxwritten:
maxwritten = numbwritten_here
offset = 0
# for all atoms that we merged, check if all bytes are written already to update `done``
@@ -669,7 +678,7 @@ def sort_atoms(atoms, numbwritten):
return out
def make_payload_dollar(data_offset, atoms, numbwritten=0, countersize=4):
def make_payload_dollar(data_offset, atoms, numbwritten=0, countersize=4, no_dollars=False):
r'''
Makes a format-string payload using glibc's dollar syntax to access the arguments.
@@ -682,6 +691,7 @@ def make_payload_dollar(data_offset, atoms, numbwritten=0, countersize=4):
atoms(list): list of atoms to execute
numbwritten(int): number of byte already written by the printf function
countersize(int): size in bytes of the format string counter (usually 4)
no_dollars(bool) : flag to generete the payload with or w/o $ notation
Examples:
>>> pwnlib.fmtstr.make_payload_dollar(1, [pwnlib.fmtstr.AtomWrite(0x0, 0x1, 0xff)])
@@ -691,6 +701,13 @@ def make_payload_dollar(data_offset, atoms, numbwritten=0, countersize=4):
fmt = ""
counter = numbwritten
if no_dollars:
# since we can't dynamically offset, we have to increment manually the parameter index, use %c, so the number of bytes written is predictable
fmt += "%c" * (data_offset - 1)
# every %c write a byte, so we need to keep track of that to have the right pad
counter += data_offset - 1
for idx, atom in enumerate(atoms):
# set format string counter to correct value
padding = atom.compute_padding(counter)
@@ -701,9 +718,54 @@ def make_payload_dollar(data_offset, atoms, numbwritten=0, countersize=4):
log.warn("padding is negative, this will not work on glibc")
# perform write
if padding:
# if the padding is less than 3, it is more convenient to write it : [ len("cc") < len("%2c") ] , this could help save some bytes, if it is 3 it will take the same amout of bytes
# we also add ( context.bytes * no_dollars ) because , "%nccccccccc%n...ptr1ptr2" is more convenient than %"n%8c%n...ptr1ccccccccptr2"
if padding < 4 + context.bytes * no_dollars:
fmt += "c" * padding
## if do not padded with %{n}c do not need to add something in data to use as argument, since we are not using a printf argument
else:
fmt += "%" + str(padding) + "c"
fmt += "%" + str(data_offset + idx) + "$" + SPECIFIER[atom.size]
if no_dollars:
data += b'c' * context.bytes
'''
[ @murph12F was here ]
the data += b'c' * context.bytes , is used to keey the arguments aligned when a %c is performed, so it wont use the actual address to write at
examplea stack and payload:
fmtsr = %44c%hhn%66c%hhn
---------
| addr2 |
---------
| 0x000 |
---------
| addr1 |
---------
| 0x000 | <-- (rsp)
---------
in this case the the first %44c will use the current arugument used pointed by rsp ( 0 ), and increment rsp
---------
| addr2 |
---------
| 0X000 |
---------
| addr1 | <-- (rsp)
---------
| 0x000 |
---------
now it will perform the %hhn, and it will correctly use the addr1 argument
'''
if no_dollars:
fmt += "%" + SPECIFIER[atom.size]
else:
fmt += "%" + str(data_offset + idx) + "$" + SPECIFIER[atom.size]
data += pack(atom.start)
return fmt.encode(), data
@@ -740,7 +802,7 @@ def make_atoms(writes, sz, szmax, numbwritten, overflows, strategy, badbytes):
all_atoms += atoms
return all_atoms
def fmtstr_split(offset, writes, numbwritten=0, write_size='byte', write_size_max='long', overflows=16, strategy="small", badbytes=frozenset()):
def fmtstr_split(offset, writes, numbwritten=0, write_size='byte', write_size_max='long', overflows=16, strategy="small", badbytes=frozenset(), no_dollars=False):
"""
Build a format string like fmtstr_payload but return the string and data separately.
"""
@@ -754,9 +816,9 @@ def fmtstr_split(offset, writes, numbwritten=0, write_size='byte', write_size_ma
szmax = WRITE_SIZE[write_size_max]
atoms = make_atoms(writes, sz, szmax, numbwritten, overflows, strategy, badbytes)
return make_payload_dollar(offset, atoms, numbwritten)
return make_payload_dollar(offset, atoms, numbwritten, no_dollars=no_dollars)
def fmtstr_payload(offset, writes, numbwritten=0, write_size='byte', write_size_max='long', overflows=16, strategy="small", badbytes=frozenset(), offset_bytes=0):
def fmtstr_payload(offset, writes, numbwritten=0, write_size='byte', write_size_max='long', overflows=16, strategy="small", badbytes=frozenset(), offset_bytes=0, no_dollars=False):
r"""fmtstr_payload(offset, writes, numbwritten=0, write_size='byte') -> str
Makes payload with given parameter.
@@ -773,6 +835,7 @@ def fmtstr_payload(offset, writes, numbwritten=0, write_size='byte', write_size_
write_size(str): must be ``byte``, ``short`` or ``int``. Tells if you want to write byte by byte, short by short or int by int (hhn, hn or n)
overflows(int): how many extra overflows (at size sz) to tolerate to reduce the length of the format string
strategy(str): either 'fast' or 'small' ('small' is default, 'fast' can be used if there are many writes)
no_dollars(bool) : flag to generete the payload with or w/o $ notation
Returns:
The payload in order to do needed writes
@@ -784,6 +847,8 @@ def fmtstr_payload(offset, writes, numbwritten=0, write_size='byte', write_size_
b'%47806c%5$lln%22649c%6$hnaaaabaa\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00'
>>> fmtstr_payload(1, {0x0: 0x1337babe}, write_size='byte')
b'%190c%7$lln%85c%8$hhn%36c%9$hhn%131c%10$hhnaaaab\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00'
>>> fmtstr_payload(6, {0x8: 0x55d15d2004a0}, badbytes=b'\n')
b'%1184c%14$lln%49c%15$hhn%6963c%16$hn%81c%17$hhn%8c%18$hhnaaaabaa\x08\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x00\x00\x00\x00\r\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00'
>>> context.clear(arch = 'i386')
>>> fmtstr_payload(1, {0x0: 0x1337babe}, write_size='int')
b'%322419390c%5$na\x00\x00\x00\x00'
@@ -792,9 +857,15 @@ def fmtstr_payload(offset, writes, numbwritten=0, write_size='byte', write_size_
>>> fmtstr_payload(1, {0x0: 0x1337babe}, write_size='byte')
b'%19c%12$hhn%36c%13$hhn%131c%14$hhn%4c%15$hhn\x03\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00'
>>> fmtstr_payload(1, {0x0: 0x00000001}, write_size='byte')
b'%1c%3$na\x00\x00\x00\x00'
b'c%3$naaa\x00\x00\x00\x00'
>>> fmtstr_payload(1, {0x0: b"\xff\xff\x04\x11\x00\x00\x00\x00"}, write_size='short')
b'%327679c%7$lln%18c%8$hhn\x00\x00\x00\x00\x03\x00\x00\x00'
>>> fmtstr_payload(10, {0x404048 : 0xbadc0ffe, 0x40403c : 0xdeadbeef}, no_dollars=True)
b'%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%125c%hhn%17c%hhn%32c%hhn%17c%hhn%203c%hhn%34c%hhn%3618c%hnacccc>@@\x00cccc=@@\x00cccc?@@\x00cccc<@@\x00ccccK@@\x00ccccJ@@\x00ccccH@@\x00'
>>> fmtstr_payload(6, {0x404048 : 0xbadbad00}, no_dollars=True)
b'%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%229c%hhn%173c%hhn%13c%hhn%33c%hhnccccH@@\x00ccccI@@\x00ccccK@@\x00ccccJ@@\x00'
>>> fmtstr_payload(6, {0x4040 : 0xbadbad00, 0x4060: 0xbadbad02}, no_dollars=True)
b'%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%212c%hhn%173c%hhn%13c%hhn%33c%hhn%39c%hhn%171c%hhn%13c%hhn%33c%hhnacccc@@\x00\x00ccccA@\x00\x00ccccC@\x00\x00ccccB@\x00\x00cccc`@\x00\x00cccca@\x00\x00ccccc@\x00\x00ccccb@\x00\x00'
"""
sz = WRITE_SIZE[write_size]
szmax = WRITE_SIZE[write_size_max]
@@ -803,7 +874,7 @@ def fmtstr_payload(offset, writes, numbwritten=0, write_size='byte', write_size_
fmt = b""
for _ in range(1000000):
data_offset = (offset_bytes + len(fmt)) // context.bytes
fmt, data = make_payload_dollar(offset + data_offset, all_atoms, numbwritten=numbwritten)
fmt, data = make_payload_dollar(offset + data_offset, all_atoms, numbwritten=numbwritten, no_dollars=no_dollars)
fmt = fmt + cyclic((-len(fmt)-offset_bytes) % context.bytes)
if len(fmt) + offset_bytes == data_offset * context.bytes:
@@ -834,11 +905,12 @@ class FmtStr(object):
"""
def __init__(self, execute_fmt, offset=None, padlen=0, numbwritten=0):
def __init__(self, execute_fmt, offset=None, padlen=0, numbwritten=0, badbytes=frozenset()):
self.execute_fmt = execute_fmt
self.offset = offset
self.padlen = padlen
self.numbwritten = numbwritten
self.badbytes = badbytes
if self.offset is None:
self.offset, self.padlen = self.find_offset()
@@ -902,7 +974,7 @@ class FmtStr(object):
"""
fmtstr = randoms(self.padlen).encode()
fmtstr += fmtstr_payload(self.offset, self.writes, numbwritten=self.padlen + self.numbwritten, write_size='byte')
fmtstr += fmtstr_payload(self.offset, self.writes, numbwritten=self.padlen + self.numbwritten, badbytes=self.badbytes, write_size='byte')
self.execute_fmt(fmtstr)
self.writes = {}
+65 -13
View File
@@ -303,7 +303,7 @@ def _gdbserver_args(pid=None, path=None, args=None, which=None, env=None):
env_args.append(b'%s=%s' % (key, env.pop(key)))
else:
env_args.append(b'%s=%s' % (key, env[key]))
gdbserver_args += ['--wrapper', 'env', '-i'] + env_args + ['--']
gdbserver_args += ['--wrapper', which('env'), '-i'] + env_args + ['--']
gdbserver_args += ['localhost:0']
gdbserver_args += args
@@ -374,8 +374,10 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, api=
exe(str): Path to the executable on disk
env(dict): Environment to start the binary in
ssh(:class:`.ssh`): Remote ssh session to use to launch the process.
sysroot(str): Foreign-architecture sysroot, used for QEMU-emulated binaries
and Android targets.
sysroot(str): Set an alternate system root. The system root is used to
load absolute shared library symbol files. This is useful to instruct
gdb to load a local version of binaries/libraries instead of downloading
them from the gdbserver, which is faster
api(bool): Enable access to GDB Python API.
Returns:
@@ -568,7 +570,7 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, api=
gdbserver.executable = exe
# Find what port we need to connect to
if context.native or (context.os == 'android'):
if ssh or context.native or (context.os == 'android'):
port = _gdbserver_port(gdbserver, ssh)
else:
port = qemu_port
@@ -597,7 +599,9 @@ def get_gdb_arch():
'powerpc64': 'powerpc:common64',
'mips64': 'mips:isa64',
'thumb': 'arm',
'sparc64': 'sparc:v9'
'sparc64': 'sparc:v9',
'riscv32': 'riscv:rv32',
'riscv64': 'riscv:rv64',
}.get(context.arch, context.arch)
def binary():
@@ -663,6 +667,48 @@ class Breakpoint:
# Handle stop() call from the server.
return self.stop()
class FinishBreakpoint:
"""Mirror of ``gdb.FinishBreakpoint`` class.
See https://sourceware.org/gdb/onlinedocs/gdb/Finish-Breakpoints-in-Python.html
for more information.
"""
def __init__(self, conn, *args, **kwargs):
"""Do not create instances of this class directly.
Use ``pwnlib.gdb.Gdb.FinishBreakpoint`` instead.
"""
# Creates a real finish breakpoint and connects it with this mirror
self.conn = conn
self.server_breakpoint = conn.root.set_finish_breakpoint(
self, hasattr(self, 'stop'), hasattr(self, 'out_of_scope'),
*args, **kwargs)
def __getattr__(self, item):
"""Return attributes of the real breakpoint."""
if item in (
'____id_pack__',
'__name__',
'____conn__',
'stop',
'out_of_scope',
):
# Ignore RPyC netref attributes.
# Also, if stop() or out_of_scope() are not defined, hasattr() call
# in our __init__() will bring us here. Don't contact the
# server in this case either.
raise AttributeError()
return getattr(self.server_breakpoint, item)
def exposed_stop(self):
# Handle stop() call from the server.
return self.stop()
def exposed_out_of_scope(self):
# Handle out_of_scope() call from the server.
return self.out_of_scope()
class Gdb:
"""Mirror of ``gdb`` module.
@@ -680,8 +726,12 @@ class Gdb:
class _Breakpoint(Breakpoint):
def __init__(self, *args, **kwargs):
super().__init__(conn, *args, **kwargs)
class _FinishBreakpoint(FinishBreakpoint):
def __init__(self, *args, **kwargs):
super().__init__(conn, *args, **kwargs)
self.Breakpoint = _Breakpoint
self.FinishBreakpoint = _FinishBreakpoint
self.stopped = Event()
def stop_handler(event):
@@ -728,8 +778,10 @@ def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysr
arch(str): Architechture of the target binary. If `exe` known GDB will
detect the architechture automatically (if it is supported).
gdb_args(list): List of additional arguments to pass to GDB.
sysroot(str): Foreign-architecture sysroot, used for QEMU-emulated binaries
and Android targets.
sysroot(str): Set an alternate system root. The system root is used to
load absolute shared library symbol files. This is useful to instruct
gdb to load a local version of binaries/libraries instead of downloading
them from the gdbserver, which is faster
api(bool): Enable access to GDB Python API.
Returns:
@@ -868,17 +920,17 @@ def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysr
# gdb script to run before `gdbscript`
pre = ''
if sysroot:
pre += 'set sysroot %s\n' % sysroot
if not context.native:
pre += 'set endian %s\n' % context.endian
pre += 'set architecture %s\n' % get_gdb_arch()
if sysroot:
pre += 'set sysroot %s\n' % sysroot
if context.os == 'android':
pre += 'set gnutarget ' + _bfdname() + '\n'
if exe and context.os != 'baremetal':
pre += 'file %s\n' % exe
pre += 'file "%s"\n' % exe
# let's see if we can find a pid to attach to
pid = None
@@ -915,7 +967,7 @@ def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysr
cmd = ['sshpass', '-p', shell.password] + cmd
if shell.keyfile:
cmd += ['-i', shell.keyfile]
cmd += ['gdb', '-q', target.executable, target.pid, '-x', tmpfile]
cmd += ['gdb', '-q', target.executable, str(target.pid), '-x', tmpfile]
misc.run_in_new_terminal(cmd)
return
@@ -967,7 +1019,7 @@ def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysr
exe = exe or findexe()
elif isinstance(target, elf.corefile.Corefile):
pre += 'target core %s\n' % target.path
pre += 'target core "%s"\n' % target.path
else:
log.error("don't know how to attach to target: %r", target)
@@ -1329,7 +1381,7 @@ def version(program='gdb'):
Example:
>>> (7,0) <= gdb.version() <= (12,0)
>>> (7,0) <= gdb.version() <= (19,0)
True
"""
program = misc.which(program)
+11
View File
@@ -89,6 +89,17 @@ class GdbService(Service):
return Breakpoint(*args, **kwargs)
return gdb.Breakpoint(*args, **kwargs)
def exposed_set_finish_breakpoint(self, client, has_stop, has_out_of_scope, *args, **kwargs):
"""Create a finish breakpoint and connect it with the client-side mirror."""
class FinishBreakpoint(gdb.FinishBreakpoint):
if has_stop:
def stop(self):
return client.stop()
if has_out_of_scope:
def out_of_scope(self):
client.out_of_scope()
return FinishBreakpoint(*args, **kwargs)
def exposed_quit(self):
"""Terminate GDB."""
gdb.post_event(lambda: gdb.execute('quit'))
+1 -1
View File
@@ -38,7 +38,7 @@ class PwntoolsLexer(RegexLexer):
string = r'"(\\"|[^"])*"'
char = r'[\w$.@-]'
identifier = r'(?:[a-zA-Z$_]' + char + r'*|\.' + char + '+|or)'
number = r'(?:0[xX][a-zA-Z0-9]+|\d+)'
number = r'(?:-?0[xX][a-zA-Z0-9]+|\d+)'
memory = r'(?:[\]\[])'
bad = r'(?:\(bad\))'
+477 -54
View File
@@ -4,24 +4,30 @@ Fetch a LIBC binary based on some heuristics.
from __future__ import absolute_import
from __future__ import division
import codecs
import json
import os
import six
import tempfile
from pwnlib.context import context
from pwnlib.elf import ELF
from pwnlib.log import getLogger
from pwnlib.util.fiddling import b64d
from pwnlib.util.fiddling import hexdump
from pwnlib.tubes.process import process
from pwnlib.util.fiddling import enhex
from pwnlib.util.misc import read
from pwnlib.util.misc import which
from pwnlib.util.misc import write
from pwnlib.util.safeeval import const
from pwnlib.util.web import wget
log = getLogger(__name__)
HASHES = ['build_id', 'sha1', 'sha256', 'md5']
DEBUGINFOD_SERVERS = [
'https://debuginfod.elfutils.org/',
]
if 'DEBUGINFOD_URLS' in os.environ:
urls = os.environ['DEBUGINFOD_URLS'].split(' ')
DEBUGINFOD_SERVERS = urls + DEBUGINFOD_SERVERS
# https://gitlab.com/libcdb/libcdb wasn't updated after 2019,
# but still is a massive database of older libc binaries.
@@ -52,65 +58,57 @@ def provider_libcdb(hex_encoded_id, hash_type):
log.warn_once("Failed to fetch libc for %s %s from libcdb: %s", hash_type, hex_encoded_id, e)
return data
# https://libc.rip/
def provider_libc_rip(hex_encoded_id, hash_type):
def query_libc_rip(params):
# Deferred import because it's slow
import requests
url = "https://libc.rip/api/find"
try:
result = requests.post(url, json=params, timeout=20)
result.raise_for_status()
if result.status_code != 200:
log.debug("Error: %s", result.text)
return None
return result.json()
except requests.RequestException as e:
log.warn_once("Failed to fetch libc info from libc.rip: %s", e)
return None
# https://libc.rip/
def provider_libc_rip(hex_encoded_id, hash_type):
# Build the request for the hash type
# https://github.com/niklasb/libc-database/blob/master/searchengine/api.yml
if hash_type == 'build_id':
hash_type = 'buildid'
url = "https://libc.rip/api/find"
params = {hash_type: hex_encoded_id}
data = b""
try:
result = requests.post(url, json=params, timeout=20)
if result.status_code != 200 or len(result.json()) == 0:
log.warn_once("Could not find libc for %s %s on libc.rip", hash_type, hex_encoded_id)
log.debug("Error: %s", result.text)
return None
libc_match = query_libc_rip(params)
if not libc_match:
log.warn_once("Could not find libc info for %s %s on libc.rip", hash_type, hex_encoded_id)
return None
libc_match = result.json()
assert len(libc_match) == 1, 'Invalid libc.rip response.'
if len(libc_match) > 1:
log.debug("Received multiple matches. Choosing the first match and discarding the others.")
log.debug("%r", libc_match)
url = libc_match[0]['download_url']
log.debug("Downloading data from libc.rip: %s", url)
data = wget(url, timeout=20)
url = libc_match[0]['download_url']
log.debug("Downloading data from libc.rip: %s", url)
data = wget(url, timeout=20)
if not data:
log.warn_once("Could not fetch libc for %s %s from libc.rip", hash_type, hex_encoded_id)
return None
except requests.RequestException as e:
log.warn_once("Failed to fetch libc for %s %s from libc.rip: %s", hash_type, hex_encoded_id, e)
if not data:
log.warn_once("Could not fetch libc binary for %s %s from libc.rip", hash_type, hex_encoded_id)
return None
return data
PROVIDERS = [provider_libcdb, provider_libc_rip]
def search_by_hash(hex_encoded_id, hash_type='build_id'):
def search_by_hash(hex_encoded_id, hash_type='build_id', unstrip=True):
assert hash_type in HASHES, hash_type
# Ensure that the libcdb cache directory exists
cache_dir = os.path.join(context.cache_dir, 'libcdb', hash_type)
if not os.path.isdir(cache_dir):
os.makedirs(cache_dir)
# If we already downloaded the file, and it looks even passingly like
# a valid ELF file, return it.
cache = os.path.join(cache_dir, hex_encoded_id)
if os.path.exists(cache):
log.debug("Found existing cached libc at %r", cache)
data = read(cache)
if data.startswith(b'\x7FELF'):
log.info_once("Using cached data from %r", cache)
return cache
else:
log.info_once("Skipping unavailable libc %s", hex_encoded_id)
return None
cache, cache_valid = _check_elf_cache('libcdb', hex_encoded_id, hash_type)
if cache_valid:
return cache
# Run through all available libc database providers to see if we have a match.
for provider in PROVIDERS:
@@ -128,16 +126,435 @@ def search_by_hash(hex_encoded_id, hash_type='build_id'):
if not data or not data.startswith(b'\x7FELF'):
return None
# Try to find debug info for this libc.
if unstrip:
unstrip_libc(cache)
return cache
def _search_debuginfo_by_hash(base_url, hex_encoded_id):
# Deferred import because it's slow
import requests
from six.moves import urllib
def search_by_build_id(hex_encoded_id):
# Check if we tried this buildid before.
cache, cache_valid = _check_elf_cache('libcdb_dbg', hex_encoded_id, 'build_id')
if cache_valid:
return cache
# Try to find separate debuginfo.
url = '/buildid/{}/debuginfo'.format(hex_encoded_id)
url = urllib.parse.urljoin(base_url, url)
data = b""
log.debug("Downloading data from debuginfod: %s", url)
try:
data = wget(url, timeout=20)
except requests.RequestException as e:
log.warn_once("Failed to fetch libc debuginfo for build_id %s from %s: %s", hex_encoded_id, base_url, e)
# Save whatever we got to the cache
write(cache, data or b'')
# Return ``None`` if we did not get a valid ELF file
if not data or not data.startswith(b'\x7FELF'):
log.warn_once("Could not fetch libc debuginfo for build_id %s from %s", hex_encoded_id, base_url)
return None
return cache
def _check_elf_cache(cache_type, hex_encoded_id, hash_type):
"""
Check if there already is an ELF file for this hash in the cache.
>>> cache, _ = _check_elf_cache('libcdb', '2d1c5e0b85cb06ff47fa6fa088ec22cb6e06074e', 'build_id')
>>> os.unlink(cache) if os.path.exists(cache)
>>> filename = search_by_hash('2d1c5e0b85cb06ff47fa6fa088ec22cb6e06074e', 'build_id', unstrip=False)
>>> hex(ELF(filename).symbols.read)
'0xe56c0'
>>> filename == cache
True
"""
# Ensure that the cache directory exists
cache_dir = os.path.join(context.cache_dir, cache_type, hash_type)
if not os.path.isdir(cache_dir):
os.makedirs(cache_dir)
# If we already downloaded the file, and it looks even passingly like
# a valid ELF file, return it.
cache = os.path.join(cache_dir, hex_encoded_id)
if not os.path.exists(cache):
return cache, False
log.debug("Found existing cached ELF at %r", cache)
data = read(cache)
if not data.startswith(b'\x7FELF'):
log.info_once("Skipping unavailable ELF %s", hex_encoded_id)
return cache, False
log.info_once("Using cached data from %r", cache)
return cache, True
def unstrip_libc(filename):
"""
Given a path to a libc binary, attempt to download matching debug info
and add them back to the given binary.
This modifies the given file.
Arguments:
filename(str):
Path to the libc binary to unstrip.
Returns:
:const:`True` if binary was unstripped, :const:`False` otherwise.
Examples:
>>> filename = search_by_build_id('69389d485a9793dbe873f0ea2c93e02efaa9aa3d', unstrip=False)
>>> libc = ELF(filename)
>>> 'main_arena' in libc.symbols
False
>>> unstrip_libc(filename)
True
>>> libc = ELF(filename)
>>> hex(libc.symbols.main_arena)
'0x219c80'
>>> unstrip_libc(pwnlib.data.elf.get('test-x86'))
False
>>> filename = search_by_build_id('d1704d25fbbb72fa95d517b883131828c0883fe9', unstrip=True)
>>> 'main_arena' in ELF(filename).symbols
True
"""
if not which('eu-unstrip'):
log.warn_once('Couldn\'t find "eu-unstrip" in PATH. Install elfutils first.')
return False
libc = ELF(filename, checksec=False)
if not libc.buildid:
log.warn_once('Given libc does not have a buildid. Cannot look for debuginfo to unstrip.')
return False
log.debug('Trying debuginfod servers: %r', DEBUGINFOD_SERVERS)
for server_url in DEBUGINFOD_SERVERS:
libc_dbg = _search_debuginfo_by_hash(server_url, enhex(libc.buildid))
if libc_dbg:
break
else:
log.warn_once('Couldn\'t find debug info for libc with build_id %s on any debuginfod server.', enhex(libc.buildid))
return False
# Add debug info to given libc binary inplace.
p = process(['eu-unstrip', '-o', filename, filename, libc_dbg])
output = p.recvall()
p.close()
if output:
log.error('Failed to unstrip libc binary: %r', output)
return False
return True
def _extract_tarfile(cache_dir, data_filename, tarball):
from six import BytesIO
import tarfile
# Handle zstandard compression, since tarfile only supports gz, bz2, and xz.
if data_filename.endswith('.zst') or data_filename.endswith('.zstd'):
import zstandard
dctx = zstandard.ZstdDecompressor()
decompressed_tar = BytesIO()
dctx.copy_stream(tarball, decompressed_tar)
decompressed_tar.seek(0)
tarball.close()
tarball = decompressed_tar
if six.PY2 and data_filename.endswith('.xz'):
# Python 2's tarfile doesn't support xz, so we need to decompress it first.
# Shell out to xz, since the Python 2 pylzma module is broken.
# (https://github.com/fancycode/pylzma/issues/67)
if not which('xz'):
log.error('Couldn\'t find "xz" in PATH. Please install xz first.')
import subprocess
try:
uncompressed_tarball = subprocess.check_output(['xz', '--decompress', '--stdout', tarball.name])
tarball = BytesIO(uncompressed_tarball)
except subprocess.CalledProcessError:
log.error('Failed to decompress xz archive.')
with tarfile.open(fileobj=tarball) as tar_file:
# Find the library folder in the archive (e.g. /lib/x86_64-linux-gnu/)
lib_dir = None
libc_name = None
for member in tar_file.getmembers():
if not member.isfile():
continue
libc_name = os.path.basename(member.name)
if libc_name == 'libc.so.6' or (libc_name.startswith('libc') and libc_name.endswith('.so')):
lib_dir = os.path.dirname(member.name)
break
else:
log.error('Couldn\'t find library folder containing the libc in the archive.')
# Extract everything in the library folder
for member in tar_file.getmembers():
if os.path.dirname(member.name) != lib_dir:
continue
if not member.isfile() and not member.issym():
continue
# Extract while keeping file permissions
tar_file.extract(member, cache_dir)
# Move the files up to the cache root
target_dir = os.path.join(cache_dir, lib_dir)
for file in os.listdir(target_dir):
os.rename(os.path.join(target_dir, file), os.path.join(cache_dir, file))
os.removedirs(target_dir)
return os.path.join(cache_dir, libc_name)
def _extract_debfile(cache_dir, package_filename, package):
# Extract data.tar in the .deb archive.
if six.PY2:
if not which('ar'):
log.error('Missing command line tool "ar" to extract .deb archive. Please install "ar" first.')
import atexit
import shutil
import subprocess
# Use mkdtemp instead of TemporaryDirectory because the latter is not available in Python 2.
tempdir = tempfile.mkdtemp(prefix=".pwntools-tmp")
atexit.register(shutil.rmtree, tempdir)
with tempfile.NamedTemporaryFile(mode='wb', dir=tempdir) as debfile:
debfile.write(package)
debfile.flush()
try:
files_in_deb = subprocess.check_output(['ar', 't', debfile.name]).split(b'\n')
except subprocess.CalledProcessError:
log.error('Failed to list files in .deb archive.')
[data_filename] = filter(lambda f: f.startswith(b'data.tar'), files_in_deb)
try:
subprocess.check_call(['ar', 'x', debfile.name, data_filename], cwd=tempdir)
except subprocess.CalledProcessError:
log.error('Failed to extract data.tar from .deb archive.')
with open(os.path.join(tempdir, data_filename), 'rb') as tarball:
return _extract_tarfile(cache_dir, data_filename, tarball)
else:
import unix_ar
from six import BytesIO
ar_file = unix_ar.open(BytesIO(package))
try:
data_filename = next(filter(lambda f: f.name.startswith(b'data.tar'), ar_file.infolist())).name.decode()
tarball = ar_file.open(data_filename)
return _extract_tarfile(cache_dir, data_filename, tarball)
finally:
ar_file.close()
def _extract_pkgfile(cache_dir, package_filename, package):
from six import BytesIO
return _extract_tarfile(cache_dir, package_filename, BytesIO(package))
def _find_libc_package_lib_url(libc):
# Check https://libc.rip for the libc package
libc_match = query_libc_rip({'buildid': enhex(libc.buildid)})
if libc_match is not None:
for match in libc_match:
yield match['libs_url']
# Check launchpad.net if it's an Ubuntu libc
# GNU C Library (Ubuntu GLIBC 2.36-0ubuntu4)
import re
version = re.search(br'GNU C Library \(Ubuntu E?GLIBC ([^\)]+)\)', libc.data)
if version is not None:
libc_version = version.group(1).decode()
yield 'https://launchpad.net/ubuntu/+archive/primary/+files/libc6_{}_{}.deb'.format(libc_version, libc.arch)
def download_libraries(libc_path, unstrip=True):
"""download_libraries(str, bool) -> str
Download the matching libraries for the given libc binary and cache
them in a local directory. The libraries are looked up using `libc.rip <https://libc.rip>`_
and fetched from the official package repositories if available.
This commonly includes the ``ld-linux-x86-64.so.2`` and ``libpthread.so.0`` binaries
which can be used to execute the program locally when the given libc is
incompatible with the local dynamic loader.
Note: Only .deb and .pkg.tar.* packages are currently supported (Debian/Ubuntu, Arch).
Arguments:
libc_path(str):
The path the libc binary.
unstrip(bool):
Try to fetch debug info for the libc and apply it to the downloaded file.
Returns:
The path to the cached directory containing the downloaded libraries.
Example:
>>> libc_path = ELF(which('ls'), checksec=False).libc.path
>>> lib_path = download_libraries(libc_path)
>>> lib_path is not None
True
>>> os.path.exists(os.path.join(lib_path, 'libc.so.6'))
True
>>> os.path.exists(os.path.join(lib_path, 'ld-linux-x86-64.so.2'))
True
"""
libc = ELF(libc_path, checksec=False)
if not libc.buildid:
log.warn_once('Given libc does not have a buildid.')
return None
# Handle caching and don't redownload if it already exists.
cache_dir = os.path.join(context.cache_dir, 'libcdb_libs')
if not os.path.isdir(cache_dir):
os.makedirs(cache_dir)
cache_dir = os.path.join(cache_dir, enhex(libc.buildid))
if os.path.exists(cache_dir):
return cache_dir
for package_url in _find_libc_package_lib_url(libc):
extension_handlers = {
'.deb': _extract_debfile,
'.pkg.tar.xz': _extract_pkgfile,
'.pkg.tar.zst': _extract_pkgfile,
}
package_filename = os.path.basename(package_url)
for extension, handler in extension_handlers.items():
if package_filename.endswith(extension):
break
else:
log.failure('Cannot handle %s (%s)', package_filename, package_url)
continue
# Download the package
package = wget(package_url, timeout=20)
if not package:
continue
# Create target cache directory to extract files into
if not os.path.isdir(cache_dir):
os.makedirs(cache_dir)
try:
# Extract the archive
libc_path = handler(cache_dir, package_filename, package)
except Exception as e:
os.removedirs(cache_dir)
log.failure('Failed to extract %s: %s', package_filename, e)
continue
# Unstrip the libc binary
try:
if unstrip:
unstrip_libc(libc_path)
except Exception:
pass
return cache_dir
log.warn_once('Failed to find matching libraries for provided libc.')
return None
def _handle_multiple_matching_libcs(matching_libcs):
from pwnlib.term import text
from pwnlib.ui import options
log.info('Multiple matching libc libraries for requested symbols:')
for idx, libc in enumerate(matching_libcs):
log.info('%d. %s', idx+1, text.red(libc['id']))
log.indented('\t%-20s %s', text.green('BuildID:'), libc['buildid'])
log.indented('\t%-20s %s', text.green('MD5:'), libc['md5'])
log.indented('\t%-20s %s', text.green('SHA1:'), libc['sha1'])
log.indented('\t%-20s %s', text.green('SHA256:'), libc['sha256'])
log.indented('\t%s', text.green('Symbols:'))
for symbol, address in libc['symbols'].items():
log.indented('\t%25s = %s', symbol, address)
selected_index = options("Select the libc version to use:", [libc['id'] for libc in matching_libcs])
return matching_libcs[selected_index]
def search_by_symbol_offsets(symbols, select_index=None, unstrip=True, return_as_list=False):
"""
Lookup possible matching libc versions based on leaked function addresses.
The leaked function addresses have to be provided as a dict mapping the
function name to the leaked value. Only the lower 3 nibbles are relevant
for the lookup.
If there are multiple matches you are presented with a list to select one
interactively, unless the ``select_index`` or ``return_as_list`` arguments
are used.
Arguments:
symbols(dict):
Dictionary mapping symbol names to their addresses.
select_index(int):
The libc to select if there are multiple matches (starting at 1).
unstrip(bool):
Try to fetch debug info for the libc and apply it to the downloaded file.
return_as_list(bool):
Return a list of build ids of all matching libc versions
instead of a path to a downloaded file.
Returns:
Path to the downloaded library on disk, or :const:`None`.
If the ``return_as_list`` argument is :const:`True`, a list of build ids
is returned instead.
Examples:
>>> filename = search_by_symbol_offsets({'puts': 0x420, 'printf': 0xc90}, select_index=1)
>>> libc = ELF(filename)
>>> libc.sym.system == 0x52290
True
>>> matched_libcs = search_by_symbol_offsets({'__libc_start_main_ret': '7f89ad926550'}, return_as_list=True)
>>> len(matched_libcs) > 1
True
>>> for buildid in matched_libcs: # doctest +SKIP
... libc = ELF(search_by_build_id(buildid)) # doctest +SKIP
"""
for symbol, address in symbols.items():
if isinstance(address, int):
symbols[symbol] = hex(address)
params = {'symbols': symbols}
log.debug('Request: %s', params)
matching_libcs = query_libc_rip(params)
log.debug('Result: %s', matching_libcs)
if matching_libcs is None or len(matching_libcs) == 0:
log.warn_once("No matching libc for symbols %r on libc.rip", symbols)
return None
if return_as_list:
return [libc['buildid'] for libc in matching_libcs]
if len(matching_libcs) == 1:
return search_by_build_id(matching_libcs[0]['buildid'], unstrip=unstrip)
if select_index is not None:
if select_index > 0 and select_index <= len(matching_libcs):
return search_by_build_id(matching_libcs[select_index - 1]['buildid'], unstrip=unstrip)
else:
log.error('Invalid selected libc index. %d is not in the range of 1-%d.', select_index, len(matching_libcs))
return None
selected_libc = _handle_multiple_matching_libcs(matching_libcs)
return search_by_build_id(selected_libc['buildid'], unstrip=unstrip)
def search_by_build_id(hex_encoded_id, unstrip=True):
"""
Given a hex-encoded Build ID, attempt to download a matching libc from libcdb.
Arguments:
hex_encoded_id(str):
Hex-encoded Build ID (e.g. 'ABCDEF...') of the library
unstrip(bool):
Try to fetch debug info for the libc and apply it to the downloaded file.
Returns:
Path to the downloaded library on disk, or :const:`None`.
@@ -152,15 +569,17 @@ def search_by_build_id(hex_encoded_id):
>>> hex(ELF(filename).symbols.read)
'0xeef40'
"""
return search_by_hash(hex_encoded_id, 'build_id')
return search_by_hash(hex_encoded_id, 'build_id', unstrip)
def search_by_md5(hex_encoded_id):
def search_by_md5(hex_encoded_id, unstrip=True):
"""
Given a hex-encoded md5sum, attempt to download a matching libc from libcdb.
Arguments:
hex_encoded_id(str):
Hex-encoded md5sum (e.g. 'ABCDEF...') of the library
unstrip(bool):
Try to fetch debug info for the libc and apply it to the downloaded file.
Returns:
Path to the downloaded library on disk, or :const:`None`.
@@ -175,15 +594,17 @@ def search_by_md5(hex_encoded_id):
>>> hex(ELF(filename).symbols.read)
'0xeef40'
"""
return search_by_hash(hex_encoded_id, 'md5')
return search_by_hash(hex_encoded_id, 'md5', unstrip)
def search_by_sha1(hex_encoded_id):
def search_by_sha1(hex_encoded_id, unstrip=True):
"""
Given a hex-encoded sha1, attempt to download a matching libc from libcdb.
Arguments:
hex_encoded_id(str):
Hex-encoded sha1sum (e.g. 'ABCDEF...') of the library
unstrip(bool):
Try to fetch debug info for the libc and apply it to the downloaded file.
Returns:
Path to the downloaded library on disk, or :const:`None`.
@@ -198,16 +619,18 @@ def search_by_sha1(hex_encoded_id):
>>> hex(ELF(filename).symbols.read)
'0xeef40'
"""
return search_by_hash(hex_encoded_id, 'sha1')
return search_by_hash(hex_encoded_id, 'sha1', unstrip)
def search_by_sha256(hex_encoded_id):
def search_by_sha256(hex_encoded_id, unstrip=True):
"""
Given a hex-encoded sha256, attempt to download a matching libc from libcdb.
Arguments:
hex_encoded_id(str):
Hex-encoded sha256sum (e.g. 'ABCDEF...') of the library
unstrip(bool):
Try to fetch debug info for the libc and apply it to the downloaded file.
Returns:
Path to the downloaded library on disk, or :const:`None`.
@@ -222,7 +645,7 @@ def search_by_sha256(hex_encoded_id):
>>> hex(ELF(filename).symbols.read)
'0xeef40'
"""
return search_by_hash(hex_encoded_id, 'sha256')
return search_by_hash(hex_encoded_id, 'sha256', unstrip)
@@ -279,4 +702,4 @@ def get_build_id_offsets():
}.get(context.arch, [])
__all__ = ['get_build_id_offsets', 'search_by_build_id', 'search_by_sha1', 'search_by_sha256', 'search_by_md5']
__all__ = ['get_build_id_offsets', 'search_by_build_id', 'search_by_sha1', 'search_by_sha256', 'search_by_md5', 'unstrip_libc', 'search_by_symbol_offsets', 'download_libraries']
+16 -1
View File
@@ -84,7 +84,7 @@ result in a message being emitted but rather an animated progress line (with a
spinner!) being created. Note that other handlers will still see a meaningful
log record.
The custom handler will only handle log records whith a level of at least
The custom handler will only handle log records with a level of at least
:data:`context.log_level`. Thus if e.g. the level for the
``'pwnlib.tubes.ssh'`` is set to ``'DEBUG'`` no additional output will show up
unless :data:`context.log_level` is also set to ``'DEBUG'``. Other handlers
@@ -99,6 +99,7 @@ import os
import random
import re
import six
import string
import sys
import threading
import time
@@ -401,6 +402,20 @@ class Logger(object):
self.info(pwnlib.util.fiddling.hexdump(message, *args, **kwargs))
def maybe_hexdump(self, message, *args, **kwargs):
"""maybe_hexdump(self, message, *args, **kwargs)
Logs a message using indented. Repeated single byte is compressed, and
unprintable message is hexdumped.
"""
if len(set(message)) == 1 and len(message) > 1:
self.indented('%r * %#x' % (message[:1], len(message)), *args, **kwargs)
elif len(message) == 1 or all(c in string.printable.encode() for c in message):
for line in message.splitlines(True):
self.indented(repr(line), *args, **kwargs)
else:
import pwnlib.util.fiddling
self.indented(pwnlib.util.fiddling.hexdump(message), *args, **kwargs)
def warning(self, message, *args, **kwargs):
"""warning(message, *args, **kwargs)
+7
View File
@@ -246,6 +246,13 @@ class Call(object):
fmt % self.target,
self.args)
def is_flat(self):
if isinstance(self, six.integer_types + (Unresolved,)):
return True
if not isinstance(self, Call):
return False
return bool(self.args or self.stack_arguments_before)
@property
def register_arguments(self):
return dict(zip(self.abi.register_arguments, self.args))
+1 -1
View File
@@ -39,7 +39,7 @@ class Gadget(object):
move = 0
def __init__(self, address, insns, regs, move):
self.address = address
self.address = int(address)
self.insns = insns
self.regs = regs
self.move = move
+33 -19
View File
@@ -31,27 +31,34 @@ def ret2csu(rop, elf, edi, rsi, rdx, rbx, rbp, r12, r13, r14, r15, call=None):
# Resolve __libc_csu_ symbols if candidate binary is stripped
if '__libc_csu_init' not in elf.symbols:
if elf.pie:
for insn in md.disasm(elf.section('.text'),
elf.offset_to_vaddr(elf.get_section_by_name('.text').header['sh_offset'])):
if insn.mnemonic == 'lea' and insn.operands[0].reg == X86_REG_R8:
elf.sym['__libc_csu_fini'] = insn.address + insn.size + insn.disp
if insn.mnemonic == 'lea' and insn.operands[0].reg == X86_REG_RCX:
elf.sym['__libc_csu_init'] = insn.address + insn.size + insn.disp
textaddr = elf.offset_to_vaddr(elf.get_section_by_name('.text').header.sh_offset)
entry = elf.entry
data = elf.section('.text')[entry-textaddr:]
mnemonic = elf.pie and 'lea' or 'mov'
for insn in md.disasm(data, entry):
if insn.mnemonic == mnemonic:
if mnemonic == 'lea':
addr = insn.address + insn.size + insn.disp
else:
addr = insn.operands[1].imm
if insn.operands[0].reg == X86_REG_R8:
elf.sym['__libc_csu_fini'] = addr
if insn.operands[0].reg == X86_REG_RCX:
elf.sym['__libc_csu_init'] = addr
break
elif insn.mnemonic == 'xor' and insn.operands[0].reg == insn.operands[1].reg == X86_REG_ECX:
log.error("This binary is compiled for glibc 2.34+ and does not have __libc_csu_init")
elif insn.mnemonic in ('hlt', 'jmp', 'call', 'syscall'):
log.error("No __libc_csu_init (no glibc _start)")
else:
for insn in md.disasm(elf.section('.text'), elf.get_section_by_name('.text').header['sh_addr']):
if insn.mnemonic == 'mov' and insn.operands[0].reg == X86_REG_R8:
elf.sym['__libc_csu_fini'] = insn.operands[1].imm
if insn.mnemonic == 'mov' and insn.operands[0].reg == X86_REG_RCX:
elf.sym['__libc_csu_init'] = insn.operands[1].imm
break
log.error("Weird _start, definitely no __libc_csu_init")
# Resolve location of _fini address if required
if not elf.pie and not call:
fini = next(elf.search(p64(elf.dynamic_by_tag('DT_FINI')['d_ptr'])))
call = next(elf.search(p64(elf.dynamic_by_tag('DT_FINI')['d_ptr'])))
elif elf.pie and not call:
log.error('No non-PIE binaries in [elfs], \'call\' parameter is required')
log.error("No non-PIE binaries in [elfs], 'call' parameter is required")
csu_function = elf.read(elf.sym['__libc_csu_init'], elf.sym['__libc_csu_fini'] - elf.sym['__libc_csu_init'])
@@ -63,26 +70,33 @@ def ret2csu(rop, elf, edi, rsi, rdx, rbx, rbp, r12, r13, r14, r15, call=None):
# rbx and rbp must be equal after 'add rbx, 1'
rop.raw(0x00) # pop rbx
rop.raw(0x01) # pop rbp
if call:
rop.raw(call) # pop r12
else:
rop.raw(fini) # pop r12
# Older versions of gcc use r13 to populate rdx then r15d to populate edi, newer versions use the reverse
# Account for this when the binary was linked against a glibc that was built with a newer gcc
for insn in md.disasm(csu_function, elf.sym['__libc_csu_init']):
if insn.mnemonic == 'mov' and insn.operands[0].reg == X86_REG_RDX and insn.operands[1].reg == X86_REG_R13:
rop.raw(call) # pop r12
rop.raw(rdx) # pop r13
rop.raw(rsi) # pop r14
rop.raw(edi) # pop r15
rop.raw(insn.address)
break
elif insn.mnemonic == 'mov' and insn.operands[0].reg == X86_REG_RDX and insn.operands[1].reg == X86_REG_R14:
rop.raw(edi) # pop r12
rop.raw(rsi) # pop r13
rop.raw(rdx) # pop r14
rop.raw(call) # pop r15
rop.raw(insn.address)
break
elif insn.mnemonic == 'mov' and insn.operands[0].reg == X86_REG_RDX and insn.operands[1].reg == X86_REG_R15:
rop.raw(call) # pop r12
rop.raw(edi) # pop r13
rop.raw(rsi) # pop r14
rop.raw(rdx) # pop r15
rop.raw(insn.address)
break
else:
log.error("This CSU init variant is not supported by pwntools")
# 2nd gadget: Populate edi, rsi & rdx. Populate optional registers
rop.raw(Padding('<add rsp, 8>')) # add rsp, 8
+5 -1
View File
@@ -303,7 +303,11 @@ class Ret2dlresolvePayload(object):
rel_addr = self.jmprel + self.reloc_index * ElfRel.size
rel_type = 7
rel = ElfRel(r_offset=self.data_addr, r_info=(index<<ELF_R_SYM_SHIFT)+rel_type)
# When a program's PIE is enabled, r_offset should be the relative address, not the absolute address
if self.elf.pie:
rel = ElfRel(r_offset=self.data_addr - (self.elf.load_addr + self.elf_load_address_fixup), r_info=(index<<ELF_R_SYM_SHIFT)+rel_type)
self.payload = fit({
symbol_name_addr - self.data_addr: symbol_name,
sym_addr - self.data_addr: sym,
+90 -33
View File
@@ -24,12 +24,13 @@ Each :class:`Gadget` has an ``address`` property which has the real address as w
>>> hex(rop.eax.address)
'0x10000004'
Other, more complicated gdagets also happen magically
Other, more complicated gadgets also happen magically
>>> rop.ecx
Gadget(0x10000006, ['pop ecx', 'pop ebx', 'ret'], ['ecx', 'ebx'], 0xc)
The easiest way to set up individual registers is to invoke the ``ROP`` object as a callable, with the registers as arguments.
This has the benefit of using multi-pop gadgets to set multiple registers with one gadget.
>>> rop(eax=0x11111111, ecx=0x22222222)
@@ -44,6 +45,37 @@ which corresponds to their offset, which is useful when debuggging your exploit.
0x000c: 0x10000004 pop eax; ret
0x0010: 0x11111111
If you really want to set one register at a time, you can also use the assignment form.
It's generally advised to use the rop(eax=..., ecx=...) form, since there may be an
e.g. ``pop eax; pop ecx; ret`` gadget that can be taken advantage of.
>>> rop = ROP(binary)
>>> rop.eax = 0xdeadf00d
>>> rop.ecx = 0xc01dbeef
>>> rop.raw(0xffffffff)
>>> print(rop.dump())
0x0000: 0x10000004 pop eax; ret
0x0004: 0xdeadf00d
0x0008: 0x10000006 pop ecx; pop ebx; ret
0x000c: 0xc01dbeef
0x0010: b'eaaa' <pad ebx>
0x0014: 0xffffffff
If you just want to FIND a ROP gadget, you can access them as a property on the ``ROP``
object by register name.
>>> rop = ROP(binary)
>>> rop.eax
Gadget(0x10000004, ['pop eax', 'ret'], ['eax'], 0x8)
>>> hex(rop.eax.address)
'0x10000004'
>>> rop.raw(rop.eax)
>>> rop.raw(0x12345678)
>>> print(rop.dump())
0x0000: 0x10000004 pop eax; ret
0x0004: 0x12345678
Let's re-create our ROP object now to show for some other examples.:
>>> rop = ROP(binary)
@@ -92,12 +124,10 @@ The stack is automatically adjusted for the next frame
0x001c: 0x6 arg2
0x0020: b'iaaa' <pad>
0x0024: 0xdecafbad write(7, 8, 9)
0x0028: 0x10000000 <adjust @0x3c> add esp, 0x10; ret
0x0028: 0xfeedface exit()
0x002c: 0x7 arg0
0x0030: 0x8 arg1
0x0034: 0x9 arg2
0x0038: b'oaaa' <pad>
0x003c: 0xfeedface exit()
You can also append complex arguments onto stack when the stack pointer is known.
@@ -188,18 +218,16 @@ Finally, let's build our ROP stack
>>> rop.exit()
>>> print(rop.dump())
0x0000: 0x10000012 write(STDOUT_FILENO, 0x10000026, 8)
0x0004: 0x1000000e <adjust @0x18> add esp, 0x10; ret
0x0004: 0x1000002f exit()
0x0008: 0x1 STDOUT_FILENO
0x000c: 0x10000026 flag
0x0010: 0x8 arg2
0x0014: b'faaa' <pad>
0x0018: 0x1000002f exit()
The raw data from the ROP stack is available via `str`.
The raw data from the ROP stack is available via `r.chain()` (or `bytes(r)`).
>>> raw_rop = rop.chain()
>>> print(enhex(raw_rop))
120000100e000010010000002600001008000000666161612f000010
120000102f000010010000002600001008000000
Let's try it out!
@@ -927,7 +955,7 @@ class ROP(object):
# If there were arguments on the stack, we need to stick something
# in the slot where the return address goes.
if len(stackArguments) > 0:
if remaining:
if remaining and (remaining > 1 or Call.is_flat(chain[-1])):
fix_size = (1 + len(stackArguments))
fix_bytes = fix_size * context.bytes
adjust = self.search(move = fix_bytes)
@@ -944,6 +972,13 @@ class ROP(object):
stackArguments.append(Padding())
# We could not find a proper "adjust" gadget, but also didn't need one.
elif remaining:
_, nxslot = next(iterable)
stack.describe(self.describe(nxslot))
if isinstance(nxslot, Call):
stack.append(nxslot.target)
else:
stack.append(nxslot)
else:
stack.append(Padding("<return address>"))
@@ -1002,10 +1037,6 @@ class ROP(object):
return stack
def find_stack_adjustment(self, slots):
self.search(move=slots * context.bytes)
def chain(self, base=None):
"""Build the ROP chain
@@ -1132,6 +1163,15 @@ class ROP(object):
if tuple(gadget.insns)[:n] == tuple(instructions):
return gadget
def _flatten(self, initial_list):
# Flatten out any nested lists.
flattened_list = []
for data in initial_list:
if isinstance(data, (list, tuple)):
flattened_list.extend(self._flatten(data))
else:
flattened_list.append(data)
return flattened_list
def raw(self, value):
"""Adds a raw integer or string to the ROP chain.
@@ -1139,14 +1179,18 @@ class ROP(object):
If your architecture requires aligned values, then make
sure that any given string is aligned!
When given a list or a tuple of values, the list is
flattened before adding every item to the chain.
Arguments:
data(int/bytes): The raw value to put onto the rop chain.
data(int/bytes/list): The raw value to put onto the rop chain.
>>> context.clear(arch='i386')
>>> rop = ROP([])
>>> rop.raw('AAAAAAAA')
>>> rop.raw('BBBBBBBB')
>>> rop.raw('CCCCCCCC')
>>> rop.raw(['DDDD', 'DDDD'])
>>> print(rop.dump())
0x0000: b'AAAA' 'AAAAAAAA'
0x0004: b'AAAA'
@@ -1154,10 +1198,16 @@ class ROP(object):
0x000c: b'BBBB'
0x0010: b'CCCC' 'CCCCCCCC'
0x0014: b'CCCC'
0x0018: b'DDDD' 'DDDD'
0x001c: b'DDDD' 'DDDD'
"""
if self.migrated:
log.error('Cannot append to a migrated chain')
self._chain.append(value)
if isinstance(value, (list, tuple)):
self._chain.extend(self._flatten(value))
else:
self._chain.append(value)
def migrate(self, next_base):
"""Explicitly set $sp, by using a ``leave; ret`` gadget"""
@@ -1189,6 +1239,9 @@ class ROP(object):
def __get_cachefile_name(self, files):
"""Given an ELF or list of ELF objects, return a cache file for the set of files"""
if context.cache_dir is None:
return None
cachedir = os.path.join(context.cache_dir, 'rop-cache')
if not os.path.exists(cachedir):
os.mkdir(cachedir)
@@ -1205,12 +1258,14 @@ class ROP(object):
@staticmethod
def clear_cache():
"""Clears the ROP gadget cache"""
if context.cache_dir is None:
return
cachedir = os.path.join(context.cache_dir, 'rop-cache')
shutil.rmtree(cachedir)
def __cache_load(self, elf):
filename = self.__get_cachefile_name(elf)
if not os.path.exists(filename):
if filename is None or not os.path.exists(filename):
return None
gadgets = eval(open(filename).read())
gadgets = {k - elf.load_addr + elf.address:v for k, v in gadgets.items()}
@@ -1218,8 +1273,11 @@ class ROP(object):
return gadgets
def __cache_save(self, elf, data):
filename = self.__get_cachefile_name(elf)
if filename is None:
return
data = {k + elf.load_addr - elf.address:v for k, v in data.items()}
open(self.__get_cachefile_name(elf), 'w+').write(repr(data))
open(filename, 'w+').write(repr(data))
def __load(self):
"""Load all ROP gadgets for the selected ELF files"""
@@ -1274,7 +1332,7 @@ class ROP(object):
pass
def __getattr__(self, k):
return self._fd.__getattribute__(k)
return getattr(self._fd, k)
gadgets = {}
for elf in self.elfs:
@@ -1329,7 +1387,11 @@ class ROP(object):
regs.append(pop.match(insn).group(1))
sp_move += context.bytes
elif add.match(insn):
sp_move += int(add.match(insn).group(1), 16)
arg = int(add.match(insn).group(1), 16)
sp_move += arg
while arg >= context.bytes:
regs.append(hex(arg))
arg -= context.bytes
elif ret.match(insn):
sp_move += context.bytes
elif leave.match(insn):
@@ -1477,23 +1539,18 @@ class ROP(object):
# Find an appropriate, non-library ELF.
# Prioritise non-PIE binaries so we can use _fini
exes = (elf for elf in self.elfs if not elf.library and elf.bits == 64)
if not exes:
log.error('No non-library binaries in [elfs]')
nonpie = csu = None
csu = None
for elf in exes:
if not elf.pie:
if '__libc_csu_init' in elf.symbols:
break
nonpie = elf
elif '__libc_csu_init' in elf.symbols:
if '__libc_csu_init' in elf.symbols:
csu = elf
if not elf.pie:
break
if elf.pie:
if nonpie:
elf = nonpie
elif csu:
elf = csu
if csu:
elf = csu
else:
log.error('No non-library binaries in [elfs]')
from .ret2csu import ret2csu
ret2csu(self, elf, edi, rsi, rdx, rbx, rbp, r12, r13, r14, r15, call)
+3 -1
View File
@@ -391,7 +391,9 @@ class SigreturnFrame(dict):
self.set_regvalue(attr, value)
def __getattr__(self, attr):
return self[attr]
if attr in self:
return self[attr]
raise AttributeError(attr)
def __bytes__(self):
frame = b""
+6 -6
View File
@@ -39,14 +39,14 @@ def run_shellcode(bytes, **kw):
Example:
>>> bytes = asm('mov ebx, 3; mov eax, SYS_exit; int 0x80;')
>>> p = run_shellcode(bytes)
>>> insn_bytes = asm('mov ebx, 3; mov eax, SYS_exit; int 0x80;')
>>> p = run_shellcode(insn_bytes)
>>> p.wait_for_close()
>>> p.poll()
3
>>> bytes = asm('mov r0, #12; mov r7, #1; svc #0', arch='arm')
>>> p = run_shellcode(bytes, arch='arm')
>>> insn_bytes = asm('mov r0, #12; mov r7, #1; svc #0', arch='arm')
>>> p = run_shellcode(insn_bytes, arch='arm')
>>> p.wait_for_close()
>>> p.poll()
12
@@ -84,8 +84,8 @@ def run_shellcode_exitcode(bytes):
Example:
>>> bytes = asm('mov ebx, 3; mov eax, SYS_exit; int 0x80;')
>>> run_shellcode_exitcode(bytes)
>>> insn_bytes = asm('mov ebx, 3; mov eax, SYS_exit; int 0x80;')
>>> run_shellcode_exitcode(insn_bytes)
3
"""
p = run_shellcode(bytes)
+3 -2
View File
@@ -153,9 +153,9 @@ class module(ModuleType):
if comment: return '%s /* %s */' % (n,self.pretty(int(n)))
else: return '%s (%s)' % (n,self.pretty(int(n)))
elif abs(n) < 10:
return str(n)
return '%d' % n
else:
return hex(n)
return '%#x' % n
def okay(self, s, *a, **kw):
if isinstance(s, six.integer_types):
@@ -186,4 +186,5 @@ class LazyImporter:
def load_module(self, fullname):
return sys.modules[fullname]
sys.meta_path.append(LazyImporter())
+11 -43
View File
@@ -2,6 +2,7 @@ from __future__ import absolute_import
from __future__ import division
import os
import sys
from pwnlib.context import context
@@ -109,47 +110,17 @@ def get_context_from_dirpath(directory):
return {'os': osys, 'arch': arch}
def make_function(funcname, filename, directory):
import functools
import inspect
path = os.path.join(directory, filename)
template = lookup_template(path)
args, varargs, keywords, defaults = inspect.getargspec(template.module.render_body)
defaults = defaults or []
if len(defaults) < len(args) and args[0] == 'context':
args.pop(0)
args_used = args[:]
for n, default in enumerate(defaults, len(args) - len(defaults)):
args[n] = '%s = %r' % (args[n], default)
if varargs:
args.append('*' + varargs)
args_used.append('*' + varargs)
if keywords not in ['pageargs', None]:
args.append('**' + keywords)
args_used.append('**' + keywords)
docstring = inspect.cleandoc(template.module.__doc__ or '')
args = ', '.join(args)
args_used = ', '.join(args_used)
local_ctx = get_context_from_dirpath(directory)
# This is a slight hack to get the right signature for the function
# It would be possible to simply create an (*args, **kwargs) wrapper,
# but what would not have the right signature.
# While we are at it, we insert the docstring too
T = r'''
def wrap(template, render_global):
import pwnlib
def %(funcname)s(%(args)s):
%(docstring)r
def res(*args, **kwargs):
with render_global.go_inside() as was_inside:
with pwnlib.context.context.local(**%(local_ctx)s):
lines = template.render(%(args_used)s).split('\n')
with context.local(**local_ctx):
lines = template.render(*args, **kwargs).split('\n')
for i, line in enumerate(lines):
def islabelchar(c):
return c.isalnum() or c == '.' or c == '_'
@@ -168,19 +139,16 @@ def wrap(template, render_global):
return s
else:
return s + '\n'
return %(funcname)s
''' % locals()
g = {}
exec(T, g, g)
wrap = g['wrap']
# Setting _relpath is a slight hack only used to get better documentation
res = wrap(template, render_global)
res._relpath = path
res.__module__ = 'pwnlib.shellcraft.' + os.path.dirname(path).replace('/','.')
import sys, functools
res.__name__ = res.__qualname__ = funcname
res.__doc__ = inspect.cleandoc(template.module.__doc__ or '')
if hasattr(inspect, 'signature'):
sig = inspect.signature(template.module.render_body)
sig = sig.replace(parameters=list(sig.parameters.values())[1:-1])
res.__signature__ = sig
@functools.wraps(res)
def function(*a):
@@ -0,0 +1,18 @@
<% from pwnlib.shellcraft import common %>
<% from pwnlib.shellcraft.aarch64 import mov,setregs %>
<%page args="sock = 'x12'"/>
<%docstring>
Args: [sock (imm/reg) = x12]
Duplicates sock to stdin, stdout and stderr
</%docstring>
<%
looplabel = common.label("loop")
%>
/* dup() file descriptor ${sock} into stdin/stdout/stderr */
${setregs({'x8': 'SYS_dup3', 'x1': 2, 'x2': 0})}
${looplabel}:
${mov('x0', sock)}
svc #0
subs x1, x1, #1
bpl ${looplabel}
@@ -0,0 +1,11 @@
<% from pwnlib.shellcraft.aarch64 import linux %>
<%page args="sock = 'x12'"/>
<%docstring>
Args: [sock (imm/reg) = x12]
Duplicates sock to stdin, stdout and stderr and spawns a shell.
</%docstring>
${linux.dupio(sock)}
${linux.sh()}
@@ -107,14 +107,14 @@ PT_LOAD = 1
mov x3, sp
stp x2, x3, [sp, #-16]!
/* argc, argv[0], argv[1], envp */
/* argc, argv[0], argv[1], envp; x0 must be zero! */
/* ideally these could all be empty, but unfortunately
we have to keep the stack aligned. it's easier to
just push an extra argument than care... */
stp x0, x1, [sp, #-16]! /* argv[1] = NULL, envp = NULL */
mov x0, 1
mov x1, sp
stp x0, x1, [sp, #-16]! /* argc = 1, argv[0] = "" */
mov x2, 1
mov x3, sp
stp x2, x3, [sp, #-16]! /* argc = 1, argv[0] = "" */
br x8
@@ -0,0 +1,18 @@
<% from pwnlib.shellcraft import common %>
<% from pwnlib.shellcraft.aarch64 import mov, linux %>
<%page args="ruid=None, euid=None, suid=None"/>
<%docstring>
Args: [ruid = geteuid(), euid = ruid, suid = ruid]
Sets real, effective and saved user ids to given values
</%docstring>
%if ruid is None:
${linux.geteuid()}
<% ruid = 'x0' %>
%endif
<%
if euid is None: euid = ruid
if suid is None: suid = ruid
%>
${linux.syscalls.setresuid(ruid, euid, suid)}
+1
View File
@@ -0,0 +1 @@
../../common/linux/sleep.asm
@@ -0,0 +1,10 @@
<%docstring>Returns code to switch from amd64 to i386 mode. Trashes eax, ecx, edx.
Note that you most surely want to set up some stack (and place this code)
in low address space before (or afterwards).</%docstring>
.code64
call $+4
.byte 0xc0 /* inc eax */
mov byte ptr [rsp+4], 0x33 /* This is the segment we want to go to */
retfd
.code32
+6 -7
View File
@@ -35,14 +35,16 @@ assert v in registers.amd64
${mov('rdi', buffer)}
${mov('rax', v)}
push rax /* save for later */
${mov('rcx', 10)}
${size_loop}:
${mov('rdx', 0)}
${mov('rcx', 10)}
div rcx
inc rdi
stosb
test rax, rax
jnz ${size_loop}
dec rdi
## null terminate
std
stosb
## Now we begin the actual division process
pop rax
${itoa_loop}:
@@ -54,7 +56,4 @@ ${size_loop}:
dec rdi
test rax, rax
jnz ${itoa_loop}
## null terminate
${mov('rdx', 0)}
mov BYTE PTR [rdi], dl
inc rdi
cld
@@ -1,5 +1,4 @@
<% from pwnlib.shellcraft import common %>
<%docstring>Returns code to switch from amd64 to i386 mode.
<%docstring>Returns code to switch from amd64 to i386 mode. Trashes eax, ecx, edx.
Note that you most surely want to set up some stack (and place this code)
in low address space before (or afterwards).</%docstring>
@@ -5,23 +5,12 @@ Args: [sock (imm/reg) = rbp]
Duplicates sock to stdin, stdout and stderr
</%docstring>
<%
dup = common.label("dup")
looplabel = common.label("loop")
after = common.label("after")
%>
/* dup() file descriptor ${sock} into stdin/stdout/stderr */
${dup}:
${amd64.mov('rbp', sock)}
push 3
${amd64.setregs({'rdi': sock, 'rsi': 2})}
${looplabel}:
pop rsi
${amd64.linux.dup2('rdi', 'rsi')}
dec rsi
js ${after}
push rsi
${amd64.linux.syscall('SYS_dup2', 'rbp', 'rsi')}
jmp ${looplabel}
${after}:
jns ${looplabel}
@@ -7,6 +7,6 @@ Args: [sock (imm/reg) = rbp]
</%docstring>
${linux.dup(sock)}
${linux.dupio(sock)}
${linux.sh()}
@@ -5,4 +5,4 @@
${amd64.linux.mmap_rwx(size)}
${amd64.mov('rsp', 'rax')}
add rsp, ${hex((size * 3 // 4) & ~7)}
add rsp, ${'%#x' % ((size * 3 // 4) & ~7)}
@@ -0,0 +1,18 @@
<% from pwnlib.shellcraft import common %>
<% from pwnlib.shellcraft.amd64 import mov, linux %>
<%page args="ruid=None, euid=None, suid=None"/>
<%docstring>
Args: [ruid = geteuid(), euid = ruid, suid = ruid]
Sets real, effective and saved user ids to given values
</%docstring>
%if ruid is None:
${linux.geteuid()}
<% ruid = 'eax' %>
%endif
<%
if euid is None: euid = ruid
if suid is None: suid = ruid
%>
${linux.syscalls.setresuid(ruid, euid, suid)}
+1
View File
@@ -0,0 +1 @@
../../common/linux/sleep.asm
@@ -0,0 +1 @@
Shellcraft module containing Intel x86_64 shellcodes for Windows.
@@ -0,0 +1,9 @@
<%
from pwnlib.shellcraft import amd64
%>
<%docstring>Execute cmd.exe and keep the parent process
in an infinite loop.
</%docstring>
${amd64.windows.winexec(b'cmd.exe')}
${amd64.infloop()}
@@ -0,0 +1,55 @@
<%
from pwnlib.shellcraft import amd64, pretty
from pwnlib.util.packing import u64, _need_bytes
%>
<%docstring>Find the address of an exported function in a dll
by manually iterating through the PE export table.
Args:
function_name (str): The name of the function to find.
dll(str): The name of the DLL to find the function in.
dest (str): The register to load the function address into.
</%docstring>
<%page args="function_name,dll='kernel32.dll',dest='rax'"/>
<%
function_name = _need_bytes(function_name)
dll = _need_bytes(dll)
if len(function_name) > 8:
raise ValueError('function_name must be <= 8 bytes')
assert dll == b'kernel32.dll'
%>
${amd64.windows.kernel32base('rbx')} /* rbx = kernel32.dll PE base */
mov r8d, [rbx + 0x3c]
mov rdx, r8
add rdx, rbx
${amd64.mov('r9', 0x88)}
add rdx, r9
mov r8d, [rdx]
add r8, rbx /* r8 = export table */
mov esi, [r8 + 0x20]
add rsi, rbx /* rsi = names table */
xor rcx, rcx
% if len(function_name) <= 8:
mov r9, ${pretty(u64(function_name.ljust(8, b'\x00')))}
% else:
${amd64.pushstr(function_name)}
mov r9, rsp
% endif
/* Loop through the names table */
FindFunction:
inc rcx
mov eax, [rsi + rcx * 4]
add rax, rbx
## TODO: implement strcmp properly for function names > 8 bytes
cmp qword ptr [rax], r9
jnz FindFunction
mov esi, [r8 + 0x24]
add rsi, rbx /* rsi = ordinals table */
mov cx, [rsi + rcx * 2]
mov esi, [r8 + 0x1c]
add rsi, rbx /* rsi = address table */
mov eax, [rsi + rcx * 4]
add rax, rbx /* rax = function address */
mov ${dest}, rax
@@ -0,0 +1,28 @@
<%
from pwnlib.shellcraft import amd64, pretty
from pwnlib.util.packing import _need_bytes
from pwnlib.util.misc import align
%>
<%docstring>Find the address of an exported function
by calling kernel32::GetProcAddress.
Args:
function_name(str): The name of the function to find.
dll(str): The name of the DLL to find the function in.
dest (str): The register to load the function address into.
</%docstring>
<%page args="function_name,dll='kernel32.dll',dest='rax'"/>
<%
function_name = _need_bytes(function_name)
dll = _need_bytes(dll)
assert dll == b'kernel32.dll'
%>
${amd64.windows.getexport(b'GetProcA', b'kernel32.dll', dest='rdi')}
${amd64.pushstr(function_name)}
mov rdx, rsp
${amd64.windows.kernel32base(dest='rcx')}
sub rsp, 0x30
call rdi
add rsp, ${pretty(0x30+align(8, len(function_name)))}
mov ${dest}, rax
@@ -0,0 +1,18 @@
<% from pwnlib.shellcraft import amd64 %>
<%docstring>Find the base address of kernel32.dll in memory.
Args:
dest (str): The register to load the kernel32.dll base address into.
</%docstring>
<%page args="dest='rax'"/>
## The loaded list of modules always starts with
## 1. the executable itself
## 2. ntdll.dll
## 3. kernel32.dll
${amd64.windows.peb(dest)}
mov ${dest}, [${dest} + 0x18] /* PEB->Ldr */
mov rsi, [${dest} + 0x20] /* PEB->Ldr.InMemOrder LIST_ENTRY */
lodsq
xchg rax, rsi
lodsq
mov ${dest}, [rax + 0x20] /* LDR_DATA_TABLE_ENTRY->DllBase */
@@ -0,0 +1,16 @@
<% from pwnlib.shellcraft import amd64 %>
<%docstring>Find the base address of ntdll.dll in memory.
Args:
dest (str): The register to load the ntdll.dll base address into.
</%docstring>
<%page args="dest='rax'"/>
## The loaded list of modules always starts with:
## 1. the executable itself
## 2. ntdll.dll
## 3. kernel32.dll
${amd64.windows.peb(dest)}
mov ${dest}, [${dest} + 0x18] /* PEB->Ldr */
mov rsi, [${dest} + 0x20] /* PEB->Ldr.InMemOrder LIST_ENTRY */
lodsq
mov ${dest}, [rax + 0x20] /* LDR_DATA_TABLE_ENTRY->DllBase */
@@ -0,0 +1,8 @@
<% from pwnlib.shellcraft import amd64 %>
<%docstring>Loads the Process Environment Block (PEB) into the target register.
Args:
dest (str): The register to load the PEB into.
</%docstring>
<%page args="dest='rax'"/>
${amd64.windows.teb(dest, 0x60)}
@@ -0,0 +1,21 @@
<%
from pwnlib.log import getLogger
from pwnlib.shellcraft import pretty
from pwnlib.shellcraft.registers import get_register, is_register
log = getLogger('pwnlib.shellcraft.amd64.teb')
%>
<%page args="dest='rax', offset=0"/>
<%docstring>Loads the Thread Environment Block (TEB) into the target register.
Args:
dest (str): The register to load the TEB into.
offset (int): The offset from the TEB to load.
</%docstring>
<%
if not is_register(dest):
log.error('%r is not a register' % dest)
dest = get_register(dest)
%>
xor esi, esi
mov ${dest}, qword ptr gs:[rsi+${pretty(offset)}]
@@ -0,0 +1,21 @@
<%
from pwnlib.shellcraft import amd64, pretty
from pwnlib.util.packing import _need_bytes
from pwnlib.util.misc import align
%>
<%docstring>Execute a program using WinExec.
Args:
cmd (str): The program to execute.
</%docstring>
<%page args="cmd"/>
<%
cmd = _need_bytes(cmd)
%>
${amd64.windows.getprocaddress(b'WinExec', b'kernel32.dll', 'rsi')}
${amd64.pushstr(cmd)}
mov rcx, rsp
sub rsp, 0x30
call rsi
add rsp, ${pretty(0x30+align(8, len(cmd)))}
@@ -0,0 +1,19 @@
<% from pwnlib.shellcraft import common %>
<% from pwnlib.shellcraft.arm import mov %>
<%page args="sock = 'r6'"/>
<%docstring>
Args: [sock (imm/reg) = r6]
Duplicates sock to stdin, stdout and stderr
</%docstring>
<%
looplabel = common.label("loop")
%>
/* dup() file descriptor ${sock} into stdin/stdout/stderr */
${mov('r1', 2)}
${mov('r7', 'SYS_dup2')}
${looplabel}:
${mov('r0', sock)}
svc 0
subs r1, #1
bpl ${looplabel}
Loaded 100 of 627 files, more files were not shown because too many files have changed in this diff. Show more