Skip to content

Commit

Permalink
Merge branch 'dev' into windows_processes
Browse files Browse the repository at this point in the history
  • Loading branch information
peace-maker authored Jan 2, 2024
2 parents 1b0e1db + 3bb756d commit e744cc7
Show file tree
Hide file tree
Showing 35 changed files with 564 additions and 621 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
key: ${{ matrix.os }}-cache-pip

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

Expand All @@ -37,6 +37,7 @@ jobs:
- name: Install Android AVD
run: |
sudo usermod -aG kvm $USER
source travis/setup_avd_fast.sh
sed -i 's/skip_android = True/skip_android = False/' docs/source/conf.py
set | grep ^PATH >.android.env
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:

- name: Set up Python ${{ matrix.python_version }}
if: matrix.python_version != '2.7'
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python_version }}

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
key: ${{ matrix.os }}-cache-pip

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

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
key: ${{ matrix.os }}-cache-pip

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

Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,26 @@ The table below shows which release corresponds to each branch, and what date th

## 4.13.0 (`dev`)

- [#2242][2242] Term module revamp: activating special handling of terminal only when necessary
- [#2277][2277] elf: Resolve more relocations into GOT entries
- [#2281][2281] FIX: Getting right amount of data for search fix
- [#2293][2293] Add x86 CET status to checksec output
- [#1763][1763] Allow to add to the existing environment in `process` instead of replacing it
- [#2307][2307] Fix `pwn libcdb file` crashing if "/bin/sh" string was not found
- [#2309][2309] Detect challenge binary and libc in `pwn template`
- [#2308][2308] Fix WinExec shellcraft to make sure it's 16 byte aligned
- [#2279][2279] Make `pwn template` always set context.binary
- [#2310][2310] Add support to start a process on Windows

[2242]: https://github.com/Gallopsled/pwntools/pull/2242
[2277]: https://github.com/Gallopsled/pwntools/pull/2277
[2281]: https://github.com/Gallopsled/pwntools/pull/2281
[2293]: https://github.com/Gallopsled/pwntools/pull/2293
[1763]: https://github.com/Gallopsled/pwntools/pull/1763
[2307]: https://github.com/Gallopsled/pwntools/pull/2307
[2309]: https://github.com/Gallopsled/pwntools/pull/2309
[2308]: https://github.com/Gallopsled/pwntools/pull/2308
[2279]: https://github.com/Gallopsled/pwntools/pull/2279
[2310]: https://github.com/Gallopsled/pwntools/pull/2310

## 4.12.0 (`beta`)
Expand Down
2 changes: 1 addition & 1 deletion examples/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@

from pwn import *

opts = [string.letters[x] for x in range(10)]
opts = [string.ascii_letters[x] for x in range(12)]
print('You choose "%s"' % opts[options('Pick one:', opts)])
16 changes: 9 additions & 7 deletions pwnlib/adb/adb.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
from pwnlib.context import LocalContext
from pwnlib.context import context
from pwnlib.device import Device
from pwnlib.exception import PwnlibException
from pwnlib.log import getLogger
from pwnlib.protocols.adb import AdbClient
from pwnlib.util.packing import _decode
Expand Down Expand Up @@ -122,7 +123,7 @@ def current_device(any=False):
>>> device = adb.current_device(any=True)
>>> device # doctest: +ELLIPSIS
AdbDevice(serial='emulator-5554', type='device', port='emulator', product='sdk_...phone_armv7', model='sdk ...phone armv7', device='generic')
AdbDevice(serial='emulator-5554', type='device', port='emulator', product='sdk_...phone_...', model='...', device='generic...')
>>> device.port
'emulator'
"""
Expand Down Expand Up @@ -252,13 +253,13 @@ class AdbDevice(Device):
>>> device = adb.wait_for_device()
>>> device.arch
'arm'
'amd64'
>>> device.bits
32
64
>>> device.os
'android'
>>> device.product # doctest: +ELLIPSIS
'sdk_...phone_armv7'
'sdk_...phone_...'
>>> device.serial
'emulator-5554'
"""
Expand Down Expand Up @@ -1364,7 +1365,7 @@ def compile(source):
>>> filename = adb.compile(temp)
>>> sent = adb.push(filename, "/data/local/tmp")
>>> adb.process(sent).recvall() # doctest: +ELLIPSIS
b'... /system/bin/linker\n...'
b'... /system/lib64/libc.so\n...'
"""

ndk_build = misc.which('ndk-build')
Expand Down Expand Up @@ -1490,8 +1491,9 @@ class Partitions(object):
@context.quietfunc
def by_name_dir(self):
try:
return next(find('/dev/block/platform','by-name'))
except StopIteration:
with context.local(log_level=logging.FATAL):
return next(find('/dev/block/platform','by-name'))
except (StopIteration, PwnlibException):
return '/dev/block'

@context.quietfunc
Expand Down
55 changes: 43 additions & 12 deletions pwnlib/commandline/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,54 @@
parser = common.parser_commands.add_parser(
'template',
help = 'Generate an exploit template',
description = 'Generate an exploit template'
description = 'Generate an exploit template. If no arguments are given, '
'the current directory is searched for an executable binary and '
'libc. If only one binary is found, it is assumed to be the '
'challenge binary.'
)

# change path to hardcoded one when building the documentation
printable_data_path = "pwnlib/data" if 'sphinx' in sys.modules else pwnlib.data.path

parser.add_argument('exe', nargs='?', help='Target binary')
parser.add_argument('exe', nargs='?', help='Target binary. If not given, the current directory is searched for an executable binary.')
parser.add_argument('--host', help='Remote host / SSH server')
parser.add_argument('--port', help='Remote port / SSH port', type=int)
parser.add_argument('--user', help='SSH Username')
parser.add_argument('--pass', '--password', help='SSH Password', dest='password')
parser.add_argument('--libc', help='Path to libc binary to use')
parser.add_argument('--libc', help='Path to libc binary to use. If not given, the current directory is searched for a libc binary.')
parser.add_argument('--path', help='Remote path of file on SSH server')
parser.add_argument('--quiet', help='Less verbose template comments', action='store_true')
parser.add_argument('--color', help='Print the output in color', choices=['never', 'always', 'auto'], default='auto')
parser.add_argument('--template', help='Path to a custom template. Tries to use \'~/.config/pwntools/templates/pwnup.mako\', if it exists. '
'Check \'%s\' for the default template shipped with pwntools.' %
os.path.join(printable_data_path, "templates", "pwnup.mako"))
parser.add_argument('--no-auto', help='Do not automatically detect missing binaries', action='store_false', dest='auto')

def detect_missing_binaries(args):
log.info("Automatically detecting challenge binaries...")
# look for challenge binary, libc, and ld in current directory
exe, libc, ld = args.exe, args.libc, None
other_files = []
for filename in os.listdir():
if not os.path.isfile(filename):
continue
if not libc and ('libc-' in filename or 'libc.' in filename):
libc = filename
elif not ld and 'ld-' in filename:
ld = filename
else:
if os.access(filename, os.X_OK):
other_files.append(filename)
if len(other_files) == 1:
exe = other_files[0]
elif len(other_files) > 1:
log.warning("Failed to find challenge binary. There are multiple binaries in the current directory: %s", other_files)

if exe != args.exe:
log.success("Found challenge binary %r", exe)
if libc != args.libc:
log.success("Found libc binary %r", libc)
return exe, libc

def main(args):

Expand All @@ -44,19 +74,20 @@ def main(args):
if not (args.path or args.exe):
log.error("Must specify --path or a exe")

s = ssh(args.user, args.host, args.port or 22, args.password or None)

try:
remote_file = args.path or args.exe
s.download(remote_file)
except Exception:
log.warning("Could not download file %r, opening a shell", remote_file)
s.interactive()
return
with ssh(args.user, args.host, args.port or 22, args.password or None) as s:
try:
remote_file = args.path or args.exe
s.download(remote_file)
except Exception:
log.warning("Could not download file %r, opening a shell", remote_file)
s.interactive()
return

if not args.exe:
args.exe = os.path.basename(args.path)

if args.auto and (args.exe is None or args.libc is None):
args.exe, args.libc = detect_missing_binaries(args)

if args.template:
template = Template(filename=args.template) # Failing on invalid file is ok
Expand Down
2 changes: 1 addition & 1 deletion pwnlib/context/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -787,7 +787,7 @@ def arch(self, arch):
try:
defaults = self.architectures[arch]
except KeyError:
raise AttributeError('AttributeError: arch must be one of %r' % sorted(self.architectures))
raise AttributeError('AttributeError: arch (%r) must be one of %r' % (arch, sorted(self.architectures)))

for k,v in defaults.items():
if k not in self._tls:
Expand Down
Binary file added pwnlib/data/elf/test-riscv64
Binary file not shown.
Binary file added pwnlib/data/elf/test-riscv64-pie
Binary file not shown.
Binary file added pwnlib/data/elf/test-riscv64-relro
Binary file not shown.
Binary file added pwnlib/data/elf/test-riscv64-relro-pie
Binary file not shown.
Binary file added pwnlib/data/elf/test-x32
Binary file not shown.
Binary file added pwnlib/data/elf/test-x32-pie
Binary file not shown.
Binary file added pwnlib/data/elf/test-x32-relro
Binary file not shown.
Binary file added pwnlib/data/elf/test-x32-relro-pie
Binary file not shown.
6 changes: 1 addition & 5 deletions pwnlib/data/templates/pwnup.mako
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ from pwn import *
%if not quiet:
# Set up pwntools for the correct architecture
%endif
%if ctx.binary:
%if ctx.binary or not host:
exe = context.binary = ELF(args.EXE or ${binary_repr})
<% binary_repr = 'exe.path' %>
%else:
Expand Down Expand Up @@ -99,11 +99,7 @@ else:
%endif
library_path = libcdb.download_libraries(${libc_repr})
if library_path:
%if ctx.binary:
exe = context.binary = ELF.patch_custom_libraries(${binary_repr}, library_path)
%else:
exe = ELF.patch_custom_libraries(exe, library_path)
%endif
libc = exe.libc
else:
libc = ELF(${libc_repr})
Expand Down
81 changes: 11 additions & 70 deletions pwnlib/elf/corefile.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@
from pwnlib.util.fiddling import unhex
from pwnlib.util.misc import read
from pwnlib.util.misc import write
from pwnlib.util.packing import _decode
from pwnlib.util.packing import pack
from pwnlib.util.packing import unpack_many

Expand All @@ -106,44 +105,11 @@
'aarch64': elf_prstatus_aarch64
}

prpsinfo_types = {
32: elf_prpsinfo_32,
64: elf_prpsinfo_64,
}

siginfo_types = {
32: elf_siginfo_32,
64: elf_siginfo_64
}

# Slightly modified copy of the pyelftools version of the same function,
# until they fix this issue:
# https://github.com/eliben/pyelftools/issues/93
def iter_notes(self):
""" Iterates the list of notes in the segment.
"""
offset = self['p_offset']
end = self['p_offset'] + self['p_filesz']
while offset < end:
note = struct_parse(
self.elffile.structs.Elf_Nhdr,
self.stream,
stream_pos=offset)
note['n_offset'] = offset
offset += self.elffile.structs.Elf_Nhdr.sizeof()
self.stream.seek(offset)
# n_namesz is 4-byte aligned.
disk_namesz = roundup(note['n_namesz'], 2)
with context.local(encoding='latin-1'):
note['n_name'] = _decode(
CString('').parse(self.stream.read(disk_namesz)))
offset += disk_namesz

desc_data = _decode(self.stream.read(note['n_descsz']))
note['n_desc'] = desc_data
offset += roundup(note['n_descsz'], 2)
note['n_size'] = offset - note['n_offset']
yield note

class Mapping(object):
"""Encapsulates information about a memory mapping in a :class:`Corefile`.
Expand Down Expand Up @@ -605,7 +571,6 @@ def __init__(self, *a, **kw):
log.warn_once("%s does not use a supported corefile architecture, registers are unavailable" % self.file.name)

prstatus_type = prstatus_types.get(self.arch)
prpsinfo_type = prpsinfo_types.get(self.bits)
siginfo_type = siginfo_types.get(self.bits)

with log.waitfor("Parsing corefile...") as w:
Expand All @@ -616,39 +581,30 @@ def __init__(self, *a, **kw):
continue


# Note that older versions of pyelftools (<=0.24) are missing enum values
# for NT_PRSTATUS, NT_PRPSINFO, NT_AUXV, etc.
# For this reason, we have to check if note.n_type is any of several values.
for note in iter_notes(segment):
if not isinstance(note.n_desc, bytes):
note['n_desc'] = note.n_desc.encode('latin1')
for note in segment.iter_notes():
# Try to find NT_PRSTATUS.
if prstatus_type and \
note.n_descsz == ctypes.sizeof(prstatus_type) and \
note.n_type in ('NT_GNU_ABI_TAG', 'NT_PRSTATUS'):
if note.n_type == 'NT_PRSTATUS':
self.NT_PRSTATUS = note
self.prstatus = prstatus_type.from_buffer_copy(note.n_desc)

# Try to find NT_PRPSINFO
if prpsinfo_type and \
note.n_descsz == ctypes.sizeof(prpsinfo_type) and \
note.n_type in ('NT_GNU_ABI_TAG', 'NT_PRPSINFO'):
if note.n_type == 'NT_PRPSINFO':
self.NT_PRPSINFO = note
self.prpsinfo = prpsinfo_type.from_buffer_copy(note.n_desc)
self.prpsinfo = note.n_desc

# Try to find NT_SIGINFO so we can see the fault
if note.n_type in (0x53494749, 'NT_SIGINFO'):
if note.n_type == 'NT_SIGINFO':
self.NT_SIGINFO = note
self.siginfo = siginfo_type.from_buffer_copy(note.n_desc)

# Try to find the list of mapped files
if note.n_type in (constants.NT_FILE, 'NT_FILE'):
if note.n_type == 'NT_FILE':
with context.local(bytes=self.bytes):
self._parse_nt_file(note)

# Try to find the auxiliary vector, which will tell us
# where the top of the stack is.
if note.n_type in (constants.NT_AUXV, 'NT_AUXV'):
if note.n_type == 'NT_AUXV':
self.NT_AUXV = note
with context.local(bytes=self.bytes):
self._parse_auxv(note)
Expand Down Expand Up @@ -684,31 +640,16 @@ def __init__(self, *a, **kw):
self._describe_core()

def _parse_nt_file(self, note):
t = tube()
t.unrecv(note.n_desc)

count = t.unpack()
page_size = t.unpack()

starts = []
addresses = {}

for i in range(count):
start = t.unpack()
end = t.unpack()
offset = t.unpack()
starts.append((start, offset))

for i in range(count):
filename = t.recvuntil(b'\x00', drop=True)
for vma, filename in zip(note.n_desc.Elf_Nt_File_Entry, note.n_desc.filename):
if not isinstance(filename, str):
filename = filename.decode('utf-8')
(start, offset) = starts[i]

filename = filename.decode('utf-8', 'surrogateescape')
for mapping in self.mappings:
if mapping.start == start:
if mapping.start == vma.vm_start:
mapping.name = filename
mapping.page_offset = offset
mapping.page_offset = vma.page_offset

self.mappings = sorted(self.mappings, key=lambda m: m.start)

Expand Down
Loading

0 comments on commit e744cc7

Please sign in to comment.