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

Gallopsled / pwntools / 11532588290

26 Oct 2024 02:29PM CUT coverage: 73.585% (+2.1%) from 71.491%
11532588290

push

github

peace-maker
Merge branch 'dev' into interactive

3767 of 6364 branches covered (59.19%)

1463 of 2234 new or added lines in 69 files covered. (65.49%)

85 existing lines in 22 files now uncovered.

13246 of 18001 relevant lines covered (73.58%)

0.74 hits per line

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

87.68
/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 shellcodes you want.  shellcode [args ...] [+ shellcode [args ...]]',
76
    type = str
77
)
78

79
p.add_argument(
1✔
80
    '-d',
81
    '--debug',
82
    help='Debug the shellcode with GDB',
83
    action='store_true'
84
)
85

86
p.add_argument(
1✔
87
    '--delim',
88
    help='Set the delimiter between multilple shellcodes',
89
    default='+'
90
)
91

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

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

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

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

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

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

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

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

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

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

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

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

173
def get_template(shellcodes):
1✔
174
    funcs = []
1✔
175
    for shellcode in shellcodes:
1✔
176
        func = shellcraft
1✔
177
        cur_name = shellcode[0]
1✔
178
        args = []
1✔
179
        if len(shellcode) > 1:
1✔
180
            args = shellcode[1:]
1✔
181
        for attr in cur_name.split('.'):
1✔
182
            func = getattr(func, attr)
1✔
183
        funcs.append((cur_name, func, args))
1✔
184
    return funcs
1✔
185

186
def is_not_a_syscall_template(name):
1✔
187
    template_src = shellcraft._get_source(name)
1✔
188
    return '/syscalls' not in template_src
1✔
189

190
def main(args):
1✔
191
    delim = '+'
1✔
192
    if args.delim:
1!
193
        delim = args.delim.strip()
1✔
194

195
    shellcodes = []
1✔
196
    if args.shellcode:
1✔
197
        current = []
1✔
198
        for s in args.shellcode:
1✔
199
            if s.strip() == delim:
1✔
200
                shellcodes.append(current)
1✔
201
                current = []
1✔
202
            else:
203
                current.append(s)
1✔
204
        if len(current) > 0:
1!
205
            shellcodes.append(current)
1✔
206

207
    if args.list:
1✔
208
        templates = shellcraft.templates
1✔
209

210
        if args.shellcode:
1✔
211
            template_array = []
1✔
212
            for s in shellcodes:
1✔
213
                template_array.extend(list(filter(lambda a: s[0] in a, templates)))
1✔
214
            templates = template_array
1✔
215
        elif not args.syscalls:
1✔
216
            templates = list(filter(is_not_a_syscall_template, templates))
1✔
217

218
        print('\n'.join(templates))
1✔
219
        exit()
1✔
220

221
    if not args.shellcode:
1!
222
        common.parser.print_usage()
×
223
        exit()
×
224

225
    try:
1✔
226
        funcs = get_template(shellcodes)
1✔
227
    except AttributeError:
×
228
        log.error("Unknown shellcraft template %r. Use --list to see available shellcodes." % args.shellcode)
×
229

230
    if args.show:
1✔
231
        for (name, func, _args) in funcs:
1✔
232
            # remove doctests
233
            doc = []
1✔
234
            in_doctest = False
1✔
235
            block_indent = None
1✔
236
            caption = None
1✔
237
            lines = func.__doc__.splitlines()
1✔
238
            i = 0
1✔
239
            if len(funcs) > 1:
1✔
240
                print('%s:' % name)
1✔
241
            while i < len(lines):
1✔
242
                line = lines[i]
1✔
243
                if line.lstrip().startswith('>>>'):
1✔
244
                    # this line starts a doctest
245
                    in_doctest = True
1✔
246
                    block_indent = None
1✔
247
                    if caption:
1✔
248
                        # delete back up to the caption
249
                        doc = doc[:caption - i]
1✔
250
                        caption = None
1✔
251
                elif line == '':
1✔
252
                    # skip blank lines
253
                    pass
1✔
254
                elif in_doctest:
1✔
255
                    # indentation marks the end of a doctest
256
                    indent = len(line) - len(line.lstrip())
1✔
257
                    if block_indent is None:
1!
258
                        if not line.lstrip().startswith('...'):
1!
259
                            block_indent = indent
1✔
NEW
260
                    elif indent < block_indent:
×
NEW
261
                        in_doctest = False
×
NEW
262
                        block_indent = None
×
263
                        # re-evalutate this line
NEW
264
                        continue
×
265
                elif line.endswith(':'):
1✔
266
                    # save index of caption
267
                    caption = i
1✔
268
                else:
269
                    # this is not blank space and we're not in a doctest, so the
270
                    # previous caption (if any) was not for a doctest
271
                    caption = None
1✔
272

273
                if not in_doctest:
1✔
274
                    doc.append(line)
1✔
275
                i += 1
1✔
276
            print('\n'.join(doc).rstrip())
1✔
277
            if len(funcs) > 1:
1✔
278
                print('')
1✔
279
        exit()
1✔
280

281
    code_array = []
1✔
282
    for (name, func, func_args) in funcs:
1✔
283
        defargs = len(six.get_function_defaults(func) or ())
1✔
284
        reqargs = six.get_function_code(func).co_argcount - defargs
1✔
285

286
        if len(func_args) < reqargs:
1!
NEW
287
            if defargs > 0:
×
NEW
288
                log.critical('%s takes at least %d arguments' % (name, reqargs))
×
NEW
289
                sys.exit(1)
×
290
            else:
NEW
291
                log.critical('%s takes exactly %d arguments' % (name, reqargs))
×
NEW
292
                sys.exit(1)
×
293

294
        # Captain uglyness saves the day!
295
        for i, val in enumerate(func_args):
1✔
296
            try:
1✔
297
                func_args[i] = util.safeeval.expr(val)
1✔
298
            except ValueError:
1✔
299
                pass
1✔
300

301
        # And he strikes again!
302
        list(map(common.context_arg, name.split('.')))
1✔
303
        code_array.append(func(*func_args))
1✔
304

305
    code = "".join(code_array)
1✔
306

307
    if args.before:
1✔
308
        code = shellcraft.trap() + code
1✔
309
    if args.after:
1✔
310
        code = code + shellcraft.trap()
1✔
311

312
    if args.format in ['a', 'asm', 'assembly']:
1✔
313
        if args.color:
1!
314
            from pygments import highlight
1✔
315
            from pygments.formatters import TerminalFormatter
1✔
316
            from pwnlib.lexer import PwntoolsLexer
1✔
317

318
            code = highlight(code, PwntoolsLexer(), TerminalFormatter())
1✔
319

320
        print(code)
1✔
321
        exit()
1✔
322
    if args.format == 'p':
1✔
323
        print(cpp(code))
1✔
324
        exit()
1✔
325

326
    assembly = code
1✔
327

328
    vma = args.address
1✔
329
    if vma:
1!
330
        vma = pwnlib.util.safeeval.expr(vma)
×
331

332
    if args.format in ['e','elf']:
1✔
333
        args.format = 'default'
1✔
334
        try: os.fchmod(args.out.fileno(), 0o700)
1✔
335
        except OSError: pass
1✔
336

337

338
        if not args.avoid:
1!
339
            code = read(make_elf_from_assembly(assembly, vma=vma, shared=args.shared))
1✔
340
        else:
341
            code = asm(assembly)
×
342
            code = encode(code, args.avoid)
×
343
            code = make_elf(code, vma=vma, shared=args.shared)
×
344
            # code = read(make_elf(encode(asm(code), args.avoid)))
345
    else:
346
        code = encode(asm(assembly), args.avoid)
1✔
347

348
    if args.format == 'default':
1✔
349
        if args.out.isatty():
1!
350
            args.format = 'hex'
×
351
        else:
352
            args.format = 'raw'
1✔
353

354
    arch = name.split('.')[0]
1✔
355

356
    if args.debug:
1!
357
        if not args.avoid:
×
358
            proc = gdb.debug_assembly(assembly, arch=arch, vma=vma)
×
359
        else:
360
            proc = gdb.debug_shellcode(code, arch=arch, vma=vma)
×
361
        proc.interactive()
×
362
        sys.exit(0)
×
363

364
    if args.run:
1✔
365
        proc = run_shellcode(code, arch=arch)
1✔
366
        proc.interactive()
1✔
367
        sys.exit(0)
1✔
368

369
    if args.format in ['s', 'str', 'string']:
1✔
370
        code = _string(code)
1✔
371
    elif args.format == 'c':
1✔
372
        code = '{' + ', '.join(map(hex, bytearray(code))) + '}' + '\n'
1✔
373
    elif args.format in ['h', 'hex']:
1!
374
        code = pwnlib.util.fiddling.enhex(code) + '\n'
×
375
    elif args.format in ['i', 'hexii']:
1✔
376
        code = hexii(code) + '\n'
1✔
377
    elif args.format in ['d', 'escaped']:
1!
378
        code = ''.join('\\x%02x' % c for c in bytearray(code)) + '\n'
×
379
    if not sys.stdin.isatty():
1!
380
        args.out.write(getattr(sys.stdin, 'buffer', sys.stdin).read())
1✔
381

382
    if not hasattr(code, 'decode'):
1✔
383
        code = code.encode()
1✔
384
    args.out.write(code)
1✔
385

386
if __name__ == '__main__':
1✔
387
    pwnlib.commandline.common.main(__file__, main)
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