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

Gallopsled / pwntools / 13600950642

01 Mar 2025 04:10AM UTC coverage: 74.211% (+3.2%) from 71.055%
13600950642

Pull #2546

github

web-flow
Merge 77df40314 into 60cff2437
Pull Request #2546: ssh: Allow passing `disabled_algorithms` keyword argument from `ssh` to paramiko

3812 of 6380 branches covered (59.75%)

0 of 1 new or added line in 1 file covered. (0.0%)

1243 existing lines in 37 files now uncovered.

13352 of 17992 relevant lines covered (74.21%)

0.74 hits per line

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

87.62
/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 sys
1✔
7
import types
1✔
8

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

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

15

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

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

32

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

39

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

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

54
p.add_argument(
1✔
55
    '-f', '--format',
56
    metavar = 'format',
57
    choices = ['r', 'raw',
58
               's', 'str', 'string',
59
               'c',
60
               'h', 'hex',
61
               'a', 'asm', 'assembly',
62
               'p',
63
               'i', 'hexii',
64
               'e', 'elf',
65
               'd', 'escaped',
66
               'default'],
67
    default = 'default',
68
    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',
69
)
70

71
p.add_argument(
1✔
72
    'shellcode',
73
    nargs = '*',
74
    help = 'The shellcodes you want.  shellcode [args ...] [+ shellcode [args ...]]',
75
    type = str
76
)
77

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

280
    code_array = []
1✔
281
    for (name, func, func_args) in funcs:
1✔
282
        defargs = len(func.__defaults__ or ())
1✔
283
        reqargs = func.__code__.co_argcount - defargs
1✔
284

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

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

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

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

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

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

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

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

325
    assembly = code
1✔
326

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

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

336

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

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

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

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

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

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

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

385
if __name__ == '__main__':
1✔
386
    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

© 2026 Coveralls, Inc