Skip to content

Commit

Permalink
Merge branch 'dev' into stable-gdb-exe
Browse files Browse the repository at this point in the history
  • Loading branch information
goreil committed Nov 23, 2023
2 parents 7461e8c + bb1d16c commit 26c031c
Show file tree
Hide file tree
Showing 16 changed files with 232 additions and 60 deletions.
14 changes: 7 additions & 7 deletions .github/workflows/docker.yml
Expand Up @@ -13,40 +13,40 @@ jobs:
steps:
# Required for subdirectories in Git context
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3

- name: Login to Docker Hub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}

- name: Build and push base image
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
if: github.event_name == 'workflow_dispatch'
with:
context: "{{defaultContext}}:extra/docker/base"
push: true
tags: pwntools/pwntools:base

- name: Build and push stable image
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/stable')
with:
context: "{{defaultContext}}:extra/docker/stable"
push: true
tags: pwntools/pwntools:stable

- name: Build and push beta image
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/beta')
with:
context: "{{defaultContext}}:extra/docker/beta"
push: true
tags: pwntools/pwntools:beta

- name: Build and push dev image
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/dev')
with:
context: "{{defaultContext}}:extra/docker/dev"
Expand All @@ -56,7 +56,7 @@ jobs:
pwntools/pwntools:latest
- name: Build and push ci image
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/dev')
with:
context: "{{defaultContext}}:travis/docker"
Expand Down
26 changes: 24 additions & 2 deletions CHANGELOG.md
Expand Up @@ -11,7 +11,8 @@ The table below shows which release corresponds to each branch, and what date th
| ---------------- | -------- | ---------------------- |
| [4.13.0](#4130-dev) | `dev` |
| [4.12.0](#4120-beta) | `beta` |
| [4.11.0](#4110-stable) | `stable` | Sep 15, 2023
| [4.11.1](#4111-stable) | `stable` | Nov 14, 2023
| [4.11.0](#4110) | | Sep 15, 2023
| [4.10.0](#4100) | | May 21, 2023
| [4.9.0](#490) | | Dec 29, 2022
| [4.8.0](#480) | | Apr 21, 2022
Expand Down Expand Up @@ -69,9 +70,16 @@ The table below shows which release corresponds to each branch, and what date th

## 4.13.0 (`dev`)

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

[2277]: https://github.com/Gallopsled/pwntools/pull/2277
[2281]: https://github.com/Gallopsled/pwntools/pull/2281
[2293]: https://github.com/Gallopsled/pwntools/pull/2293

## 4.12.0 (`beta`)

- [#2202][2202] Fix `remote` and `listen` in sagemath
- [#2117][2117] Add -p (--prefix) and -s (--separator) arguments to `hex` command
- [#2221][2221] Add shellcraft.sleep template wrapping SYS_nanosleep
Expand All @@ -90,7 +98,21 @@ The table below shows which release corresponds to each branch, and what date th
[2225]: https://github.com/Gallopsled/pwntools/pull/2225
[2227]: https://github.com/Gallopsled/pwntools/pull/2227

## 4.11.0 (`stable`)
## 4.11.1 (`stable`)

- [#2271][2271] FIX: Generated shebang with path to python invalid if path contains spaces
- [#2272][2272] Fix `tube.clean_and_log` not logging buffered data
- [#2281][2281] FIX: Getting right amount of data for search fix
- [#2287][2287] Fix `_countdown_handler` not invoking `timeout_change`
- [#2294][2294] Fix atexit SEGV in aarch64 loader

[2271]: https://github.com/Gallopsled/pwntools/pull/2271
[2272]: https://github.com/Gallopsled/pwntools/pull/2272
[2281]: https://github.com/Gallopsled/pwntools/pull/2281
[2287]: https://github.com/Gallopsled/pwntools/pull/2287
[2294]: https://github.com/Gallopsled/pwntools/pull/2294

## 4.11.0

- [#2185][2185] make fmtstr module able to create payload without $ notation
- [#2103][2103] Add search for libc binary by leaked function addresses `libcdb.search_by_symbol_offsets()`
Expand Down
2 changes: 1 addition & 1 deletion MANIFEST.in
Expand Up @@ -6,4 +6,4 @@ include *.md *.txt *.sh *.yml MANIFEST.in
recursive-include docs *.rst *.png Makefile *.py *.txt
recursive-include pwnlib *.py *.asm *.rst *.md *.txt *.sh __doc__ *.mako
recursive-include pwn *.py *.asm *.rst *.md *.txt *.sh
recursive-exclude *.pyc
global-exclude *.pyc
30 changes: 18 additions & 12 deletions examples/clean_and_log.py
Expand Up @@ -11,18 +11,24 @@
"""

from pwn import *
from multiprocessing import Process

os.system('''((
echo prefix sometext ;
echo prefix someothertext ;
echo here comes the flag ;
echo LostInTheInterTubes
) | nc -l 1337) &
''')
def submit_data():
with context.quiet:
with listen(1337) as io:
io.wait_for_connection()
io.sendline(b'prefix sometext')
io.sendline(b'prefix someothertext')
io.sendline(b'here comes the flag')
io.sendline(b'LostInTheInterTubes')

r = remote('localhost', 1337)
atexit.register(r.clean_and_log)
if __name__ == '__main__':
p = Process(target=submit_data)
p.start()

while True:
line = r.recvline()
print(re.findall(r'^prefix (\S+)$', line)[0])
r = remote('localhost', 1337)
atexit.register(r.clean_and_log)

while True:
line = r.recvline()
print(re.findall(br'^prefix (\S+)$', line)[0])
2 changes: 1 addition & 1 deletion extra/docker/beta/Dockerfile
Expand Up @@ -2,6 +2,6 @@ FROM pwntools/pwntools:stable

USER root
RUN python2.7 -m pip install --upgrade git+https://github.com/Gallopsled/pwntools@beta \
&& python3 -m pip install --upgrade git+https://github.com/Gallopsled/pwntools@beta
&& python3 -m pip install --force-reinstall --upgrade git+https://github.com/Gallopsled/pwntools@beta
RUN PWNLIB_NOTERM=1 pwn update
USER pwntools
2 changes: 1 addition & 1 deletion extra/docker/dev/Dockerfile
Expand Up @@ -2,6 +2,6 @@ FROM pwntools/pwntools:stable

USER root
RUN python2.7 -m pip install --upgrade git+https://github.com/Gallopsled/pwntools@dev \
&& python3 -m pip install --upgrade git+https://github.com/Gallopsled/pwntools@dev
&& python3 -m pip install --force-reinstall --upgrade git+https://github.com/Gallopsled/pwntools@dev
RUN PWNLIB_NOTERM=1 pwn update
USER pwntools
2 changes: 1 addition & 1 deletion extra/docker/stable/Dockerfile
Expand Up @@ -2,6 +2,6 @@ FROM pwntools/pwntools:base

USER root
RUN python2.7 -m pip install --upgrade git+https://github.com/Gallopsled/pwntools@stable \
&& python3 -m pip install --upgrade git+https://github.com/Gallopsled/pwntools@stable
&& python3 -m pip install --force-reinstall --upgrade git+https://github.com/Gallopsled/pwntools@stable
RUN PWNLIB_NOTERM=1 pwn update
USER pwntools
86 changes: 79 additions & 7 deletions pwnlib/elf/elf.py
Expand Up @@ -47,14 +47,15 @@

from six import BytesIO

from collections import namedtuple
from collections import namedtuple, defaultdict

from elftools.elf.constants import P_FLAGS
from elftools.elf.constants import SHN_INDICES
from elftools.elf.descriptions import describe_e_type
from elftools.elf.elffile import ELFFile
from elftools.elf.enums import ENUM_GNU_PROPERTY_X86_FEATURE_1_FLAGS
from elftools.elf.gnuversions import GNUVerDefSection
from elftools.elf.relocation import RelocationSection
from elftools.elf.relocation import RelocationSection, RelrRelocationSection
from elftools.elf.sections import SymbolTableSection
from elftools.elf.segments import InterpSegment

Expand Down Expand Up @@ -510,6 +511,29 @@ def iter_segments_by_type(self, t):
if t == seg.header.p_type or t in str(seg.header.p_type):
yield seg

def iter_notes(self):
"""
Yields:
All the notes in the PT_NOTE segments. Each result is a dictionary-
like object with ``n_name``, ``n_type``, and ``n_desc`` fields, amongst
others.
"""
for seg in self.iter_segments_by_type('PT_NOTE'):
for note in seg.iter_notes():
yield note

def iter_properties(self):
"""
Yields:
All the GNU properties in the PT_NOTE segments. Each result is a dictionary-
like object with ``pr_type``, ``pr_datasz``, and ``pr_data`` fields.
"""
for note in self.iter_notes():
if note.n_type != 'NT_GNU_PROPERTY_TYPE_0':
continue
for prop in note.n_desc:
yield prop

def get_segment_for_address(self, address, size=1):
"""get_segment_for_address(address, size=1) -> Segment
Expand Down Expand Up @@ -917,15 +941,25 @@ def _populate_synthetic_symbols(self):
self.symbols['got.' + symbol] = address

def _populate_got(self):
"""Loads the symbols for all relocations"""
"""Loads the symbols for all relocations.
>>> libc = ELF(which('bash')).libc
>>> assert 'strchrnul' in libc.got
>>> assert 'memcpy' in libc.got
>>> assert libc.got.strchrnul != libc.got.memcpy
"""
# Statically linked implies no relocations, since there is no linker
# Could always be self-relocating like Android's linker *shrug*
if self.statically_linked:
return

revsymbols = defaultdict(list)
for name, addr in self.symbols.items():
revsymbols[addr].append(name)

for section in self.sections:
# We are only interested in relocations
if not isinstance(section, RelocationSection):
if not isinstance(section, (RelocationSection, RelrRelocationSection)):
continue

# Only get relocations which link to another section (for symbols)
Expand All @@ -937,7 +971,13 @@ def _populate_got(self):
for rel in section.iter_relocations():
sym_idx = rel.entry.r_info_sym

if not sym_idx:
if not sym_idx and rel.is_RELA():
# TODO: actually resolve relocations
relocated = rel.entry.r_addend # sufficient for now

symnames = revsymbols[relocated]
for symname in symnames:
self.got[symname] = rel.entry.r_offset
continue

symbol = symbols.get_symbol(sym_idx)
Expand Down Expand Up @@ -1195,9 +1235,10 @@ def search(self, needle, writable = False, executable = False):
for seg in segments:
addr = seg.header.p_vaddr
memsz = seg.header.p_memsz
zeroed = memsz - seg.header.p_filesz
filesz = seg.header.p_filesz
zeroed = memsz - filesz
offset = seg.header.p_offset
data = self.mmap[offset:offset+memsz]
data = self.mmap[offset:offset+filesz]
data += b'\x00' * zeroed
offset = 0
while True:
Expand Down Expand Up @@ -2059,6 +2100,12 @@ def checksec(self, banner=True, color=True):

if self.ubsan:
res.append("UBSAN:".ljust(10) + green("Enabled"))

if self.shadowstack:
res.append("SHSTK:".ljust(10) + green("Enabled"))

if self.ibt:
res.append("IBT:".ljust(10) + green("Enabled"))

# Check for Linux configuration, it must contain more than
# just the version.
Expand Down Expand Up @@ -2116,6 +2163,31 @@ def ubsan(self):
""":class:`bool`: Whether the current binary was built with
Undefined Behavior Sanitizer (``UBSAN``)."""
return any(s.startswith('__ubsan_') for s in self.symbols)

@property
def shadowstack(self):
""":class:`bool`: Whether the current binary was built with
Shadow Stack (``SHSTK``)"""
if self.arch not in ['i386', 'amd64']:
return False
for prop in self.iter_properties():
if prop.pr_type != 'GNU_PROPERTY_X86_FEATURE_1_AND':
continue
return prop.pr_data & ENUM_GNU_PROPERTY_X86_FEATURE_1_FLAGS['GNU_PROPERTY_X86_FEATURE_1_SHSTK'] > 0
return False

@property
def ibt(self):
""":class:`bool`: Whether the current binary was built with
Indirect Branch Tracking (``IBT``)"""
if self.arch not in ['i386', 'amd64']:
return False
for prop in self.iter_properties():
if prop.pr_type != 'GNU_PROPERTY_X86_FEATURE_1_AND':
continue
return prop.pr_data & ENUM_GNU_PROPERTY_X86_FEATURE_1_FLAGS['GNU_PROPERTY_X86_FEATURE_1_IBT'] > 0
return False


def _update_args(self, kw):
kw.setdefault('arch', self.arch)
Expand Down
25 changes: 14 additions & 11 deletions pwnlib/rop/rop.py
Expand Up @@ -717,15 +717,20 @@ def setRegisters(self, registers):
name = ",".join(goodregs)
stack.append((gadget.address, gadget))
for r in gadget.regs:
moved += context.bytes
if r in registers:
stack.append((registers[r], r))
else:
stack.append((Padding('<pad %s>' % r), r))
if isinstance(r, str):
if r in registers:
stack.append((registers[r], r))
else:
stack.append((Padding('<pad %s>' % r), r))
moved += context.bytes
continue

for slot in range(moved, moved + r, context.bytes):
left = gadget.move - slot
stack.append((Padding('<pad %#x>' % left), 'stack padding'))
moved += context.bytes

for slot in range(moved, gadget.move, context.bytes):
left = gadget.move - slot
stack.append((Padding('<pad %#x>' % left), 'stack padding'))
assert moved == gadget.move

return stack

Expand Down Expand Up @@ -1389,9 +1394,7 @@ def __getattr__(self, k):
elif add.match(insn):
arg = int(add.match(insn).group(1), 16)
sp_move += arg
while arg >= context.bytes:
regs.append(hex(arg))
arg -= context.bytes
regs.append(arg)
elif ret.match(insn):
sp_move += context.bytes
elif leave.match(insn):
Expand Down
8 changes: 4 additions & 4 deletions pwnlib/shellcraft/templates/aarch64/linux/loader.asm
Expand Up @@ -107,14 +107,14 @@ PT_LOAD = 1
mov x3, sp
stp x2, x3, [sp, #-16]!

/* argc, argv[0], argv[1], envp */
/* argc, argv[0], argv[1], envp; x0 must be zero! */
/* ideally these could all be empty, but unfortunately
we have to keep the stack aligned. it's easier to
just push an extra argument than care... */
stp x0, x1, [sp, #-16]! /* argv[1] = NULL, envp = NULL */
mov x0, 1
mov x1, sp
stp x0, x1, [sp, #-16]! /* argc = 1, argv[0] = "" */
mov x2, 1
mov x3, sp
stp x2, x3, [sp, #-16]! /* argc = 1, argv[0] = "" */

br x8

Expand Down

0 comments on commit 26c031c

Please sign in to comment.