3 # Interpret a file that crashes an fuzz_ndr_X binary.
5 # Copyright (C) Catalyst IT Ltd. 2019
10 from base64 import b64encode
16 TYPES = ['struct', 'in', 'out']
19 (4, 'ndr64', '--ndr64'),
23 def print_if_verbose(*args, **kwargs):
25 print(*args, **kwargs)
28 def process_one_file(f):
29 print_if_verbose(f.name)
30 print_if_verbose('-' * len(f.name))
33 flags, function = struct.unpack('<HH', b[:4])
34 if opnum is not None and opnum != function:
37 t = TYPES[flags & TYPE_MASK]
38 if ndr_type and ndr_type != t:
42 data64 = b64encode(payload).decode('utf-8')
52 for flag, name, option in FLAGS:
54 print_if_verbose("flag: %s" % name)
57 print_if_verbose("length: %d\n" % len(payload))
63 parser = argparse.ArgumentParser()
64 parser.add_argument('-p', '--pipe', default=None,
65 help=('pipe name (for output command line, '
66 'default is a guess or "$PIPE")'))
67 parser.add_argument('-t', '--type', default=None, choices=TYPES,
68 help='restrict to this type')
69 parser.add_argument('-o', '--opnum', default=None, type=int,
70 help='restrict to this function/struct number')
71 parser.add_argument('FILES', nargs='*', default=(),
72 help="read from these files")
73 parser.add_argument('-k', '--ignore-errors', action='store_true',
74 help='do not stop on errors')
75 parser.add_argument('-v', '--verbose', action='store_true',
77 parser.add_argument('-H', '--honggfuzz-file',
78 help="extract crashes from this honggfuzz report")
79 parser.add_argument('-f', '--crash-filter',
80 help="only print crashes matching this rexexp")
82 args = parser.parse_args()
84 global pipe, opnum, ndr_type, verbose
88 verbose = args.verbose
90 if not args.FILES and not args.honggfuzz_file:
96 m = re.search(r'clusterfuzz-testcase.+-fuzz_ndr_([a-z]+)', fn)
102 if args.crash_filter is not None:
103 if not re.search(args.crash_filter, fn):
104 print_if_verbose(f"skipping {fn}")
108 process_one_file(sys.stdin)
110 with open(fn, 'rb') as f:
113 print_if_verbose("Error processing %s\n" % fn)
114 if args.ignore_errors:
118 if args.honggfuzz_file:
119 print_if_verbose(f"looking at {args.honggfuzz_file}")
120 with open(args.honggfuzz_file) as f:
124 m = re.match(r'^\s*fuzzTarget\s*:\s*bin/fuzz_ndr_(\w+)\s*$', line)
126 pipe = m.group(1).split('_TYPE_', 1)[0]
127 print_if_verbose(f"found pipe {pipe}")
128 m = re.match(r'^FUZZ_FNAME: (\S+)$', line)
131 if args.crash_filter is not None:
132 if not re.search(args.crash_filter, crash):
133 print_if_verbose(f"skipping {crash}")
137 print_if_verbose(f"found crash {crash}")
138 if pipe is not None and crash is not None:
139 with open(crash, 'rb') as f: