mirror of
https://github.com/Gallopsled/pwntools.git
synced 2026-11-09 01:59:33 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72a93f7a3c | ||
|
|
c520efca42 | ||
|
|
a743ccc7fe | ||
|
|
0eb7fdbd88 | ||
|
|
ec4cc53bd0 | ||
|
|
99d3cd708f | ||
|
|
0ee2939a9d | ||
|
|
917ffd8667 | ||
|
|
3ff46ff8d4 | ||
|
|
2fcc39eb6e | ||
|
|
ccb49abe35 | ||
|
|
0a978df7bf | ||
|
|
ad4c62b9fd | ||
|
|
21c9d00078 | ||
|
|
1b6393f381 | ||
|
|
1f68bf6560 | ||
|
|
9706275a3d | ||
|
|
8210bba2ab | ||
|
|
c0e4db94f1 | ||
|
|
163c19dd49 | ||
|
|
c4c11a37f8 | ||
|
|
9b56b58f95 | ||
|
|
c4dd1c22ba | ||
|
|
dd1f776c6c | ||
|
|
3d4853f9a7 | ||
|
|
a8e594d7a3 | ||
|
|
1b6e663f93 | ||
|
|
4fe423c1b4 | ||
|
|
8673f53aab | ||
|
|
3875a8ff18 | ||
|
|
97f5742f5c | ||
|
|
5b8acf0893 | ||
|
|
9bb47493ba | ||
|
|
b952728fad | ||
|
|
d8b69dde24 | ||
|
|
e5f17630dc | ||
|
|
b7c985ad42 | ||
|
|
34ef7dce64 | ||
|
|
c9909eea0d | ||
|
|
1d92ee3583 | ||
|
|
8eb242df5a | ||
|
|
4b2037028e | ||
|
|
b6bc3629e9 | ||
|
|
1d7d5ecd7f | ||
|
|
0a558391d9 | ||
|
|
e33a3fd517 | ||
|
|
ca0b08f500 | ||
|
|
358581a26f | ||
|
|
60461af312 | ||
|
|
37492fd579 | ||
|
|
e4a0703178 | ||
|
|
a1e74b3d57 | ||
|
|
055d8b87ae | ||
|
|
d82bab2849 | ||
|
|
904872edc8 | ||
|
|
7b072dc7f7 | ||
|
|
d8219b8826 | ||
|
|
eee1503252 | ||
|
|
c3784268a1 | ||
|
|
6d340bf2ac | ||
|
|
2124f3607a | ||
|
|
f727b66475 | ||
|
|
c334cebade | ||
|
|
010c89c3b3 | ||
|
|
82dce28722 | ||
|
|
5fdeb21de1 | ||
|
|
8a4288f895 | ||
|
|
ecc160a36c | ||
|
|
df59b10eb3 | ||
|
|
7b9bcf8d7b | ||
|
|
59ec1b1a1b | ||
|
|
41702e89e6 | ||
|
|
f6fe196780 | ||
|
|
6667a239be | ||
|
|
59c78105e1 | ||
|
|
b78fd6e3c8 | ||
|
|
ab67da5d8e | ||
|
|
0491972d1c | ||
|
|
98bdcfd9f0 | ||
|
|
dbf0549213 | ||
|
|
11e9d814c9 | ||
|
|
aaa3cb48b9 | ||
|
|
9476e2e5b0 | ||
|
|
99e9bcbf30 | ||
|
|
adaba022e6 | ||
|
|
37aeb060c6 | ||
|
|
faab6ea3dd | ||
|
|
5bba036c54 | ||
|
|
11a70f2e9e | ||
|
|
55dfe9b5e4 | ||
|
|
70de143278 |
No files matched your search
+3
-1
@@ -1,8 +1,10 @@
|
||||
[run]
|
||||
branch = True
|
||||
parallel = True
|
||||
omit =
|
||||
*/constants/*
|
||||
source =
|
||||
pwn
|
||||
pwnlib
|
||||
~/.pwntools-cache/
|
||||
~/.pwntools-cache-2.7/
|
||||
~/.pwntools-cache-3.8/
|
||||
@@ -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
|
||||
```
|
||||
@@ -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!
|
||||
@@ -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.
|
||||
@@ -0,0 +1,197 @@
|
||||
name: Continuous Integration
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [2.7, 3.8]
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
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
|
||||
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: Lint
|
||||
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__
|
||||
|
||||
- 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 gdbserver socat sshpass \
|
||||
binutils-multiarch qemu-user-static \
|
||||
binutils-aarch64-linux-gnu \
|
||||
binutils-arm-linux-gnueabihf \
|
||||
binutils-mips-linux-gnu \
|
||||
binutils-powerpc-linux-gnu \
|
||||
gcc-multilib \
|
||||
$ANDROID_JRE
|
||||
sudo apt-get install -y -o Acquire::Retries=3 \
|
||||
gcc-aarch64-linux-gnu \
|
||||
gcc-arm-linux-gnueabihf
|
||||
|
||||
- name: Install android avd
|
||||
if: steps.java-needed.outputs.need
|
||||
run: |
|
||||
USER=travis source travis/install.sh
|
||||
set | egrep '^(ANDROID|PATH)' >.android.env
|
||||
|
||||
- name: Set up SSH
|
||||
run: |
|
||||
travis/ssh_setup.sh
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install --upgrade flake8 appdirs
|
||||
python setup.py egg_info
|
||||
pip install --upgrade --editable .
|
||||
|
||||
- name: Sanity checks
|
||||
run: PWNLIB_NOTERM=1 python -c 'from pwn import *; print(pwnlib.term.term_mode)'
|
||||
|
||||
- name: Install documentation dependencies
|
||||
run: pip install -r docs/requirements.txt
|
||||
|
||||
- name: Disable broken tests
|
||||
run: |
|
||||
rm -f docs/source/elf/corefile.rst
|
||||
rm -f docs/source/ui.rst
|
||||
|
||||
- name: Manually install non-broken Unicorn
|
||||
run: pip install unicorn==1.0.2rc3
|
||||
|
||||
- name: Coverage doctests
|
||||
run: |
|
||||
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 || : # 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() { coverage run -m pwnlib.commandline.main "$@"; }
|
||||
set -x
|
||||
|
||||
pwn cyclic 32
|
||||
pwn cyclic -l 0x62616161
|
||||
|
||||
pwn shellcraft -f asm --color amd64.linux.sh
|
||||
|
||||
pwn disasm --color ff3424c3ebfe
|
||||
|
||||
pwn hex abcd
|
||||
pwn unhex 4141 4141
|
||||
|
||||
head -c256 /dev/urandom | pwn phd
|
||||
|
||||
pwn errno 2
|
||||
|
||||
pwn constgrep -c freebsd -m ^PROT_ '3 + 4'
|
||||
pwn constgrep ^MAP_ 0
|
||||
pwn constgrep ^O_
|
||||
|
||||
- name: Build source and wheel distributions
|
||||
run: |
|
||||
python setup.py sdist
|
||||
python setup.py bdist_wheel --universal
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
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: build
|
||||
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"
|
||||
|
||||
pypi:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
|
||||
needs: build
|
||||
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
|
||||
@@ -16,4 +16,3 @@ venv
|
||||
*.egg-info
|
||||
*.core
|
||||
.coverage
|
||||
.tox
|
||||
+17
-5
@@ -1,9 +1,7 @@
|
||||
dist: xenial
|
||||
language: python
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
- lib32stdc++6
|
||||
- ash
|
||||
- bash
|
||||
@@ -13,6 +11,8 @@ addons:
|
||||
- zsh
|
||||
- pandoc
|
||||
- gdb
|
||||
- socat
|
||||
- sshpass
|
||||
- binutils
|
||||
- qemu-user-static
|
||||
- binutils-multiarch
|
||||
@@ -20,15 +20,22 @@ addons:
|
||||
- binutils-arm-linux-gnueabihf
|
||||
- binutils-mips-linux-gnu
|
||||
- binutils-powerpc-linux-gnu
|
||||
- gcc-aarch64-linux-gnu
|
||||
- gcc-arm-linux-gnueabihf
|
||||
- bash-static
|
||||
|
||||
cache:
|
||||
- pip
|
||||
- directories:
|
||||
- usr
|
||||
- /home/travis/virtualenv/python2.7.15/lib/python2.7/site-packages/
|
||||
- /home/travis/virtualenv/python2.7.15/bin/
|
||||
- /home/travis/virtualenv/python3.8.*/lib/python3.8/site-packages/
|
||||
- /home/travis/virtualenv/python3.8.*/bin/
|
||||
- docs/build/doctest/.doctrees
|
||||
python:
|
||||
- "2.7.15"
|
||||
- "3.6"
|
||||
- "2.7"
|
||||
- "3.8"
|
||||
before_install:
|
||||
- echo $-
|
||||
- source travis/install.sh
|
||||
@@ -36,7 +43,7 @@ before_install:
|
||||
- echo $-
|
||||
install:
|
||||
- pip install --upgrade pip
|
||||
- pip install --upgrade appdirs # https://github.com/ActiveState/appdirs/issues/89#issuecomment-282326570
|
||||
- pip install --upgrade flake8 appdirs # https://github.com/ActiveState/appdirs/issues/89#issuecomment-282326570
|
||||
- python setup.py egg_info # pypa/pip#6275
|
||||
- pip install --upgrade --editable .
|
||||
- pip install --upgrade --requirement docs/requirements.txt
|
||||
@@ -45,8 +52,13 @@ before_script:
|
||||
- PWNLIB_NOTERM=1 python -c 'from pwn import *; print(pwnlib.term.term_mode)'
|
||||
- PWNLIB_NOTERM=1 python -c 'from pwn import *; print(pwnlib.term.term_mode)'
|
||||
- PWNLIB_NOTERM=1 python -c 'from pwn import *; print(pwnlib.term.term_mode)'
|
||||
- sudo sh -c "echo 0 > /proc/sys/kernel/yama/ptrace_scope"
|
||||
script:
|
||||
# stop the build if there are Python syntax errors or undefined names
|
||||
- flake8 . --count --select=E9,F63,F7 --show-source --statistics --exclude=android-?dk # TODO: Add F82
|
||||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||
- PWNLIB_NOTERM=1 coverage run -m sphinx -b doctest docs/source docs/build/doctest
|
||||
- coverage combine
|
||||
after_success:
|
||||
- coveralls
|
||||
- source travis/update_demo.sh
|
||||
+73
-39
@@ -9,10 +9,14 @@ The table below shows which release corresponds to each branch, and what date th
|
||||
|
||||
| Version | Branch | Release Date |
|
||||
| ---------------- | -------- | ---------------------- |
|
||||
| [4.2.0](#420) | `dev` | Feb 10, 2020 (planned)
|
||||
| [4.1.0](#410) | `beta` | Jan 30, 2020 (planned)
|
||||
| [4.0.0](#400) | `stable` | Jan 09, 2020
|
||||
| [4.3.0](#430) | `dev` | Jun 30, 2020 (planned)
|
||||
| [4.2.0](#420) | `beta` | Jun 5, 2020 (planned)
|
||||
| [4.1.1](#411) | `stable` | 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
|
||||
@@ -45,13 +49,33 @@ 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.2.0 (`dev`)
|
||||
## 4.3.0 (`dev`)
|
||||
|
||||
To be released on Feb 10, 2020.
|
||||
To be released on Jun 30, 2020.
|
||||
|
||||
## 4.1.0 (`beta`)
|
||||
## 4.2.0 (`beta`)
|
||||
|
||||
To be released on Jan 30, 2020.
|
||||
To be released on Jun 5, 2020.
|
||||
|
||||
- [#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][1454] Support for windows console colors
|
||||
|
||||
[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.1 (`stable`)
|
||||
|
||||
- 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
|
||||
@@ -60,10 +84,21 @@ To be released on Jan 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.0 (`stable`)
|
||||
## 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/pull/1412
|
||||
|
||||
## 4.0.0
|
||||
|
||||
- **Python 3 support! <3**
|
||||
- [#1402][1402] Fix serialtube in python 3
|
||||
@@ -75,12 +110,13 @@ To be released on Jan 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
|
||||
@@ -91,32 +127,16 @@ To be released on Jan 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
|
||||
@@ -134,7 +154,15 @@ To be released on Jan 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
|
||||
@@ -144,6 +172,12 @@ To be released on Jan 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
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||

|
||||
|
||||
[](https://docs.pwntools.com/)
|
||||
[](https://pypi.python.org/pypi/pwntools/)
|
||||
[](https://pypi.python.org/pypi/pwntools/)
|
||||
[](https://travis-ci.org/Gallopsled/pwntools)
|
||||
[](https://coveralls.io/github/Gallopsled/pwntools?branch=dev)
|
||||
[](https://twitter.com/pwntools)
|
||||
|
||||
@@ -7,6 +7,7 @@ paramiko>=1.15.2
|
||||
pip>=6.0.8
|
||||
pyelftools>=0.2.3
|
||||
pygments>=2.0
|
||||
pypandoc
|
||||
pyserial>=2.7
|
||||
requests>=2.5.1
|
||||
ropgadget>=5.3
|
||||
@@ -14,4 +15,3 @@ sphinx
|
||||
sphinx_rtd_theme
|
||||
sphinxcontrib-napoleon
|
||||
sphinxcontrib-autoprogram
|
||||
tox>=1.8.1
|
||||
@@ -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``.
|
||||
|
||||
|
||||
+9
-33
@@ -13,6 +13,7 @@
|
||||
|
||||
import os
|
||||
import doctest
|
||||
import signal
|
||||
import six
|
||||
import subprocess
|
||||
import sys
|
||||
@@ -350,7 +351,7 @@ def linkcode_resolve(domain, info):
|
||||
if isinstance(val, (types.ModuleType, types.MethodType, types.FunctionType, types.TracebackType, types.FrameType, types.CodeType) + six.class_types):
|
||||
try:
|
||||
lines, first = inspect.getsourcelines(val)
|
||||
filename += '#L%d-%d' % (first, first + len(lines) - 1)
|
||||
filename += '#L%d-L%d' % (first, first + len(lines) - 1)
|
||||
except (IOError, TypeError):
|
||||
pass
|
||||
|
||||
@@ -374,42 +375,11 @@ if build_dash:
|
||||
|
||||
# -- Customization to Sphinx autodoc generation --------------------------------------------
|
||||
import sphinx.ext.autodoc
|
||||
from sphinx.util.inspect import safe_getmembers, safe_getattr
|
||||
|
||||
# Test hidden members (e.g. def _foo(...))
|
||||
def dont_skip_any_doctests(app, what, name, obj, skip, options):
|
||||
return False
|
||||
|
||||
def get_object_members_all(self, want_all):
|
||||
if want_all:
|
||||
# if not hasattr(self.object, '__all__'):
|
||||
# for implicit module members, check __module__ to avoid
|
||||
# documenting imported objects
|
||||
return True, safe_getmembers(self.object)
|
||||
# else:
|
||||
# memberlist = self.object.__all__
|
||||
# # Sometimes __all__ is broken...
|
||||
# if not isinstance(memberlist, (list, tuple)) or not \
|
||||
# all(isinstance(entry, string_types) for entry in memberlist):
|
||||
# self.directive.warn(
|
||||
# '__all__ should be a list of strings, not %r '
|
||||
# '(in module %s) -- ignoring __all__' %
|
||||
# (memberlist, self.fullname))
|
||||
# # fall back to all members
|
||||
# return True, safe_getmembers(self.object)
|
||||
else:
|
||||
memberlist = self.options.members or []
|
||||
ret = []
|
||||
for mname in memberlist:
|
||||
try:
|
||||
ret.append((mname, safe_getattr(self.object, mname)))
|
||||
except AttributeError:
|
||||
self.directive.warn(
|
||||
'missing attribute mentioned in :members: or __all__: '
|
||||
'module %s, attribute %s' % (
|
||||
safe_getattr(self.object, '__name__', '???'), mname))
|
||||
return False, ret
|
||||
|
||||
class _DummyClass(object): pass
|
||||
|
||||
class Py2OutputChecker(_DummyClass, doctest.OutputChecker):
|
||||
@@ -437,6 +407,13 @@ 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)
|
||||
@@ -444,4 +421,3 @@ if 'doctest' in sys.argv:
|
||||
if sys.version_info[:1] < (3,):
|
||||
import sphinx.ext.doctest
|
||||
sphinx.ext.doctest.SphinxDocTestRunner.__init__ = py2_doctest_init
|
||||
sphinx.ext.autodoc.ModuleDocumenter.get_object_members = get_object_members_all
|
||||
@@ -0,0 +1,9 @@
|
||||
.. testsetup:: *
|
||||
|
||||
from pwnlib.filepointer import *
|
||||
|
||||
:mod:`pwnlib.filepointer` --- `FILE*` structure exploitation
|
||||
============================================================
|
||||
|
||||
.. automodule:: pwnlib.filepointer
|
||||
:members:
|
||||
@@ -1,6 +1,8 @@
|
||||
.. testsetup:: *
|
||||
|
||||
from pwn import *
|
||||
context.arch = 'amd64'
|
||||
context.terminal = [os.path.join(os.path.dirname(pwnlib.__file__), 'gdb_faketerminal.py')]
|
||||
|
||||
:mod:`pwnlib.gdb` --- Working with GDB
|
||||
======================================
|
||||
|
||||
@@ -54,6 +54,7 @@ Each of the ``pwntools`` modules is documented here.
|
||||
encoders
|
||||
elf
|
||||
exception
|
||||
filepointer
|
||||
flag
|
||||
fmtstr
|
||||
gdb
|
||||
|
||||
@@ -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
|
||||
--------------
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from pwn import *
|
||||
from pwnlib.protocols.adb import AdbClient
|
||||
AdbClient().wait_for_device()
|
||||
|
||||
:mod:`pwnlib.protocols.adb` --- ADB Protocol Implementation
|
||||
===========================================================
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
.. testsetup:: *
|
||||
|
||||
from pwnlib.term.readline import *
|
||||
import io
|
||||
|
||||
:mod:`pwnlib.term.readline` --- Terminal nice readline
|
||||
======================================================
|
||||
|
||||
.. automodule:: pwnlib.term.readline
|
||||
:members:
|
||||
@@ -1,3 +1,8 @@
|
||||
.. testsetup:: *
|
||||
|
||||
from pwn import *
|
||||
import io
|
||||
|
||||
:mod:`pwnlib.ui` --- Functions for user interaction
|
||||
===================================================
|
||||
|
||||
|
||||
+11
-4
@@ -26,6 +26,10 @@ Try running this example with::
|
||||
```
|
||||
Example showing the interface to `pwnlib.asm.asm` and `pwnlib.shellcraft`.
|
||||
```
|
||||
* `attach.py`
|
||||
```
|
||||
Example showing `pwnlib.gdb.attach()`
|
||||
```
|
||||
* `clean_and_log.py`
|
||||
```
|
||||
Use case for `pwnlib.tubes.tube.clean_and_log`.
|
||||
@@ -65,8 +69,7 @@ Example showing `pwnlib.ui.options()`
|
||||
```
|
||||
* `port_forward.py`
|
||||
```
|
||||
A very simple port forwarder using `pwnlib.tubes.tube.connect_both()`. Notice
|
||||
that `<>` is just a shorthand.
|
||||
A very simple port forwarder using `pwnlib.tubes.tube.connect_both()`.
|
||||
```
|
||||
* `readline_completers.py`
|
||||
```
|
||||
@@ -74,6 +77,10 @@ Example showing pwnlib's readline implementation and a few completers. This
|
||||
part of pwnlib will probably see some major changes soon, but we wanted to show
|
||||
off some proof-of-concepts.
|
||||
```
|
||||
* `remote.py`
|
||||
```
|
||||
Example showing how to use the remote class.
|
||||
```
|
||||
* `remote_gdb_debugging.py`
|
||||
```
|
||||
Simple example showing how to use the remote
|
||||
@@ -103,7 +110,7 @@ and::
|
||||
|
||||
$ TERM=xterm-256color python text.py
|
||||
```
|
||||
* `remote.py`
|
||||
* `yesno.py`
|
||||
```
|
||||
Example showing how to use the remote class.
|
||||
Example showing `pwnlib.ui.yesno()`
|
||||
```
|
||||
+12
-9
@@ -1,22 +1,25 @@
|
||||
from pwn import *
|
||||
from __future__ import print_function
|
||||
|
||||
import gdb
|
||||
from pwn import adb, context
|
||||
|
||||
# Tell pwntools that the target is an Android device
|
||||
context.os='android'
|
||||
context.arch='aarch64' # or 'arm'
|
||||
context.os = "android"
|
||||
context.arch = "aarch64" # or 'arm'
|
||||
|
||||
# Optionally, set the remote ADB server address
|
||||
context.adb_host='172.16.110.1'
|
||||
context.adb_host = "172.16.110.1"
|
||||
|
||||
# Wait for a device to become available
|
||||
print adb.wait_for_device()
|
||||
print(adb.wait_for_device())
|
||||
|
||||
# Who am I?
|
||||
print adb.process('id').recvall().strip()
|
||||
print(adb.process("id").recvall().strip())
|
||||
|
||||
# Interactive sessions!
|
||||
io = adb.shell()
|
||||
io.sendline('echo Hello, world; exit')
|
||||
print io.recvall().replace('\r\n', '\n').strip()
|
||||
io.sendline("echo Hello, world; exit")
|
||||
print(io.recvall().replace("\r\n", "\n").strip())
|
||||
|
||||
# Debugging!
|
||||
gdb.debug('sh').interactive()
|
||||
gdb.debug("sh").interactive()
|
||||
+2
-2
@@ -14,5 +14,5 @@ Try running this example with::
|
||||
|
||||
from pwn import *
|
||||
|
||||
print args['RHOST'] or 'RHOST is not set'
|
||||
print args['RPORT'] or 'RPORT is not set'
|
||||
print(args['RHOST'] or 'RHOST is not set')
|
||||
print(args['RPORT'] or 'RPORT is not set')
|
||||
+2
-2
@@ -9,5 +9,5 @@ context(arch='i386', os='linux')
|
||||
shellcode = shellcraft.i386_to_amd64()
|
||||
shellcode_asm = asm(shellcode)
|
||||
|
||||
print enhex(shellcode_asm)
|
||||
print disasm(shellcode_asm)
|
||||
print(enhex(shellcode_asm))
|
||||
print(disasm(shellcode_asm))
|
||||
+1
-1
@@ -5,7 +5,7 @@ Example showing `pwnlib.gdb.attach()`
|
||||
from pwn import *
|
||||
|
||||
bash = process('/bin/bash')
|
||||
gdb.attach(bash, execute = '''
|
||||
gdb.attach(bash, gdbscript = '''
|
||||
p "hello from pwnlib"
|
||||
c
|
||||
''')
|
||||
|
||||
@@ -25,4 +25,4 @@ atexit.register(r.clean_and_log)
|
||||
|
||||
while True:
|
||||
line = r.recvline()
|
||||
print re.findall('^prefix (\S+)$', line)[0]
|
||||
print(re.findall('^prefix (\S+)$', line)[0])
|
||||
@@ -16,7 +16,7 @@ def exploit(binary):
|
||||
payload = fmtstr_payload(offset, {addr: p32(0x1337babe)})
|
||||
p.sendline(payload)
|
||||
p.recvuntil("DONE")
|
||||
print hex(u32(p.recv(4)))
|
||||
print(hex(u32(p.recv(4))))
|
||||
|
||||
binaries = [
|
||||
"printf.mips",
|
||||
|
||||
@@ -24,7 +24,7 @@ def append_example(_arg, top, names):
|
||||
continue
|
||||
path = os.path.join(top, name)[2:] # strip './'
|
||||
log.info('-> %s' % path)
|
||||
data = read(path).strip()
|
||||
data = read(path).strip().decode()
|
||||
if data[0:3] not in ('"""', "'''"):
|
||||
log.warning(' Has no docstring!')
|
||||
continue
|
||||
@@ -37,6 +37,7 @@ def append_example(_arg, top, names):
|
||||
out += '* `%s`\n' % path
|
||||
out += '```%s```\n' % doc
|
||||
|
||||
os.path.walk('.', append_example, None)
|
||||
for path, dirs, files in os.walk('.', onerror=None):
|
||||
append_example(dirs, path, sorted(files))
|
||||
|
||||
write('README.md', out)
|
||||
+1
-1
@@ -5,4 +5,4 @@ Example showing `pwnlib.ui.options()`
|
||||
from pwn import *
|
||||
|
||||
opts = [string.letters[x] for x in range(10)]
|
||||
print 'You choose "%s"' % opts[options('Pick one:', opts)]
|
||||
print('You choose "%s"' % opts[options('Pick one:', opts)])
|
||||
@@ -1,11 +1,10 @@
|
||||
"""
|
||||
A very simple port forwarder using `pwnlib.tubes.tube.connect_both()`. Notice
|
||||
that `<>` is just a shorthand.
|
||||
A very simple port forwarder using `pwnlib.tubes.tube.connect_both()`.
|
||||
"""
|
||||
|
||||
from pwn import *
|
||||
|
||||
while True:
|
||||
listen(1337).wait_for_connection() <> remote('google.com', 80)
|
||||
listen(1337).wait_for_connection().connect_both(remote('google.com', 80))
|
||||
|
||||
# now point your browser (or curl(1)) to http://localhost:1337
|
||||
@@ -19,13 +19,13 @@ c1 = LongestPrefixCompleter([
|
||||
c2 = PathCompleter(mask = '*.py')
|
||||
|
||||
with c1:
|
||||
print 'type "exit" to exit'
|
||||
print('type "exit" to exit')
|
||||
while True:
|
||||
s = term.readline.readline(prompt = '> ').strip()
|
||||
if s in ('exit', 'enough!'):
|
||||
break
|
||||
print 'You wrote', s
|
||||
print('You wrote', s)
|
||||
with c2:
|
||||
print 'choose a file'
|
||||
print('choose a file')
|
||||
s = term.readline.readline(prompt = text.bold_green('$ ')).strip()
|
||||
print 'You picked', s
|
||||
print('You picked', s)
|
||||
+1
-1
@@ -6,6 +6,6 @@ from pwn import *
|
||||
|
||||
sock = remote('127.0.0.1', 9001)
|
||||
|
||||
print sock.recvline()
|
||||
print(sock.recvline())
|
||||
sock.send('foo')
|
||||
sock.interactive()
|
||||
@@ -8,10 +8,10 @@ import getpass
|
||||
from pwn import *
|
||||
|
||||
s = ssh(getpass.getuser(), '127.0.0.1', port = 22, keyfile = "~/.ssh/id_rsa")
|
||||
c = gdb.ssh_gdb(s, '/bin/sh', execute = '''
|
||||
c = gdb.ssh_gdb(s, '/bin/sh', gdbscript = '''
|
||||
p/x $pc
|
||||
c''')
|
||||
|
||||
c.sendline('ls -la')
|
||||
c.sendline('exit')
|
||||
print c.recvall()
|
||||
print(c.recvall())
|
||||
@@ -10,7 +10,7 @@ n = 1
|
||||
h = log.waitfor('spinners running', status = str(n))
|
||||
|
||||
hs = []
|
||||
print 'type "q" to quit'
|
||||
print('type "q" to quit')
|
||||
while True:
|
||||
s = raw_input('> ')
|
||||
if s == 'q':
|
||||
|
||||
+5
-5
@@ -14,7 +14,7 @@ log.info("pwd: %s" % shell.pwd())
|
||||
tube = shell.run('cat')
|
||||
tube.send("Hello, cat")
|
||||
tube.shutdown("out")
|
||||
print tube.recvall()
|
||||
print(tube.recvall())
|
||||
|
||||
# Show automatic working directories
|
||||
shell.set_working_directory()
|
||||
@@ -29,12 +29,12 @@ int main() {
|
||||
|
||||
shell.gcc(['example.c','-o','example'])
|
||||
|
||||
print shell['./example']
|
||||
print(shell['./example'])
|
||||
|
||||
# Show the different styles of calling
|
||||
print shell.echo("single string")
|
||||
print shell.echo(["list","of","strings"])
|
||||
print shell["echo single statement"]
|
||||
print(shell.echo("single string"))
|
||||
print(shell.echo(["list","of","strings"]))
|
||||
print(shell["echo single statement"])
|
||||
|
||||
# Show off the interactive shell
|
||||
shell.interactive()
|
||||
+5
-5
@@ -12,8 +12,8 @@ and::
|
||||
from pwn import *
|
||||
|
||||
s = 'hello from pwntools'
|
||||
print text.black_on_green(s)
|
||||
print text.black_on_bright_green(s)
|
||||
print text.green_on_black(s)
|
||||
print text.bright_green_on_black(s)
|
||||
print text.bold_green_on_black(s)
|
||||
print(text.black_on_green(s))
|
||||
print(text.black_on_bright_green(s))
|
||||
print(text.green_on_black(s))
|
||||
print(text.bright_green_on_black(s))
|
||||
print(text.bold_green_on_black(s))
|
||||
+2
-2
@@ -5,6 +5,6 @@ Example showing `pwnlib.ui.yesno()`
|
||||
from pwn import *
|
||||
|
||||
if ui.yesno('Do you like Pwntools?'):
|
||||
print ':D'
|
||||
print(':D')
|
||||
else:
|
||||
print ':('
|
||||
print(':(')
|
||||
@@ -29,6 +29,7 @@ from pwnlib.elf.elf import ELF, load
|
||||
from pwnlib.encoders import *
|
||||
from pwnlib.exception import PwnlibException
|
||||
from pwnlib.gdb import attach, debug, debug_assembly, debug_shellcode
|
||||
from pwnlib.filepointer import *
|
||||
from pwnlib.flag import *
|
||||
from pwnlib.fmtstr import FmtStr, fmtstr_payload
|
||||
from pwnlib.log import getLogger
|
||||
|
||||
+73
-30
@@ -115,7 +115,7 @@ def current_device(any=False):
|
||||
|
||||
>>> device = adb.current_device(any=True)
|
||||
>>> device
|
||||
AdbDevice(serial='emulator-5554', type='device', port='emulator', product='sdk_phone_armv7', model='sdk phone armv7', device='generic')
|
||||
AdbDevice(serial='emulator-5554', type='device', port='emulator', product='sdk_google_phone_armv7', model='sdk google phone armv7', device='generic')
|
||||
>>> device.port
|
||||
'emulator'
|
||||
"""
|
||||
@@ -228,7 +228,7 @@ class AdbDevice(Device):
|
||||
>>> device.os
|
||||
'android'
|
||||
>>> device.product
|
||||
'sdk_phone_armv7'
|
||||
'sdk_google_phone_armv7'
|
||||
>>> device.serial
|
||||
'emulator-5554'
|
||||
"""
|
||||
@@ -277,7 +277,7 @@ class AdbDevice(Device):
|
||||
return
|
||||
|
||||
with context.local(device=self.serial):
|
||||
abi = str(properties.ro.product.cpu.abi)
|
||||
abi = getprop('ro.product.cpu.abi')
|
||||
context.clear()
|
||||
context.arch = str(abi)
|
||||
self._arch = context.arch
|
||||
@@ -680,7 +680,7 @@ def exists(path):
|
||||
|
||||
>>> adb.exists('/')
|
||||
True
|
||||
>>> adb.exists('/init')
|
||||
>>> adb.exists('/etc/hosts')
|
||||
True
|
||||
>>> adb.exists('/does/not/exist')
|
||||
False
|
||||
@@ -975,17 +975,17 @@ def fastboot(args, *a, **kw):
|
||||
@with_device
|
||||
def fingerprint():
|
||||
"""Returns the device build fingerprint."""
|
||||
return str(properties.ro.build.fingerprint)
|
||||
return getprop('ro.build.fingerprint')
|
||||
|
||||
@with_device
|
||||
def product():
|
||||
"""Returns the device product identifier."""
|
||||
return str(properties.ro.build.product)
|
||||
return getprop('ro.build.product')
|
||||
|
||||
@with_device
|
||||
def build():
|
||||
"""Returns the Build ID of the device."""
|
||||
return str(properties.ro.build.id)
|
||||
return getprop('ro.build.id')
|
||||
|
||||
@with_device
|
||||
@no_emulator
|
||||
@@ -995,9 +995,33 @@ def unlock_bootloader():
|
||||
Note:
|
||||
This requires physical interaction with the device.
|
||||
"""
|
||||
AdbClient().reboot_bootloader()
|
||||
fastboot(['oem', 'unlock'])
|
||||
fastboot(['continue'])
|
||||
w = log.waitfor("Unlocking bootloader")
|
||||
with w:
|
||||
if getprop('ro.oem_unlock_supported') == '0':
|
||||
log.error("Bootloader cannot be unlocked: ro.oem_unlock_supported=0")
|
||||
|
||||
if getprop('ro.boot.oem_unlock_support') == '0':
|
||||
log.error("Bootloader cannot be unlocked: ro.boot.oem_unlock_support=0")
|
||||
|
||||
if getprop('sys.oem_unlock_allowed') == '0':
|
||||
log.error("Bootloader cannot be unlocked: Enable OEM Unlock in developer settings first", context.device)
|
||||
|
||||
AdbClient().reboot_bootloader()
|
||||
|
||||
# Check to see if it's unlocked before attempting unlock
|
||||
unlocked = fastboot(['getvar', 'unlocked'])
|
||||
if 'unlocked: yes' in unlocked:
|
||||
w.success("Already unlocked")
|
||||
fastboot(['continue'])
|
||||
return
|
||||
|
||||
fastboot(['oem', 'unlock'])
|
||||
unlocked = fastboot(['getvar', 'unlocked'])
|
||||
|
||||
fastboot(['continue'])
|
||||
|
||||
if 'unlocked: yes' not in unlocked:
|
||||
log.error("Unlock failed")
|
||||
|
||||
class Kernel(object):
|
||||
_kallsyms = None
|
||||
@@ -1054,7 +1078,7 @@ class Kernel(object):
|
||||
|
||||
def enable_uart(self):
|
||||
"""Reboots the device with kernel logging to the UART enabled."""
|
||||
model = str(properties.ro.product.model)
|
||||
model = getprop('ro.product.model')
|
||||
|
||||
known_commands = {
|
||||
'Nexus 4': None,
|
||||
@@ -1102,15 +1126,15 @@ kernel = Kernel()
|
||||
|
||||
class Property(object):
|
||||
def __init__(self, name=None):
|
||||
# Need to avoid overloaded setattr() so we go through __dict__
|
||||
self.__dict__['_name'] = name
|
||||
|
||||
def __str__(self):
|
||||
return str(getprop(self._name)).strip()
|
||||
|
||||
def __repr__(self):
|
||||
return repr(str(self))
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if attr.startswith('_'):
|
||||
raise AttributeError(attr)
|
||||
if self._name:
|
||||
attr = '%s.%s' % (self._name, attr)
|
||||
return Property(attr)
|
||||
@@ -1123,6 +1147,17 @@ class Property(object):
|
||||
attr = '%s.%s' % (self._name, attr)
|
||||
setprop(attr, value)
|
||||
|
||||
def __eq__(self, other):
|
||||
# Allow simple comparison, e.g.:
|
||||
# adb.properties.ro.oem_unlock_supported == "1"
|
||||
if isinstance(other, six.string_types):
|
||||
return str(self) == other
|
||||
return super(Property, self).__eq__(other)
|
||||
|
||||
def __hash__(self, other):
|
||||
# Allow hash indices matching on the property
|
||||
return hash(self._name)
|
||||
|
||||
properties = Property()
|
||||
|
||||
def _build_date():
|
||||
@@ -1225,8 +1260,8 @@ def compile(source):
|
||||
|
||||
# If we have an attached device, use its settings.
|
||||
if context.device:
|
||||
abi = str(properties.ro.product.cpu.abi)
|
||||
sdk = str(properties.ro.build.version.sdk)
|
||||
abi = getprop('ro.product.cpu.abi')
|
||||
sdk = getprop('ro.build.version.sdk')
|
||||
|
||||
if abi is None:
|
||||
log.error("Unknown CPU ABI")
|
||||
@@ -1322,9 +1357,10 @@ class Partitions(object):
|
||||
for name in listdir(self.by_name_dir):
|
||||
yield name
|
||||
|
||||
@context.quietfunc
|
||||
@with_device
|
||||
def __getattr__(self, attr):
|
||||
if name.startswith("_"):
|
||||
raise AttributeError(attr)
|
||||
|
||||
for name in self:
|
||||
if name == attr:
|
||||
break
|
||||
@@ -1333,19 +1369,20 @@ class Partitions(object):
|
||||
|
||||
path = os.path.join(self.by_name_dir, name)
|
||||
|
||||
# Find the actual path of the device
|
||||
devpath = readlink(path)
|
||||
devname = os.path.basename(devpath)
|
||||
with context.quiet:
|
||||
# Find the actual path of the device
|
||||
devpath = readlink(path)
|
||||
devname = os.path.basename(devpath)
|
||||
|
||||
# Get the size of the partition
|
||||
for line in read('/proc/partitions').splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
major, minor, blocks, name = line.split(None, 4)
|
||||
if devname == name:
|
||||
break
|
||||
else:
|
||||
log.error("Could not find size of partition %r" % name)
|
||||
# Get the size of the partition
|
||||
for line in read('/proc/partitions').splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
major, minor, blocks, name = line.split(None, 4)
|
||||
if devname == name:
|
||||
break
|
||||
else:
|
||||
log.error("Could not find size of partition %r" % name)
|
||||
|
||||
return Partition(devpath, attr, int(blocks))
|
||||
|
||||
@@ -1397,3 +1434,9 @@ def packages():
|
||||
"""Returns a list of packages installed on the system"""
|
||||
packages = process(['pm', 'list', 'packages']).recvall()
|
||||
return [line.split('package:', 1)[-1] for line in packages.splitlines()]
|
||||
|
||||
@context.quietfunc
|
||||
def version():
|
||||
"""Returns rthe platform version as a tuple."""
|
||||
prop = getprop('ro.build.version.release')
|
||||
return [int(v) for v in prop.split('.')]
|
||||
+1
-1
@@ -576,7 +576,7 @@ def make_elf(data,
|
||||
_run(assembler + ['-o', step2, step1])
|
||||
|
||||
linker_options = ['-z', 'execstack']
|
||||
if vma:
|
||||
if vma is not None:
|
||||
linker_options += ['--section-start=.shellcode=%#x' % vma,
|
||||
'--entry=%#x' % vma]
|
||||
elif shared:
|
||||
|
||||
@@ -130,7 +130,7 @@ def main(args):
|
||||
output = output.encode('ascii')
|
||||
args.output.write(output)
|
||||
|
||||
if tty and fmt is not 'raw':
|
||||
if tty and fmt != 'raw':
|
||||
args.output.write(b'\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -3,6 +3,7 @@ from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import argparse
|
||||
import six
|
||||
import string
|
||||
import sys
|
||||
|
||||
@@ -20,7 +21,8 @@ parser = common.parser_commands.add_parser(
|
||||
parser.add_argument(
|
||||
'-a', '--alphabet',
|
||||
metavar = 'alphabet',
|
||||
default = string.ascii_lowercase,
|
||||
default = string.ascii_lowercase.encode(),
|
||||
type = six.ensure_binary,
|
||||
help = 'The alphabet to use in the cyclic pattern (defaults to all lower case letters)',
|
||||
)
|
||||
|
||||
@@ -64,9 +66,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)
|
||||
@@ -90,10 +93,11 @@ def main(args):
|
||||
if got < want:
|
||||
log.failure("Alphabet too small (max length = %i)" % got)
|
||||
|
||||
sys.stdout.write(result)
|
||||
out = getattr(sys.stdout, 'buffer', sys.stdout)
|
||||
out.write(result)
|
||||
|
||||
if sys.stdout.isatty():
|
||||
sys.stdout.write('\n')
|
||||
if out.isatty():
|
||||
out.write(b'\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnlib.commandline.common.main(__file__)
|
||||
@@ -685,6 +685,7 @@ class ContextType(object):
|
||||
('x86_64', 'amd64'),
|
||||
('x86', 'i386'),
|
||||
('i686', 'i386'),
|
||||
('armv7l', 'arm'),
|
||||
('armeabi', 'arm'),
|
||||
('arm64', 'aarch64')]
|
||||
for k, v in transform:
|
||||
|
||||
+1
-1
@@ -857,7 +857,7 @@ class DynELF(object):
|
||||
if self.leak.compare(address + 0xC, "GNU\x00"):
|
||||
return enhex(b''.join(self.leak.raw(address + 0x10, 20)))
|
||||
else:
|
||||
self.status("Magic did not match")
|
||||
self.status("Build ID not found at offset %#x" % offset)
|
||||
pass
|
||||
|
||||
def _make_absolute_ptr(self, ptr_or_offset):
|
||||
|
||||
+21
-23
@@ -381,13 +381,15 @@ class Corefile(ELF):
|
||||
This requires GDB to be installed, and can only be done with native
|
||||
processes. Getting a "complete" corefile requires GDB 7.11 or better.
|
||||
|
||||
>>> elf = ELF('/bin/bash')
|
||||
>>> elf = ELF('/bin/bash-static')
|
||||
>>> context.clear(binary=elf)
|
||||
>>> io = process(elf.path, env={'HELLO': 'WORLD'})
|
||||
>>> core = io.corefile
|
||||
|
||||
Data can also be extracted directly from the corefile.
|
||||
|
||||
>>> elf.address > 0
|
||||
True
|
||||
>>> core.exe[elf.address:elf.address+4]
|
||||
b'\x7fELF'
|
||||
>>> core.exe.data[:4]
|
||||
@@ -429,13 +431,13 @@ class Corefile(ELF):
|
||||
|
||||
Corefiles can also be pulled from remote machines via SSH!
|
||||
|
||||
>>> s = ssh('travis', 'example.pwnme')
|
||||
>>> s = ssh(host='example.pwnme')
|
||||
>>> _ = s.set_working_directory()
|
||||
>>> elf = ELF.from_assembly(shellcraft.trap())
|
||||
>>> path = s.upload(elf.path)
|
||||
>>> _ =s.chmod('+x', path)
|
||||
>>> io = s.process(path)
|
||||
>>> io.wait()
|
||||
>>> io.wait(1)
|
||||
-1
|
||||
>>> io.corefile.signal == signal.SIGTRAP # doctest: +SKIP
|
||||
True
|
||||
@@ -445,7 +447,7 @@ class Corefile(ELF):
|
||||
>>> context.clear(arch='amd64')
|
||||
>>> elf = ELF.from_assembly('push 1234; ret')
|
||||
>>> io = elf.process()
|
||||
>>> io.wait()
|
||||
>>> io.wait(1)
|
||||
>>> io.corefile.fault_addr
|
||||
1234
|
||||
|
||||
@@ -459,7 +461,7 @@ class Corefile(ELF):
|
||||
|
||||
>>> elf = ELF.from_assembly(shellcraft.crash())
|
||||
>>> io = elf.process(env={'FOO': 'BAR=BAZ'})
|
||||
>>> io.wait()
|
||||
>>> io.wait(1)
|
||||
>>> core = io.corefile
|
||||
>>> core.getenv('FOO')
|
||||
b'BAR=BAZ'
|
||||
@@ -480,7 +482,7 @@ class Corefile(ELF):
|
||||
... '''
|
||||
>>> elf = ELF.from_assembly(assembly)
|
||||
>>> io = elf.process()
|
||||
>>> io.wait()
|
||||
>>> io.wait(2)
|
||||
>>> core = io.corefile
|
||||
[!] End of the stack is corrupted, skipping stack parsing (got: 4141414141414141)
|
||||
>>> core.argc, core.argv, core.env
|
||||
@@ -747,13 +749,13 @@ class Corefile(ELF):
|
||||
|
||||
>>> elf = ELF.from_assembly(shellcraft.trap())
|
||||
>>> io = elf.process()
|
||||
>>> io.wait()
|
||||
>>> io.wait(1)
|
||||
>>> io.corefile.signal == signal.SIGTRAP
|
||||
True
|
||||
|
||||
>>> elf = ELF.from_assembly(shellcraft.crash())
|
||||
>>> io = elf.process()
|
||||
>>> io.wait()
|
||||
>>> io.wait(1)
|
||||
>>> io.corefile.signal == signal.SIGSEGV
|
||||
True
|
||||
"""
|
||||
@@ -774,7 +776,7 @@ class Corefile(ELF):
|
||||
|
||||
>>> elf = ELF.from_assembly('mov eax, 0xdeadbeef; jmp eax', arch='i386')
|
||||
>>> io = elf.process()
|
||||
>>> io.wait()
|
||||
>>> io.wait(1)
|
||||
>>> io.corefile.fault_addr == io.corefile.eax == 0xdeadbeef
|
||||
True
|
||||
"""
|
||||
@@ -943,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
|
||||
@@ -966,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.
|
||||
#
|
||||
@@ -1071,7 +1070,7 @@ class Corefile(ELF):
|
||||
|
||||
>>> elf = ELF.from_assembly(shellcraft.trap())
|
||||
>>> io = elf.process(env={'GREETING': 'Hello!'})
|
||||
>>> io.wait()
|
||||
>>> io.wait(1)
|
||||
>>> io.corefile.getenv('GREETING')
|
||||
b'Hello!'
|
||||
"""
|
||||
@@ -1090,7 +1089,7 @@ class Corefile(ELF):
|
||||
|
||||
>>> elf = ELF.from_assembly('mov eax, 0xdeadbeef;' + shellcraft.trap(), arch='i386')
|
||||
>>> io = elf.process()
|
||||
>>> io.wait()
|
||||
>>> io.wait(1)
|
||||
>>> io.corefile.registers['eax'] == 0xdeadbeef
|
||||
True
|
||||
"""
|
||||
@@ -1533,4 +1532,3 @@ class CorefileFinder(object):
|
||||
return keys['interpreter']
|
||||
|
||||
return ''
|
||||
|
||||
+4
-2
@@ -591,6 +591,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()
|
||||
@@ -1020,7 +1022,7 @@ class ELF(ELFFile):
|
||||
section.data(),
|
||||
inv_symbols)
|
||||
|
||||
for address, target in reversed(sorted(res.items())):
|
||||
for address, target in sorted(res.items()):
|
||||
self.plt[inv_symbols[target]] = address
|
||||
|
||||
for a,n in sorted({v:k for k,v in self.plt.items()}.items()):
|
||||
@@ -1871,7 +1873,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
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
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.
|
||||
|
||||
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)
|
||||
>>> fileStr.vtable = 0xcafebabe
|
||||
>>> payload = bytes(fileStr)
|
||||
>>> payload
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xef\xbe\xad\xde\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xef\xbe\xad\xde\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe\xba\xfe\xca\x00\x00\x00\x00'
|
||||
|
||||
Now payload contains the FILE structure with its vtable pointer pointing to 0xcafebabe
|
||||
|
||||
Currently only 'amd64' and 'i386' architectures are supported
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
from pwnlib.context import context
|
||||
from pwnlib.log import getLogger
|
||||
from pwnlib.util.packing import pack
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
length=0
|
||||
size='size'
|
||||
name='name'
|
||||
|
||||
variables={
|
||||
0:{name:'flags',size:length},
|
||||
1:{name:'_IO_read_ptr',size:length},
|
||||
2:{name:'_IO_read_end',size:length},
|
||||
3:{name:'_IO_read_base',size:length},
|
||||
4:{name:'_IO_write_base',size:length},
|
||||
5:{name:'_IO_write_ptr',size:length},
|
||||
6:{name:'_IO_write_end',size:length},
|
||||
7:{name:'_IO_buf_base',size:length},
|
||||
8:{name:'_IO_buf_end',size:length},
|
||||
9:{name:'_IO_save_base',size:length},
|
||||
10:{name:'_IO_backup_base',size:length},
|
||||
11:{name:'_IO_save_end',size:length},
|
||||
12:{name:'markers',size:length},
|
||||
13:{name:'chain',size:length},
|
||||
14:{name:'fileno',size:4},
|
||||
15:{name:'_flags2',size:4},
|
||||
16:{name:'_old_offset',size:length},
|
||||
17:{name:'_cur_column',size:2},
|
||||
18:{name:'_vtable_offset',size:1},
|
||||
19:{name:'_shortbuf',size:1},
|
||||
20:{name:'unknown1',size:-4},
|
||||
21:{name:'_lock',size:length},
|
||||
22:{name:'_offset',size:8},
|
||||
23:{name:'_codecvt',size:length},
|
||||
24:{name:'_wide_data',size:length},
|
||||
25:{name:'unknown2',size:length},
|
||||
26:{name:'vtable',size:length}
|
||||
}
|
||||
|
||||
|
||||
def update_var(l):
|
||||
r"""
|
||||
Since different members of the file structure have different sizes, we need to keep track of the sizes. The following function is used by the FileStructure class to initialise the lengths of the various fields.
|
||||
|
||||
Arguments:
|
||||
l(int)
|
||||
l=8 for 'amd64' architecture and l=4 for 'i386' architecture
|
||||
|
||||
Return Value:
|
||||
Returns a dictionary in which each field is mapped to its corresponding length according to the architecture set
|
||||
|
||||
Examples:
|
||||
|
||||
>>> update_var(8)
|
||||
{'flags': 8, '_IO_read_ptr': 8, '_IO_read_end': 8, '_IO_read_base': 8, '_IO_write_base': 8, '_IO_write_ptr': 8, '_IO_write_end': 8, '_IO_buf_base': 8, '_IO_buf_end': 8, '_IO_save_base': 8, '_IO_backup_base': 8, '_IO_save_end': 8, 'markers': 8, 'chain': 8, 'fileno': 4, '_flags2': 4, '_old_offset': 8, '_cur_column': 2, '_vtable_offset': 1, '_shortbuf': 1, 'unknown1': 4, '_lock': 8, '_offset': 8, '_codecvt': 8, '_wide_data': 8, 'unknown2': 48, 'vtable': 8}
|
||||
"""
|
||||
var={}
|
||||
for i in variables:
|
||||
var[variables[i]['name']]=variables[i]['size']
|
||||
for i in var:
|
||||
if var[i]<=0:
|
||||
var[i]+=l
|
||||
if l==4:
|
||||
var['unknown2']=56
|
||||
else:
|
||||
var['unknown2']=48
|
||||
return var
|
||||
|
||||
class FileStructure(object):
|
||||
r"""
|
||||
Crafts a FILE structure, with default values for some fields, like _lock which should point to null ideally, set.
|
||||
|
||||
Arguments:
|
||||
null(int)
|
||||
A pointer to NULL value in memory. This pointer can lie in any segment (stack, heap, bss, libc etc)
|
||||
|
||||
Examples:
|
||||
|
||||
FILE structure with flags as 0xfbad1807 and _IO_buf_base and _IO_buf_end pointing to 0xcafebabe and 0xfacef00d
|
||||
|
||||
>>> context.clear(arch='amd64')
|
||||
>>> fileStr = FileStructure(null=0xdeadbeeef)
|
||||
>>> fileStr.flags = 0xfbad1807
|
||||
>>> fileStr._IO_buf_base = 0xcafebabe
|
||||
>>> fileStr._IO_buf_end = 0xfacef00d
|
||||
>>> payload = bytes(fileStr)
|
||||
>>> payload
|
||||
b'\x07\x18\xad\xfb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe\xba\xfe\xca\x00\x00\x00\x00\r\xf0\xce\xfa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xef\xee\xdb\xea\r\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xef\xee\xdb\xea\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
|
||||
Check the length of the FileStructure
|
||||
|
||||
>>> len(fileStr)
|
||||
224
|
||||
|
||||
The defination for __repr__ orders the structure members and displays then in a dictionary format. It's useful when viewing a structure objet in python/IPython shell
|
||||
|
||||
>>> q=FileStructure(0xdeadbeef)
|
||||
>>> q
|
||||
{ flags: 0x0
|
||||
_IO_read_ptr: 0x0
|
||||
_IO_read_end: 0x0
|
||||
_IO_read_base: 0x0
|
||||
_IO_write_base: 0x0
|
||||
_IO_write_ptr: 0x0
|
||||
_IO_write_end: 0x0
|
||||
_IO_buf_base: 0x0
|
||||
_IO_buf_end: 0x0
|
||||
_IO_save_base: 0x0
|
||||
_IO_backup_base: 0x0
|
||||
_IO_save_end: 0x0
|
||||
markers: 0x0
|
||||
chain: 0x0
|
||||
fileno: 0x0
|
||||
_flags2: 0x0
|
||||
_old_offset: 0xffffffffffffffff
|
||||
_cur_column: 0x0
|
||||
_vtable_offset: 0x0
|
||||
_shortbuf: 0x0
|
||||
unknown1: 0x0
|
||||
_lock: 0xdeadbeef
|
||||
_offset: 0xffffffffffffffff
|
||||
_codecvt: 0x0
|
||||
_wide_data: 0xdeadbeef
|
||||
unknown2: 0x0
|
||||
vtable: 0x0}
|
||||
"""
|
||||
|
||||
vars_=[]
|
||||
length={}
|
||||
|
||||
def __init__(self, null=0):
|
||||
self.vars_ = [variables[i]['name'] for i in sorted(variables.keys())]
|
||||
self.setdefault(null)
|
||||
self.length = update_var(context.bytes)
|
||||
self._old_offset = (1 << context.bits) - 1
|
||||
|
||||
def __setattr__(self,item,value):
|
||||
if item in FileStructure.__dict__ or item in self.vars_:
|
||||
object.__setattr__(self,item,value)
|
||||
else:
|
||||
log.error("Unknown variable %r" % item)
|
||||
|
||||
def __repr__(self):
|
||||
structure=[]
|
||||
for i in self.vars_:
|
||||
structure.append(" %s: %s" % (i,hex(self.__getattr__(i))))
|
||||
return "{"+ "\n".join(structure)+"}"
|
||||
|
||||
def __getattr__(self,item):
|
||||
if item in FileStructure.__dict__ or item in self.vars_:
|
||||
return object.__getattribute__(self,item)
|
||||
log.error("Unknown variable %r" % item)
|
||||
|
||||
def __len__(self):
|
||||
return len(bytes(self))
|
||||
|
||||
def __bytes__(self):
|
||||
structure = b''
|
||||
for val in self.vars_:
|
||||
if isinstance(self.__getattr__(val), bytes):
|
||||
structure += self.__getattr__(val).ljust(context.bytes, b'\x00')
|
||||
else:
|
||||
structure += pack(self.__getattr__(val), self.length[val]*8)
|
||||
return structure
|
||||
__str__ = __bytes__
|
||||
|
||||
def struntil(self,v):
|
||||
r"""
|
||||
Payload for stuff till 'v' where 'v' is a structure member. This payload includes 'v' as well.
|
||||
|
||||
Arguments:
|
||||
v(string)
|
||||
The name of the field uptil which the payload should be created.
|
||||
|
||||
Example:
|
||||
|
||||
Payload for data uptil _IO_buf_end
|
||||
|
||||
>>> context.clear(arch='amd64')
|
||||
>>> fileStr = FileStructure(0xdeadbeef)
|
||||
>>> payload = fileStr.struntil("_IO_buf_end")
|
||||
>>> payload
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
"""
|
||||
if v not in self.vars_:
|
||||
return b''
|
||||
structure = b''
|
||||
for val in self.vars_:
|
||||
if isinstance(self.__getattr__(val), bytes):
|
||||
structure += self.__getattr__(val).ljust(context.bytes, b'\x00')
|
||||
else:
|
||||
structure += pack(self.__getattr__(val), self.length[val]*8)
|
||||
if val == v:
|
||||
break
|
||||
return structure[:-1]
|
||||
|
||||
def setdefault(self,null):
|
||||
self.flags=0
|
||||
self._IO_read_ptr=0
|
||||
self._IO_read_end=0
|
||||
self._IO_read_base=0
|
||||
self._IO_write_base=0
|
||||
self._IO_write_ptr=0
|
||||
self._IO_write_end=0
|
||||
self._IO_buf_base=0
|
||||
self._IO_buf_end=0
|
||||
self._IO_save_base=0
|
||||
self._IO_backup_base=0
|
||||
self._IO_save_end=0
|
||||
self.markers=0
|
||||
self.chain=0
|
||||
self.fileno=0
|
||||
self._flags2=0
|
||||
self._old_offset=0
|
||||
self._cur_column=0
|
||||
self._vtable_offset=0
|
||||
self._shortbuf=0
|
||||
self.unknown1=0
|
||||
self._lock=null
|
||||
self._offset=0xffffffffffffffff
|
||||
self._codecvt=0
|
||||
self._wide_data=null
|
||||
self.unknown2=0
|
||||
self.vtable=0
|
||||
|
||||
def write(self,addr=0,size=0):
|
||||
r"""
|
||||
Writing data out from arbitrary memory address.
|
||||
|
||||
Arguments:
|
||||
addr(int)
|
||||
The address from which data is to be printed to stdout
|
||||
size(int)
|
||||
The size, in bytes, of the data to be printed
|
||||
|
||||
Example:
|
||||
|
||||
Payload for writing 100 bytes to stdout from the address 0xcafebabe
|
||||
|
||||
>>> context.clear(arch='amd64')
|
||||
>>> fileStr = FileStructure(0xdeadbeef)
|
||||
>>> payload = fileStr.write(addr=0xcafebabe, size=100)
|
||||
>>> payload
|
||||
b'\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe\xba\xfe\xca\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe\xba\xfe\xca\x00\x00\x00\x00"\xbb\xfe\xca\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00'
|
||||
"""
|
||||
self.flags &=~8
|
||||
self.flags |=0x800
|
||||
self._IO_write_base = addr
|
||||
self._IO_write_ptr = addr+size
|
||||
self._IO_read_end = addr
|
||||
self.fileno = 1
|
||||
return self.struntil('fileno')
|
||||
|
||||
def read(self,addr=0,size=0):
|
||||
r"""
|
||||
Reading data into arbitrary memory location.
|
||||
|
||||
Arguments:
|
||||
addr(int)
|
||||
The address into which data is to be written from stdin
|
||||
size(int)
|
||||
The size, in bytes, of the data to be written
|
||||
|
||||
Example:
|
||||
|
||||
Payload for reading 100 bytes from stdin into the address 0xcafebabe
|
||||
|
||||
>>> context.clear(arch='amd64')
|
||||
>>> fileStr = FileStructure(0xdeadbeef)
|
||||
>>> payload = fileStr.read(addr=0xcafebabe, size=100)
|
||||
>>> payload
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe\xba\xfe\xca\x00\x00\x00\x00"\xbb\xfe\xca\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
"""
|
||||
self.flags &=~4
|
||||
self._IO_read_base = 0
|
||||
self._IO_read_ptr = 0
|
||||
self._IO_buf_base = addr
|
||||
self._IO_buf_end = addr+size
|
||||
self.fileno = 0
|
||||
return self.struntil('fileno')
|
||||
|
||||
def orange(self,io_list_all,vtable):
|
||||
r"""
|
||||
Perform a House of Orange (https://github.com/shellphish/how2heap/blob/master/glibc_2.25/house_of_orange.c), provided you have libc leaks.
|
||||
|
||||
Arguments:
|
||||
io_list_all(int)
|
||||
Address of _IO_list_all in libc.
|
||||
vtable(int)
|
||||
Address of the fake vtable in memory
|
||||
|
||||
Example:
|
||||
|
||||
Example payload if address of _IO_list_all is 0xfacef00d and fake vtable is at 0xcafebabe -
|
||||
|
||||
>>> context.clear(arch='amd64')
|
||||
>>> fileStr = FileStructure(0xdeadbeef)
|
||||
>>> payload = fileStr.orange(io_list_all=0xfacef00d, vtable=0xcafebabe)
|
||||
>>> payload
|
||||
b'/bin/sh\x00a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfd\xef\xce\xfa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xef\xbe\xad\xde\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xef\xbe\xad\xde\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe\xba\xfe\xca\x00\x00\x00\x00'
|
||||
"""
|
||||
if context.bits == 64:
|
||||
self.flags = b'/bin/sh\x00'
|
||||
self._IO_read_ptr = 0x61
|
||||
self._IO_read_base = io_list_all-0x10
|
||||
elif context.bits == 32:
|
||||
self.flags = b'sh\x00'
|
||||
self._IO_read_ptr = 0x121
|
||||
self._IO_read_base = io_list_all-0x8
|
||||
self._IO_write_base = 0
|
||||
self._IO_write_ptr = 1
|
||||
self.vtable = vtable
|
||||
return self.__bytes__()
|
||||
+1
-1
@@ -50,7 +50,7 @@ def submit_flag(flag,
|
||||
>>> _ = submit_flag('flag', server='localhost', port=l.lport)
|
||||
>>> c = l.wait_for_connection()
|
||||
>>> c.recvall().split()
|
||||
['flag', 'unnamed-exploit', 'unknown-target', 'unknown-team']
|
||||
[b'flag', b'unnamed-exploit', b'unknown-target', b'unknown-team']
|
||||
"""
|
||||
flag = flag.strip()
|
||||
|
||||
|
||||
+3
-2
@@ -31,8 +31,9 @@ Examples:
|
||||
... write(1, my_var, sizeof(int));
|
||||
... return 0;
|
||||
... }''')
|
||||
>>> cmdline = ["gcc", source, "-Wno-format-security", "-m32", "-o", program]
|
||||
>>> process(cmdline).wait_for_close()
|
||||
>>> gcc = process(["gcc", source, "-Wno-format-security", "-m32", "-o", program])
|
||||
>>> gcc.poll(True) and gcc.recvall()
|
||||
0
|
||||
>>> def exec_fmt(payload):
|
||||
... p = process(program)
|
||||
... p.sendline(payload)
|
||||
|
||||
+155
-148
@@ -136,12 +136,10 @@ def debug_assembly(asm, gdbscript=None, vma=None):
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
assembly = shellcraft.echo("Hello world!\n")
|
||||
io = gdb.debug_assembly(assembly)
|
||||
io.recvline()
|
||||
# 'Hello world!'
|
||||
>>> assembly = shellcraft.echo("Hello world!\n")
|
||||
>>> io = gdb.debug_assembly(assembly)
|
||||
>>> io.recvline()
|
||||
b'Hello world!\n'
|
||||
"""
|
||||
tmp_elf = make_elf_from_assembly(asm, vma=vma, extract=False)
|
||||
os.chmod(tmp_elf, 0o777)
|
||||
@@ -157,7 +155,7 @@ def debug_assembly(asm, gdbscript=None, vma=None):
|
||||
|
||||
@LocalContext
|
||||
def debug_shellcode(data, gdbscript=None, vma=None):
|
||||
r"""
|
||||
r"""debug_shellcode(data, gdbscript=None, vma=None) -> tube
|
||||
Creates an ELF file, and launches it under a debugger.
|
||||
|
||||
Arguments:
|
||||
@@ -171,13 +169,11 @@ def debug_shellcode(data, gdbscript=None, vma=None):
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
assembly = shellcraft.echo("Hello world!\n")
|
||||
shellcode = asm(assembly)
|
||||
io = gdb.debug_shellcode(shellcode)
|
||||
io.recvline()
|
||||
# 'Hello world!'
|
||||
>>> assembly = shellcraft.echo("Hello world!\n")
|
||||
>>> shellcode = asm(assembly)
|
||||
>>> io = gdb.debug_shellcode(shellcode)
|
||||
>>> io.recvline()
|
||||
b'Hello world!\n'
|
||||
"""
|
||||
if isinstance(data, six.text_type):
|
||||
log.error("Shellcode is cannot be unicode. Did you mean debug_assembly?")
|
||||
@@ -193,8 +189,8 @@ def debug_shellcode(data, gdbscript=None, vma=None):
|
||||
|
||||
return debug(tmp_elf, gdbscript=gdbscript, arch=context.arch)
|
||||
|
||||
def _gdbserver_args(pid=None, path=None, args=None, which=None):
|
||||
"""_gdbserver_args(pid=None, path=None) -> list
|
||||
def _gdbserver_args(pid=None, path=None, args=None, which=None, env=None):
|
||||
"""_gdbserver_args(pid=None, path=None, args=None, which=None, env=None) -> list
|
||||
|
||||
Sets up a listening gdbserver, to either connect to the specified
|
||||
PID, or launch the specified binary by its full path.
|
||||
@@ -240,6 +236,14 @@ def _gdbserver_args(pid=None, path=None, args=None, which=None):
|
||||
if pid:
|
||||
gdbserver_args += ['--once', '--attach']
|
||||
|
||||
if env:
|
||||
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 + ['--']
|
||||
|
||||
gdbserver_args += ['localhost:0']
|
||||
gdbserver_args += args
|
||||
|
||||
@@ -296,7 +300,7 @@ def _get_runner(ssh=None):
|
||||
|
||||
@LocalContext
|
||||
def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, **kwargs):
|
||||
"""debug(args) -> tube
|
||||
r"""debug(args) -> tube
|
||||
|
||||
Launch a GDB server with the specified command line,
|
||||
and launches GDB to attach to it.
|
||||
@@ -345,68 +349,58 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, **kw
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: python
|
||||
>>> # Create a new process, and stop it at 'main'
|
||||
>>> io = gdb.debug('bash', '''
|
||||
... break main
|
||||
... continue
|
||||
... ''')
|
||||
>>> io.sendline("echo hello")
|
||||
>>> io.recvline()
|
||||
b'hello\n'
|
||||
>>> io.interactive() # doctest: +SKIP
|
||||
>>> io.close()
|
||||
|
||||
# Create a new process, and stop it at 'main'
|
||||
io = gdb.debug('bash', '''
|
||||
break main
|
||||
continue
|
||||
''')
|
||||
>>> # Create a new process, and stop it at '_start'
|
||||
>>> io = gdb.debug('bash', '''
|
||||
... # Wait until we hit the main executable's entry point
|
||||
... break _start
|
||||
... continue
|
||||
...
|
||||
... # Now set breakpoint on shared library routines
|
||||
... break malloc
|
||||
... break free
|
||||
... continue
|
||||
... ''')
|
||||
>>> io.sendline("echo hello")
|
||||
>>> io.recvline()
|
||||
b'hello\n'
|
||||
>>> io.interactive() # doctest: +SKIP
|
||||
>>> io.close()
|
||||
|
||||
# Send a command to Bash
|
||||
io.sendline("echo hello")
|
||||
You can use :func:`debug` to spawn new processes on remote machines as well,
|
||||
by using the ``ssh=`` keyword to pass in your :class:`.ssh` instance.
|
||||
|
||||
# Interact with the process
|
||||
io.interactive()
|
||||
>>> # Connect to the SSH server
|
||||
|
||||
.. code-block:: python
|
||||
>>> # Start a process on the server
|
||||
>>> shell = ssh('travis', 'example.pwnme', password='demopass')
|
||||
>>> io = gdb.debug(['bash'],
|
||||
... ssh = shell,
|
||||
... gdbscript = '''
|
||||
... break main
|
||||
... continue
|
||||
... ''')
|
||||
|
||||
# Create a new process, and stop it at 'main'
|
||||
io = gdb.debug('bash', '''
|
||||
# Wait until we hit the main executable's entry point
|
||||
break _start
|
||||
continue
|
||||
>>> # Send a command to Bash
|
||||
>>> io.sendline("echo hello")
|
||||
|
||||
# Now set breakpoint on shared library routines
|
||||
break malloc
|
||||
break free
|
||||
continue
|
||||
''')
|
||||
|
||||
# Send a command to Bash
|
||||
io.sendline("echo hello")
|
||||
|
||||
# Interact with the process
|
||||
io.interactive()
|
||||
|
||||
You can use :func:`debug` to spawn new processes on remote machines as well,
|
||||
by using the ``ssh=`` keyword to pass in your :class:`.ssh` instance.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Connect to the SSH server
|
||||
shell = ssh('passcode', 'pwnable.kr', 2222, password='guest')
|
||||
|
||||
# Start a process on the server
|
||||
io = gdb.debug(['bash'],
|
||||
ssh=shell,
|
||||
gdbscript='''
|
||||
break main
|
||||
continue
|
||||
''')
|
||||
|
||||
# Send a command to Bash
|
||||
io.sendline("echo hello")
|
||||
|
||||
# Interact with the process
|
||||
io.interactive()
|
||||
>>> # Interact with the process
|
||||
>>> io.interactive() # doctest: +SKIP
|
||||
>>> io.close()
|
||||
"""
|
||||
if isinstance(args, (int, tubes.process.process, tubes.ssh.ssh_channel)):
|
||||
log.error("Use gdb.attach() to debug a running process")
|
||||
|
||||
if env is None:
|
||||
env = os.environ
|
||||
|
||||
if isinstance(args, (bytes, six.text_type)):
|
||||
args = [args]
|
||||
|
||||
@@ -421,14 +415,17 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, **kw
|
||||
return runner(args, executable=exe, env=env)
|
||||
|
||||
if ssh or context.native or (context.os == 'android'):
|
||||
args = _gdbserver_args(args=args, which=which)
|
||||
args = _gdbserver_args(args=args, which=which, env=env)
|
||||
else:
|
||||
qemu_port = random.randint(1024, 65535)
|
||||
qemu_user = qemu.user_path()
|
||||
sysroot = sysroot or qemu.ld_prefix(env=env)
|
||||
if not qemu_user:
|
||||
log.error("Cannot debug %s binaries without appropriate QEMU binaries" % context.arch)
|
||||
args = [qemu_user, '-g', str(qemu_port)] + args
|
||||
qemu_args = [qemu_user, '-g', str(qemu_port)]
|
||||
if sysroot:
|
||||
qemu_args += ['-L', sysroot]
|
||||
args = qemu_args + args
|
||||
|
||||
# Use a sane default sysroot for Android
|
||||
if not sysroot and context.os == 'android':
|
||||
@@ -438,18 +435,17 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, **kw
|
||||
if not which(args[0]):
|
||||
log.error("%s is not installed" % args[0])
|
||||
|
||||
exe = exe or which(orig_args[0])
|
||||
if not exe:
|
||||
log.error("%s does not exist" % orig_args[0])
|
||||
else:
|
||||
gdbscript = 'file "%s"\n%s' % (exe, gdbscript)
|
||||
if not ssh:
|
||||
exe = exe or which(orig_args[0])
|
||||
if not (exe and os.path.exists(exe)):
|
||||
log.error("%s does not exist" % exe)
|
||||
|
||||
# Start gdbserver/qemu
|
||||
# (Note: We override ASLR here for the gdbserver process itself.)
|
||||
gdbserver = runner(args, env=env, aslr=1, **kwargs)
|
||||
|
||||
# Set the .executable on the process object.
|
||||
gdbserver.executable = which(orig_args[0])
|
||||
gdbserver.executable = exe
|
||||
|
||||
# Find what port we need to connect to
|
||||
if context.native or (context.os == 'android'):
|
||||
@@ -465,7 +461,7 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, **kw
|
||||
|
||||
# gdbserver outputs a message when a client connects
|
||||
garbage = gdbserver.recvline(timeout=1)
|
||||
|
||||
|
||||
# Some versions of gdbserver output an additional message
|
||||
garbage2 = gdbserver.recvline_startswith(b"Remote debugging from host ", timeout=1)
|
||||
|
||||
@@ -509,8 +505,8 @@ def binary():
|
||||
return gdb
|
||||
|
||||
@LocalContext
|
||||
def attach(target, gdbscript = None, exe = None, need_ptrace_scope = True, gdb_args = None, ssh = None, sysroot = None):
|
||||
"""attach(target, gdbscript = None, exe = None, arch = None, ssh = None) -> None
|
||||
def attach(target, gdbscript = '', exe = None, need_ptrace_scope = True, gdb_args = None, ssh = None, sysroot = None):
|
||||
r"""attach(target, gdbscript = None, exe = None, arch = None, ssh = None) -> None
|
||||
|
||||
Start GDB in a new terminal and attach to `target`.
|
||||
|
||||
@@ -549,64 +545,68 @@ def attach(target, gdbscript = None, exe = None, need_ptrace_scope = True, gdb_a
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: python
|
||||
>>> # Attach directly to pid 1234
|
||||
>>> gdb.attach(1234) # doctest: +SKIP
|
||||
|
||||
# Attach directly to pid 1234
|
||||
gdb.attach(1234)
|
||||
|
||||
.. code-block:: python
|
||||
>>> # Attach to the youngest "bash" process
|
||||
>>> gdb.attach('bash') # doctest: +SKIP
|
||||
|
||||
# Attach to the youngest "bash" process
|
||||
gdb.attach('bash')
|
||||
|
||||
.. code-block:: python
|
||||
>>> # Start a process
|
||||
>>> bash = process('bash')
|
||||
|
||||
# Start a process
|
||||
bash = process('bash')
|
||||
>>> # Attach the debugger
|
||||
>>> pid = gdb.attach(bash, '''
|
||||
... set follow-fork-mode child
|
||||
... break execve
|
||||
... continue
|
||||
... ''')
|
||||
|
||||
# Attach the debugger
|
||||
gdb.attach(bash, '''
|
||||
set follow-fork-mode child
|
||||
break execve
|
||||
continue
|
||||
''')
|
||||
>>> # Interact with the process
|
||||
>>> bash.sendline("/bin/echo hello")
|
||||
>>> bash.recvline()
|
||||
b'hello\n'
|
||||
>>> bash.close()
|
||||
|
||||
# Interact with the process
|
||||
bash.sendline('whoami')
|
||||
>>> # Start a forking server
|
||||
>>> server = process(['socat', 'tcp-listen:12345,fork,reuseaddr', 'exec:/bin/bash,nofork'])
|
||||
>>> sleep(1)
|
||||
|
||||
.. code-block:: python
|
||||
>>> # Connect to the server
|
||||
>>> io = remote('127.0.0.1', 12345)
|
||||
|
||||
# Start a forking server
|
||||
server = process(['socat', 'tcp-listen:1234,fork,reuseaddr', 'exec:/bin/sh'])
|
||||
>>> # Connect the debugger to the server-spawned process
|
||||
>>> pid = gdb.attach(io, '''
|
||||
... break exit
|
||||
... continue
|
||||
... ''', exe = '/bin/bash')
|
||||
|
||||
# Connect to the server
|
||||
io = remote('localhost', 1234)
|
||||
>>> # Talk to the spawned 'sh'
|
||||
>>> io.sendline("echo hello")
|
||||
>>> io.recvline()
|
||||
b'hello\n'
|
||||
>>> io.sendline("exit")
|
||||
|
||||
# Connect the debugger to the server-spawned process
|
||||
gdb.attach(io, '''
|
||||
break exit
|
||||
continue
|
||||
''')
|
||||
>>> io.close()
|
||||
|
||||
# Talk to the spawned 'sh'
|
||||
io.sendline('exit')
|
||||
>>> # Connect to the SSH server
|
||||
>>> shell = ssh('travis', 'example.pwnme', password='demopass')
|
||||
|
||||
.. code-block:: python
|
||||
>>> # Start a process on the server
|
||||
>>> cat = shell.process(['cat'])
|
||||
|
||||
# Connect to the SSH server
|
||||
shell = ssh('bandit0', 'bandit.labs.overthewire.org', password='bandit0', port=2220)
|
||||
>>> # Attach a debugger to it
|
||||
>>> gdb.attach(cat, '''
|
||||
... break exit
|
||||
... continue
|
||||
... ''')
|
||||
|
||||
# Start a process on the server
|
||||
cat = shell.process(['cat'])
|
||||
|
||||
# Attach a debugger to it
|
||||
gdb.attach(cat, '''
|
||||
break exit
|
||||
continue
|
||||
''')
|
||||
|
||||
# Cause `cat` to exit
|
||||
cat.close()
|
||||
>>> cat.sendline("hello")
|
||||
>>> cat.recvline()
|
||||
b'hello\n'
|
||||
>>> # Cause `cat` to exit
|
||||
>>> cat.close()
|
||||
"""
|
||||
if context.noptrace:
|
||||
log.warn_once("Skipping debug attach since context.noptrace==True")
|
||||
@@ -649,7 +649,7 @@ def attach(target, gdbscript = None, exe = None, need_ptrace_scope = True, gdb_a
|
||||
if context.os == 'android':
|
||||
pidof = adb.pidof
|
||||
|
||||
pids = pidof(target)
|
||||
pids = list(pidof(target))
|
||||
if not pids:
|
||||
log.error('No such process: %s' % target)
|
||||
pid = pids[0]
|
||||
@@ -662,8 +662,10 @@ def attach(target, gdbscript = None, exe = None, need_ptrace_scope = True, gdb_a
|
||||
shell = target.parent
|
||||
|
||||
tmpfile = shell.mktemp()
|
||||
if six.PY3:
|
||||
tmpfile = tmpfile.decode()
|
||||
gdbscript = 'shell rm %s\n%s' % (tmpfile, gdbscript)
|
||||
shell.upload_data(context._encode(gdbscript), tmpfile)
|
||||
shell.upload_data(gdbscript or '', tmpfile)
|
||||
|
||||
cmd = ['ssh', '-C', '-t', '-p', str(shell.port), '-l', shell.user, shell.host]
|
||||
if shell.password:
|
||||
@@ -672,7 +674,10 @@ def attach(target, gdbscript = None, exe = None, need_ptrace_scope = True, gdb_a
|
||||
cmd = ['sshpass', '-p', shell.password] + cmd
|
||||
if shell.keyfile:
|
||||
cmd += ['-i', shell.keyfile]
|
||||
cmd += ['gdb -q %r %s -x "%s"' % (target.executable,
|
||||
exefile = target.executable
|
||||
if six.PY3:
|
||||
exefile = exefile.decode()
|
||||
cmd += ['gdb -q %s %s -x "%s"' % (exefile,
|
||||
target.pid,
|
||||
tmpfile)]
|
||||
|
||||
@@ -684,7 +689,15 @@ def attach(target, gdbscript = None, exe = None, need_ptrace_scope = True, gdb_a
|
||||
if not pids:
|
||||
log.error('could not find remote process (%s:%d) on this machine' %
|
||||
target.sock.getpeername())
|
||||
pid = pids[0]
|
||||
waiting = True
|
||||
if exe:
|
||||
while waiting:
|
||||
for pid in pids:
|
||||
if proc.exe(pid) == exe:
|
||||
waiting = False
|
||||
break
|
||||
else:
|
||||
time.sleep(0.01)
|
||||
elif isinstance(target, tubes.process.process):
|
||||
pid = proc.pidof(target)[0]
|
||||
exe = exe or target.executable
|
||||
@@ -722,13 +735,9 @@ def attach(target, gdbscript = None, exe = None, need_ptrace_scope = True, gdb_a
|
||||
exe_fn = adb.proc_exe
|
||||
exe = exe_fn(pid)
|
||||
|
||||
if not pid and not exe:
|
||||
if not pid and not exe and not ssh:
|
||||
log.error('could not find target process')
|
||||
|
||||
if exe:
|
||||
# The 'file' statement should go first
|
||||
pre = 'file "%s"\n%s' % (exe, pre)
|
||||
|
||||
cmd = binary()
|
||||
|
||||
if gdb_args:
|
||||
@@ -752,7 +761,7 @@ def attach(target, gdbscript = None, exe = None, need_ptrace_scope = True, gdb_a
|
||||
if context.os == 'android' and pid:
|
||||
runner = _get_runner()
|
||||
which = _get_which()
|
||||
gdb_cmd = _gdbserver_args(pid=pid, which=which)
|
||||
gdb_cmd = _gdbserver_args(pid=pid, which=which, env=env)
|
||||
gdbserver = runner(gdb_cmd)
|
||||
port = _gdbserver_port(gdbserver, None)
|
||||
host = context.adb_host
|
||||
@@ -779,7 +788,7 @@ def attach(target, gdbscript = None, exe = None, need_ptrace_scope = True, gdb_a
|
||||
gdb_pid = misc.run_in_new_terminal(cmd)
|
||||
|
||||
if pid and context.native:
|
||||
proc.wait_for_debugger(pid)
|
||||
proc.wait_for_debugger(pid, gdb_pid)
|
||||
|
||||
return gdb_pid
|
||||
|
||||
@@ -856,10 +865,10 @@ def find_module_addresses(binary, ssh=None, ulimit=False):
|
||||
|
||||
Example:
|
||||
|
||||
>>> with context.local(log_level=9999): # doctest: +SKIP
|
||||
... shell = ssh(host='bandit.labs.overthewire.org',user='bandit0',password='bandit0', port=2220)
|
||||
>>> with context.local(log_level=9999):
|
||||
... 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) # doctest: +SKIP
|
||||
>>> os.path.basename(bash_libs[0].path)
|
||||
'libc.so.6'
|
||||
>>> hex(bash_libs[0].symbols['system']) # doctest: +SKIP
|
||||
'0x7ffff7634660'
|
||||
@@ -878,31 +887,29 @@ def find_module_addresses(binary, ssh=None, ulimit=False):
|
||||
local_elf = elf.ELF(binary)
|
||||
local_libs = local_elf.libs
|
||||
|
||||
entry = local_elf.header.e_entry
|
||||
|
||||
#
|
||||
# Get the addresses from GDB
|
||||
#
|
||||
libs = {}
|
||||
cmd = "gdb -q --args %s" % (binary)
|
||||
cmd = "gdb -q -nh --args %s | cat" % (binary) # pipe through cat to disable colored output on GDB 9+
|
||||
expr = re.compile(r'(0x\S+)[^/]+(.*)')
|
||||
|
||||
if ulimit:
|
||||
cmd = 'sh -c "(ulimit -s unlimited; %s)"' % cmd
|
||||
|
||||
cmd = shlex.split(cmd)
|
||||
cmd = ['sh', '-c', "(ulimit -s unlimited; %s)" % cmd]
|
||||
else:
|
||||
cmd = ['sh', '-c', cmd]
|
||||
|
||||
with runner(cmd) as gdb:
|
||||
if context.aslr:
|
||||
gdb.sendline('set disable-randomization off')
|
||||
|
||||
gdb.send("""
|
||||
set prompt
|
||||
break *%#x
|
||||
catch load
|
||||
run
|
||||
""" % entry)
|
||||
gdb.clean(2)
|
||||
""")
|
||||
gdb.sendline('info sharedlibrary')
|
||||
lines = gdb.recvrepeat(2)
|
||||
lines = context._decode(gdb.recvrepeat(2))
|
||||
|
||||
for line in lines.splitlines():
|
||||
m = expr.match(line)
|
||||
@@ -992,7 +999,7 @@ def version(program='gdb'):
|
||||
|
||||
Example:
|
||||
|
||||
>>> (7,0) <= gdb.version() <= (8,0)
|
||||
>>> (7,0) <= gdb.version() <= (10,0)
|
||||
True
|
||||
"""
|
||||
program = misc.which(program)
|
||||
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
from pwnlib.tubes.process import process
|
||||
from time import sleep
|
||||
from sys import argv
|
||||
sleep(1)
|
||||
sh = process(argv[1], shell=True)
|
||||
sh.sendline('set prompt (gdb)')
|
||||
res = sh.sendlineafter('(gdb)', 'c')
|
||||
while b'The program is not being run.' not in res:
|
||||
res = sh.sendlineafter('(gdb)', 'c')
|
||||
sh.close()
|
||||
+2
-2
@@ -56,7 +56,7 @@ def search_by_hash(hex_encoded_id, hash_type='build_id'):
|
||||
data = b""
|
||||
while not data.startswith(b'\x7fELF'):
|
||||
log.debug("Downloading data from LibcDB: %s", url)
|
||||
data = wget(url)
|
||||
data = wget(url, timeout=20)
|
||||
|
||||
if not data:
|
||||
log.warn_once("Could not fetch libc for build_id %s", hex_encoded_id)
|
||||
@@ -197,7 +197,7 @@ def get_build_id_offsets():
|
||||
# $ check_arch "x86-64"
|
||||
# 6 Displaying notes found at file offset 0x00000174 with length 0x00000024:
|
||||
# 82 Displaying notes found at file offset 0x00000270 with length 0x00000024:
|
||||
'amd64': [0x270, 0x174],
|
||||
'amd64': [0x270, 0x174, 0x2e0],
|
||||
# $ check_arch "PowerPC or cisco"
|
||||
# 88 Displaying notes found at file offset 0x00000174 with length 0x00000024:
|
||||
'powerpc': [0x174],
|
||||
|
||||
@@ -107,6 +107,7 @@ class AdbClient(Logger):
|
||||
and self.port == context.defaults['adb_port']:
|
||||
log.warn("Could not connect to ADB server, trying to start it")
|
||||
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))
|
||||
@@ -168,6 +169,7 @@ class AdbClient(Logger):
|
||||
|
||||
>>> c.version() > (4,0)
|
||||
True
|
||||
>>> c.wait_for_device() # ensure doctests alive
|
||||
"""
|
||||
try:
|
||||
self.send('host:kill')
|
||||
@@ -223,8 +225,8 @@ class AdbClient(Logger):
|
||||
l = l.decode('utf-8')
|
||||
yield l
|
||||
|
||||
def transport(self, serial=None):
|
||||
"""Sets the Transport on the rmeote device.
|
||||
def transport(self, serial=None, try_again=True):
|
||||
"""Sets the Transport on the remote device.
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -244,10 +246,14 @@ class AdbClient(Logger):
|
||||
msg = 'host:transport-any'
|
||||
|
||||
if self.send(msg) == FAIL:
|
||||
err = self.recvl().decode('utf-8')
|
||||
if err == 'device offline' and try_again:
|
||||
self.wait_for_device(serial)
|
||||
return self.transport(serial, try_again=False)
|
||||
if serial:
|
||||
self.error("Could not set transport to %r" % serial)
|
||||
self.error("Could not set transport to %r (%s)" % (serial, err))
|
||||
else:
|
||||
self.error("Could not set transport 'any'")
|
||||
self.error("Could not set transport 'any' (%s)" % err)
|
||||
|
||||
@_autoclose
|
||||
@_with_transport
|
||||
@@ -289,7 +295,9 @@ class AdbClient(Logger):
|
||||
@_with_transport
|
||||
def root(self):
|
||||
self.send('root:')
|
||||
return self.c.recvall().decode('utf-8')
|
||||
rv = self.c.recvall().decode('utf-8')
|
||||
time.sleep(0.1)
|
||||
return rv
|
||||
|
||||
@_autoclose
|
||||
@_with_transport
|
||||
@@ -336,22 +344,25 @@ class AdbClient(Logger):
|
||||
|
||||
# The first OKAY is that the command was understood
|
||||
if response != OKAY:
|
||||
self.error("An error occurred while waiting for device with serial %r" % serial)
|
||||
if response == FAIL:
|
||||
response = self.recvl().decode('utf-8')
|
||||
self.error("An error occurred while trying to wait for device with serial %r (%r)" % (serial, response))
|
||||
|
||||
# The second OKAY is that the device is available
|
||||
response = self.c.recvn(4)
|
||||
if response != OKAY:
|
||||
self.error("An error occurred while waiting for device with serial %r" % serial)
|
||||
if response == FAIL:
|
||||
response = self.recvl().decode('utf-8')
|
||||
self.error("An error occurred while waiting for device with serial %r (%r)" % (serial, response))
|
||||
|
||||
def _sync(fn):
|
||||
"""Decorator which enters 'sync:' mode to the selected transport,
|
||||
then invokes the decorated funciton."""
|
||||
@functools.wraps(fn)
|
||||
def wrapper(self, *a, **kw):
|
||||
rv = None
|
||||
if FAIL != self.send('sync:'):
|
||||
rv = fn(self, *a, **kw)
|
||||
return rv
|
||||
if self.send('sync:') == FAIL:
|
||||
self.error("An error occurred while trying to use SYNC API (%r)" % self.recvl().decode('utf-8'))
|
||||
return fn(self, *a, **kw)
|
||||
return wrapper
|
||||
|
||||
def list(self, path):
|
||||
@@ -381,8 +392,10 @@ class AdbClient(Logger):
|
||||
|
||||
Examples:
|
||||
|
||||
>>> _ = AdbClient().root()
|
||||
>>> AdbClient().wait_for_device()
|
||||
>>> pprint(AdbClient().list('/data/user'))
|
||||
{'0': {'mode': 41471, 'size': 11, 'time': ...}}
|
||||
{'0': {'mode': 41471, 'size': 10, 'time': ...}}
|
||||
>>> AdbClient().list('/does/not/exist')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
|
||||
+13
-3
@@ -277,6 +277,7 @@ import hashlib
|
||||
import itertools
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import six
|
||||
import string
|
||||
import sys
|
||||
@@ -880,7 +881,7 @@ class ROP(object):
|
||||
|
||||
|
||||
def find_stack_adjustment(self, slots):
|
||||
self.search(move=slots * context.arch)
|
||||
self.search(move=slots * context.bytes)
|
||||
|
||||
def chain(self):
|
||||
"""Build the ROP chain
|
||||
@@ -965,7 +966,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',
|
||||
@@ -1060,13 +1064,19 @@ class ROP(object):
|
||||
|
||||
return os.path.join(cachedir, sha256.hexdigest())
|
||||
|
||||
@staticmethod
|
||||
def clear_cache():
|
||||
"""Clears the ROP gadget cache"""
|
||||
cachedir = os.path.join(tempfile.gettempdir(), 'pwntools-rop-cache')
|
||||
shutil.rmtree(cachedir)
|
||||
|
||||
def __cache_load(self, elf):
|
||||
filename = self.__get_cachefile_name(elf)
|
||||
if not os.path.exists(filename):
|
||||
return None
|
||||
log.info_once('Loaded cached gadgets for %r' % elf.file.name)
|
||||
gadgets = eval(open(filename).read())
|
||||
gadgets = {k - elf.load_addr + elf.address:v for k, v in gadgets.items()}
|
||||
log.info_once('Loaded %s cached gadgets for %r', len(gadgets), elf.file.name)
|
||||
return gadgets
|
||||
|
||||
def __cache_save(self, elf, data):
|
||||
|
||||
@@ -17,18 +17,19 @@ 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'''
|
||||
# ... int main() {
|
||||
# ... printf("Hello, %s!\\n", "world");
|
||||
# ... }
|
||||
# ... ''')
|
||||
# >>> gcc.shutdown('send')
|
||||
# >>> gcc.poll(True)
|
||||
# 0
|
||||
# >>> sc = shellcraft.loader_append('a.out')
|
||||
# >>> run_assembly(sc).recvline()
|
||||
# 'Hello, world!\n'
|
||||
>>> 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()
|
||||
b'Hello, world!\n'
|
||||
|
||||
</%docstring>
|
||||
<%page args="data = None"/>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<% from pwnlib.shellcraft import aarch64, pretty, common %>
|
||||
<%docstring>Copies memory.
|
||||
|
||||
Args:
|
||||
dest: Destination address
|
||||
src: Source address
|
||||
n: Number of bytes
|
||||
</%docstring>
|
||||
<%page args="dest, src, n"/>
|
||||
<%
|
||||
memcpy_loop = common.label("memcpy_loop")
|
||||
%>
|
||||
/* memcpy(${pretty(dest)}, ${pretty(src)}, ${pretty(n)}) */
|
||||
${aarch64.setregs({'x0': dest, 'x1': src, 'x2': n})}
|
||||
${memcpy_loop}:
|
||||
ldrb w3, [x1], #1
|
||||
strb w3, [x0], #1
|
||||
subs x2, x2, #1
|
||||
bge ${memcpy_loop}
|
||||
@@ -16,20 +16,21 @@ 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)
|
||||
>>> 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, amd64!\n'
|
||||
# True
|
||||
>>> run_assembly(sc).recvline() == b'Hello, amd64!\n'
|
||||
True
|
||||
|
||||
</%docstring>
|
||||
<%page args="data = None"/>
|
||||
|
||||
@@ -104,6 +104,9 @@ PT_LOAD = 1
|
||||
/* AT_RANDOM */
|
||||
push esp
|
||||
push 25
|
||||
/* AT_SECURE */
|
||||
push ebx
|
||||
push 23
|
||||
|
||||
push ebx /* envp */
|
||||
push ebx /* argv */
|
||||
|
||||
@@ -16,20 +16,21 @@ 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)
|
||||
>>> 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'
|
||||
# True
|
||||
>>> run_assembly(sc).recvline()
|
||||
b'Hello, i386!\n'
|
||||
|
||||
</%docstring>
|
||||
<%page args="data = None"/>
|
||||
|
||||
@@ -9,4 +9,4 @@ Example:
|
||||
</%docstring>
|
||||
pop {r0-r12,lr}
|
||||
ldr sp, [sp, 64]
|
||||
bx r1
|
||||
pop {pc}
|
||||
@@ -17,18 +17,19 @@ 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(['arm-linux-gnueabihf-gcc','-xc','-static','-Wl,-Ttext-segment=0x20000000','-'])
|
||||
# >>> gcc.write(b'''
|
||||
# ... int main() {
|
||||
# ... printf("Hello, %s!\\n", "world");
|
||||
# ... }
|
||||
# ... ''')
|
||||
# >>> gcc.shutdown('send')
|
||||
# >>> gcc.poll(True)
|
||||
# 0
|
||||
# >>> sc = shellcraft.loader_append('a.out')
|
||||
# >>> run_assembly(sc).recvline()
|
||||
# 'Hello, world!\n'
|
||||
>>> gcc = process(['arm-linux-gnueabihf-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()
|
||||
b'Hello, world!\n'
|
||||
|
||||
</%docstring>
|
||||
<%page args="data = None"/>
|
||||
|
||||
+19
-5
@@ -378,7 +378,7 @@ def readline(_size=None, prompt='', float=True, priority=10):
|
||||
# XXX circular imports
|
||||
from pwnlib.term import term_mode
|
||||
if not term_mode:
|
||||
print(prompt, end='', flush=True)
|
||||
six.print_(prompt, end='', flush=True)
|
||||
return sys.stdin.readline(_size).rstrip('\n')
|
||||
show_suggestions = False
|
||||
eof = False
|
||||
@@ -435,6 +435,19 @@ def raw_input(prompt='', float=True):
|
||||
"""
|
||||
return readline(None, prompt, float)
|
||||
|
||||
def str_input(prompt='', float=True):
|
||||
r"""str_input(prompt='', float=True)
|
||||
|
||||
Replacement for the built-in ``input`` in python3 using ``pwnlib`` readline
|
||||
implementation.
|
||||
|
||||
Arguments:
|
||||
prompt(str): The prompt to show to the user.
|
||||
float(bool): If set to `True`, prompt and input will float to the
|
||||
bottom of the screen when `term.term_mode` is enabled.
|
||||
"""
|
||||
return readline(None, prompt, float).decode()
|
||||
|
||||
def eval_input(prompt='', float=True):
|
||||
"""eval_input(prompt='', float=True)
|
||||
|
||||
@@ -450,11 +463,12 @@ def eval_input(prompt='', float=True):
|
||||
Example:
|
||||
|
||||
>>> try:
|
||||
... saved_stdin = sys.stdin
|
||||
... sys.stdin = io.StringIO("{'a':20}")
|
||||
... saved = sys.stdin, pwnlib.term.term_mode
|
||||
... pwnlib.term.term_mode = False
|
||||
... sys.stdin = io.StringIO(u"{'a': 20}")
|
||||
... eval_input("Favorite object? ")['a']
|
||||
... finally:
|
||||
... sys.stdin = saved_stdin
|
||||
... sys.stdin, pwnlib.term.term_mode = saved
|
||||
Favorite object? 20
|
||||
"""
|
||||
from pwnlib.util import safeeval
|
||||
@@ -480,4 +494,4 @@ def init():
|
||||
builtins.raw_input = raw_input
|
||||
builtins.input = eval_input
|
||||
else:
|
||||
builtins.input = raw_input
|
||||
builtins.input = str_input
|
||||
+6
-4
@@ -460,11 +460,13 @@ def render_from(i, force = False, clear_after = False):
|
||||
def redraw():
|
||||
for i in reversed(range(len(cells))):
|
||||
row = cells[i].start[0]
|
||||
if row - scroll + height - 1 < 0:
|
||||
if row - scroll + height <= 0:
|
||||
# XXX: remove this line when render_cell is fixed
|
||||
i += 1
|
||||
break
|
||||
# XXX: remove this line when render_cell is fixed
|
||||
if cells[i].start[0] - scroll + height <= 0:
|
||||
i += 1
|
||||
else:
|
||||
if not cells:
|
||||
return
|
||||
render_from(i, force = True, clear_after = True)
|
||||
|
||||
lock = threading.Lock()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
__all__ = ['get']
|
||||
import curses
|
||||
@@ -48,7 +49,8 @@ def init():
|
||||
# Fix for BPython
|
||||
try:
|
||||
curses.setupterm()
|
||||
except:
|
||||
pass
|
||||
except curses.error as e:
|
||||
import traceback
|
||||
print('Warning:', ''.join(traceback.format_exception_only(e.__class__, e)), file=sys.stderr)
|
||||
|
||||
cache = {}
|
||||
+12
-11
@@ -242,10 +242,6 @@ class process(tube):
|
||||
if not shell:
|
||||
executable, argv, env = self._validate(cwd, executable, argv, env)
|
||||
|
||||
# Permit invocation as process('sh') and process(['sh'])
|
||||
if isinstance(argv, (bytes, six.text_type)):
|
||||
argv = [argv]
|
||||
|
||||
# Avoid the need to have to deal with the STDOUT magic value.
|
||||
if stderr is STDOUT:
|
||||
stderr = stdout
|
||||
@@ -308,13 +304,15 @@ class process(tube):
|
||||
# and binfmt is not installed (e.g. when running on
|
||||
# Travis CI), re-try with qemu-XXX if we get an
|
||||
# 'Exec format error'.
|
||||
prefixes = [([], executable)]
|
||||
executables = [executable]
|
||||
prefixes = [([], self.executable)]
|
||||
exception = None
|
||||
|
||||
for prefix, executable in prefixes:
|
||||
try:
|
||||
self.proc = subprocess.Popen(args = prefix + argv,
|
||||
args = argv
|
||||
if prefix:
|
||||
args = prefix + args
|
||||
self.proc = subprocess.Popen(args = args,
|
||||
shell = shell,
|
||||
executable = executable,
|
||||
cwd = cwd,
|
||||
@@ -509,11 +507,14 @@ class process(tube):
|
||||
# - Must be a list/tuple of strings
|
||||
# - Each string must not contain '\x00'
|
||||
#
|
||||
if isinstance(argv, (bytes, six.text_type)):
|
||||
if isinstance(argv, (six.text_type, six.binary_type)):
|
||||
argv = [argv]
|
||||
|
||||
if not all(isinstance(arg, (bytes, six.text_type)) for arg in argv):
|
||||
self.error("argv must be strings: %r" % argv)
|
||||
if not isinstance(argv, (list, tuple)):
|
||||
self.error('argv must be a list or tuple: %r' % argv)
|
||||
|
||||
if not all(isinstance(arg, (six.text_type, six.binary_type)) for arg in argv):
|
||||
self.error("argv must be strings or bytes: %r" % argv)
|
||||
|
||||
# Create a duplicate so we can modify it
|
||||
argv = list(argv or [])
|
||||
@@ -950,7 +951,7 @@ class process(tube):
|
||||
|
||||
Example:
|
||||
|
||||
>>> e = ELF('/bin/bash')
|
||||
>>> e = ELF('/bin/bash-static')
|
||||
>>> p = process(e.path)
|
||||
|
||||
In order to make sure there's not a race condition against
|
||||
|
||||
+19
-16
@@ -21,7 +21,6 @@ from pwnlib.log import Logger
|
||||
from pwnlib.log import getLogger
|
||||
from pwnlib.term import text
|
||||
from pwnlib.timeout import Timeout
|
||||
from pwnlib.tubes.process import process
|
||||
from pwnlib.tubes.sock import sock
|
||||
from pwnlib.util import hashes
|
||||
from pwnlib.util import misc
|
||||
@@ -172,7 +171,8 @@ class ssh_channel(sock):
|
||||
|
||||
return data
|
||||
|
||||
def wait(self):
|
||||
def wait(self, timeout=sock.default):
|
||||
# TODO: deal with timeouts
|
||||
return self.poll(block=True)
|
||||
|
||||
def poll(self, block=False):
|
||||
@@ -536,9 +536,9 @@ class ssh(Timeout, Logger):
|
||||
#: PID of the remote ``sshd`` process servicing this connection.
|
||||
pid = None
|
||||
|
||||
def __init__(self, user, host, port = 22, password = None, key = None,
|
||||
keyfile = None, proxy_command = None, proxy_sock = None,
|
||||
level = None, cache = True, ssh_agent = False, *a, **kw):
|
||||
def __init__(self, user=None, host=None, port=22, password=None, key=None,
|
||||
keyfile=None, proxy_command=None, proxy_sock=None,
|
||||
level=None, cache=True, ssh_agent=False, *a, **kw):
|
||||
"""Creates a new ssh connection.
|
||||
|
||||
Arguments:
|
||||
@@ -796,14 +796,14 @@ class ssh(Timeout, Logger):
|
||||
>>> s.process(['LOLOLOL\x00', '/proc/self/cmdline'], executable='cat').recvall()
|
||||
b'LOLOLOL\x00/proc/self/cmdline\x00'
|
||||
>>> sh = s.process(executable='/bin/sh')
|
||||
>>> sh.pid in pidof('sh') # doctest: +SKIP
|
||||
>>> str(sh.pid).encode() in s.pidof('sh') # doctest: +SKIP
|
||||
True
|
||||
>>> s.process(['pwd'], cwd='/tmp').recvall()
|
||||
b'/tmp\n'
|
||||
>>> p = s.process(['python','-c','import os; print(os.read(2, 1024))'], stderr=0)
|
||||
>>> p = s.process(['python','-c','import os; os.write(1, os.read(2, 1024))'], stderr=0)
|
||||
>>> p.send(b'hello')
|
||||
>>> p.recv()
|
||||
b'hello\n'
|
||||
b'hello'
|
||||
>>> s.process(['/bin/echo', 'hello']).recvall()
|
||||
b'hello\n'
|
||||
>>> s.process(['/bin/echo', 'hello'], stdout='/dev/null').recvall()
|
||||
@@ -845,11 +845,17 @@ class ssh(Timeout, Logger):
|
||||
if not isinstance(argv, (list, tuple)):
|
||||
self.error('argv must be a list or tuple')
|
||||
|
||||
if not all(isinstance(arg, (six.text_type, six.binary_type)) for arg in argv):
|
||||
self.error("argv must be strings or bytes: %r" % argv)
|
||||
|
||||
if shell:
|
||||
if len(argv) != 1:
|
||||
self.error('Cannot provide more than 1 argument if shell=True')
|
||||
argv = ['/bin/sh', '-c'] + argv
|
||||
|
||||
# Create a duplicate so we can modify it
|
||||
argv = list(argv or [])
|
||||
|
||||
# Python doesn't like when an arg in argv contains '\x00'
|
||||
# -> execve() arg 2 must contain only strings
|
||||
for i, oarg in enumerate(argv):
|
||||
@@ -882,12 +888,8 @@ class ssh(Timeout, Logger):
|
||||
# Validate, since failures on the remote side will suck.
|
||||
if not isinstance(executable, (six.text_type, six.binary_type)):
|
||||
self.error("executable / argv[0] must be a string: %r" % executable)
|
||||
if not isinstance(argv, (list, tuple)):
|
||||
self.error("argv must be a list or tuple: %r" % argv)
|
||||
if env is not None and not isinstance(env, dict) and env != os.environ:
|
||||
self.error("env must be a dict: %r" % env)
|
||||
if not all(isinstance(s, (six.text_type, six.binary_type)) for s in argv):
|
||||
self.error("argv must only contain strings: %r" % argv)
|
||||
|
||||
# Allow passing in sys.stdin/stdout/stderr objects
|
||||
handles = {sys.stdin: 0, sys.stdout:1, sys.stderr:2}
|
||||
@@ -1214,7 +1216,7 @@ from ctypes import *; libc = CDLL('libc.so.6'); print(libc.getenv(%r))
|
||||
... user='travis',
|
||||
... password='demopass')
|
||||
>>> a = s.connect_remote(s.host, l.lport)
|
||||
>>> b = l.wait_for_connection()
|
||||
>>> a=a; b = l.wait_for_connection() # a=a; prevents hangs
|
||||
>>> a.sendline(b'Hello')
|
||||
>>> print(repr(b.recvline()))
|
||||
b'Hello\n'
|
||||
@@ -1240,7 +1242,7 @@ from ctypes import *; libc = CDLL('libc.so.6'); print(libc.getenv(%r))
|
||||
... password='demopass')
|
||||
>>> l = s.listen_remote()
|
||||
>>> a = remote(s.host, l.port)
|
||||
>>> b = l.wait_for_connection()
|
||||
>>> a=a; b = l.wait_for_connection() # a=a; prevents hangs
|
||||
>>> a.sendline(b'Hello')
|
||||
>>> print(repr(b.recvline()))
|
||||
b'Hello\n'
|
||||
@@ -1350,7 +1352,7 @@ from ctypes import *; libc = CDLL('libc.so.6'); print(libc.getenv(%r))
|
||||
self.error('Unable to find libraries for %r' % remote)
|
||||
return {}
|
||||
|
||||
return misc.parse_ldd_output(data)
|
||||
return misc.parse_ldd_output(context._decode(data))
|
||||
|
||||
def _get_fingerprint(self, remote):
|
||||
cmd = '(sha256 || sha256sum || openssl sha256) 2>/dev/null < '
|
||||
@@ -1574,6 +1576,7 @@ from ctypes import *; libc = CDLL('libc.so.6'); print(libc.getenv(%r))
|
||||
>>> print(open('/tmp/upload_bar').read())
|
||||
Hello, world
|
||||
"""
|
||||
data = context._encode(data)
|
||||
# If a relative path was provided, prepend the cwd
|
||||
if os.path.normpath(remote) == os.path.basename(remote):
|
||||
remote = os.path.join(self.cwd, remote)
|
||||
@@ -1721,7 +1724,7 @@ from ctypes import *; libc = CDLL('libc.so.6'); print(libc.getenv(%r))
|
||||
|
||||
libs = self._libs_remote(remote)
|
||||
|
||||
remote = self.readlink('-f',remote).strip()
|
||||
remote = context._decode(self.readlink('-f',remote).strip())
|
||||
libs[remote] = 0
|
||||
|
||||
if directory == None:
|
||||
|
||||
@@ -498,11 +498,11 @@ class tube(Timeout, Logger):
|
||||
try:
|
||||
line = self.recvline(keepends=True)
|
||||
except Exception:
|
||||
self.buffer.add(tmpbuf)
|
||||
self.buffer.unget(tmpbuf)
|
||||
raise
|
||||
|
||||
if not line:
|
||||
self.buffer.add(tmpbuf)
|
||||
self.buffer.unget(tmpbuf)
|
||||
return b''
|
||||
|
||||
if pred(line):
|
||||
@@ -1106,11 +1106,14 @@ class tube(Timeout, Logger):
|
||||
"""
|
||||
self << other << self
|
||||
|
||||
def wait_for_close(self):
|
||||
def wait_for_close(self, timeout=default):
|
||||
"""Waits until the tube is closed."""
|
||||
|
||||
while self.connected():
|
||||
time.sleep(0.05)
|
||||
with self.countdown(timeout):
|
||||
while self.countdown_active():
|
||||
if not self.connected():
|
||||
return
|
||||
time.sleep(min(self.timeout, 0.05))
|
||||
|
||||
wait = wait_for_close
|
||||
|
||||
|
||||
+155
-21
@@ -1,19 +1,53 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import fcntl
|
||||
import os
|
||||
import signal
|
||||
import six
|
||||
import string
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import termios
|
||||
import time
|
||||
import types
|
||||
|
||||
from pwnlib import term
|
||||
from pwnlib.log import getLogger
|
||||
from pwnlib.term.readline import raw_input
|
||||
from pwnlib.tubes.process import process
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
def yesno(prompt, default = None):
|
||||
"""Presents the user with prompt (typically in the form of question) which
|
||||
the user must answer yes or no.
|
||||
def testpwnproc(cmd):
|
||||
env = dict(os.environ)
|
||||
env.pop("PWNLIB_NOTERM", None)
|
||||
env["TERM"] = "xterm-256color"
|
||||
def handleusr1(sig, frame):
|
||||
s = p.stderr.read()
|
||||
log.error("child process failed:\n%s", s.decode())
|
||||
signal.signal(signal.SIGUSR1, handleusr1)
|
||||
cmd = """
|
||||
from pwn import *
|
||||
import signal
|
||||
atexception.register(lambda:os.kill(os.getppid(), signal.SIGUSR1))
|
||||
""" + cmd
|
||||
if "coverage" in sys.modules:
|
||||
cmd = "import coverage; coverage.process_startup()\n" + cmd
|
||||
env.setdefault("COVERAGE_PROCESS_START", ".coveragerc")
|
||||
p = process([sys.executable, "-c", cmd], env=env, stderr=subprocess.PIPE)
|
||||
try:
|
||||
p.recvuntil(b"\33[6n")
|
||||
except EOFError:
|
||||
raise EOFError("process terminated with code: %r (%r)" % (p.poll(True), p.stderr.read()))
|
||||
fcntl.ioctl(p.stdout.fileno(), termios.TIOCSWINSZ, struct.pack("hh", 80, 80))
|
||||
p.stdout.write(b"\x1b[1;1R")
|
||||
return p
|
||||
|
||||
def yesno(prompt, default=None):
|
||||
r"""Presents the user with prompt (typically in the form of question)
|
||||
which the user must answer yes or no.
|
||||
|
||||
Arguments:
|
||||
prompt (str): The prompt to show
|
||||
@@ -21,7 +55,33 @@ def yesno(prompt, default = None):
|
||||
|
||||
Returns:
|
||||
`True` if the answer was "yes", `False` if "no"
|
||||
"""
|
||||
|
||||
Examples:
|
||||
>>> yesno("A number:", 20)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: yesno(): default must be a boolean or None
|
||||
>>> saved_stdin = sys.stdin
|
||||
>>> try:
|
||||
... sys.stdin = io.StringIO(u"x\nyes\nno\n\n")
|
||||
... yesno("is it good 1")
|
||||
... yesno("is it good 2", True)
|
||||
... yesno("is it good 3", False)
|
||||
... finally:
|
||||
... sys.stdin = saved_stdin
|
||||
[?] is it good 1 [yes/no] Please answer yes or no
|
||||
[?] is it good 1 [yes/no] True
|
||||
[?] is it good 2 [Yes/no] False
|
||||
[?] is it good 3 [yes/No] False
|
||||
|
||||
Tests:
|
||||
>>> p = testpwnproc("print(yesno('is it ok??'))")
|
||||
>>> b"is it ok" in p.recvuntil("??")
|
||||
True
|
||||
>>> p.sendline(b"x\nny")
|
||||
>>> b"True" in p.recvall()
|
||||
True
|
||||
"""
|
||||
|
||||
if default is not None and not isinstance(default, bool):
|
||||
raise ValueError('yesno(): default must be a boolean or None')
|
||||
@@ -30,9 +90,9 @@ def yesno(prompt, default = None):
|
||||
term.output(' [?] %s [' % prompt)
|
||||
yesfocus, yes = term.text.bold('Yes'), 'yes'
|
||||
nofocus, no = term.text.bold('No'), 'no'
|
||||
hy = term.output(yesfocus if default == True else yes)
|
||||
hy = term.output(yesfocus if default is True else yes)
|
||||
term.output('/')
|
||||
hn = term.output(nofocus if default == False else no)
|
||||
hn = term.output(nofocus if default is False else no)
|
||||
term.output(']\n')
|
||||
cur = default
|
||||
while True:
|
||||
@@ -50,12 +110,12 @@ def yesno(prompt, default = None):
|
||||
return cur
|
||||
else:
|
||||
prompt = ' [?] %s [%s/%s] ' % (prompt,
|
||||
'Yes' if default == True else 'yes',
|
||||
'No' if default == False else 'no',
|
||||
'Yes' if default is True else 'yes',
|
||||
'No' if default is False else 'no',
|
||||
)
|
||||
while True:
|
||||
opt = raw_input(prompt).lower()
|
||||
if opt == '' and default != None:
|
||||
if not opt and default is not None:
|
||||
return default
|
||||
elif opt in ('y','yes'):
|
||||
return True
|
||||
@@ -64,7 +124,7 @@ def yesno(prompt, default = None):
|
||||
print('Please answer yes or no')
|
||||
|
||||
def options(prompt, opts, default = None):
|
||||
"""Presents the user with a prompt (typically in the
|
||||
r"""Presents the user with a prompt (typically in the
|
||||
form of a question) and a number of options.
|
||||
|
||||
Arguments:
|
||||
@@ -74,7 +134,44 @@ def options(prompt, opts, default = None):
|
||||
|
||||
Returns:
|
||||
The users choice in the form of an integer.
|
||||
"""
|
||||
|
||||
Examples:
|
||||
>>> options("Select a color", ("red", "green", "blue"), "green")
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: options(): default must be a number or None
|
||||
|
||||
Tests:
|
||||
>>> p = testpwnproc("print(options('select a color', ('red', 'green', 'blue')))")
|
||||
>>> p.sendline(b"\33[C\33[A\33[A\33[B\33[1;5A\33[1;5B 0310")
|
||||
>>> _ = p.recvall()
|
||||
>>> saved_stdin = sys.stdin
|
||||
>>> try:
|
||||
... sys.stdin = io.StringIO(u"\n4\n\n3\n")
|
||||
... with context.local(log_level="INFO"):
|
||||
... options("select a color A", ("red", "green", "blue"), 0)
|
||||
... options("select a color B", ("red", "green", "blue"))
|
||||
... finally:
|
||||
... sys.stdin = saved_stdin
|
||||
[?] select a color A
|
||||
1) red
|
||||
2) green
|
||||
3) blue
|
||||
Choice [1] 0
|
||||
[?] select a color B
|
||||
1) red
|
||||
2) green
|
||||
3) blue
|
||||
Choice [?] select a color B
|
||||
1) red
|
||||
2) green
|
||||
3) blue
|
||||
Choice [?] select a color B
|
||||
1) red
|
||||
2) green
|
||||
3) blue
|
||||
Choice 2
|
||||
"""
|
||||
|
||||
if default is not None and not isinstance(default, six.integer_types):
|
||||
raise ValueError('options(): default must be a number or None')
|
||||
@@ -93,7 +190,6 @@ def options(prompt, opts, default = None):
|
||||
term.output(opt + '\n', indent = len(num) + len(space))
|
||||
hs.append(h)
|
||||
ds = ''
|
||||
prev = 0
|
||||
while True:
|
||||
prev = cur
|
||||
was_digit = False
|
||||
@@ -115,16 +211,17 @@ def options(prompt, opts, default = None):
|
||||
elif k in ('<enter>', '<right>'):
|
||||
if cur is not None:
|
||||
return cur
|
||||
elif k in tuple('1234567890'):
|
||||
elif k in tuple(string.digits):
|
||||
was_digit = True
|
||||
d = str(k)
|
||||
n = int(ds + d)
|
||||
if n > 0 and n <= len(opts):
|
||||
if 0 < n <= len(opts):
|
||||
ds += d
|
||||
cur = n - 1
|
||||
elif d != '0':
|
||||
ds = d
|
||||
n = int(ds)
|
||||
cur = n - 1
|
||||
n = int(ds)
|
||||
cur = n - 1
|
||||
|
||||
if prev != cur:
|
||||
if prev is not None:
|
||||
@@ -135,6 +232,8 @@ def options(prompt, opts, default = None):
|
||||
hs[cur].update(arrow)
|
||||
else:
|
||||
linefmt = ' %' + str(len(str(len(opts)))) + 'd) %s'
|
||||
if default is not None:
|
||||
default += 1
|
||||
while True:
|
||||
print(' [?] ' + prompt)
|
||||
for i, opt in enumerate(opts):
|
||||
@@ -147,12 +246,39 @@ def options(prompt, opts, default = None):
|
||||
except (ValueError, TypeError):
|
||||
continue
|
||||
if x >= 1 and x <= len(opts):
|
||||
return x
|
||||
return x - 1
|
||||
|
||||
def pause(n = None):
|
||||
"""Waits for either user input or a specific number of seconds."""
|
||||
def pause(n=None):
|
||||
r"""Waits for either user input or a specific number of seconds.
|
||||
|
||||
if n == None:
|
||||
Examples:
|
||||
>>> with context.local(log_level="INFO"):
|
||||
... pause(1)
|
||||
[x] Waiting
|
||||
[x] Waiting: 1...
|
||||
[+] Waiting: Done
|
||||
>>> pause("whatever")
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: pause(): n must be a number or None
|
||||
|
||||
Tests:
|
||||
>>> saved_stdin = sys.stdin
|
||||
>>> try:
|
||||
... sys.stdin = io.StringIO(u"\n")
|
||||
... with context.local(log_level="INFO"):
|
||||
... pause()
|
||||
... finally:
|
||||
... sys.stdin = saved_stdin
|
||||
[*] Paused (press enter to continue)
|
||||
>>> p = testpwnproc("pause()")
|
||||
>>> b"Paused" in p.recvuntil(b"press any")
|
||||
True
|
||||
>>> p.send(b"x")
|
||||
>>> _ = p.recvall()
|
||||
"""
|
||||
|
||||
if n is None:
|
||||
if term.term_mode:
|
||||
log.info('Paused (press any to continue)')
|
||||
term.getkey()
|
||||
@@ -169,7 +295,7 @@ def pause(n = None):
|
||||
raise ValueError('pause(): n must be a number or None')
|
||||
|
||||
def more(text):
|
||||
"""more(text)
|
||||
r"""more(text)
|
||||
|
||||
Shows text like the command line tool ``more``.
|
||||
|
||||
@@ -180,6 +306,14 @@ def more(text):
|
||||
|
||||
Returns:
|
||||
:const:`None`
|
||||
|
||||
Tests:
|
||||
>>> more("text")
|
||||
text
|
||||
>>> p = testpwnproc("more('text\\n' * (term.height + 2))")
|
||||
>>> p.send(b"x")
|
||||
>>> b"text" in p.recvall()
|
||||
True
|
||||
"""
|
||||
if term.term_mode:
|
||||
lines = text.split('\n')
|
||||
|
||||
+130
-22
@@ -6,7 +6,7 @@ import string
|
||||
|
||||
from pwnlib.context import context, LocalNoarchContext
|
||||
from pwnlib.log import getLogger
|
||||
from pwnlib.util import packing
|
||||
from pwnlib.util import packing, iters
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
@@ -129,19 +129,10 @@ def cyclic(length = None, alphabet = None, n = None):
|
||||
log.error("Can't create a pattern length=%i with len(alphabet)==%i and n==%i" \
|
||||
% (length, len(alphabet), n))
|
||||
|
||||
out = []
|
||||
for ndx, c in enumerate(de_bruijn(alphabet, n)):
|
||||
if length != None and ndx >= length:
|
||||
break
|
||||
else:
|
||||
out.append(c)
|
||||
generator = de_bruijn(alphabet, n)
|
||||
out = iters.take(length, generator)
|
||||
|
||||
if isinstance(alphabet, six.text_type):
|
||||
return ''.join(out)
|
||||
elif isinstance(alphabet, bytes):
|
||||
return bytes(bytearray(out))
|
||||
else:
|
||||
return out
|
||||
return _join_sequence(out, alphabet)
|
||||
|
||||
@LocalNoarchContext
|
||||
def cyclic_find(subseq, alphabet = None, n = None):
|
||||
@@ -218,6 +209,7 @@ def cyclic_find(subseq, alphabet = None, n = None):
|
||||
|
||||
if isinstance(subseq, six.integer_types):
|
||||
subseq = packing.pack(subseq, bytes=n)
|
||||
subseq = context._encode(subseq)
|
||||
|
||||
if len(subseq) != n:
|
||||
log.warn_once("cyclic_find() expects %i-byte subsequences by default, you gave %r\n"\
|
||||
@@ -228,6 +220,7 @@ def cyclic_find(subseq, alphabet = None, n = None):
|
||||
|
||||
if alphabet is None:
|
||||
alphabet = context.cyclic_alphabet
|
||||
alphabet = context._encode(alphabet)
|
||||
|
||||
if any(c not in alphabet for c in subseq):
|
||||
return -1
|
||||
@@ -287,21 +280,15 @@ def cyclic_metasploit(length = None, sets = None):
|
||||
20280
|
||||
"""
|
||||
sets = sets or [ string.ascii_uppercase.encode(), string.ascii_lowercase.encode(), string.digits.encode() ]
|
||||
out = bytearray()
|
||||
|
||||
for ndx, c in enumerate(metasploit_pattern(sets)):
|
||||
if length != None and ndx >= length:
|
||||
break
|
||||
else:
|
||||
out.append(c)
|
||||
|
||||
out = bytes(out)
|
||||
generator = metasploit_pattern(sets)
|
||||
out = iters.take(length, generator)
|
||||
|
||||
if length != None and len(out) < length:
|
||||
log.error("Can't create a pattern of length %i with sets of lengths %s. Maximum pattern length is %i." \
|
||||
% (length, list(map(len, sets)), len(out)))
|
||||
|
||||
return out
|
||||
return _join_sequence(out, sets[0])
|
||||
|
||||
def cyclic_metasploit_find(subseq, sets = None):
|
||||
"""cyclic_metasploit_find(subseq, sets = [ string.ascii_uppercase, string.ascii_lowercase, string.digits ]) -> int
|
||||
@@ -344,3 +331,124 @@ def _gen_find(subseq, generator):
|
||||
if saved == subseq:
|
||||
return pos
|
||||
return -1
|
||||
|
||||
def _join_sequence(seq, alphabet):
|
||||
if isinstance(alphabet, six.text_type):
|
||||
return ''.join(seq)
|
||||
elif isinstance(alphabet, bytes):
|
||||
return bytes(bytearray(seq))
|
||||
else:
|
||||
return seq
|
||||
|
||||
class cyclic_gen(object):
|
||||
"""
|
||||
Creates a stateful cyclic generator which can generate sequential chunks of de Bruijn sequences.
|
||||
|
||||
>>> g = cyclic_gen() # Create a generator
|
||||
>>> g.get(4) # Get a chunk of length 4
|
||||
b'aaaa'
|
||||
>>> g.get(4) # Get a chunk of length 4
|
||||
b'baaa'
|
||||
>>> g.get(8) # Get a chunk of length 8
|
||||
b'caaadaaa'
|
||||
>>> g.get(4) # Get a chunk of length 4
|
||||
b'eaaa'
|
||||
>>> g.find(b'caaa') # Position 8, which is in chunk 2 at index 0
|
||||
(8, 2, 0)
|
||||
>>> g.find(b'aaaa') # Position 0, which is in chunk 0 at index 0
|
||||
(0, 0, 0)
|
||||
>>> g.find(b'baaa') # Position 4, which is in chunk 1 at index 0
|
||||
(4, 1, 0)
|
||||
>>> g.find(b'aaad') # Position 9, which is in chunk 2 at index 1
|
||||
(9, 2, 1)
|
||||
>>> g.find(b'aada') # Position 10, which is in chunk 2 at index 2
|
||||
(10, 2, 2)
|
||||
>>> g.get() # Get the rest of the sequence
|
||||
b'faaagaaahaaaiaaajaaa...yyxzyzxzzyxzzzyyyyzyyzzyzyzzzz'
|
||||
>>> g.find(b'racz') # Position 7760, which is in chunk 4 at index 7740
|
||||
(7760, 4, 7740)
|
||||
>>> g.get(12) # Generator is exhausted
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
StopIteration
|
||||
|
||||
>>> g = cyclic_gen(string.ascii_uppercase, n=8) # Custom alphabet and item size
|
||||
>>> g.get(12) # Get a chunk of length 12
|
||||
'AAAAAAAABAAA'
|
||||
>>> g.get(18) # Get a chunk of length 18
|
||||
'AAAACAAAAAAADAAAAA'
|
||||
>>> g.find('CAAAAAAA') # Position 16, which is in chunk 1 at index 4
|
||||
(16, 1, 4)
|
||||
"""
|
||||
|
||||
def __init__(self, alphabet = None, n = None):
|
||||
if n is None:
|
||||
n = context.cyclic_size
|
||||
|
||||
if alphabet is None:
|
||||
alphabet = context.cyclic_alphabet
|
||||
|
||||
self._generator = de_bruijn(alphabet, n)
|
||||
self._alphabet = alphabet
|
||||
self._total_length = 0
|
||||
self._n = n
|
||||
self._chunks = []
|
||||
|
||||
def get(self, length = None):
|
||||
"""
|
||||
Get the next de Bruijn sequence from this generator.
|
||||
|
||||
>>> g = cyclic_gen()
|
||||
>>> g.get(4) # Get a chunk of length 4
|
||||
b'aaaa'
|
||||
>>> g.get(4) # Get a chunk of length 4
|
||||
b'baaa'
|
||||
>>> g.get(8) # Get a chunk of length 8
|
||||
b'caaadaaa'
|
||||
>>> g.get(4) # Get a chunk of length 4
|
||||
b'eaaa'
|
||||
>>> g.get() # Get the rest of the sequence
|
||||
b'faaagaaahaaaiaaajaaa...yyxzyzxzzyxzzzyyyyzyyzzyzyzzzz'
|
||||
>>> g.get(12) # Generator is exhausted
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
StopIteration
|
||||
"""
|
||||
|
||||
if length != None:
|
||||
self._chunks.append(length)
|
||||
self._total_length += length
|
||||
if len(self._alphabet) ** self._n < self._total_length:
|
||||
log.error("Can't create a pattern length=%i with len(alphabet)==%i and n==%i" \
|
||||
% (self._total_length, len(self._alphabet), self._n))
|
||||
else:
|
||||
self._chunks.append(float("inf"))
|
||||
|
||||
out = [next(self._generator) for _ in range(length)] if length != None else [next(self._generator)] + list(self._generator)
|
||||
return _join_sequence(out, self._alphabet)
|
||||
|
||||
def find(self, subseq):
|
||||
"""
|
||||
Find a chunk and subindex from all the generates de Bruijn sequences.
|
||||
|
||||
>>> g = cyclic_gen()
|
||||
>>> g.get(4)
|
||||
b'aaaa'
|
||||
>>> g.get(4)
|
||||
b'baaa'
|
||||
>>> g.get(8)
|
||||
b'caaadaaa'
|
||||
>>> g.get(4)
|
||||
b'eaaa'
|
||||
>>> g.find(b'caaa') # Position 8, which is in chunk 2 at index 0
|
||||
(8, 2, 0)
|
||||
"""
|
||||
|
||||
global_index = cyclic_find(subseq, self._alphabet, self._n)
|
||||
remaining_index = global_index
|
||||
for chunk_idx in range(len(self._chunks)):
|
||||
chunk = self._chunks[chunk_idx]
|
||||
if remaining_index < chunk:
|
||||
return (global_index, chunk_idx, remaining_index)
|
||||
remaining_index -= chunk
|
||||
return -1
|
||||
+1
-1
@@ -226,4 +226,4 @@ def sockaddr(host, port, network = 'ipv4'):
|
||||
sockaddr += p32(0xffffffff) # Save three bytes 'push -1' vs 'push 0'
|
||||
sockaddr += host
|
||||
length = len(sockaddr) + 4 # Save five bytes 'push 0'
|
||||
return (sockaddr, length, address_family)
|
||||
return (sockaddr, length, getattr(address_family, "name", address_family))
|
||||
+15
-4
@@ -325,10 +325,11 @@ def state(pid):
|
||||
"""
|
||||
return status(pid)['State']
|
||||
|
||||
def wait_for_debugger(pid):
|
||||
"""wait_for_debugger(pid) -> None
|
||||
def wait_for_debugger(pid, debugger_pid=None):
|
||||
"""wait_for_debugger(pid, debugger_pid=None) -> None
|
||||
|
||||
Sleeps until the process with PID `pid` is being traced.
|
||||
If debugger_pid is set and debugger exits, raises an error.
|
||||
|
||||
Arguments:
|
||||
pid (int): PID of the process.
|
||||
@@ -337,6 +338,16 @@ def wait_for_debugger(pid):
|
||||
None
|
||||
"""
|
||||
with log.waitfor('Waiting for debugger') as l:
|
||||
while tracer(pid) is None:
|
||||
time.sleep(0.01)
|
||||
if debugger_pid:
|
||||
debugger = psutil.Process(debugger_pid)
|
||||
while tracer(pid) is None:
|
||||
try:
|
||||
debugger.wait(0.01)
|
||||
except psutil.TimeoutExpired:
|
||||
pass
|
||||
else:
|
||||
l.error("debugger exited! (maybe check /proc/sys/kernel/yama/ptrace_scope)")
|
||||
else:
|
||||
while tracer(pid) is None:
|
||||
time.sleep(0.01)
|
||||
l.success()
|
||||
+2
-2
@@ -13,7 +13,7 @@ from pwnlib.util.misc import size
|
||||
log = getLogger(__name__)
|
||||
|
||||
def wget(url, save=None, timeout=5, **kwargs):
|
||||
"""wget(url, save=None, timeout=5) -> str
|
||||
r"""wget(url, save=None, timeout=5) -> str
|
||||
|
||||
Downloads a file via HTTP/HTTPS.
|
||||
|
||||
@@ -28,7 +28,7 @@ def wget(url, save=None, timeout=5, **kwargs):
|
||||
>>> url = 'https://httpbin.org/robots.txt'
|
||||
>>> result = wget(url, timeout=60)
|
||||
>>> result
|
||||
b'User-agent: *\\nDisallow: /deny\\n'
|
||||
b'User-agent: *\nDisallow: /deny\n'
|
||||
>>> result2 = wget(url, True, timeout=60)
|
||||
>>> result == open('robots.txt', 'rb').read()
|
||||
True
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__ = '4.0.0'
|
||||
__version__ = '4.1.2'
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
from __future__ import print_function
|
||||
|
||||
import glob
|
||||
@@ -53,7 +53,6 @@ install_requires = ['paramiko>=1.15.2',
|
||||
'pyserial>=2.7',
|
||||
'requests>=2.0',
|
||||
'pip>=6.0.8',
|
||||
'tox>=1.8.1',
|
||||
'pygments>=2.0',
|
||||
'pysocks',
|
||||
'python-dateutil',
|
||||
@@ -61,7 +60,9 @@ install_requires = ['paramiko>=1.15.2',
|
||||
'psutil>=3.3.0',
|
||||
'intervaltree>=3.0',
|
||||
'sortedcontainers',
|
||||
'unicorn']
|
||||
'unicorn>=1.0.2rc1,<1.0.2rc4', # see unicorn-engine/unicorn#1100, unicorn-engine/unicorn#1170, Gallopsled/pwntools#1538
|
||||
'six>=1.12.0',
|
||||
]
|
||||
|
||||
# Check that the user has installed the Python development headers
|
||||
PythonH = os.path.join(get_python_inc(), 'Python.h')
|
||||
@@ -82,7 +83,7 @@ setup(
|
||||
name = 'pwntools',
|
||||
python_requires = '>=2.7',
|
||||
packages = find_packages(),
|
||||
version = '4.0.0',
|
||||
version = '4.1.2',
|
||||
data_files = [('',
|
||||
glob.glob('*.md') + glob.glob('*.txt')),
|
||||
],
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
[tox]
|
||||
envlist = py27
|
||||
[testenv]
|
||||
deps=-r{toxinidir}/requirements.txt
|
||||
commands=make PWNLIB_NOTERM=1 -C docs doctest
|
||||
@@ -0,0 +1,12 @@
|
||||
import coverage
|
||||
import os
|
||||
import sys
|
||||
|
||||
os.environ.setdefault("COVERAGE_PROCESS_START", ".coveragerc")
|
||||
coverage.process_startup()
|
||||
|
||||
__file__ = os.path.abspath(sys.argv.pop(2))
|
||||
with open(__file__) as fp:
|
||||
code = compile(fp.read(), __file__, 'exec')
|
||||
os.chdir(sys.argv.pop(1))
|
||||
exec(code, globals())
|
||||
Regular → Executable
+31
-31
@@ -64,6 +64,13 @@ setup_travis()
|
||||
rm -rf usr/share
|
||||
}
|
||||
|
||||
setup_gdbserver()
|
||||
{
|
||||
# https://docs.improbable.io/reference/14.3/shared/debug-cloud-workers#common-issues
|
||||
wget http://archive.ubuntu.com/ubuntu/pool/main/g/gdb/gdbserver_8.3-0ubuntu1_amd64.deb
|
||||
sudo apt-get install ./gdbserver_8.3-0ubuntu1_amd64.deb
|
||||
}
|
||||
|
||||
setup_linux()
|
||||
{
|
||||
sudo apt-get install -y software-properties-common openssh-server libncurses5-dev libncursesw5-dev openjdk-8-jre-headless
|
||||
@@ -119,60 +126,52 @@ setup_android_emulator()
|
||||
if (uname | grep -i Darwin &>/dev/null); then
|
||||
brew install android-sdk android-ndk
|
||||
else
|
||||
if [ ! -f android-sdk/android ]; then
|
||||
if [ ! -f android-sdk/tools/bin/sdkmanager ]; then
|
||||
# Install the SDK, which gives us the 'android' and 'emulator' commands
|
||||
wget -nv https://dl.google.com/android/android-sdk_r24.4.1-linux.tgz
|
||||
tar xf android-sdk_r24.4.1-linux.tgz
|
||||
rm -f android-sdk_r24.4.1-linux.tgz
|
||||
wget -nv -O sdk-tools-linux.zip https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip
|
||||
unzip -q sdk-tools-linux.zip
|
||||
rm -f sdk-tools-linux.zip
|
||||
|
||||
# Travis caching causes this to exist already
|
||||
rm -rf android-sdk
|
||||
|
||||
mv android-sdk-linux android-sdk
|
||||
file android-sdk/tools/android
|
||||
mkdir android-sdk
|
||||
mv tools android-sdk/
|
||||
file android-sdk/tools/bin/sdk-manager
|
||||
fi
|
||||
|
||||
export PATH="$PWD/android-sdk/tools:$PATH"
|
||||
export PATH="$PWD/android-sdk/tools/bin:$PATH"
|
||||
export PATH="$PWD/android-sdk/platform-tools:$PATH"
|
||||
which android
|
||||
|
||||
# Install the NDK, which is required for adb.compile()
|
||||
NDK_VERSION=android-ndk-r12b
|
||||
if [ ! -f android-ndk/ndk-build ]; then
|
||||
wget -nv https://dl.google.com/android/repository/$NDK_VERSION-linux-x86_64.zip
|
||||
unzip -q android-ndk-*.zip
|
||||
rm -f android-ndk-*.zip
|
||||
|
||||
# Travis caching causes this to exist already
|
||||
rm -rf android-ndk
|
||||
|
||||
mv $NDK_VERSION android-ndk
|
||||
fi
|
||||
|
||||
export NDK=$PWD/android-ndk
|
||||
export PATH=$NDK:$PATH
|
||||
export ANDROID_SDK_ROOT="$PWD/android-sdk"
|
||||
export ANDROID_HOME="$PWD/android-sdk"
|
||||
which sdkmanager
|
||||
fi
|
||||
|
||||
# Grab prerequisites
|
||||
echo y | android update sdk --no-ui --all --filter platform-tools,extra-android-support
|
||||
echo y | android update sdk --no-ui --all --filter android-21
|
||||
|
||||
# Valid ABIs:
|
||||
# - armeabi-v7a
|
||||
# - arm64-v8a
|
||||
# - x86
|
||||
# - x86_64
|
||||
ABI='armeabi-v7a'
|
||||
ANDROID_ABI='armeabi-v7a'
|
||||
ANDROIDV=android-24
|
||||
yes | sdkmanager --install platform-tools 'extras;android;m2repository' emulator ndk-bundle \
|
||||
"platforms;$ANDROIDV" "system-images;$ANDROIDV;default;$ANDROID_ABI" >/dev/null
|
||||
yes | sdkmanager --licenses
|
||||
|
||||
# Grab the emulator image
|
||||
echo y | android update sdk --no-ui --all --filter sys-img-$ABI-android-21
|
||||
# enable NDK for adb.compile()
|
||||
for d in "$PWD/android-sdk/ndk-bundle/"*/; do
|
||||
export PATH="$PATH:$d"
|
||||
done
|
||||
|
||||
# Create our emulator Android Virtual Device (AVD)
|
||||
echo no | android --silent create avd --name android-$ABI --target android-21 --force --snapshot --abi $ABI
|
||||
# --snapshot flag is deprecated, see bitrise-steplib/steps-create-android-emulator#18
|
||||
echo no | avdmanager --silent create avd --name android-$ANDROID_ABI --force --package "system-images;$ANDROIDV;default;$ANDROID_ABI"
|
||||
|
||||
# In the future, it would be nice to be able to use snapshots.
|
||||
# However, I haven't gotten them to work nicely.
|
||||
emulator -avd android-$ABI -no-window -no-boot-anim -no-skin -no-audio -no-window -no-snapshot &
|
||||
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
|
||||
adb shell id
|
||||
adb shell getprop
|
||||
@@ -187,6 +186,7 @@ setup_osx()
|
||||
|
||||
if [[ "$USER" == "travis" ]]; then
|
||||
# setup_travis
|
||||
setup_gdbserver
|
||||
setup_android_emulator
|
||||
elif [[ "$USER" == "shippable" ]]; then
|
||||
sudo apt-get update
|
||||
|
||||
Regular → Executable
+8
-1
@@ -32,6 +32,7 @@ fi
|
||||
|
||||
# Generate a new key so that we can log into it
|
||||
ssh-keygen -t rsa -f ~/.ssh/$U -N ''
|
||||
chmod og-rw ~/.ssh
|
||||
|
||||
# Load the public key into a memory for below
|
||||
pubkey=$(cat ~/.ssh/$U.pub)
|
||||
@@ -42,6 +43,7 @@ USUDO mkdir $H/.ssh || true
|
||||
USUDO tee -a $H/.ssh/authorized_keys <<EOF
|
||||
from="127.0.0.1" $pubkey
|
||||
EOF
|
||||
USUDO chmod 700 $H $H/.ssh $H/.ssh/authorized_keys
|
||||
|
||||
# In the pwntools examples, we ssh to 'example.pwnme'
|
||||
# Set up an SSH config entry to make this actually work
|
||||
@@ -51,8 +53,13 @@ Host example.pwnme
|
||||
User $U
|
||||
HostName 127.0.0.1
|
||||
IdentityFile ~/.ssh/$U
|
||||
StrictHostKeyChecking no
|
||||
EOF
|
||||
chmod 700 ~ ~/.ssh
|
||||
|
||||
ssh -o "StrictHostKeyChecking no" -vvvv [email protected] id
|
||||
ls -la ~/.ssh
|
||||
USUDO ls -la $H/.ssh
|
||||
|
||||
ssh -v [email protected] id
|
||||
|
||||
set +ex
|
||||
Regular → Executable
File mode changed.
Reference in new issue
Block a user