1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
#!/usr/bin/env python3
#
# Interpret a file that crashes an fuzz_ndr_X binary.
#
# Copyright (C) Catalyst IT Ltd. 2019
import sys
import os
from base64 import b64encode
import struct
import argparse
import re
TYPE_MASK = 3
TYPES = ['struct', 'in', 'out']
FLAGS = [
(4, 'ndr64', '--ndr64'),
]
def print_if_verbose(*args, **kwargs):
if verbose:
print(*args, **kwargs)
def process_one_file(f):
print_if_verbose(f.name)
print_if_verbose('-' * len(f.name))
b = f.read()
flags, function = struct.unpack('<HH', b[:4])
if opnum is not None and opnum != function:
return
t = TYPES[flags & TYPE_MASK]
if ndr_type and ndr_type != t:
return
payload = b[4:]
data64 = b64encode(payload).decode('utf-8')
cmd = ['bin/ndrdump',
pipe,
str(function),
t,
'--base64-input',
'--input', data64,
]
for flag, name, option in FLAGS:
if flags & flag:
print_if_verbose("flag: %s" % name)
cmd.append(option)
print_if_verbose("length: %d\n" % len(payload))
print(' '.join(cmd))
print_if_verbose()
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-p', '--pipe', default='$PIPE',
help='pipe name (for output command line)')
parser.add_argument('-t', '--type', default=None, choices=TYPES,
help='restrict to this type')
parser.add_argument('-o', '--opnum', default=None, type=int,
help='restrict to this function/struct number')
parser.add_argument('FILES', nargs='*', default=(),
help="read from these files")
parser.add_argument('-k', '--ignore-errors', action='store_true',
help='do not stop on errors')
parser.add_argument('-v', '--verbose', action='store_true',
help='say more')
parser.add_argument('-H', '--honggfuzz-file',
help="extract crashes from this honggfuzz report")
parser.add_argument('-f', '--crash-filter',
help="only print crashes matching this rexexp")
args = parser.parse_args()
global pipe, opnum, ndr_type, verbose
pipe = args.pipe
opnum = args.opnum
ndr_type = args.type
verbose = args.verbose
if not args.FILES and not args.honggfuzz_file:
parser.print_usage()
sys.exit(1)
for fn in args.FILES:
if args.crash_filter is not None:
if not re.search(args.crash_filter, fn):
print_if_verbose(f"skipping {fn}")
continue
try:
if fn == '-':
process_one_file(sys.stdin)
else:
with open(fn, 'rb') as f:
process_one_file(f)
except Exception:
print_if_verbose("Error processing %s\n" % fn)
if args.ignore_errors:
continue
raise
if args.honggfuzz_file:
print_if_verbose(f"looking at {args.honggfuzz_file}")
with open(args.honggfuzz_file) as f:
pipe = None
crash = None
for line in f:
m = re.match(r'^\s*fuzzTarget\s*:\s*bin/fuzz_ndr_(\w+)\s*$', line)
if m:
pipe = m.group(1).split('_TYPE_', 1)[0]
print_if_verbose(f"found pipe {pipe}")
m = re.match(r'^FUZZ_FNAME: (\S+)$', line)
if m:
crash = m.group(1)
if args.crash_filter is not None:
if not re.search(args.crash_filter, crash):
print_if_verbose(f"skipping {crash}")
pipe = None
crash = None
continue
print_if_verbose(f"found crash {crash}")
if pipe is not None and crash is not None:
with open(crash, 'rb') as f:
process_one_file(f)
pipe = None
crash = None
main()
|