Skip to content

Commit

Permalink
Merge branch 'beta' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
Arusekk committed Apr 7, 2020
2 parents 91d464b + a8e594d commit f0f0c36
Show file tree
Hide file tree
Showing 25 changed files with 347 additions and 200 deletions.
107 changes: 107 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
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
- name: Cache for pip
uses: actions/cache@v1
id: cache-pip
with:
path: ~/.cache/pip
key: ${{ matrix.os }}-cache-pip

- name: Cache for dependencies
uses: actions/cache@v1
id: cache-deps
with:
path: android-?dk
key: ${{ matrix.os }}-cache-deps

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}

- name: Lint
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: Install Linux dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends -o Acquire::Retries=3 \
ash bash-static dash ksh mksh zsh \
pandoc gdb socat sshpass \
binutils-multiarch qemu-user-static \
binutils-aarch64-linux-gnu \
binutils-arm-linux-gnueabihf \
binutils-mips-linux-gnu \
binutils-powerpc-linux-gnu \
gcc-multilib \
openjdk-8-jre-headless
sudo apt-get install -y -o Acquire::Retries=3 \
gcc-aarch64-linux-gnu \
gcc-arm-linux-gnueabihf
- name: Install android avd
if: steps.cache-deps.outputs.cache-hit != 'true'
run: |
USER=travis source travis/install.sh
adb emu kill
set | egrep '^(ANDROID|PATH)' >android-sdk/.android.env
- name: Set up SSH
run: |
chmod og-rw ~ # see https://stackoverflow.com/a/60367309/3869724
ssh-keygen -t ed25519 -f ~/.ssh/pwntools-ci -N ''
cat > ~/.ssh/config <<EOF
Host example.pwnme
User $USER
HostName 127.0.0.1
IdentityFile ~/.ssh/pwntools-ci
EOF
echo -n 'from="127.0.0.1" ' | cat - ~/.ssh/pwntools-ci.pub > ~/.ssh/authorized_keys
ssh -o 'StrictHostKeyChecking no' example.pwnme id
- 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: Coverage doctests
run: |
source android-sdk/.android.env
android-sdk/emulator/emulator -avd android-$ANDROID_ABI -no-window -no-boot-anim -read-only -no-audio -no-window -no-snapshot &
adb wait-for-device
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
coverage combine
- name: Build source and wheel distributions
run: |
python setup.py sdist
python setup.py bdist_wheel --universal
- uses: actions/upload-artifact@v2-preview
with:
path: dist/*

- name: Upload coverage to coveralls.io
run: COVERALLS_REPO_TOKEN=PP20MEgztXIQJJTguQwe2jeCh6Bm4lkbv coveralls
42 changes: 9 additions & 33 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import os
import doctest
import signal
import six
import subprocess
import sys
Expand Down Expand Up @@ -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

Expand All @@ -374,42 +375,11 @@ def linkcode_resolve(domain, info):

# -- 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):
Expand Down Expand Up @@ -437,11 +407,17 @@ 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)

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
1 change: 1 addition & 0 deletions docs/source/protocols/adb.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from pwn import *
from pwnlib.protocols.adb import AdbClient
AdbClient().wait_for_device()

:mod:`pwnlib.protocols.adb` --- ADB Protocol Implementation
===========================================================
Expand Down
43 changes: 23 additions & 20 deletions pwnlib/adb/adb.py
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ def exists(path):
>>> adb.exists('/')
True
>>> adb.exists('/init')
>>> adb.exists('/etc/hosts')
True
>>> adb.exists('/does/not/exist')
False
Expand Down Expand Up @@ -1132,10 +1132,9 @@ def __init__(self, name=None):
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)
Expand All @@ -1151,7 +1150,9 @@ def __setattr__(self, attr, value):
def __eq__(self, other):
# Allow simple comparison, e.g.:
# adb.properties.ro.oem_unlock_supported == "1"
return str(self) == other
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
Expand Down Expand Up @@ -1356,9 +1357,10 @@ def __iter__(self):
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
Expand All @@ -1367,19 +1369,20 @@ def __getattr__(self, attr):

path = os.path.join(self.by_name_dir, name)

# 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)
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)

return Partition(devpath, attr, int(blocks))

Expand Down
24 changes: 13 additions & 11 deletions pwnlib/elf/corefile.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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'
Expand All @@ -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
Expand Down Expand Up @@ -747,13 +749,13 @@ def signal(self):
>>> 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
"""
Expand All @@ -774,7 +776,7 @@ def fault_addr(self):
>>> 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
"""
Expand Down Expand Up @@ -1071,7 +1073,7 @@ def getenv(self, name):
>>> elf = ELF.from_assembly(shellcraft.trap())
>>> io = elf.process(env={'GREETING': 'Hello!'})
>>> io.wait()
>>> io.wait(1)
>>> io.corefile.getenv('GREETING')
b'Hello!'
"""
Expand All @@ -1090,7 +1092,7 @@ def registers(self):
>>> 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
"""
Expand Down
2 changes: 1 addition & 1 deletion pwnlib/flag/flag.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
5 changes: 3 additions & 2 deletions pwnlib/fmtstr.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@
... 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)
Expand Down
Loading

0 comments on commit f0f0c36

Please sign in to comment.