import random def fuzzer(min_length=0, max_length=100, char_start=32, char_range=32): """A string of up to `max_length` characters in the range [`char_start`, `char_start` + `char_range`]""" string_length = random.randrange(min_length, max_length + 1) out = "" for i in range(0, string_length): out += chr(random.randrange(char_start, char_start + char_range)) return out
import os import random import tempfile import subprocess
def fuzzer(min_length=0, max_length=100, char_start=32, char_range=32): """A string of up to `max_length` characters in the range [`char_start`, `char_start` + `char_range`]""" string_length = random.randrange(min_length, max_length + 1) out = "" for i in range(0, string_length): out += chr(random.randrange(char_start, char_start + char_range)) return out def tmpfile(): basename = "input.txt" tempdir = tempfile.mkdtemp() FILE = os.path.join(tempdir, basename) print(FILE) return FILE
def fuzz(num = 100): data = fuzzer() FILE = tmpfile() with open(FILE, "w") as f: f.write(data) runs = [] program = "bc" for i in range(num): data = fuzzer() with open(FILE, "w") as f: f.write(data) result = subprocess.run([program, FILE], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) runs.append((data, result)) print(sum(1 for (data, result) in runs if result.stderr == "")) print(sum(1 for (data, result) in runs if result.returncode != 0)) errors = [(data, result) for (data, result) in runs if result.stderr != ""] (first_data, first_result) = errors[0] print(repr(first_data)) print(first_result.stderr)
from fuzzingbook.ExpectError import ExpectError import random def fuzzer(min_length=0, max_length=100, char_start=32, char_range=32): """A string of up to `max_length` characters in the range [`char_start`, `char_start` + `char_range`]""" string_length = random.randrange(min_length, max_length + 1) out = "" for i in range(0, string_length): out += chr(random.randrange(char_start, char_start + char_range)) return out def crash_if_too_long(s): buffer = "Thursday" if len(s) > len(buffer): raise ValueError
trials = 100 with ExpectError(): for i in range(trials): s = fuzzer() crash_if_too_long(s)
import random def fuzzer(min_length=0, max_length=100, char_start=32, char_range=32): """A string of up to `max_length` characters in the range [`char_start`, `char_start` + `char_range`]""" string_length = random.randrange(min_length, max_length + 1) out = "" for i in range(0, string_length): out += chr(random.randrange(char_start, char_start + char_range)) return out
def hang_if_no_space(s): i = 0 while True: if i < len(s): if s[i] == ' ': break i += 1
trials = 100 with ExpectTimeout(2): for i in range(trials): s = fuzzer() hang_if_no_space(s)
def collapse_if_too_large(s): if int(s) > 1000: raise ValueError
Fuzz:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
from fuzzingbook.ExpectError import ExpectError import random def fuzzer(min_length=0, max_length=100, char_start=32, char_range=32): """A string of up to `max_length` characters in the range [`char_start`, `char_start` + `char_range`]""" string_length = random.randrange(min_length, max_length + 1) out = "" for i in range(0, string_length): out += chr(random.randrange(char_start, char_start + char_range)) return out def collapse_if_too_large(s): if int(s) > 1000: raise ValueError long_number = fuzzer(0,100, ord('0'), 10) print(long_number) with ExpectError(): collapse_if_too_large(long_number)
./program 110 ================================================================= ==51215==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60b0000000ae at pc 0x000109f7deef bp 0x7ffee5c854d0 sp 0x7ffee5c854c8 READ of size 1 at 0x60b0000000ae thread T0 #0 0x109f7deee in main program.c:12 #1 0x7fff204f6f3c in start+0x0 (libdyld.dylib:x86_64+0x15f3c)
0x60b0000000ae is located 10 bytes to the right of 100-byte region [0x60b000000040,0x60b0000000a4) allocated by thread T0 here: #0 0x109fd54c0 in wrap_malloc+0xa0 (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x484c0) #1 0x109f7de3f in main program.c:7 #2 0x7fff204f6f3c in start+0x0 (libdyld.dylib:x86_64+0x15f3c)
SUMMARY: AddressSanitizer: heap-buffer-overflow program.c:12 in main Shadow bytes around the buggy address: 0x1c15ffffffc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x1c15ffffffd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x1c15ffffffe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x1c15fffffff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x1c1600000000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00 =>0x1c1600000010: 00 00 00 00 04[fa]fa fa fa fa fa fa fa fa fa fa 0x1c1600000020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x1c1600000030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x1c1600000040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x1c1600000050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x1c1600000060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb Shadow gap: cc ==51215==ABORTING [1] 51215 abort ./program 110
import random def fuzzer(min_length=0, max_length=100, char_start=32, char_range=32): """A string of up to `max_length` characters in the range [`char_start`, `char_start` + `char_range`]""" string_length = random.randrange(min_length, max_length + 1) out = "" for i in range(0, string_length): out += chr(random.randrange(char_start, char_start + char_range)) return out
def heartbeat(reply, length, memory): # Store reply in memory memory = reply + memory[len(reply):]
# Send back heartbeat s = "" for i in range(length): s += memory[i] return s
from fuzzingbook.ExpectError import ExpectError import random def fuzzer(min_length=0, max_length=100, char_start=32, char_range=32): """A string of up to `max_length` characters in the range [`char_start`, `char_start` + `char_range`]""" string_length = random.randrange(min_length, max_length + 1) out = "" for i in range(0, string_length): out += chr(random.randrange(char_start, char_start + char_range)) return out
def heartbeat(reply, length, memory): # Store reply in memory memory = reply + memory[len(reply):]
# Send back heartbeat s = "" for i in range(length): s += memory[i] return s
secrets = ("<space for reply>" + fuzzer(0,100) + "<secret-certificate>" + fuzzer(0,100) + "<secret-key>" + fuzzer(0,100) + "<other-secrets>") uninitialized_memory_marker = "deadbeef" while len(secrets) < 2048: secrets += uninitialized_memory_marker with ExpectError(): for i in range(10): s = heartbeat(fuzzer(), random.randint(1, 500), memory=secrets) assert not s.find(uninitialized_memory_marker) assert not s.find("secret") ''' Traceback (most recent call last): File "exp.py", line 32, in <module> assert not s.find(uninitialized_memory_marker) AssertionError (expected) '''
import subporcess class ProgramRunner(Runner): def __init__(self, program): """Initialize. `program` is a program spec as passed to `subprocess.run()`""" self.program = program
def run_process(self, inp=""): """Run the program with `inp` as input. Return result of `subprocess.run()`.""" return subprocess.run(self.program, input=inp, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
def run(self, inp=""): """Run the program with `inp` as input. Return test outcome based on result of `subprocess.run()`.""" result = self.run_process(inp)
class BinaryProgramRunner(ProgramRunner): def run_process(self, inp=""): """Run the program with `inp` as input. Return result of `subprocess.run()`.""" return subprocess.run(self.program, input=inp.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
def run(self, runner=Runner()): """Run `runner` with fuzz input""" return runner.run(self.fuzz())
def runs(self, runner=PrintRunner(), trials=10): """Run `runner` with fuzz input, `trials` times""" # Note: the list comprehension below does not invoke self.run() for subclasses # return [self.run(runner) for i in range(trials)] outcomes = [] for i in range(trials): outcomes.append(self.run(runner)) return outcomes
对于RandomFuzzer,只需要继承Fuzzer类,并是吸纳对应的fuzz方法即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
class RandomFuzzer(Fuzzer): def __init__(self, min_length=10, max_length=100, char_start=32, char_range=32): """Produce strings of `min_length` to `max_length` characters in the range [`char_start`, `char_start` + `char_range`]""" self.min_length = min_length self.max_length = max_length self.char_start = char_start self.char_range = char_range
def fuzz(self): string_length = random.randrange(self.min_length, self.max_length + 1) out = "" for i in range(0, string_length): out += chr(random.randrange(self.char_start, self.char_start + self.char_range)) return out
使用如下:
1 2 3
random_fuzzer = RandomFuzzer(min_length=20, max_length=20) for i in range(10): print(random_fuzzer.fuzz())