Skip to content

Commit

Permalink
Add "pwn template" command for generating templates (#909)
Browse files Browse the repository at this point in the history
  • Loading branch information
zachriggle committed Feb 17, 2017
1 parent 81239be commit e9fc012
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 3 deletions.
1 change: 1 addition & 0 deletions pwnlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
'commandline',
'constants',
'context',
'data',
'dynelf',
'encoders',
'elf',
Expand Down
6 changes: 5 additions & 1 deletion pwnlib/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,12 @@
from pwnlib import term
from pwnlib.context import context

class PwnlibArgs(collections.defaultdict):
def __getattr__(self, attr):
return self[attr]

args = PwnlibArgs(str)
term_mode = True
args = collections.defaultdict(str)
env_prefix = 'PWNLIB_'
free_form = True

Expand Down
2 changes: 2 additions & 0 deletions pwnlib/commandline/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from pwnlib.commandline import pwnstrip
from pwnlib.commandline import scramble
from pwnlib.commandline import shellcraft
from pwnlib.commandline import template
from pwnlib.commandline import unhex
from pwnlib.commandline import update
from pwnlib.commandline.common import parser
Expand All @@ -39,6 +40,7 @@
'pwnstrip': pwnstrip.main,
'scramble': scramble.main,
'shellcraft': shellcraft.main,
'template': template.main,
'unhex': unhex.main,
'update': update.main,
}
Expand Down
52 changes: 52 additions & 0 deletions pwnlib/commandline/template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env python2
from __future__ import absolute_import

import re

from pwn import *
from pwnlib.commandline import common

from mako.lookup import TemplateLookup

parser = common.parser_commands.add_parser(
'template',
help = 'Generate an exploit template'
)

parser.add_argument('exe', nargs='?', help='Target binary')
parser.add_argument('--host', help='Remote host / SSH server')
parser.add_argument('--port', help='Remote port / SSH port')
parser.add_argument('--user', help='SSH Username')
parser.add_argument('--pass', help='SSH Password', dest='password')
parser.add_argument('--path', help='Remote path of file on SSH server')

def main(args):
cache = None

if cache:
cache = os.path.join(context.cache_dir, 'mako')

lookup = TemplateLookup(
directories = [os.path.join(pwnlib.data.path, 'templates')],
module_directory = cache
)

template = lookup.get_template('pwnup.mako')
output = template.render(args.exe,
args.host,
args.port,
args.user,
args.password,
args.path)

# Fix Mako formatting bs
output = re.sub('\n\n\n', '\n\n', output)

print output

if not sys.stdout.isatty():
try: os.fchmod(sys.stdout.fileno(), 0700)
except OSError: pass

if __name__ == '__main__':
pwnlib.commandline.common.main(__file__)
12 changes: 12 additions & 0 deletions pwnlib/data/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from __future__ import absolute_import

try:
# These files are not distributed with Pwntools, but
# are in the source tree and used for testing.
from pwnlib.data import elf
except ImportError:
pass

import os
path = os.path.dirname(__file__)

130 changes: 130 additions & 0 deletions pwnlib/data/templates/pwnup.mako
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<%page args="binary, host=None, port=None, user=None, password=None, remote_path=None"/>\
<%
from pwnlib.context import context as ctx
from pwnlib.elf.elf import ELF
from elftools.common.exceptions import ELFError
import os
try:
if binary:
ctx.binary = ELF(binary, checksec=False)
except ELFError:
pass
if not binary:
binary = './path/to/binary'
exe = os.path.basename(binary)
ssh = user or password
if ssh and not port:
port = 22
elif host and not port:
port = 4141
remote_path = remote_path or exe
password = password or 'secret1234'
binary_repr = repr(binary)
%>\
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from pwn import *

# Set up pwntools for the correct architecture
%if ctx.binary:
exe = context.binary = ELF(${binary_repr})
<% binary_repr = 'exe.path' %>
%else:
context.update(arch='i386')
exe = ${binary_repr}
<% binary_repr = 'exe' %>
%endif

# Many built-in settings can be controlled on the command-line and show up
# in "args". For example, to dump all data sent/received, and disable ASLR
# for all created processes...
# ./exploit.py DEBUG NOASLR
%if host or port or user:
# ./exploit.py GDB HOST=example.com PORT=4141
%endif
%if host:
host = args.HOST or ${repr(host)}
%endif
%if port:
port = int(args.PORT or ${port})
%endif
%if user:
user = args.USER or ${repr(user)}
password = args.PASSWORD or ${repr(password)}
%endif
%if ssh:
remote_path = ${repr(remote_path)}
%endif

%if exe or remote_path:
gdbscript = '''
%if ctx.binary:
%if 'main' in ctx.binary.symbols:
break *0x{exe.symbols.main:x}
%else:
break *0x{exe.entry:x}
%endif
%endif
continue
'''.format(**locals())
%endif

%if ssh:
shell = None
if not args.LOCAL:
shell = ssh(user, host, port, password)
shell.set_working_directory(symlink=True)
%endif

%if host:
def local():
if args.GDB:
return gdb.debug(exe.path, gdbscript=gdbscript)
else:
return process(exe.path)

def remote():
%if ssh:
if args.GDB:
return gdb.debug(remote_path, gdbscript=gdbscript, ssh=shell)
else:
return shell.process(remote_path)
%else:
return connect(host, port)
%endif
%endif

%if host:
start = local if args.LOCAL else remote

io = start()
%else:
if args.GDB:
io = gdb.debug(${binary_repr}, gdbscript=gdbscript)
else:
io = process(${binary_repr})
%endif

%if host and not ssh:
if args.GDB and not args.LOCAL:
gdb.attach(io, gdbscript=gdbscript)
%endif

#===========================================================
# EXPLOIT GOES HERE
#===========================================================
# shellcode = asm(shellcraft.sh())
# payload = fit({
# 32: 0xdeadbeef,
# 'iaaa': [1, 2, 'Hello', 3]
# }, length=128)
# io.send(payload)
# flag = io.recv(...)
# log.success(flag)

io.interactive()
5 changes: 3 additions & 2 deletions pwnlib/elf/elf.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ class ELF(ELFFile):
_fill_gaps = True


def __init__(self, path):
def __init__(self, path, checksec=True):
# elftools uses the backing file for all reads and writes
# in order to permit writing without being able to write to disk,
# mmap() the file.
Expand Down Expand Up @@ -294,7 +294,8 @@ def __init__(self, path):
self._populate_functions()
self._populate_kernel_version()

self._describe()
if checksec:
self._describe()

@staticmethod
@LocalContext
Expand Down
1 change: 1 addition & 0 deletions travis/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -134,5 +134,6 @@ RUN mkdir /home/travis/pwntools
WORKDIR /home/travis/pwntools
ADD run.sh .
ADD pwntools.tar.gz .
RUN sudo pip install --editable .
RUN sudo chown -R travis .
ENTRYPOINT bash run.sh

0 comments on commit e9fc012

Please sign in to comment.