From d61895ae96ea2e026fee4c1d436cc406470074c8 Mon Sep 17 00:00:00 2001 From: Zach Riggle Date: Thu, 21 Sep 2017 13:22:24 -0700 Subject: [PATCH 1/4] Slightly enhance template and advertise for the feature itself in scripts Add single-dash to the "very good" characters in shell escaping, to avoid the need to single-quote things like --host and --port. --- pwnlib/data/templates/pwnup.mako | 10 +++++++++- pwnlib/util/sh_string.py | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pwnlib/data/templates/pwnup.mako b/pwnlib/data/templates/pwnup.mako index e42ed4ee3..bfc68a520 100644 --- a/pwnlib/data/templates/pwnup.mako +++ b/pwnlib/data/templates/pwnup.mako @@ -1,10 +1,16 @@ <%page args="binary, host=None, port=None, user=None, password=None, remote_path=None"/>\ <% +import os +import sys + from pwnlib.context import context as ctx from pwnlib.elf.elf import ELF +from pwnlib.util.sh_string import sh_string from elftools.common.exceptions import ELFError -import os +argv = list(sys.argv) +argv[0] = os.path.basename(argv[0]) + try: if binary: ctx.binary = ELF(binary, checksec=False) @@ -28,6 +34,8 @@ binary_repr = repr(binary) %>\ #!/usr/bin/env python2 # -*- coding: utf-8 -*- +# This exploit template was generated via: +# $ ${' '.join(map(sh_string, argv))} from pwn import * # Set up pwntools for the correct architecture diff --git a/pwnlib/util/sh_string.py b/pwnlib/util/sh_string.py index e1eda9a93..33b667b33 100644 --- a/pwnlib/util/sh_string.py +++ b/pwnlib/util/sh_string.py @@ -393,7 +393,7 @@ def sh_string(s): return "''" chars = set(s) - very_good = set(string.ascii_letters + string.digits + "_+.,/") + very_good = set(string.ascii_letters + string.digits + "_+.,/-") # Alphanumeric can always just be used verbatim. if chars <= very_good: From 82a5bba6219fb0b98a1c6b4585903a45c7175fa6 Mon Sep 17 00:00:00 2001 From: Zach Riggle Date: Thu, 21 Sep 2017 13:29:19 -0700 Subject: [PATCH 2/4] Add --quiet option for pwn template, move gdbscript down a bit --- pwnlib/commandline/template.py | 4 ++- pwnlib/data/templates/pwnup.mako | 57 +++++++++++++++++++------------- 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/pwnlib/commandline/template.py b/pwnlib/commandline/template.py index d05abe611..3bfb34c83 100644 --- a/pwnlib/commandline/template.py +++ b/pwnlib/commandline/template.py @@ -19,6 +19,7 @@ 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') +parser.add_argument('--quiet', help='Less verbose template comments', action='store_true') def main(args): cache = None @@ -49,7 +50,8 @@ def main(args): args.port, args.user, args.password, - args.path) + args.path, + args.quiet) # Fix Mako formatting bs output = re.sub('\n\n\n', '\n\n', output) diff --git a/pwnlib/data/templates/pwnup.mako b/pwnlib/data/templates/pwnup.mako index bfc68a520..7fd4601fc 100644 --- a/pwnlib/data/templates/pwnup.mako +++ b/pwnlib/data/templates/pwnup.mako @@ -1,4 +1,4 @@ -<%page args="binary, host=None, port=None, user=None, password=None, remote_path=None"/>\ +<%page args="binary, host=None, port=None, user=None, password=None, remote_path=None, quiet=False"/>\ <% import os import sys @@ -34,11 +34,15 @@ binary_repr = repr(binary) %>\ #!/usr/bin/env python2 # -*- coding: utf-8 -*- +%if not quiet: # This exploit template was generated via: # $ ${' '.join(map(sh_string, argv))} +%endif from pwn import * +%if not quiet: # Set up pwntools for the correct architecture +%endif %if ctx.binary: exe = context.binary = ELF(${binary_repr}) <% binary_repr = 'exe.path' %> @@ -48,6 +52,7 @@ exe = ${binary_repr} <% binary_repr = 'exe' %> %endif +%if not quiet: # 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... @@ -55,6 +60,7 @@ exe = ${binary_repr} %if host or port or user: # ./exploit.py GDB HOST=example.com PORT=4141 %endif +%endif %if host: host = args.HOST or ${repr(host)} %endif @@ -69,22 +75,6 @@ password = args.PASSWORD or ${repr(password)} remote_path = ${repr(remote_path)} %endif -%if exe or remote_path: -# Specify your GDB script here for debugging -# GDB will be launched if the exploit is run via e.g. -# ./exploit.py GDB -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: # Connect to the remote SSH server shell = None @@ -94,25 +84,22 @@ if not args.LOCAL: %endif %if host: -# Execute the target binary locally def local(argv=[], *a, **kw): + '''Execute the target binary locally''' if args.GDB: return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw) else: return process([exe.path] + argv, *a, **kw) -%if ssh: -# Execute the target binary on the remote host -%else: -# Connect to the process on the remote host -%endif def remote(argv=[], *a, **kw): %if ssh: + '''Execute the target binary on the remote host''' if args.GDB: return gdb.debug([remote_path] + argv, gdbscript=gdbscript, ssh=shell, *a, **kw) else: return shell.process([remote_path] + argv, *a, **kw) %else: + '''Connect to the process on the remote host''' io = connect(host, port) if args.GDB: gdb.attach(io, gdbscript=gdbscript) @@ -130,11 +117,34 @@ def start(argv=[], *a, **kw): return process([${binary_repr}] + argv, *a, **kw) %endif +%if exe or remote_path: +%if not quiet: +# Specify your GDB script here for debugging +# GDB will be launched if the exploit is run via e.g. +# ./exploit.py GDB +%endif +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 not quiet: #=========================================================== # EXPLOIT GOES HERE #=========================================================== +%else: +# -- Exploit goes here -- +%endif io = start() +%if not quiet: # shellcode = asm(shellcraft.sh()) # payload = fit({ # 32: 0xdeadbeef, @@ -143,5 +153,6 @@ io = start() # io.send(payload) # flag = io.recv(...) # log.success(flag) +%endif io.interactive() From a41524e74cc5b58a51be7495351b3f82c5f5552b Mon Sep 17 00:00:00 2001 From: Zach Riggle Date: Thu, 21 Sep 2017 13:33:57 -0700 Subject: [PATCH 3/4] Slightly better docs for 'pwn template' --- pwnlib/data/templates/pwnup.mako | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pwnlib/data/templates/pwnup.mako b/pwnlib/data/templates/pwnup.mako index 7fd4601fc..b8437e97e 100644 --- a/pwnlib/data/templates/pwnup.mako +++ b/pwnlib/data/templates/pwnup.mako @@ -87,9 +87,9 @@ if not args.LOCAL: def local(argv=[], *a, **kw): '''Execute the target binary locally''' if args.GDB: - return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw) + return gdb.debug([${binary_repr}] + argv, gdbscript=gdbscript, *a, **kw) else: - return process([exe.path] + argv, *a, **kw) + return process([${binary_repr}] + argv, *a, **kw) def remote(argv=[], *a, **kw): %if ssh: @@ -108,9 +108,15 @@ def remote(argv=[], *a, **kw): %endif %if host: -start = local if args.LOCAL else remote +def start(argv=[], *a, **kw): + '''Start the exploit against the target.''' + if args.LOCAL: + return local(argv, *a, **kw) + else: + return remote(argv, *a, **kw) %else: def start(argv=[], *a, **kw): + '''Start the exploit against the target.''' if args.GDB: return gdb.debug([${binary_repr}] + argv, gdbscript=gdbscript, *a, **kw) else: From 96102fb7b32b32ce010867a33f3a4f4c1b57144e Mon Sep 17 00:00:00 2001 From: Zach Riggle Date: Thu, 21 Sep 2017 14:01:39 -0700 Subject: [PATCH 4/4] Add checksec information directly to 'pwn template' output --- pwnlib/data/templates/pwnup.mako | 13 ++++++++++++- pwnlib/elf/elf.py | 12 ++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/pwnlib/data/templates/pwnup.mako b/pwnlib/data/templates/pwnup.mako index b8437e97e..81307d12b 100644 --- a/pwnlib/data/templates/pwnup.mako +++ b/pwnlib/data/templates/pwnup.mako @@ -13,7 +13,7 @@ argv[0] = os.path.basename(argv[0]) try: if binary: - ctx.binary = ELF(binary, checksec=False) + ctx.binary = ELF(binary, checksec=False) except ELFError: pass @@ -141,6 +141,7 @@ continue '''.format(**locals()) %endif + %if not quiet: #=========================================================== # EXPLOIT GOES HERE @@ -148,6 +149,16 @@ continue %else: # -- Exploit goes here -- %endif +%if ctx.binary and not quiet: +# ${'%-10s%s-%s-%s' % ('Arch:', + ctx.binary.arch, + ctx.binary.bits, + ctx.binary.endian)} +%for line in ctx.binary.checksec(color=False).splitlines(): +# ${line} +%endfor +%endif + io = start() %if not quiet: diff --git a/pwnlib/elf/elf.py b/pwnlib/elf/elf.py index c9c3b9115..c927c7a4c 100644 --- a/pwnlib/elf/elf.py +++ b/pwnlib/elf/elf.py @@ -408,10 +408,10 @@ def debug(self, argv=[], *a, **kw): import pwnlib.gdb return pwnlib.gdb.debug([self.path] + argv, *a, **kw) - def _describe(self): + def _describe(self, *a, **kw): log.info_once('\n'.join((repr(self.path), '%-10s%s-%s-%s' % ('Arch:', self.arch, self.bits, self.endian), - self.checksec()))) + self.checksec(*a, **kw)))) def __repr__(self): return "ELF(%r)" % self.path @@ -1490,7 +1490,7 @@ def runpath(self): return self.dynamic_string(dt_rpath.entry.d_ptr) - def checksec(self, banner=True): + def checksec(self, banner=True, color=True): """checksec(banner=True) Prints out information in the binary, similar to ``checksec.sh``. @@ -1498,9 +1498,9 @@ def checksec(self, banner=True): Arguments: banner(bool): Whether to print the path to the ELF binary. """ - red = text.red - green = text.green - yellow = text.yellow + red = text.red if color else str + green = text.green if color else str + yellow = text.yellow if color else str res = []