Skip to content

Commit

Permalink
Merge 589fb87 into 42c0798
Browse files Browse the repository at this point in the history
  • Loading branch information
peace-maker committed Jul 18, 2023
2 parents 42c0798 + 589fb87 commit aea325a
Show file tree
Hide file tree
Showing 8 changed files with 412 additions and 58 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ jobs:
binutils-sparc64-linux-gnu \
gcc-multilib \
libc6-dbg \
elfutils
elfutils \
patchelf
- name: Testing Corefiles
run: |
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,13 @@ The table below shows which release corresponds to each branch, and what date th
- [#2117][2117] Add -p (--prefix) and -s (--separator) arguments to `hex` command
- [#2221][2221] Add shellcraft.sleep template wrapping SYS_nanosleep
- [#2219][2219] Fix passing arguments on the stack in shellcraft syscall template
- [#2212][2212] Add `--libc libc.so` argument to `pwn template` command

[2202]: https://github.com/Gallopsled/pwntools/pull/2202
[2117]: https://github.com/Gallopsled/pwntools/pull/2117
[2221]: https://github.com/Gallopsled/pwntools/pull/2221
[2219]: https://github.com/Gallopsled/pwntools/pull/2219
[2212]: https://github.com/Gallopsled/pwntools/pull/2212

## 4.11.0 (`beta`)

Expand Down
1 change: 1 addition & 0 deletions docs/source/elf/elf.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from pwn import *
from glob import glob
from pwnlib.elf.maps import CAT_PROC_MAPS_EXIT
import shutil

:mod:`pwnlib.elf.elf` --- ELF Files
===========================================================
Expand Down
2 changes: 2 additions & 0 deletions pwnlib/commandline/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
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('--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')
Expand Down Expand Up @@ -53,6 +54,7 @@ def main(args):
args.port,
args.user,
args.password,
args.libc,
args.path,
args.quiet)

Expand Down
30 changes: 29 additions & 1 deletion pwnlib/data/templates/pwnup.mako
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<%page args="binary, host=None, port=None, user=None, password=None, remote_path=None, quiet=False"/>\
<%page args="binary, host=None, port=None, user=None, password=None, libc=None, remote_path=None, quiet=False"/>\
<%
import os
import sys
Expand Down Expand Up @@ -31,6 +31,7 @@ elif host and not port:
remote_path = remote_path or exe
password = password or 'secret1234'
binary_repr = repr(binary)
libc_repr = repr(libc)
%>\
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
Expand Down Expand Up @@ -83,6 +84,33 @@ if not args.LOCAL:
shell.set_working_directory(symlink=True)
%endif

%if libc:
# Use the specified remote libc version unless explicitly told to use the
# local system version with the `LOCAL_LIBC` argument.
# ./exploit.py LOCAL LOCAL_LIBC
if args.LOCAL_LIBC:
libc = exe.libc
%if host:
elif args.LOCAL:
%else:
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})
%if host:
else:
libc = ELF(${libc_repr})
%endif
%endif

%if host:
def start_local(argv=[], *a, **kw):
'''Execute the target binary locally'''
Expand Down
121 changes: 120 additions & 1 deletion pwnlib/elf/elf.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
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.misc import align, align_down, which
from pwnlib.util.sh_string import sh_string

log = getLogger(__name__)
Expand Down Expand Up @@ -2246,3 +2246,122 @@ def disable_nx(self):
return

log.error("Could not find PT_GNU_STACK, stack should already be executable")

@staticmethod
def set_runpath(exepath, runpath):
r"""set_runpath(str, str) -> ELF
Patches the RUNPATH of the ELF to the given path using the `patchelf utility <https://github.com/NixOS/patchelf>`_.
The dynamic loader will look for any needed shared libraries in the given path first,
before trying the system library paths. This is useful to run a binary with a different
libc binary.
Arguments:
exepath(str): Path to the binary to patch.
runpath(str): Path containing the needed libraries.
Returns:
A new ELF instance is returned after patching the binary with the external ``patchelf`` tool.
Example:
>>> tmpdir = tempfile.mkdtemp()
>>> ls_path = os.path.join(tmpdir, 'ls')
>>> _ = shutil.copy(which('ls'), ls_path)
>>> e = ELF.set_runpath(ls_path, './libs')
>>> e.runpath == b'./libs'
True
"""
if not which('patchelf'):
log.error('"patchelf" tool not installed. See https://github.com/NixOS/patchelf')
return None
try:
subprocess.check_output(['patchelf', '--set-rpath', runpath, exepath], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
log.failure('Patching RUNPATH failed (%d): %r', e.returncode, e.stdout)
return ELF(exepath, checksec=False)

@staticmethod
def set_interpreter(exepath, interpreter_path):
r"""set_interpreter(str, str) -> ELF
Patches the interpreter of the ELF to the given binary using the `patchelf utility <https://github.com/NixOS/patchelf>`_.
When running the binary, the new interpreter will be used to load the ELF.
Arguments:
exepath(str): Path to the binary to patch.
interpreter_path(str): Path to the ld.so dynamic loader.
Returns:
A new ELF instance is returned after patching the binary with the external ``patchelf`` tool.
Example:
>>> tmpdir = tempfile.mkdtemp()
>>> ls_path = os.path.join(tmpdir, 'ls')
>>> _ = shutil.copy(which('ls'), ls_path)
>>> e = ELF.set_interpreter(ls_path, '/tmp/correct_ld.so')
>>> e.linker == b'/tmp/correct_ld.so'
True
"""
# patch the interpreter
if not which('patchelf'):
log.error('"patchelf" tool not installed. See https://github.com/NixOS/patchelf')
return None
try:
subprocess.check_output(['patchelf', '--set-interpreter', interpreter_path, exepath], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
log.failure('Patching interpreter failed (%d): %r', e.returncode, e.stdout)
return ELF(exepath, checksec=False)

@staticmethod
def patch_custom_libraries(exe_path, custom_library_path, create_copy=True, suffix='_remotelibc'):
r"""patch_custom_libraries(str, str, bool, str) -> ELF
Looks for the interpreter binary in the given path and patches the binary to use
it if available. Also patches the RUNPATH to the given path using the `patchelf utility <https://github.com/NixOS/patchelf>`_.
Arguments:
exe_path(str): Path to the binary to patch.
custom_library_path(str): Path to a folder containing the libraries.
create_copy(bool): Create a copy of the binary and apply the patches to the copy.
suffix(str): Suffix to append to the filename when creating the copy to patch.
Returns:
A new ELF instance is returned after patching the binary with the external ``patchelf`` tool.
Example:
>>> tmpdir = tempfile.mkdtemp()
>>> linker_path = os.path.join(tmpdir, 'ld-mock.so')
>>> write(linker_path, b'loader')
>>> ls_path = os.path.join(tmpdir, 'ls')
>>> _ = shutil.copy(which('ls'), ls_path)
>>> e = ELF.patch_custom_libraries(ls_path, tmpdir)
>>> e.runpath.decode() == tmpdir
True
>>> e.linker.decode() == linker_path
True
"""
if not which('patchelf'):
log.error('"patchelf" tool not installed. See https://github.com/NixOS/patchelf')
return None

# Create a copy of the ELF to patch instead of the original file.
if create_copy:
import shutil
patched_path = exe_path + suffix
shutil.copy2(exe_path, patched_path)
exe_path = patched_path

# Set interpreter in ELF to the one in the library path.
interpreter_name = [filename for filename in os.listdir(custom_library_path) if filename.startswith('ld-')]
if interpreter_name:
interpreter_path = os.path.realpath(os.path.join(custom_library_path, interpreter_name[0]))
ELF.set_interpreter(exe_path, interpreter_path)
else:
log.warn("Couldn't find ld.so in library path. Interpreter not set.")

# Set RUNPATH to library path in order to find other libraries.
return ELF.set_runpath(exe_path, custom_library_path)
Loading

0 comments on commit aea325a

Please sign in to comment.