• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Gallopsled / pwntools / 10355860582

12 Aug 2024 04:39PM UTC coverage: 74.329% (-0.1%) from 74.438%
10355860582

push

github

peace-maker
Pin colored_traceback < 0.4 for Python 2

4451 of 7185 branches covered (61.95%)

12983 of 17467 relevant lines covered (74.33%)

0.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.47
/pwnlib/commandline/shellcraft.py
1
from __future__ import absolute_import
1✔
2
from __future__ import division
1✔
3

4
import argparse
1✔
5
import os
1✔
6
import six
1✔
7
import sys
1✔
8
import types
1✔
9

10
import pwnlib.args
1✔
11
pwnlib.args.free_form = False
1✔
12

13
from pwn import *
1✔
14
from pwnlib.commandline import common
1✔
15

16

17
#  ____  _          _ _                 __ _
18
# / ___|| |__   ___| | | ___ _ __ __ _ / _| |_
19
# \___ \| '_ \ / _ \ | |/ __| '__/ _` | |_| __|
20
#  ___) | | | |  __/ | | (__| | | (_| |  _| |_
21
# |____/|_| |_|\___|_|_|\___|_|  \__,_|_|  \__|
22

23
def _string(s):
1✔
24
    out = []
1✔
25
    for co in bytearray(s):
1✔
26
        c = chr(co)
1✔
27
        if co >= 0x20 and co <= 0x7e and c not in '/$\'"`':
1✔
28
            out.append(c)
1✔
29
        else:
30
            out.append('\\x%02x' % co)
1✔
31
    return '"' + ''.join(out) + '"\n'
1✔
32

33

34
p = common.parser_commands.add_parser(
1✔
35
    'shellcraft',
36
    help = 'Microwave shellcode -- Easy, fast and delicious',
37
    description = 'Microwave shellcode -- Easy, fast and delicious',
38
)
39

40

41
p.add_argument(
1✔
42
    '-?', '--show',
43
    action = 'store_true',
44
    help = 'Show shellcode documentation',
45
)
46

47
p.add_argument(
1✔
48
    '-o', '--out',
49
    metavar = 'file',
50
    type = argparse.FileType('wb'),
51
    default = getattr(sys.stdout, 'buffer', sys.stdout),
52
    help = 'Output file (default: stdout)',
53
)
54

55
p.add_argument(
1✔
56
    '-f', '--format',
57
    metavar = 'format',
58
    choices = ['r', 'raw',
59
               's', 'str', 'string',
60
               'c',
61
               'h', 'hex',
62
               'a', 'asm', 'assembly',
63
               'p',
64
               'i', 'hexii',
65
               'e', 'elf',
66
               'd', 'escaped',
67
               'default'],
68
    default = 'default',
69
    help = 'Output format (default: hex), choose from {e}lf, {r}aw, {s}tring, {c}-style array, {h}ex string, hex{i}i, {a}ssembly code, {p}reprocssed code, escape{d} hex string',
70
)
71

72
p.add_argument(
1✔
73
    'shellcode',
74
    nargs = '?',
75
    help = 'The shellcode you want',
76
    type = str
77
)
78

79
p.add_argument(
1✔
80
    'args',
81
    nargs = '*',
82
    metavar = 'arg',
83
    default = (),
84
    help = 'Argument to the chosen shellcode',
85
)
86

87
p.add_argument(
1✔
88
    '-d',
89
    '--debug',
90
    help='Debug the shellcode with GDB',
91
    action='store_true'
92
)
93

94
p.add_argument(
1✔
95
    '-b',
96
    '--before',
97
    help='Insert a debug trap before the code',
98
    action='store_true'
99
)
100

101
p.add_argument(
1✔
102
    '-a',
103
    '--after',
104
    help='Insert a debug trap after the code',
105
    action='store_true'
106
)
107

108
p.add_argument(
1✔
109
    '-v', '--avoid',
110
    action='append',
111
    help = 'Encode the shellcode to avoid the listed bytes'
112
)
113

114
p.add_argument(
1✔
115
    '-n', '--newline',
116
    dest='avoid',
117
    action='append_const',
118
    const='\n',
119
    help = 'Encode the shellcode to avoid newlines'
120
)
121

122
p.add_argument(
1✔
123
    '-z', '--zero',
124
    dest='avoid',
125
    action='append_const',
126
    const='\x00',
127
    help = 'Encode the shellcode to avoid NULL bytes'
128
)
129

130
p.add_argument(
1✔
131
    '-r',
132
    '--run',
133
    help="Run output",
134
    action='store_true'
135
)
136

137
p.add_argument(
1✔
138
    '--color',
139
    help="Color output",
140
    action='store_true',
141
    default=sys.stdout.isatty()
142
)
143

144
p.add_argument(
1✔
145
    '--no-color',
146
    help="Disable color output",
147
    action='store_false',
148
    dest='color'
149
)
150

151
p.add_argument(
1✔
152
    '--syscalls',
153
    help="List syscalls",
154
    action='store_true'
155
)
156

157
p.add_argument(
1✔
158
    '--address',
159
    help="Load address",
160
    default=None
161
)
162

163
p.add_argument(
1✔
164
    '-l', '--list',
165
    action='store_true',
166
    help='List available shellcodes, optionally provide a filter'
167
)
168

169
p.add_argument(
1✔
170
    '-s', '--shared',
171
    action='store_true',
172
    help='Generated ELF is a shared library'
173
)
174

175
def get_template(name):
1✔
176
    func = shellcraft
1✔
177
    for attr in name.split('.'):
1✔
178
        func = getattr(func, attr)
1✔
179
    return func
1✔
180

181
def is_not_a_syscall_template(name):
1✔
182
    template_src = shellcraft._get_source(name)
1✔
183
    return '/syscalls' not in template_src
1✔
184

185
def main(args):
1✔
186
    if args.list:
1✔
187
        templates = shellcraft.templates
1✔
188

189
        if args.shellcode:
1✔
190
            templates = filter(lambda a: args.shellcode in a, templates)
1✔
191
        elif not args.syscalls:
1✔
192
            templates = filter(is_not_a_syscall_template, templates)
1✔
193

194
        print('\n'.join(templates))
1✔
195
        exit()
1✔
196

197
    if not args.shellcode:
1!
198
        common.parser.print_usage()
×
199
        exit()
×
200

201
    try:
1✔
202
        func = get_template(args.shellcode)
1✔
203
    except AttributeError:
×
204
        log.error("Unknown shellcraft template %r. Use --list to see available shellcodes." % args.shellcode)
×
205

206
    if args.show:
1✔
207
        # remove doctests
208
        doc = []
1✔
209
        in_doctest = False
1✔
210
        block_indent = None
1✔
211
        caption = None
1✔
212
        lines = func.__doc__.splitlines()
1✔
213
        i = 0
1✔
214
        while i < len(lines):
1✔
215
            line = lines[i]
1✔
216
            if line.lstrip().startswith('>>>'):
1✔
217
                # this line starts a doctest
218
                in_doctest = True
1✔
219
                block_indent = None
1✔
220
                if caption:
1✔
221
                    # delete back up to the caption
222
                    doc = doc[:caption - i]
1✔
223
                    caption = None
1✔
224
            elif line == '':
1✔
225
                # skip blank lines
226
                pass
1✔
227
            elif in_doctest:
1✔
228
                # indentation marks the end of a doctest
229
                indent = len(line) - len(line.lstrip())
1✔
230
                if block_indent is None:
1!
231
                    if not line.lstrip().startswith('...'):
1!
232
                        block_indent = indent
1✔
233
                elif indent < block_indent:
×
234
                    in_doctest = False
×
235
                    block_indent = None
×
236
                    # re-evalutate this line
237
                    continue
×
238
            elif line.endswith(':'):
1✔
239
                # save index of caption
240
                caption = i
1✔
241
            else:
242
                # this is not blank space and we're not in a doctest, so the
243
                # previous caption (if any) was not for a doctest
244
                caption = None
1✔
245

246
            if not in_doctest:
1✔
247
                doc.append(line)
1✔
248
            i += 1
1✔
249
        print('\n'.join(doc).rstrip())
1✔
250
        exit()
1✔
251

252
    defargs = len(six.get_function_defaults(func) or ())
1✔
253
    reqargs = six.get_function_code(func).co_argcount - defargs
1✔
254
    if len(args.args) < reqargs:
1!
255
        if defargs > 0:
×
256
            log.critical('%s takes at least %d arguments' % (args.shellcode, reqargs))
×
257
            sys.exit(1)
×
258
        else:
259
            log.critical('%s takes exactly %d arguments' % (args.shellcode, reqargs))
×
260
            sys.exit(1)
×
261

262
    # Captain uglyness saves the day!
263
    for i, val in enumerate(args.args):
1✔
264
        try:
1✔
265
            args.args[i] = util.safeeval.expr(val)
1✔
266
        except ValueError:
1✔
267
            pass
1✔
268

269
    # And he strikes again!
270
    list(map(common.context_arg, args.shellcode.split('.')))
1✔
271
    code = func(*args.args)
1✔
272

273

274
    if args.before:
1✔
275
        code = shellcraft.trap() + code
1✔
276
    if args.after:
1✔
277
        code = code + shellcraft.trap()
1✔
278

279

280
    if args.format in ['a', 'asm', 'assembly']:
1✔
281
        if args.color:
1!
282
            from pygments import highlight
1✔
283
            from pygments.formatters import TerminalFormatter
1✔
284
            from pwnlib.lexer import PwntoolsLexer
1✔
285

286
            code = highlight(code, PwntoolsLexer(), TerminalFormatter())
1✔
287

288
        print(code)
1✔
289
        exit()
1✔
290
    if args.format == 'p':
1✔
291
        print(cpp(code))
1✔
292
        exit()
1✔
293

294
    assembly = code
1✔
295

296
    vma = args.address
1✔
297
    if vma:
1!
298
        vma = pwnlib.util.safeeval.expr(vma)
×
299

300
    if args.format in ['e','elf']:
1✔
301
        args.format = 'default'
1✔
302
        try: os.fchmod(args.out.fileno(), 0o700)
1✔
303
        except OSError: pass
1✔
304

305

306
        if not args.avoid:
1!
307
            code = read(make_elf_from_assembly(assembly, vma=vma, shared=args.shared))
1✔
308
        else:
309
            code = asm(assembly)
×
310
            code = encode(code, args.avoid)
×
311
            code = make_elf(code, vma=vma, shared=args.shared)
×
312
            # code = read(make_elf(encode(asm(code), args.avoid)))
313
    else:
314
        code = encode(asm(assembly), args.avoid)
1✔
315

316
    if args.format == 'default':
1✔
317
        if args.out.isatty():
1!
318
            args.format = 'hex'
×
319
        else:
320
            args.format = 'raw'
1✔
321

322
    arch = args.shellcode.split('.')[0]
1✔
323

324
    if args.debug:
1!
325
        if not args.avoid:
×
326
            proc = gdb.debug_assembly(assembly, arch=arch, vma=vma)
×
327
        else:
328
            proc = gdb.debug_shellcode(code, arch=arch, vma=vma)
×
329
        proc.interactive()
×
330
        sys.exit(0)
×
331

332
    if args.run:
1✔
333
        proc = run_shellcode(code, arch=arch)
1✔
334
        proc.interactive()
1✔
335
        sys.exit(0)
1✔
336

337
    if args.format in ['s', 'str', 'string']:
1✔
338
        code = _string(code)
1✔
339
    elif args.format == 'c':
1✔
340
        code = '{' + ', '.join(map(hex, bytearray(code))) + '}' + '\n'
1✔
341
    elif args.format in ['h', 'hex']:
1!
342
        code = pwnlib.util.fiddling.enhex(code) + '\n'
×
343
    elif args.format in ['i', 'hexii']:
1✔
344
        code = hexii(code) + '\n'
1✔
345
    elif args.format in ['d', 'escaped']:
1!
346
        code = ''.join('\\x%02x' % c for c in bytearray(code)) + '\n'
×
347
    if not sys.stdin.isatty():
1!
348
        args.out.write(getattr(sys.stdin, 'buffer', sys.stdin).read())
1✔
349

350
    if not hasattr(code, 'decode'):
1✔
351
        code = code.encode()
1✔
352
    args.out.write(code)
1✔
353

354
if __name__ == '__main__':
1✔
355
    pwnlib.commandline.common.main(__file__)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc