{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "# Greybox Fuzzing\n", "\n", "In the [previous chapter](MutationFuzzer.ipynb), we have introduced _mutation-based fuzzing_, a technique that generates fuzz inputs by applying small mutations to given inputs. In this chapter, we show how to _guide_ these mutations towards specific goals such as coverage. The algorithms in this chapter stem from the popular [American Fuzzy Lop](http://lcamtuf.coredump.cx/afl/) (AFL) fuzzer, in particular from its [AFLFast](https://github.com/mboehme/aflfast) and [AFLGo](https://github.com/aflgo/aflgo) flavors. We will explore the greybox fuzzing algorithm behind AFL and how we can exploit it to solve various problems for automated vulnerability detection." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.027976Z", "iopub.status.busy": "2024-01-18T17:14:42.027573Z", "iopub.status.idle": "2024-01-18T17:14:42.088449Z", "shell.execute_reply": "2024-01-18T17:14:42.088100Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from bookutils import YouTubeVideo\n", "YouTubeVideo('vBrNT9q2t1Y')" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "**Prerequisites**\n", "\n", "* Reading the introduction on [mutation-based fuzzing](MutationFuzzer.ipynb) is recommended." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "## Synopsis\n", "\n", "\n", "To [use the code provided in this chapter](Importing.ipynb), write\n", "\n", "```python\n", ">>> from fuzzingbook.GreyboxFuzzer import \n", "```\n", "\n", "and then make use of the following features.\n", "\n", "\n", "This chapter introduces advanced methods for grey-box fuzzing inspired by the popular AFL fuzzer. The `GreyboxFuzzer` class has three arguments. First, a list of seed inputs:\n", "\n", "```python\n", ">>> seed_input = \"http://www.google.com/search?q=fuzzing\"\n", ">>> seeds = [seed_input]\n", "```\n", "Second, a _mutator_ that changes individual parts of the input.\n", "\n", "```python\n", ">>> mutator = Mutator()\n", "```\n", "Third, a _power schedule_ that assigns fuzzing effort across the population:\n", "\n", "```python\n", ">>> schedule = PowerSchedule()\n", "```\n", "These three go into the `GreyboxFuzzer` constructor:\n", "\n", "```python\n", ">>> greybox_fuzzer = GreyboxFuzzer(seeds=seeds, mutator=mutator, schedule=schedule)\n", "```\n", "The `GreyboxFuzzer` class is used in conjunction with a `FunctionCoverageRunner`:\n", "\n", "```python\n", ">>> http_runner = FunctionCoverageRunner(http_program)\n", ">>> outcomes = greybox_fuzzer.runs(http_runner, trials=10000)\n", "```\n", "After fuzzing, we can inspect the population:\n", "\n", "```python\n", ">>> greybox_fuzzer.population[:20]\n", "[http://www.google.com/search?q=fuzzing,\n", " htpp://w.gfoogne.com/seaRchw?q=fuzzng,\n", " http://ww.google.com/search?q=furzing,\n", " xvtp:{/www.foogle.com/seapch=q=uzzing,\n", " ht:w/ww.goog\\l*#m/seaXrceh?bq=rzilgl,\n", " http:/wwwgoogne.coe/seazch?q=uzzin&,\n", " h|t4p://.PgoolL.com/sdrh?Qq=Fuzi,\n", " htzBjQex/c&*ieq=08T,\n", " vpe7pJ5/nb/nUc{jICo-m /gacw0qmfQuzz#ng,\n", " htpp://w.gf/ogn.#m/seaRchw?q=fuzzng,\n", " J;w?G/&!7jwA,j7/o!fL\u0003mX\\gh?2-bz@\"i,\n", " ;?p8.gnoox\u0007n%ncooms'aRhqW=f\"Dzg]\n", "```\n", "Besides the simple `PowerSchedule`, we can have advanced power schedules.\n", "\n", "* `AFLFastSchedule` assigns high energy to \"unusual\" paths not taken very often.\n", "* `AFLGoSchedule` assigns high energy to paths close to uncovered program locations. \n", "\n", "The `AFLGoSchedule` class constructor requires a `distance` metric from each node towards target locations, as determined via analysis of the program code. See the chapter for details.\n", "\n", "![](PICS/GreyboxFuzzer-synopsis-1.svg)\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": true, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## AFL: An Effective Greybox Fuzzer\n", "\n", "The algorithms in this chapter stem from the popular [American Fuzzy Lop](http://lcamtuf.coredump.cx/afl/) (AFL) fuzzer." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": true, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "AFL is a *mutation-based fuzzer*. Meaning, AFL generates new inputs by slightly modifying a seed input (i.e., mutation), or by joining the first half of one input with the second half of another (i.e., splicing)." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": true, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "AFL is also a *greybox fuzzer* (not blackbox nor whitebox). Meaning, AFL leverages coverage-feedback to learn how to reach deeper into the program. It is not entirely blackbox because AFL leverages at least *some* program analysis. It is not entirely whitebox either because AFL does not build on heavyweight program analysis or constraint solving. Instead, AFL uses lightweight program instrumentation to glean some information about the (branch) coverage of a generated input.\n", "If a generated input increases coverage, it is added to the seed corpus for further fuzzing." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": { "button": false, "new_sheet": true, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "To instrument a program, AFL injects a piece of code right after every conditional jump instruction. When executed, this so-called trampoline assigns the exercised branch a unique identifier and increments a counter that is associated with this branch. For efficiency, only a coarse branch hit count is maintained. In other words, for each input the fuzzer knows which branches and roughly how often they are exercised. \n", "The instrumentation is usually done at compile-time, i.e., when the program source code is compiled to an executable binary. However, it is possible to run AFL on non-instrumented binaries using tools such as a virtual machine (e.g., [QEMU](https://github.com/mirrorer/afl/blob/master/qemu_mode)) or a dynamic instrumentation tool (e.g., [Intel PinTool](https://github.com/vanhauser-thc/afl-pin)). For Python programs, we can collect coverage information without any instrumentation (see chapter on [collecting coverage](Coverage.ipynb#Coverage-of-Basic-Fuzzing))." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": true, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## Ingredients for Greybox Fuzzing\n", "\n", "We start with discussing the most important parts we need for mutational testing and goal guidance." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": true, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "### Mutators\n", "\n", "We introduce specific classes for mutating a seed." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.111074Z", "iopub.status.busy": "2024-01-18T17:14:42.110893Z", "iopub.status.idle": "2024-01-18T17:14:42.113041Z", "shell.execute_reply": "2024-01-18T17:14:42.112711Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import bookutils.setup" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.114604Z", "iopub.status.busy": "2024-01-18T17:14:42.114488Z", "iopub.status.idle": "2024-01-18T17:14:42.116196Z", "shell.execute_reply": "2024-01-18T17:14:42.115914Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from typing import List, Set, Any, Tuple, Dict, Union\n", "from collections.abc import Sequence" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.117733Z", "iopub.status.busy": "2024-01-18T17:14:42.117631Z", "iopub.status.idle": "2024-01-18T17:14:42.119159Z", "shell.execute_reply": "2024-01-18T17:14:42.118909Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import random" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.120503Z", "iopub.status.busy": "2024-01-18T17:14:42.120421Z", "iopub.status.idle": "2024-01-18T17:14:42.463447Z", "shell.execute_reply": "2024-01-18T17:14:42.462126Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from Coverage import population_coverage" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "First, we'll introduce the `Mutator` class. Given a seed input `inp`, the mutator returns a slightly modified version of `inp`. In the [chapter on greybox grammar fuzzing](GreyboxGrammarFuzzer.ipynb), we extend this class to consider the input grammar for smart greybox fuzzing." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.469784Z", "iopub.status.busy": "2024-01-18T17:14:42.469057Z", "iopub.status.idle": "2024-01-18T17:14:42.479241Z", "shell.execute_reply": "2024-01-18T17:14:42.478414Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class Mutator:\n", " \"\"\"Mutate strings\"\"\"\n", "\n", " def __init__(self) -> None:\n", " \"\"\"Constructor\"\"\"\n", " self.mutators = [\n", " self.delete_random_character,\n", " self.insert_random_character,\n", " self.flip_random_character\n", " ]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "For insertion, we add a random character in a random position." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.483622Z", "iopub.status.busy": "2024-01-18T17:14:42.483491Z", "iopub.status.idle": "2024-01-18T17:14:42.485666Z", "shell.execute_reply": "2024-01-18T17:14:42.485370Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class Mutator(Mutator):\n", " def insert_random_character(self, s: str) -> str:\n", " \"\"\"Returns s with a random character inserted\"\"\"\n", " pos = random.randint(0, len(s))\n", " random_character = chr(random.randrange(32, 127))\n", " return s[:pos] + random_character + s[pos:]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "For deletion, if the string is non-empty choose a random position and delete the character. Otherwise, use the insertion-operation." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.487198Z", "iopub.status.busy": "2024-01-18T17:14:42.487090Z", "iopub.status.idle": "2024-01-18T17:14:42.489229Z", "shell.execute_reply": "2024-01-18T17:14:42.488932Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class Mutator(Mutator):\n", " def delete_random_character(self, s: str) -> str:\n", " \"\"\"Returns s with a random character deleted\"\"\"\n", " if s == \"\":\n", " return self.insert_random_character(s)\n", "\n", " pos = random.randint(0, len(s) - 1)\n", " return s[:pos] + s[pos + 1:]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "For substitution, if the string is non-empty choose a random position and flip a random bit in the character. Otherwise, use the insertion-operation." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.490812Z", "iopub.status.busy": "2024-01-18T17:14:42.490698Z", "iopub.status.idle": "2024-01-18T17:14:42.492991Z", "shell.execute_reply": "2024-01-18T17:14:42.492737Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class Mutator(Mutator):\n", " def flip_random_character(self, s: str) -> str:\n", " \"\"\"Returns s with a random bit flipped in a random position\"\"\"\n", " if s == \"\":\n", " return self.insert_random_character(s)\n", "\n", " pos = random.randint(0, len(s) - 1)\n", " c = s[pos]\n", " bit = 1 << random.randint(0, 6)\n", " new_c = chr(ord(c) ^ bit)\n", " return s[:pos] + new_c + s[pos + 1:]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The main method is `mutate` which chooses a random mutation operator from the list of operators." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.494655Z", "iopub.status.busy": "2024-01-18T17:14:42.494531Z", "iopub.status.idle": "2024-01-18T17:14:42.496497Z", "shell.execute_reply": "2024-01-18T17:14:42.496238Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class Mutator(Mutator):\n", " def mutate(self, inp: Any) -> Any: # can be str or Seed (see below)\n", " \"\"\"Return s with a random mutation applied. Can be overloaded in subclasses.\"\"\"\n", " mutator = random.choice(self.mutators)\n", " return mutator(inp)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Let's try the mutator. You can actually interact with such a \"cell\" and try other inputs by loading this chapter as Jupyter notebook. After opening, run all cells in the notebook using \"Kernel -> Restart & Run All\"." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.497987Z", "iopub.status.busy": "2024-01-18T17:14:42.497883Z", "iopub.status.idle": "2024-01-18T17:14:42.500064Z", "shell.execute_reply": "2024-01-18T17:14:42.499796Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'cood'" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Mutator().mutate(\"good\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" }, "tags": [] }, "source": [ "### Seeds and Power Schedules\n", "\n", "Now we introduce a new concept; the *power schedule*. A power schedule distributes the precious fuzzing time among the seeds in the population. Our objective is to maximize the time spent fuzzing those (most progressive) seeds which lead to higher coverage increase in shorter time.\n", "\n", "We call the likelihood with which a seed is chosen from the population as the seed's *energy*. Throughout a fuzzing campaign, we would like to prioritize seeds that are more promising. Simply said, we do not want to waste energy fuzzing non-progressive seeds. We call the procedure that decides a seed's energy as the fuzzer's *power schedule*. For instance, AFL's schedule assigns more energy to seeds that are shorter, that execute faster, and yield coverage increases more often.\n", "\n", "First, there is some information that we need to attach to each seed in addition to the seed's data. Hence, we define the following `Seed` class." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.501632Z", "iopub.status.busy": "2024-01-18T17:14:42.501508Z", "iopub.status.idle": "2024-01-18T17:14:42.503079Z", "shell.execute_reply": "2024-01-18T17:14:42.502830Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from Coverage import Location" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.504754Z", "iopub.status.busy": "2024-01-18T17:14:42.504648Z", "iopub.status.idle": "2024-01-18T17:14:42.506939Z", "shell.execute_reply": "2024-01-18T17:14:42.506674Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class Seed:\n", " \"\"\"Represent an input with additional attributes\"\"\"\n", "\n", " def __init__(self, data: str) -> None:\n", " \"\"\"Initialize from seed data\"\"\"\n", " self.data = data\n", "\n", " # These will be needed for advanced power schedules\n", " self.coverage: Set[Location] = set()\n", " self.distance: Union[int, float] = -1\n", " self.energy = 0.0\n", "\n", " def __str__(self) -> str:\n", " \"\"\"Returns data as string representation of the seed\"\"\"\n", " return self.data\n", "\n", " __repr__ = __str__" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The power schedule that is implemented below assigns each seed the same energy. Once a seed is in the population, it will be fuzzed as often as any other seed in the population.\n", "\n", "In Python, we can squeeze long for-loops into much smaller statements.\n", "* `lambda x: ...` returns a function that takes `x` as input. Lambda allows for quick definitions unnamed functions.\n", "* `map(f, l)` returns a list where the function `f` is applied to each element in list `l`.\n", "* `random.choices(l, weights)[0]` returns element `l[i]` with probability in `weights[i]`." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.508528Z", "iopub.status.busy": "2024-01-18T17:14:42.508410Z", "iopub.status.idle": "2024-01-18T17:14:42.511593Z", "shell.execute_reply": "2024-01-18T17:14:42.511265Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class PowerSchedule:\n", " \"\"\"Define how fuzzing time should be distributed across the population.\"\"\"\n", "\n", " def __init__(self) -> None:\n", " \"\"\"Constructor\"\"\"\n", " self.path_frequency: Dict = {}\n", "\n", " def assignEnergy(self, population: Sequence[Seed]) -> None:\n", " \"\"\"Assigns each seed the same energy\"\"\"\n", " for seed in population:\n", " seed.energy = 1\n", "\n", " def normalizedEnergy(self, population: Sequence[Seed]) -> List[float]:\n", " \"\"\"Normalize energy\"\"\"\n", " energy = list(map(lambda seed: seed.energy, population))\n", " sum_energy = sum(energy) # Add up all values in energy\n", " assert sum_energy != 0\n", " norm_energy = list(map(lambda nrg: nrg / sum_energy, energy))\n", " return norm_energy\n", "\n", " def choose(self, population: Sequence[Seed]) -> Seed:\n", " \"\"\"Choose weighted by normalized energy.\"\"\"\n", " self.assignEnergy(population)\n", " norm_energy = self.normalizedEnergy(population)\n", " seed: Seed = random.choices(population, weights=norm_energy)[0]\n", " return seed" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Let's see whether this power schedule chooses seeds uniformly at random. We ask the schedule 10k times to choose a seed from the population of three seeds (A, B, C) and keep track of the number of times we have seen each seed. We should see each seed about 3.3k times." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.513115Z", "iopub.status.busy": "2024-01-18T17:14:42.513027Z", "iopub.status.idle": "2024-01-18T17:14:42.514878Z", "shell.execute_reply": "2024-01-18T17:14:42.514601Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "population = [Seed(\"A\"), Seed(\"B\"), Seed(\"C\")]\n", "schedule = PowerSchedule()\n", "hits = {\n", " \"A\": 0,\n", " \"B\": 0,\n", " \"C\": 0\n", "}" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.516355Z", "iopub.status.busy": "2024-01-18T17:14:42.516265Z", "iopub.status.idle": "2024-01-18T17:14:42.538376Z", "shell.execute_reply": "2024-01-18T17:14:42.538126Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "for i in range(10000):\n", " seed = schedule.choose(population)\n", " hits[seed.data] += 1" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.539917Z", "iopub.status.busy": "2024-01-18T17:14:42.539825Z", "iopub.status.idle": "2024-01-18T17:14:42.541992Z", "shell.execute_reply": "2024-01-18T17:14:42.541695Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "{'A': 3387, 'B': 3255, 'C': 3358}" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hits" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Looks good. Every seed has been chosen about a third of the time." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Runners and a Sample Program\n", "\n", "We'll start with a small sample program of six lines. In order to collect coverage information during execution, we import the `FunctionCoverageRunner` class from the chapter on [mutation-based fuzzing](MutationFuzzer.ipynb#Guiding-by-Coverage). \n", "\n", "The `FunctionCoverageRunner` constructor takes a Python `function` to execute. The function `run` takes an input, passes it on to the Python `function`, and collects the coverage information for this execution. The function `coverage()` returns a list of tuples `(function name, line number)` for each statement that has been covered in the Python `function`." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.543780Z", "iopub.status.busy": "2024-01-18T17:14:42.543661Z", "iopub.status.idle": "2024-01-18T17:14:42.555975Z", "shell.execute_reply": "2024-01-18T17:14:42.555699Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from MutationFuzzer import FunctionCoverageRunner, http_program" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The `crashme()` function raises an exception for the input \"bad!\". Let's see which statements are covered for the input \"good\"." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.557666Z", "iopub.status.busy": "2024-01-18T17:14:42.557582Z", "iopub.status.idle": "2024-01-18T17:14:42.559648Z", "shell.execute_reply": "2024-01-18T17:14:42.559389Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "def crashme(s: str) -> None:\n", " if len(s) > 0 and s[0] == 'b':\n", " if len(s) > 1 and s[1] == 'a':\n", " if len(s) > 2 and s[2] == 'd':\n", " if len(s) > 3 and s[3] == '!':\n", " raise Exception()" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.561171Z", "iopub.status.busy": "2024-01-18T17:14:42.561081Z", "iopub.status.idle": "2024-01-18T17:14:42.563364Z", "shell.execute_reply": "2024-01-18T17:14:42.563123Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "[('run_function', 132), ('crashme', 2)]" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "crashme_runner = FunctionCoverageRunner(crashme)\n", "crashme_runner.run(\"good\")\n", "list(crashme_runner.coverage())" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "In `crashme`, the input \"good\" only covers the if-statement in line 2. The branch condition `len(s) > 0 and s[0] == 'b'` evaluates to False." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Advanced Blackbox Mutation-based Fuzzing\n", "\n", "Let's integrate both the mutator and power schedule into a fuzzer. We'll start with a blackbox fuzzer -- which does *not* leverage any coverage information. \n", "\n", "Our `AdvancedMutationFuzzer` class is an advanced and _parameterized_ version of the `MutationFuzzer` class from the [chapter on mutation-based fuzzing](MutationFuzzer.ipynb). It also inherits from the [Fuzzer](Fuzzer.ipynb#Fuzzer-Classes) class. For now, we only need to know the functions `fuzz()` which returns a generated input and `runs()` which executes `fuzz()` a specified number of times. For our `AdvancedMutationFuzzer` class, we override the function `fuzz()`." ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.564948Z", "iopub.status.busy": "2024-01-18T17:14:42.564845Z", "iopub.status.idle": "2024-01-18T17:14:42.566399Z", "shell.execute_reply": "2024-01-18T17:14:42.566168Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from Fuzzer import Fuzzer" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The `AdvancedMutationFuzzer` is constructed with a set of initial seeds, a mutator, and a power schedule. Throughout the fuzzing campaign, it maintains a seed corpus called `population`. The function `fuzz` returns either an unfuzzed seed from the initial seeds, or the result of fuzzing a seed in the population. The function `create_candidate` handles the latter. It randomly chooses an input from the population and applies a number of mutations." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.567850Z", "iopub.status.busy": "2024-01-18T17:14:42.567768Z", "iopub.status.idle": "2024-01-18T17:14:42.571170Z", "shell.execute_reply": "2024-01-18T17:14:42.570923Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class AdvancedMutationFuzzer(Fuzzer):\n", " \"\"\"Base class for mutation-based fuzzing.\"\"\"\n", "\n", " def __init__(self, seeds: List[str],\n", " mutator: Mutator,\n", " schedule: PowerSchedule) -> None:\n", " \"\"\"Constructor.\n", " `seeds` - a list of (input) strings to mutate.\n", " `mutator` - the mutator to apply.\n", " `schedule` - the power schedule to apply.\n", " \"\"\"\n", " self.seeds = seeds\n", " self.mutator = mutator\n", " self.schedule = schedule\n", " self.inputs: List[str] = []\n", " self.reset()\n", "\n", " def reset(self) -> None:\n", " \"\"\"Reset the initial population and seed index\"\"\"\n", " self.population = list(map(lambda x: Seed(x), self.seeds))\n", " self.seed_index = 0\n", "\n", " def create_candidate(self) -> str:\n", " \"\"\"Returns an input generated by fuzzing a seed in the population\"\"\"\n", " seed = self.schedule.choose(self.population)\n", "\n", " # Stacking: Apply multiple mutations to generate the candidate\n", " candidate = seed.data\n", " trials = min(len(candidate), 1 << random.randint(1, 5))\n", " for i in range(trials):\n", " candidate = self.mutator.mutate(candidate)\n", " return candidate\n", "\n", " def fuzz(self) -> str:\n", " \"\"\"Returns first each seed once and then generates new inputs\"\"\"\n", " if self.seed_index < len(self.seeds):\n", " # Still seeding\n", " self.inp = self.seeds[self.seed_index]\n", " self.seed_index += 1\n", " else:\n", " # Mutating\n", " self.inp = self.create_candidate()\n", "\n", " self.inputs.append(self.inp)\n", " return self.inp" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Okay, let's take the mutation fuzzer for a spin. Given a single seed, we ask it to generate three inputs." ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.572750Z", "iopub.status.busy": "2024-01-18T17:14:42.572668Z", "iopub.status.idle": "2024-01-18T17:14:42.574753Z", "shell.execute_reply": "2024-01-18T17:14:42.574493Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "good\n", "gDoodC\n", "/\n" ] } ], "source": [ "seed_input = \"good\"\n", "mutation_fuzzer = AdvancedMutationFuzzer([seed_input], Mutator(), PowerSchedule())\n", "print(mutation_fuzzer.fuzz())\n", "print(mutation_fuzzer.fuzz())\n", "print(mutation_fuzzer.fuzz())" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Let's see how many statements the mutation-based blackbox fuzzer covers in a campaign with n=30k inputs.\n", "\n", "The fuzzer function `runs(crashme_runner, trials=n)` generates `n` inputs and executes them on the `crashme` function via the `crashme_runner`. As stated earlier, the `crashme_runner` also collects coverage information." ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.576275Z", "iopub.status.busy": "2024-01-18T17:14:42.576189Z", "iopub.status.idle": "2024-01-18T17:14:42.577778Z", "shell.execute_reply": "2024-01-18T17:14:42.577438Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import time" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.579230Z", "iopub.status.busy": "2024-01-18T17:14:42.579143Z", "iopub.status.idle": "2024-01-18T17:14:42.580571Z", "shell.execute_reply": "2024-01-18T17:14:42.580338Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "n = 30000" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.581958Z", "iopub.status.busy": "2024-01-18T17:14:42.581856Z", "iopub.status.idle": "2024-01-18T17:14:42.920566Z", "shell.execute_reply": "2024-01-18T17:14:42.920275Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "'It took the blackbox mutation-based fuzzer 0.34 seconds to generate and execute 30000 inputs.'" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "blackbox_fuzzer = AdvancedMutationFuzzer([seed_input], Mutator(), PowerSchedule())\n", "\n", "start = time.time()\n", "blackbox_fuzzer.runs(FunctionCoverageRunner(crashme), trials=n)\n", "end = time.time()\n", "\n", "\"It took the blackbox mutation-based fuzzer %0.2f seconds to generate and execute %d inputs.\" % (end - start, n)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "In order to measure coverage, we import the [population_coverage](Coverage.ipynb#Coverage-of-Basic-Fuzzing) function. It takes a set of inputs and a Python function, executes the inputs on that function and collects coverage information. Specifically, it returns a tuple `(all_coverage, cumulative_coverage)` where `all_coverage` is the set of statements covered by all inputs, and `cumulative_coverage` is the number of statements covered as the number of executed inputs increases. We are just interested in the latter to plot coverage over time." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We extract the generated inputs from the blackbox fuzzer and measure coverage as the number of inputs increases." ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.922392Z", "iopub.status.busy": "2024-01-18T17:14:42.922272Z", "iopub.status.idle": "2024-01-18T17:14:42.986512Z", "shell.execute_reply": "2024-01-18T17:14:42.986236Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'The blackbox mutation-based fuzzer achieved a maximum coverage of 2 statements.'" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "_, blackbox_coverage = population_coverage(blackbox_fuzzer.inputs, crashme)\n", "bb_max_coverage = max(blackbox_coverage)\n", "\n", "\"The blackbox mutation-based fuzzer achieved a maximum coverage of %d statements.\" % bb_max_coverage" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The following generated inputs increased the coverage for our `crashme` [example](#Runner-and-Sample-Program)." ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.988136Z", "iopub.status.busy": "2024-01-18T17:14:42.988029Z", "iopub.status.idle": "2024-01-18T17:14:42.992178Z", "shell.execute_reply": "2024-01-18T17:14:42.991920Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "['good', 'bo']" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[seed_input] + \\\n", " [\n", " blackbox_fuzzer.inputs[idx] for idx in range(len(blackbox_coverage))\n", " if blackbox_coverage[idx] > blackbox_coverage[idx - 1]\n", " ]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "***Summary***. This is how a blackbox mutation-based fuzzer works. We have integrated the *mutator* to generate inputs by fuzzing a provided set of initial seeds and the *power schedule* to decide which seed to choose next." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Greybox Mutation-based Fuzzing\n", "\n", "In contrast to a blackbox fuzzer, a greybox fuzzer like [AFL](http://lcamtuf.coredump.cx/afl/) _does_ leverage coverage information. Specifically, a greybox fuzzer adds to the seed population generated inputs which increase code coverage.\n", "\n", "The method `run()` is inherited from the [Fuzzer](Fuzzer.ipynb#Fuzzer-Classes) class. It is called to generate and execute exactly one input. We override this function to add an input to the `population` that increases coverage. The greybox fuzzer attribute `coverages_seen` maintains the set of statements, that have previously been covered." ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.993717Z", "iopub.status.busy": "2024-01-18T17:14:42.993632Z", "iopub.status.idle": "2024-01-18T17:14:42.996292Z", "shell.execute_reply": "2024-01-18T17:14:42.996042Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class GreyboxFuzzer(AdvancedMutationFuzzer):\n", " \"\"\"Coverage-guided mutational fuzzing.\"\"\"\n", "\n", " def reset(self):\n", " \"\"\"Reset the initial population, seed index, coverage information\"\"\"\n", " super().reset()\n", " self.coverages_seen = set()\n", " self.population = [] # population is filled during greybox fuzzing\n", "\n", " def run(self, runner: FunctionCoverageRunner) -> Tuple[Any, str]: # type: ignore\n", " \"\"\"Run function(inp) while tracking coverage.\n", " If we reach new coverage,\n", " add inp to population and its coverage to population_coverage\n", " \"\"\"\n", " result, outcome = super().run(runner)\n", " new_coverage = frozenset(runner.coverage())\n", " if new_coverage not in self.coverages_seen:\n", " # We have new coverage\n", " seed = Seed(self.inp)\n", " seed.coverage = runner.coverage()\n", " self.coverages_seen.add(new_coverage)\n", " self.population.append(seed)\n", "\n", " return (result, outcome)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Let's take our greybox fuzzer for a spin." ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:42.997691Z", "iopub.status.busy": "2024-01-18T17:14:42.997613Z", "iopub.status.idle": "2024-01-18T17:14:43.335536Z", "shell.execute_reply": "2024-01-18T17:14:43.335243Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'It took the greybox mutation-based fuzzer 0.34 seconds to generate and execute 30000 inputs.'" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "seed_input = \"good\"\n", "greybox_fuzzer = GreyboxFuzzer([seed_input], Mutator(), PowerSchedule())\n", "\n", "start = time.time()\n", "greybox_fuzzer.runs(FunctionCoverageRunner(crashme), trials=n)\n", "end = time.time()\n", "\n", "\"It took the greybox mutation-based fuzzer %0.2f seconds to generate and execute %d inputs.\" % (end - start, n)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Does the greybox fuzzer cover more statements after generating the same number of test inputs?" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:43.337171Z", "iopub.status.busy": "2024-01-18T17:14:43.337062Z", "iopub.status.idle": "2024-01-18T17:14:43.405729Z", "shell.execute_reply": "2024-01-18T17:14:43.405455Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "'Our greybox mutation-based fuzzer covers 2 more statements'" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "_, greybox_coverage = population_coverage(greybox_fuzzer.inputs, crashme)\n", "gb_max_coverage = max(greybox_coverage)\n", "\n", "\"Our greybox mutation-based fuzzer covers %d more statements\" % (gb_max_coverage - bb_max_coverage)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Our seed population for our [example](#Runner-and-Sample-Program) now contains the following seeds." ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:43.407310Z", "iopub.status.busy": "2024-01-18T17:14:43.407221Z", "iopub.status.idle": "2024-01-18T17:14:43.409322Z", "shell.execute_reply": "2024-01-18T17:14:43.409064Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "[good, bo, baof, bad4u]" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "greybox_fuzzer.population" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Coverage-feedback is indeed helpful. The new seeds are like bread crumbs or milestones that guide the fuzzer to progress more quickly into deeper code regions. Following is a simple plot showing the coverage achieved over time for both fuzzers on our simple [example](#Runner-and-Sample-Program)." ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:43.410799Z", "iopub.status.busy": "2024-01-18T17:14:43.410714Z", "iopub.status.idle": "2024-01-18T17:14:43.415616Z", "shell.execute_reply": "2024-01-18T17:14:43.415373Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "%matplotlib inline" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:43.417061Z", "iopub.status.busy": "2024-01-18T17:14:43.416980Z", "iopub.status.idle": "2024-01-18T17:14:43.418628Z", "shell.execute_reply": "2024-01-18T17:14:43.418347Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import matplotlib.pyplot as plt # type: ignore" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:43.420036Z", "iopub.status.busy": "2024-01-18T17:14:43.419951Z", "iopub.status.idle": "2024-01-18T17:14:43.574552Z", "shell.execute_reply": "2024-01-18T17:14:43.574236Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjcAAAHHCAYAAABDUnkqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABNJElEQVR4nO3deVxU5f4H8M+wDSAMqMgqm4KICmKug3tqSmqQpaQWmlum5r5h3RTzhkvmUq6VmpqZWGJXcUtFE3EHc+UqoZgCmgsjqAjM8/vDn3OdAJ1BZmH4vF8v7nXOec4533lY5tM5z3OORAghQERERGQizAxdABEREVFFYrghIiIik8JwQ0RERCaF4YaIiIhMCsMNERERmRSGGyIiIjIpDDdERERkUhhuiIiIyKQw3BAREZFJYbghIjJyHTp0QIcOHQxdBlGlwXBDZITS09PxwQcfoE6dOrC2toZMJkPr1q2xaNEiPHz40NDlkQ6cP38eM2bMwJUrVwxdClGlJ+GzpYiMy/bt29G7d29IpVJERUWhUaNGePz4MQ4dOoSff/4ZAwcOxMqVKw1dJlWwzZs3o3fv3ti/f3+JszSPHz8GAFhZWRmgMqLKx8LQBRDR/2RkZOCdd96Bt7c39u3bBzc3N9W6kSNH4vLly9i+fbsBKyzbgwcPYGtra+gyjFp+fj6qVaum9XYMNUTa4WUpIiMyd+5c5OXl4bvvvlMLNk/5+flhzJgxqtdFRUX47LPPULduXUilUvj4+GDatGkoKChQtenRowfq1KlT6vHkcjmaNWumtmz9+vVo2rQpbGxsUKNGDbzzzju4du2aWpsOHTqgUaNGOHnyJNq1awdbW1tMmzYNALB161Z0794d7u7ukEqlqFu3Lj777DMUFxeXOP6SJUtQp04d2NjYoEWLFvj9999LHV9SUFCA6dOnw8/PD1KpFJ6enpg8ebLa+3yeuLg41XtycnLCu+++i+vXr6vWf/HFF5BIJLh69WqJbaOjo2FlZYW7d++qlh09ehTdunWDg4MDbG1t0b59eyQlJaltN2PGDEgkEpw/fx79+vVD9erV0aZNm1LrW7NmDXr37g0A6NixIyQSCSQSCRITEwGUHHOTmJgIiUSCTZs2ISYmBh4eHrC3t8fbb7+N3NxcFBQUYOzYsXB2doadnR3ef//9UvtKk+81UaUkiMhoeHh4iDp16mjcfsCAAQKAePvtt8WSJUtEVFSUACAiIiJUbdauXSsAiGPHjqlte+XKFQFAzJs3T7Vs1qxZQiKRiMjISLF06VIRExMjnJychI+Pj7h7966qXfv27YWrq6uoVauW+Oijj8SKFStEfHy8EEKIiIgI0adPHzFv3jyxbNky0bt3bwFATJw4Ue34S5cuFQBE27ZtxeLFi8X48eNFjRo1RN26dUX79u1V7YqLi8Vrr70mbG1txdixY8WKFSvEqFGjhIWFhQgPD39hH61evVoAEM2bNxcLFiwQU6dOFTY2Nmrv6erVq0IikYi5c+eW2L5OnTqie/fuqtd79+4VVlZWQi6Xi/nz54sFCxaI4OBgYWVlJY4ePapqN336dAFANGjQQISHh4ulS5eKJUuWlFpjenq6GD16tAAgpk2bJtatWyfWrVsnsrOzVf39bJ/s379fABAhISFCLpeLxYsXi9GjRwuJRCLeeecd0a9fPxEWFiaWLFki3nvvPQFAxMTEqB1T0+81UWXEcENkJHJzcwUAjT6whRAiNTVVABBDhgxRWz5x4kQBQOzbt0+1X6lUKiZMmKDWbu7cuUIikYirV68KIZ6EHXNzc/Hvf/9brd2ZM2eEhYWF2vL27dsLAGL58uUl6nrw4EGJZR988IGwtbUVjx49EkIIUVBQIGrWrCmaN28uCgsLVe3WrFkjAKh9kK9bt06YmZmJ33//XW2fy5cvFwBEUlJSmX30+PFj4ezsLBo1aiQePnyoWr5t2zYBQHz66aeqZXK5XDRt2lRt+2PHjgkAYu3atUIIIZRKpfD39xddu3YVSqVS7T37+vqKLl26qJY9DTd9+/Yts75nxcXFCQBi//79JdaVFW4aNWokHj9+rFret29fIZFIRFhYmNr2crlceHt7q15r870mqox4WYrISCgUCgCAvb29Ru0TEhIAAOPHj1dbPmHCBABQjc2RyWQICwvDpk2bIJ6ZP/DTTz+hVatW8PLyAgD88ssvUCqV6NOnD/7++2/Vl6urK/z9/bF//36140ilUrz//vsl6rKxsVH9+/79+/j777/Rtm1bPHjwABcvXgQAnDhxArdv38bQoUNhYfG/oX/9+/dH9erV1fYXFxeHwMBA1K9fX62uV199FQBK1PWsEydO4ObNmxgxYgSsra1Vy7t374769eurjV+KjIzEyZMnkZ6ertZHUqkU4eHhAIDU1FRcunQJ/fr1w+3bt1W15Ofno1OnTjh48CCUSqVaDcOHDy+zvpcVFRUFS0tL1euWLVtCCIFBgwaptWvZsiWuXbuGoqIiANp/r4kqGw4oJjISMpkMwJNAoImrV6/CzMwMfn5+astdXV3h6OioNn4kMjIS8fHxSE5ORmhoKNLT03Hy5EksXLhQ1ebSpUsQQsDf37/U4z37IQoAHh4epQ50PXfuHD755BPs27dPFdieys3NVdUOoETtFhYW8PHxUVt26dIlXLhwAbVq1Sq1rps3b5a6/NnjBAQElFhXv359HDp0SPW6d+/eGD9+PH766SdMmzYNQgjExcUhLCxM9b25dOkSAGDAgAFlHjM3N1ctoPn6+pbZ9mU9DaZPOTg4AAA8PT1LLFcqlcjNzUXNmjW1/l4TVTYMN0RGQiaTwd3dHWfPntVqO4lE8sI2PXv2hK2tLTZt2oTQ0FBs2rQJZmZmqkGsAKBUKiGRSLBjxw6Ym5uX2IednZ3a62fP0Dx17949tG/fHjKZDDNnzkTdunVhbW2NU6dOYcqUKSXOamhCqVQiKCgIX375Zanr//lBXl7u7u5o27YtNm3ahGnTpuHIkSPIzMzEnDlz1GoBgHnz5iEkJKTU/WjSTxWltO/T85Y/PXOn7feaqLJhuCEyIj169MDKlSuRnJwMuVz+3Lbe3t5QKpW4dOkSAgMDVctzcnJw7949eHt7q5ZVq1YNPXr0QFxcHL788kv89NNPaNu2Ldzd3VVt6tatCyEEfH19Ua9evXLVn5iYiNu3b+OXX35Bu3btVMszMjJK1A4Aly9fRseOHVXLi4qKcOXKFQQHB6vVdfr0aXTq1EmjIFfacdLS0lSXsZ5KS0tT6yPgyRmuESNGIC0tDT/99BNsbW3Rs2dPtVqAJ0G0c+fOWtXyItq+t5dREd9rImPGMTdERmTy5MmoVq0ahgwZgpycnBLr09PTsWjRIgDA66+/DgBql5YAqM5wdO/eXW15ZGQkbty4gW+//RanT59GZGSk2vpevXrB3NwcMTExamNzgCf/xX/79u0X1v/0LMCz2z9+/BhLly5Va9esWTPUrFkT33zzjWocCAD88MMPalOuAaBPnz64fv06vvnmmxLHe/jwIfLz88usp1mzZnB2dsby5cvVpkLv2LEDFy5cKNFHb731FszNzfHjjz8iLi4OPXr0ULsvTdOmTVG3bl188cUXyMvLK3G8W7dulVnLizw9zr1798q9D01VxPeayJjxzA2REalbty42bNiAyMhIBAYGqt2h+PDhw4iLi8PAgQMBAI0bN8aAAQOwcuVK1eWgY8eO4fvvv0dERITaGRHgSRiyt7fHxIkTYW5ujrfeeqvEsWfNmoXo6GhcuXIFERERsLe3R0ZGBrZs2YJhw4Zh4sSJz60/NDQU1atXx4ABAzB69GhIJBKsW7euxAeolZUVZsyYgY8++givvvoq+vTpgytXrmDNmjWoW7eu2lmM9957D5s2bcLw4cOxf/9+tG7dGsXFxbh48SI2bdqEXbt2lbhXz1OWlpaYM2cO3n//fbRv3x59+/ZFTk4OFi1aBB8fH4wbN06tvbOzMzp27Igvv/wS9+/fLxEAzczM8O233yIsLAwNGzbE+++/Dw8PD1y/fh379++HTCbDf/7zn+f2UVlCQkJgbm6OOXPmIDc3F1KpFK+++iqcnZ3Ltb/nqYjvNZFRM8QULSJ6vv/+979i6NChwsfHR1hZWQl7e3vRunVr8dVXX6mmUwshRGFhoYiJiRG+vr7C0tJSeHp6iujoaLU2z+rfv78AIDp37lzmsX/++WfRpk0bUa1aNVGtWjVRv359MXLkSJGWlqZq0759e9GwYcNSt09KShKtWrUSNjY2wt3dXUyePFns2rWr1GnOixcvFt7e3kIqlYoWLVqIpKQk0bRpU9GtWze1do8fPxZz5swRDRs2FFKpVFSvXl00bdpUxMTEiNzc3Bd1p/jpp59EkyZNhFQqFTVq1BD9+/cXf/31V6ltv/nmGwFA2Nvbq00ff1ZKSoro1auXqFmzppBKpcLb21v06dNH7N27V9Xm6VTwW7duvbC+Z49dp04dYW5urtZfZU0Fj4uLU9v+6T19jh8/rra8rFo0+V4TVUZ8thQRGQ2lUolatWqhV69epV6GIiLSBMfcEJFBPHr0qMTlqrVr1+LOnTslHr9ARKQNnrkhIoNITEzEuHHj0Lt3b9SsWROnTp3Cd999h8DAQJw8eZIPiySicuOAYiIyCB8fH3h6emLx4sW4c+cOatSogaioKMyePZvBhoheCs/cEBERkUnhmBsiIiIyKQw3REREZFKq3JgbpVKJGzduwN7eXq+3OyciIqLyE0Lg/v37cHd3h5nZ88/NVLlwc+PGjQp70B4RERHp17Vr11C7du3ntqly4cbe3h7Ak86RyWQGroaIiIg0oVAo4Onpqfocf54qF26eXoqSyWQMN0RERJWMJkNKOKCYiIiITArDDREREZkUhhsiIiIyKQw3REREZFIYboiIiMikMNwQERGRSWG4ISIiIpPCcENEREQmheGGiIiITArDDREREZkUowk3s2fPhkQiwdixY5/bLi4uDvXr14e1tTWCgoKQkJCgnwKJiIioUjCKcHP8+HGsWLECwcHBz213+PBh9O3bF4MHD0ZKSgoiIiIQERGBs2fP6qlSIiIiMnYSIYQwZAF5eXl45ZVXsHTpUsyaNQshISFYuHBhqW0jIyORn5+Pbdu2qZa1atUKISEhWL58uUbHUygUcHBwQG5uLh+cqa3HD4AHfxu6CiIiMnbmUsDepUJ3qc3nt8GfCj5y5Eh0794dnTt3xqxZs57bNjk5GePHj1db1rVrV8THx5e5TUFBAQoKClSvFQrFS9VbZT24AywOAR7lGroSIiIydrVbAEP2GOzwBg03GzduxKlTp3D8+HGN2mdnZ8PFRT0Juri4IDs7u8xtYmNjERMT81J1EoDb6f8LNhbWhq2FiIiMm7mVQQ9vsHBz7do1jBkzBnv27IG1te4+LKOjo9XO9igUCnh6eurseCavug8w5rShqyAiIiqTwcLNyZMncfPmTbzyyiuqZcXFxTh48CC+/vprFBQUwNzcXG0bV1dX5OTkqC3LycmBq6trmceRSqWQSqUVWzwREREZLYPNlurUqRPOnDmD1NRU1VezZs3Qv39/pKamlgg2ACCXy7F37161ZXv27IFcLtdX2VWYQcedExERacxgZ27s7e3RqFEjtWXVqlVDzZo1VcujoqLg4eGB2NhYAMCYMWPQvn17zJ8/H927d8fGjRtx4sQJrFy5Uu/1ExERkXEyivvclCUzMxNZWVmq16GhodiwYQNWrlyJxo0bY/PmzYiPjy8RkkiXJIYugIiI6LkMfp8bfeN9bsrp2jHguy5AdV9gTKqhqyEioipGm89voz5zQ0akamVgIiKqxBhuiIiIyKQw3JB2JBxzQ0RExo3hhoiIiEwKww1piGNuiIiocmC4ISIiIpPCcENa4pgbIiIybgw3pBlOBSciokqC4YaIiIhMCsMNaYdTwYmIyMgx3BAREZFJYbghDXHMDRERVQ4MN0RERGRSGG5ISxxzQ0RExo3hhoiIiEwKww1phve5ISKiSoLhhoiIiEwKww1ph/e5ISIiI8dwQxriZSkiIqocGG6IiIjIpDDckJZ4WYqIiIwbww0RERGZFIYb0gynghMRUSXBcENEREQmheGGtMOp4EREZOQYboiIiMikMNyQhjjmhoiIKgeGGyIiIjIpDDekJY65ISIi48ZwQ5rhVHAiIqokGG6IiIjIpDDckHY4FZyIiIwcww0RERGZFIYb0hDH3BARUeXAcENEREQmheGGtMQxN0REZNwYboiIiMikMNyQZnifGyIiqiQYboiIiMikMNyQdnifGyIiMnIMN0RERGRSGG5IQxxzQ0RElQPDDREREZkUhhvSEsfcEBGRcWO4Ic1wKjgREVUSBg03y5YtQ3BwMGQyGWQyGeRyOXbs2FFm+zVr1kAikah9WVtb67FiIiIiMnYWhjx47dq1MXv2bPj7+0MIge+//x7h4eFISUlBw4YNS91GJpMhLS1N9VrCqcn6xe4mIiIjZ9Bw07NnT7XX//73v7Fs2TIcOXKkzHAjkUjg6uqqj/KIiIioEjKaMTfFxcXYuHEj8vPzIZfLy2yXl5cHb29veHp6Ijw8HOfOnXvufgsKCqBQKNS+qDw45oaIiCoHg4ebM2fOwM7ODlKpFMOHD8eWLVvQoEGDUtsGBARg1apV2Lp1K9avXw+lUonQ0FD89ddfZe4/NjYWDg4Oqi9PT09dvRUiIiIyAhIhDDsN5vHjx8jMzERubi42b96Mb7/9FgcOHCgz4DyrsLAQgYGB6Nu3Lz777LNS2xQUFKCgoED1WqFQwNPTE7m5uZDJZBX2Pkze5d+A9W8BrsHA8N8NXQ0REVUxCoUCDg4OGn1+G3TMDQBYWVnBz88PANC0aVMcP34cixYtwooVK164raWlJZo0aYLLly+X2UYqlUIqlVZYvURERGTcDH5Z6p+USqXamZbnKS4uxpkzZ+Dm5qbjqohDboiIqLIw6Jmb6OhohIWFwcvLC/fv38eGDRuQmJiIXbt2AQCioqLg4eGB2NhYAMDMmTPRqlUr+Pn54d69e5g3bx6uXr2KIUOGGPJtEBERkRExaLi5efMmoqKikJWVBQcHBwQHB2PXrl3o0qULACAzMxNmZv87uXT37l0MHToU2dnZqF69Opo2bYrDhw9rND6HKgjvK0REREbO4AOK9U2bAUn0jEt7gB/eBtwaAx8cNHQ1RERUxWjz+W10Y26IiIiIXgbDDWmJl6WIiMi4MdwQERGRSWG4Ic1UraFZRERUiTHcEBERkUlhuCHtcCo4EREZOYYbIiIiMikMN6QhjrkhIqLKgeGGiIiITArDDWmJY26IiMi4MdyQZjgVnIiIKgmGGyIiIjIpDDekHU4FJyIiI8dwQ0RERCaF4YY0xDE3RERUOTDcEBERkUlhuCEtccwNEREZN4YbIiIiMikMN6QZ3ueGiIgqCYYbIiIiMikMN6Qd3ueGiIiMHMMNaYiXpYiIqHJguCEiIiKTwnBDREREJoXhhrTEMTdERGTcGG5IM5wKTkRElQTDDREREZkUhhvSDqeCExGRkWO4ISIiIpPCcEMa4pgbIiKqHBhuiIiIyKQw3JCWOOaGiIiMG8MNERERmRSGG9IM73NDRESVBMMNERERmRSGG9IO73NDRERGjuGGNMTLUkREVDkw3BAREZFJYbghLfGyFBERGTeGGyIiIjIpDDekGU4FJyKiSoLhhoiIiEwKww1ph1PBiYjIyDHcEBERkUlhuCENccwNERFVDgYNN8uWLUNwcDBkMhlkMhnkcjl27Njx3G3i4uJQv359WFtbIygoCAkJCXqqloiIiCoDg4ab2rVrY/bs2Th58iROnDiBV199FeHh4Th37lyp7Q8fPoy+ffti8ODBSElJQUREBCIiInD27Fk9V16VccwNEREZN4kQxjXHt0aNGpg3bx4GDx5cYl1kZCTy8/Oxbds21bJWrVohJCQEy5cv12j/CoUCDg4OyM3NhUwmq7C6nyvvJlD0SD/H0pX/7gISJgLebYD3txu6GiIiqmK0+fy20FNNL1RcXIy4uDjk5+dDLpeX2iY5ORnjx49XW9a1a1fEx8eXud+CggIUFBSoXisUigqpV2NHVwI7Jun3mERERFWYwcPNmTNnIJfL8ejRI9jZ2WHLli1o0KBBqW2zs7Ph4uKitszFxQXZ2dll7j82NhYxMTEVWrNWrp988v8Sc8Dc0nB1VAQzC6DBG4augoiI6LkMHm4CAgKQmpqK3NxcbN68GQMGDMCBAwfKDDjaio6OVjvbo1Ao4OnpWSH71krnGUDr0fo/LhERURVj8HBjZWUFPz8/AEDTpk1x/PhxLFq0CCtWrCjR1tXVFTk5OWrLcnJy4OrqWub+pVIppFJpxRZNRERERsvo7nOjVCrVxsg8Sy6XY+/evWrL9uzZU+YYHeNgVOO1iYiITJ5Bz9xER0cjLCwMXl5euH//PjZs2IDExETs2rULABAVFQUPDw/ExsYCAMaMGYP27dtj/vz56N69OzZu3IgTJ05g5cqVhnwbREREZEQMGm5u3ryJqKgoZGVlwcHBAcHBwdi1axe6dOkCAMjMzISZ2f9OLoWGhmLDhg345JNPMG3aNPj7+yM+Ph6NGjUy1FvQHJ/JREREpBcGDTfffffdc9cnJiaWWNa7d2/07t1bRxURERFRZWd0Y25MjnHdI5GIiMjkMdwQERGRSWG40RuOuSEiItIHhhsiIiIyKQw3OscxN0RERPqk0Wyp6tWrQ6LhVOY7d+68VEEmi1PBiYiI9EKjcLNw4ULVv2/fvo1Zs2aha9euqjsDJycnY9euXfjXv/6lkyKJiIiINKVRuBkwYIDq32+99RZmzpyJUaNGqZaNHj0aX3/9NX777TeMGzeu4quszDgVnIiISK+0HnOza9cudOvWrcTybt264bfffquQooiIiIjKS+twU7NmTWzdurXE8q1bt6JmzZoVUpRp4pgbIiIifdD68QsxMTEYMmQIEhMT0bJlSwDA0aNHsXPnTnzzzTcVXiARERGRNrQONwMHDkRgYCAWL16MX375BQAQGBiIQ4cOqcIOPYtjboiIiPSpXA/ObNmyJX744YeKrsW0cSo4ERGRXpTrJn7p6en45JNP0K9fP9y8eRMAsGPHDpw7d65CiyMiIiLSltbh5sCBAwgKCsLRo0fx888/Iy8vDwBw+vRpTJ8+vcILJCIiItKG1uFm6tSpmDVrFvbs2QMrKyvV8ldffRVHjhyp0OJMAu9zQ0REpFdah5szZ87gzTffLLHc2dkZf//9d4UUZZo45oaIiEgftA43jo6OyMrKKrE8JSUFHh4eFVIUERERUXlpHW7eeecdTJkyBdnZ2ZBIJFAqlUhKSsLEiRMRFRWlixorOV6WIiIi0ietw83nn3+O+vXrw9PTE3l5eWjQoAHatWuH0NBQfPLJJ7qokYiIiEhjWt3nRgiB7OxsLF68GJ9++inOnDmDvLw8NGnSBP7+/rqq0TTwPjdERER6oXW48fPzw7lz5+Dv7w9PT09d1UVERERULlpdljIzM4O/vz9u376tq3pMD6eCExER6ZXWY25mz56NSZMm4ezZs7qox4TxshQREZE+aP1sqaioKDx48ACNGzeGlZUVbGxs1NbfuXOnwoojIiIi0pbW4WbhwoU6KIOIiIioYmgdbgYMGKCLOkwYx9wQERHp00s9Fbxv3758KrimOBWciIhIL17qqeC//PILnwpORERERoVPBdc1TgUnIiLSKz4VnIiIiEwKnwpOREREJoVPBSciIiKTwqeC6xzH3BAREemT1ve5sbKywjfffIN//etfOHv2LJ8KrilOBSciItILrcPNoUOH0KZNG3h5ecHLy0sXNRERERGVm9aXpV599VX4+vpi2rRpOH/+vC5qIiIiIio3rcPNjRs3MGHCBBw4cACNGjVCSEgI5s2bh7/++ksX9VV+qvvc8LIUERGRPmgdbpycnDBq1CgkJSUhPT0dvXv3xvfffw8fHx+8+uqruqiRiIiISGPlerbUU76+vpg6dSpmz56NoKAgHDhwoKLqIiIiIiqXcoebpKQkjBgxAm5ubujXrx8aNWqE7du3V2RtRERERFrTerZUdHQ0Nm7ciBs3bqBLly5YtGgRwsPDYWtrq4v6TAenghMREemF1uHm4MGDmDRpEvr06QMnJydd1ERERERUblqHm6SkJF3UQURERFQhtA43AJCeno6FCxfiwoULAIAGDRpgzJgxqFu3boUWZxIEH79ARESkT1oPKN61axcaNGiAY8eOITg4GMHBwTh69CgaNmyIPXv2aLWv2NhYNG/eHPb29nB2dkZERATS0tKeu82aNWsgkUjUvqytrbV9GwbAMTdERET6oPWZm6lTp2LcuHGYPXt2ieVTpkxBly5dNN7XgQMHMHLkSDRv3hxFRUWYNm0aXnvtNZw/fx7VqlUrczuZTKYWgiQcrEtERET/T+twc+HCBWzatKnE8kGDBmHhwoVa7Wvnzp1qr9esWQNnZ2ecPHkS7dq1K3M7iUQCV1dXrY5FREREVYPWl6Vq1aqF1NTUEstTU1Ph7Oz8UsXk5uYCAGrUqPHcdnl5efD29oanpyfCw8Nx7ty5MtsWFBRAoVCofenX/4+54dklIiIivdD6zM3QoUMxbNgw/PnnnwgNDQXwZAbVnDlzMH78+HIXolQqMXbsWLRu3RqNGjUqs11AQABWrVqF4OBg5Obm4osvvkBoaCjOnTuH2rVrl2gfGxuLmJiYctdFRERElYtECO2m8wghsHDhQsyfPx83btwAALi7u2PSpEkYPXp0uce/fPjhh9ixYwcOHTpUakgpS2FhIQIDA9G3b1989tlnJdYXFBSgoKBA9VqhUMDT0xO5ubmQyWTlqlUrP/YF0hKAnouApgN1fzwiIiITpFAo4ODgoNHnt9ZnbiQSCcaNG4dx48bh/v37AAB7e/vyVfr/Ro0ahW3btuHgwYNaBRsAsLS0RJMmTXD58uVS10ulUkil0peqj4iIiCoPrcfcZGRk4NKlSwCehJqnwebSpUu4cuWKVvsSQmDUqFHYsmUL9u3bB19fX23LQXFxMc6cOQM3Nzett9UL1YkxjrkhIiLSB63DzcCBA3H48OESy48ePYqBAwdqta+RI0di/fr12LBhA+zt7ZGdnY3s7Gw8fPhQ1SYqKgrR0dGq1zNnzsTu3bvx559/4tSpU3j33Xdx9epVDBkyRNu3QkRERCZI63CTkpKC1q1bl1jeqlWrUmdRPc+yZcuQm5uLDh06wM3NTfX1008/qdpkZmYiKytL9fru3bsYOnQoAgMD8frrr0OhUODw4cNo0KCBtm+FiIiITFC5xtw8HWvzrNzcXBQXF2u1L03GMicmJqq9XrBgARYsWKDVcQyLU8GJiIj0SeszN+3atUNsbKxakCkuLkZsbCzatGlTocURERERaUvrMzdz5sxBu3btEBAQgLZt2wIAfv/9dygUCuzbt6/CCyQiIiLShtZnbho0aIA//vgDffr0wc2bN3H//n1ERUXh4sWLz735HhEREZE+aH3mBnhy077PP/+8omsxTZwKTkREpFdan7khIiIiMmYMN0RERGRSGG70hVPBiYiI9ILhRue0ei4pERERvSStw83Dhw/x4MED1eurV69i4cKF2L17d4UWRkRERFQeWoeb8PBwrF27FgBw7949tGzZEvPnz0d4eDiWLVtW4QUSERERaUPrcHPq1CnVzfs2b94MFxcXXL16FWvXrsXixYsrvMBKj1PBiYiI9ErrcPPgwQPY29sDAHbv3o1evXrBzMwMrVq1wtWrVyu8QCIiIiJtaB1u/Pz8EB8fj2vXrmHXrl147bXXAAA3b96ETCar8AKJiIiItKF1uPn0008xceJE+Pj4oEWLFpDL5QCenMVp0qRJhRdIREREpA2tH7/w9ttvo02bNsjKykLjxo1Vyzt16oQ333yzQoszDf8/5ob3uSEiItKLct3nxtXVFfb29tizZw8ePnwIAGjevDnq169focURERERaUvrcHP79m106tQJ9erVw+uvv46srCwAwODBgzFhwoQKL5CIiIhIG1qHm3HjxsHS0hKZmZmwtbVVLY+MjMTOnTsrtDjTwstSRERE+qD1mJvdu3dj165dqF27ttpyf39/TgUvjeDjF4iIiPRJ6zM3+fn5amdsnrpz5w6kUmmFFEVERERUXlqHm7Zt26oevwAAEokESqUSc+fORceOHSu0OCIiIiJtaX1Zau7cuejUqRNOnDiBx48fY/LkyTh37hzu3LmDpKQkXdRYyXEqOBERkT5pfeamUaNG+O9//4s2bdogPDwc+fn56NWrF1JSUlC3bl1d1EhERESkMa3P3ACAg4MDPv7444quhYiIiOillSvc3Lt3D8eOHcPNmzehVCrV1kVFRVVIYaaHl6WIiIj0Qetw85///Af9+/dHXl4eZDIZJM+MJZFIJAw3/8Sp4ERERHql9ZibCRMmYNCgQcjLy8O9e/dw9+5d1dedO3d0USMRERGRxrQON9evX8fo0aNLvdcNERERkaFpHW66du2KEydO6KIW08ap4ERERHqh9Zib7t27Y9KkSTh//jyCgoJgaWmptv6NN96osOJMA8fcEBER6ZPW4Wbo0KEAgJkzZ5ZYJ5FIUFxc/PJVEREREZWT1uHmn1O/iYiIiIyJ1mNuqLw45oaIiEgfNDpzs3jxYgwbNgzW1tZYvHjxc9uOHj26QgozGbzPDRERkV5pFG4WLFiA/v37w9raGgsWLCiznUQiYbghIiIig9Io3GRkZJT6b9ICp4ITERHpBcfc6BwvSxEREemTRmduxo8fr/EOv/zyy3IXQ0RERPSyNAo3KSkpGu1MwksvREREZGAahZv9+/frug4iIiKiCsExN7rGqeBERER6xXBDREREJoXhRl84HomIiEgvGG6IiIjIpDDcEBERkUkxaLiJjY1F8+bNYW9vD2dnZ0RERCAtLe2F28XFxaF+/fqwtrZGUFAQEhIS9FAtERERVQYGDTcHDhzAyJEjceTIEezZsweFhYV47bXXkJ+fX+Y2hw8fRt++fTF48GCkpKQgIiICEREROHv2rB4rLw+OuSEiItIHiRDGM1f51q1bcHZ2xoEDB9CuXbtS20RGRiI/Px/btm1TLWvVqhVCQkKwfPnyFx5DoVDAwcEBubm5kMlkFVZ7CcWFEIobKNz0PqyyTuJ22Ao8rPeG7o5HRERkJKwszOBsb12h+9Tm81ujm/jpS25uLgCgRo0aZbZJTk4u8TiIrl27Ij4+vtT2BQUFKCgoUL1WKBQvX+iLCAGsaA/JzXOw+v9Fn249h+1Ke90fm4iIyMBe8XLELyNaG+z4RhNulEolxo4di9atW6NRo0ZltsvOzoaLi4vaMhcXF2RnZ5faPjY2FjExMRVa6wsVPQJungMAFAhL5KA6zprVg9SM47eJiMj0WZob9vPOaMLNyJEjcfbsWRw6dKhC9xsdHa12pkehUMDT07NCj/E8TQpWYH7/UBwIctPbMYmIiKoyowg3o0aNwrZt23Dw4EHUrl37uW1dXV2Rk5OjtiwnJweurq6ltpdKpZBKpRVWKxERERk3g543EkJg1KhR2LJlC/bt2wdfX98XbiOXy7F37161ZXv27IFcLtdVmdoznjHaREREVY5Bz9yMHDkSGzZswNatW2Fvb68aN+Pg4AAbGxsAQFRUFDw8PBAbGwsAGDNmDNq3b4/58+eje/fu2LhxI06cOIGVK1ca7H0QERGR8TDomZtly5YhNzcXHTp0gJubm+rrp59+UrXJzMxEVlaW6nVoaCg2bNiAlStXonHjxti8eTPi4+OfOwjZkAT4WCkiIiJ9MuiZG01usZOYmFhiWe/evdG7d28dVERERESVHecm6wTH3BARERkKww0RERGZFIYbHROQgM+VIiIi0h+GG13gVHAiIiKDYbghIiIik8Jwo2MCEk4FJyIi0iOGGyIiIjIpDDc6wTE3REREhsJwQ0RERCaF4UYPOOSGiIhIfxhudIFTwYmIiAyG4YaIiIhMCsONjj2ZCs4LU0RERPrCcENEREQmheFGJzjmhoiIyFAYboiIiMikMNzomICEU8GJiIj0iOFGFzgVnIiIyGAYboiIiMikMNwQERGRSWG40TEBgLe5ISIi0h+GG53gmBsiIiJDYbghIiIik8Jwo2NPHr9g6CqIiIiqDoYbIiIiMikMN7rA+9wQEREZDMMNERERmRSGGx178vgFDrohIiLSF4YbIiIiMikMN0RERGRSGG50TADgVSkiIiL9YbghIiIik8JwowucCk5ERGQwDDdERERkUhhudExwIjgREZFeMdzoBC9LERERGQrDDREREZkUhhudk0DCx4ITERHpDcMNERERmRSGG13gVHAiIiKDYbghIiIik8JwowcccUNERKQ/DDdERERkUhhudIJjboiIiAyF4YaIiIhMikHDzcGDB9GzZ0+4u7tDIpEgPj7+ue0TExMhkUhKfGVnZ+unYC0p/3+0DW9zQ0REpD8GDTf5+flo3LgxlixZotV2aWlpyMrKUn05OzvrqMJy4lRwIiIig7Ew5MHDwsIQFham9XbOzs5wdHSs+IKIiIio0quUY25CQkLg5uaGLl26ICkp6bltCwoKoFAo1L70RTy9LMXJ4ERERHpTqcKNm5sbli9fjp9//hk///wzPD090aFDB5w6darMbWJjY+Hg4KD68vT01GPFREREpG8GvSylrYCAAAQEBKheh4aGIj09HQsWLMC6detK3SY6Ohrjx49XvVYoFHoIOBxzQ0REZCiVKtyUpkWLFjh06FCZ66VSKaRSqR4rIiIiIkOqVJelSpOamgo3NzdDl/FcnApORESkPwY9c5OXl4fLly+rXmdkZCA1NRU1atSAl5cXoqOjcf36daxduxYAsHDhQvj6+qJhw4Z49OgRvv32W+zbtw+7d+821FsoHaeCExERGYxBw82JEyfQsWNH1eunY2MGDBiANWvWICsrC5mZmar1jx8/xoQJE3D9+nXY2toiODgYv/32m9o+iIiIqGqTCFG1TjMoFAo4ODggNzcXMplMRwfJAr6sjyKYw+/ROmwY0hKhfk66ORYREVEVoM3nd6Ufc0NERET0LIYbnahSJ8OIiIiMCsMNERERmRSGGx16+vgFPn2BiIhIfxhudKFqjdEmIiIyKgw3REREZFIYbnTo6fkbPhWciIhIfxhuiIiIyKQw3OgEx9wQEREZCsMNERERmRSGG516MtaGTwUnIiLSH4YbIiIiMikGfSq4yeJ9boiIDKq4uBiFhYWGLoO0ZGVlBTOzlz/vwnBDREQmQwiB7Oxs3Lt3z9ClUDmYmZnB19cXVlZWL7Ufhhsdevr4BQ65ISLSj6fBxtnZGba2tpBw0GOloVQqcePGDWRlZcHLy+ulvncMNzrBy1JERPpWXFysCjY1a9Y0dDlUDrVq1cKNGzdQVFQES0vLcu+HA4qJiMgkPB1jY2tra+BKqLyeXo4qLi5+qf0w3OiQ6vELPC1KRKQ3/JtbeVXU947hhoiIyMhduXIFEokEqampFbZPiUSC+Pj4Mtf7+Phg4cKFFXY8fWK40QVOBSciIi0MHDgQEolE9VWzZk1069YNf/zxh6FLq5QYboiIiIxAt27dkJWVhaysLOzduxcWFhbo0aOHocuqlBhudEjw8QtERKQhqVQKV1dXuLq6IiQkBFOnTsW1a9dw69atEm2Li4sxePBg+Pr6wsbGBgEBAVi0aFGJdqtWrULDhg0hlUrh5uaGUaNGlXn86dOnw83NTe1s0f3799G3b19Uq1YNHh4eWLJkido2mZmZCA8Ph52dHWQyGfr06YOcnBwAwMWLF2Fra4sNGzao2m/atAk2NjY4f/681v2jDU4F1wleliIiMgZCCDwsfLmZN+VlY2le7gGyeXl5WL9+Pfz8/FCzZk3k5+errVcqlahduzbi4uJQs2ZNHD58GMOGDYObmxv69OkDAFi2bBnGjx+P2bNnIywsDLm5uUhKSipxLCEERo8ejW3btuH333+Hn5+fat28efMwbdo0xMTEYNeuXRgzZgzq1auHLl26QKlUqoLNgQMHUFRUhJEjRyIyMhKJiYmoX78+vvjiC4wYMQJt2rSBmZkZhg8fjjlz5qBBgwbl6hdNMdwQEZHJelhYjAaf7jLIsc/P7ApbK80/Zrdt2wY7OzsAQH5+Ptzc3LBt27ZSH0dgaWmJmJgY1WtfX18kJydj06ZNqnAza9YsTJgwAWPGjFG1a968udp+ioqK8O677yIlJQWHDh2Ch4eH2vrWrVtj6tSpAIB69eohKSkJCxYsQJcuXbB3716cOXMGGRkZ8PT0BACsXbsWDRs2xPHjx9G8eXOMGDECCQkJePfdd2FlZYXmzZvjo48+0rhPyovhRqd4h2IiItJMx44dsWzZMgDA3bt3sXTpUoSFheHYsWOltl+yZAlWrVqFzMxMPHz4EI8fP0ZISAgA4ObNm7hx4wY6der03GOOGzcOUqkUR44cgZOTU4n1crm8xOunM6guXLgAT09PVbABgAYNGsDR0REXLlxQBalVq1ahXr16MDMzw7lz5/QyVZ/hhoiITJaNpTnOz+xqsGNro1q1amqXhL799ls4ODjgm2++wZAhQ9Tabty4ERMnTsT8+fMhl8thb2+PefPm4ejRo0+ObWOj0TG7dOmCH3/8Ebt27UL//v21qldTp0+fRn5+PszMzJCVlQU3NzedHOdZDDe6wKngRERGQSKRaHVpyJhIJBKYmZnh4cOHJdYlJSUhNDQUI0aMUC1LT09X/dve3h4+Pj7Yu3cvOnbsWOYx3njjDfTs2RP9+vWDubk53nnnHbX1R44cKfE6MDAQABAYGIhr167h2rVrqrM358+fx71791Rjau7cuYOBAwfi448/RlZWFvr3749Tp05pHL7Kq3J+x4mIiExMQUEBsrOzATy5LPX1118jLy8PPXv2LNHW398fa9euxa5du+Dr64t169bh+PHj8PX1VbWZMWMGhg8fDmdnZ4SFheH+/ftISkoqMeblzTffxLp16/Dee+/BwsICb7/9tmpdUlIS5s6di4iICOzZswdxcXHYvn07AKBz584ICgpC//79sXDhQhQVFWHEiBFo3749mjVrBgAYPnw4PD098cknn6CgoABNmjTBxIkTS8y6qmgMNzr0v8cvGLQMIiKqBHbu3Km6ZGNvb4/69esjLi4OHTp0wJUrV9TafvDBB0hJSUFkZCQkEgn69u2LESNGYMeOHao2AwYMwKNHj7BgwQJMnDgRTk5OasHlWW+//TaUSiXee+89mJmZoVevXgCACRMm4MSJE4iJiYFMJsOXX36Jrl2fXOaTSCTYunUrPvroI7Rr1w5mZmbo1q0bvvrqKwBPBhcnJCQgJSUFFhYWsLCwwPr169GmTRv06NEDYWFhFd2FKhIhqtY1FIVCAQcHB+Tm5kImk+nmIHf+BBY3wQNYo8GjVfj5QzmaetfQzbGIiAgA8OjRI2RkZMDX1xfW1taGLofK4XnfQ20+v3kTPyIiIjIpDDdERERkUhhudEio7nDDQTdERET6wnCjC1VrGBMREZFRYbghIiIik8Jwo0N8KjgREZH+MdwQERGRSWG4ISIiIpPCcENEREQmheFGh1SPXzBoFURERM/XoUMHjB071tBlVBiGG13gVHAiItJSdnY2xowZAz8/P1hbW8PFxQWtW7fGsmXL8ODBA0OXV6nwwZlEREQG9ueff6J169ZwdHTE559/jqCgIEilUpw5cwYrV66Eh4cH3njjjRLbFRYWwtLS0gAVGzeeudGpp1PBeWGKiIjKNmLECFhYWODEiRPo06cPAgMDUadOHYSHh2P79u3o2bMngCefJ8uWLcMbb7yBatWq4d///jcAYOvWrXjllVdgbW2NOnXqICYmBkVFRQCAQYMGoUePHmrHKywshLOzM7777jvVsqKiIowaNQoODg5wcnLCv/71Lzz7bO27d+8iKioK1atXh62tLcLCwnDp0iUAwK1bt+Dq6orPP/9c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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "line_bb, = plt.plot(blackbox_coverage, label=\"Blackbox\")\n", "line_gb, = plt.plot(greybox_coverage, label=\"Greybox\")\n", "plt.legend(handles=[line_bb, line_gb])\n", "plt.title('Coverage over time')\n", "plt.xlabel('# of inputs')\n", "plt.ylabel('lines covered');" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "***Summary***. We have seen how a greybox fuzzer \"discovers\" interesting seeds that can lead to more progress. From the input `good`, our greybox fuzzer has slowly learned how to generate the input `bad!` which raises the exception. Now, how can we do that even faster?\n", "\n", "***Try it***. How much coverage would be achieved over time using a blackbox *generation-based* fuzzer? Try plotting the coverage for all three fuzzers. You can define the blackbox generation-based fuzzer as follows.\n", "```Python\n", "from Fuzzer import RandomFuzzer\n", "blackbox_gen_fuzzer = RandomFuzzer(min_length=4, max_length=4, char_start=32, char_range=96)\n", "```\n", "You can execute your own code by opening this chapter as Jupyter notebook.\n", "\n", "***Read***. This is the high-level view how AFL works, one of the most successful vulnerability detection tools. If you are interested in the technical details, have a look at: https://github.com/mirrorer/afl/blob/master/docs/technical_details.txt" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Boosted Greybox Fuzzing\n", "\n", "Our boosted greybox fuzzer assigns more energy to seeds that promise to achieve more coverage. We change the power schedule such that seeds that exercise \"unusual\" paths have more energy. With *unusual paths*, we mean paths that are not exercised very often by generated inputs.\n", "\n", "In order to identify which path is exercised by an input, we leverage the function `getPathID` from the section on [trace coverage](WhenIsEnough.ipynb#Trace-Coverage)." ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:43.576309Z", "iopub.status.busy": "2024-01-18T17:14:43.576185Z", "iopub.status.idle": "2024-01-18T17:14:43.577935Z", "shell.execute_reply": "2024-01-18T17:14:43.577550Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import pickle # serializes an object by producing a byte array from all the information in the object\n", "import hashlib # produces a 128-bit hash value from a byte array" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The function `getPathID` returns a unique hash for a coverage set." ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:43.579463Z", "iopub.status.busy": "2024-01-18T17:14:43.579348Z", "iopub.status.idle": "2024-01-18T17:14:43.581187Z", "shell.execute_reply": "2024-01-18T17:14:43.580942Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "def getPathID(coverage: Any) -> str:\n", " \"\"\"Returns a unique hash for the covered statements\"\"\"\n", " pickled = pickle.dumps(sorted(coverage))\n", " return hashlib.md5(pickled).hexdigest()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "There are several ways to assign energy based on how unusual the exercised path is. In this case, we implement an _exponential power schedule_ which computes the energy $e(s)$ for a seed $s$ as follows\n", "$$e(s) = \\frac{1}{f(p(s))^a}$$\n", "where \n", "* $p(s)$ returns the ID of the path exercised by $s$, \n", "* $f(p)$ returns the number of times the path $p$ is exercised by generated inputs, and \n", "* $a$ is a given exponent." ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:43.583072Z", "iopub.status.busy": "2024-01-18T17:14:43.582960Z", "iopub.status.idle": "2024-01-18T17:14:43.585119Z", "shell.execute_reply": "2024-01-18T17:14:43.584866Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class AFLFastSchedule(PowerSchedule):\n", " \"\"\"Exponential power schedule as implemented in AFL\"\"\"\n", "\n", " def __init__(self, exponent: float) -> None:\n", " self.exponent = exponent\n", "\n", " def assignEnergy(self, population: Sequence[Seed]) -> None:\n", " \"\"\"Assign exponential energy inversely proportional to path frequency\"\"\"\n", " for seed in population:\n", " seed.energy = 1 / (self.path_frequency[getPathID(seed.coverage)] ** self.exponent)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "In the greybox fuzzer, let's keep track of the number of times $f(p)$ each path $p$ is exercised, and update the power schedule." ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:43.586683Z", "iopub.status.busy": "2024-01-18T17:14:43.586579Z", "iopub.status.idle": "2024-01-18T17:14:43.589124Z", "shell.execute_reply": "2024-01-18T17:14:43.588876Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class CountingGreyboxFuzzer(GreyboxFuzzer):\n", " \"\"\"Count how often individual paths are exercised.\"\"\"\n", "\n", " def reset(self):\n", " \"\"\"Reset path frequency\"\"\"\n", " super().reset()\n", " self.schedule.path_frequency = {}\n", "\n", " def run(self, runner: FunctionCoverageRunner) -> Tuple[Any, str]: # type: ignore\n", " \"\"\"Inform scheduler about path frequency\"\"\"\n", " result, outcome = super().run(runner)\n", "\n", " path_id = getPathID(runner.coverage())\n", " if path_id not in self.schedule.path_frequency:\n", " self.schedule.path_frequency[path_id] = 1\n", " else:\n", " self.schedule.path_frequency[path_id] += 1\n", "\n", " return(result, outcome)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Okay, let's run our boosted greybox fuzzer $n=10k$ times on our simple [example](#Runner-and-Sample-Program). We set the exponentent of our exponential power schedule to $a=5$." ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:43.590684Z", "iopub.status.busy": "2024-01-18T17:14:43.590578Z", "iopub.status.idle": "2024-01-18T17:14:43.819288Z", "shell.execute_reply": "2024-01-18T17:14:43.819010Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'It took the fuzzer w/ exponential schedule 0.23 seconds to generate and execute 10000 inputs.'" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "n = 10000\n", "seed_input = \"good\"\n", "fast_schedule = AFLFastSchedule(5)\n", "fast_fuzzer = CountingGreyboxFuzzer([seed_input], Mutator(), fast_schedule)\n", "start = time.time()\n", "fast_fuzzer.runs(FunctionCoverageRunner(crashme), trials=n)\n", "end = time.time()\n", "\n", "\"It took the fuzzer w/ exponential schedule %0.2f seconds to generate and execute %d inputs.\" % (end - start, n)" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:43.820947Z", "iopub.status.busy": "2024-01-18T17:14:43.820830Z", "iopub.status.idle": "2024-01-18T17:14:43.822390Z", "shell.execute_reply": "2024-01-18T17:14:43.822107Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import numpy as np" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:43.824113Z", "iopub.status.busy": "2024-01-18T17:14:43.823986Z", "iopub.status.idle": "2024-01-18T17:14:43.881424Z", "shell.execute_reply": "2024-01-18T17:14:43.881145Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "image/png": "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Of/4xz+0dOlSff/739emTZt033336de//rVWrlwpSfL5fJIkt9sdcT+3220f8/l8SklJiTjevn17denSJWLmfOf44mP8t+LiYrlcLntLS0v7hlcLAABaqqhHTjgc1pAhQ/T444/ryiuv1OTJkzVp0iQtW7Ys2g910WbMmKFAIGBvDQ0NsV4SAABoJlGPnG7duqlfv34R+/r27auDBw9KkjwejyTJ7/dHzPj9fvuYx+NRY2NjxPEzZ87o8OHDETPnO8cXH+O/ORwOOZ3OiA0AAJgp6pEzbNgw1dfXR+x777331LNnT0lSenq6PB6PKioq7OPBYFDV1dXyer2SJK/Xq6NHj6qmpsae2bx5s8LhsDIzM+2ZyspKnT592p4pLy9X7969I77JBQAA2qaoR87UqVO1bds2Pf744/rggw+0atUqLV++XPn5+ZKkuLg4FRYWau7cuVq3bp327Nmj8ePHKzU1VaNHj5b0+Ts/N954oyZNmqTt27fr7bffVkFBgcaOHavU1FRJ0h133KGEhATl5eWprq5Oq1ev1oIFC1RUVBTtSwIAAK1Rc3y16/XXX7f69+9vORwOq0+fPtby5csjjofDYWvmzJmW2+22HA6HNXLkSKu+vj5i5tNPP7XGjRtndezY0XI6ndbEiROtY8eORczs3r3bGj58uOVwOKzLL7/cKikpuah18hVyAABanwt9/Y6zLMuKdWjFSjAYlMvlUiAQ4PM5AAC0Ehf6+s2/XQUAAIxE5AAAACMROQAAwEhEDgAAMBKRAwAAjETkAAAAIxE5AADASO1jvQBT9ZpeFusltBofluTEegkAAAPxTg4AADASkQMAAIxE5AAAACMROQAAwEhEDgAAMBKRAwAAjETkAAAAIxE5AADASEQOAAAwEpEDAACMROQAAAAjETkAAMBIRA4AADASkQMAAIxE5AAAACMROQAAwEhEDgAAMBKRAwAAjETkAAAAIxE5AADASEQOAAAwEpEDAACMROQAAAAjETkAAMBIRA4AADASkQMAAIxE5AAAACMROQAAwEhEDgAAMBKRAwAAjETkAAAAIxE5AADASEQOAAAwEpEDAACMROQAAAAjETkAAMBIRA4AADASkQMAAIxE5AAAACMROQAAwEhEDgAAMBKRAwAAjETkAAAAIxE5AADASEQOAAAwEpEDAACMROQAAAAjETkAAMBIRA4AADASkQMAAIxE5AAAACMROQAAwEhEDgAAMBKRAwAAjETkAAAAIzV75JSUlCguLk6FhYX2vpMnTyo/P19du3ZVx44dNWbMGPn9/oj7HTx4UDk5Obr00kuVkpKiBx54QGfOnImYefPNNzVkyBA5HA5dccUVWrFiRXNfDgAAaCWaNXJ27NihZ555RgMHDozYP3XqVL3++ut65ZVXtGXLFn388ce67bbb7ONNTU3KycnRqVOntHXrVq1cuVIrVqzQrFmz7JkDBw4oJydH119/vWpra1VYWKh77rlHmzZtas5LAgAArUSzRc7x48eVm5urZ599Vp07d7b3BwIBPf/883ryySd1ww03aOjQoXrhhRe0detWbdu2TZL0t7/9Tfv27dOLL76owYMH66abbtKjjz6qxYsX69SpU5KkZcuWKT09XfPnz1ffvn1VUFCg22+/XU899dT/XFMoFFIwGIzYAACAmZotcvLz85WTk6OsrKyI/TU1NTp9+nTE/j59+qhHjx6qqqqSJFVVVWnAgAFyu932THZ2toLBoOrq6uyZ/z53dna2fY7zKS4ulsvlsre0tLRvfJ0AAKBlapbIefnll7Vr1y4VFxefc8zn8ykhIUHJyckR+91ut3w+nz3zxcA5e/zssS+bCQaD+uyzz867rhkzZigQCNhbQ0PD17o+AADQ8rWP9gkbGho0ZcoUlZeXKzExMdqn/0YcDoccDkeslwEAAL4FUX8np6amRo2NjRoyZIjat2+v9u3ba8uWLVq4cKHat28vt9utU6dO6ejRoxH38/v98ng8kiSPx3POt63O3v6qGafTqaSkpGhfFgAAaGWiHjkjR47Unj17VFtba28ZGRnKzc21//cll1yiiooK+z719fU6ePCgvF6vJMnr9WrPnj1qbGy0Z8rLy+V0OtWvXz975ovnODtz9hwAAKBti/qfqzp16qT+/ftH7OvQoYO6du1q78/Ly1NRUZG6dOkip9Op+++/X16vV9dcc40kadSoUerXr5/uuusulZaWyufz6eGHH1Z+fr7956Z7771XixYt0oMPPqi7775bmzdv1po1a1RWVhbtSwIAAK1Q1CPnQjz11FNq166dxowZo1AopOzsbC1ZssQ+Hh8fr/Xr1+u+++6T1+tVhw4dNGHCBD3yyCP2THp6usrKyjR16lQtWLBA3bt313PPPafs7OxYXBIAAGhh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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "x_axis = np.arange(len(fast_schedule.path_frequency))\n", "y_axis = list(fast_schedule.path_frequency.values())\n", "\n", "plt.bar(x_axis, y_axis)\n", "plt.xticks(x_axis)\n", "plt.ylim(0, n)\n", "# plt.yscale(\"log\")\n", "# plt.yticks([10,100,1000,10000])\n", "plt;" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:43.883168Z", "iopub.status.busy": "2024-01-18T17:14:43.883043Z", "iopub.status.idle": "2024-01-18T17:14:43.885524Z", "shell.execute_reply": "2024-01-18T17:14:43.885208Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " path id 'p' : path frequency 'f(p)'\n" ] }, { "data": { "text/plain": [ "{'e014b68ad4f3bc2daf207e2498d14cbf': 5612,\n", " '0a1008773804033d8a4c0e3aba4b96a0': 2607,\n", " 'eae4df5b039511eac56625f47c337d24': 1105,\n", " 'b14f545c3b39716a455034d9a0c61b8c': 457,\n", " '11529f85aaa30be08110f3076748e420': 219}" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "print(\" path id 'p' : path frequency 'f(p)'\")\n", "fast_schedule.path_frequency" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "How does it compare to our greybox fuzzer with the classical power schedule?" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:43.887212Z", "iopub.status.busy": "2024-01-18T17:14:43.887087Z", "iopub.status.idle": "2024-01-18T17:14:44.043007Z", "shell.execute_reply": "2024-01-18T17:14:44.042698Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "'It took the fuzzer w/ original schedule 0.15 seconds to generate and execute 10000 inputs.'" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "seed_input = \"good\"\n", "orig_schedule = PowerSchedule()\n", "orig_fuzzer = CountingGreyboxFuzzer([seed_input], Mutator(), orig_schedule)\n", "start = time.time()\n", "orig_fuzzer.runs(FunctionCoverageRunner(crashme), trials=n)\n", "end = time.time()\n", "\n", "\"It took the fuzzer w/ original schedule %0.2f seconds to generate and execute %d inputs.\" % (end - start, n)" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:44.044862Z", "iopub.status.busy": "2024-01-18T17:14:44.044737Z", "iopub.status.idle": "2024-01-18T17:14:44.104166Z", "shell.execute_reply": "2024-01-18T17:14:44.103851Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "image/png": "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3Xef1dzcbK9pbW21HnjgAcvtdlsOh8MaM2aM1dDQEHKezz//3Lrhhhusbt26WU6n05o0aZJ18ODBkDVvv/22dfHFF1sOh8M699xzrZkzZ57RrIFAwJJkBQKBb3/BAADge3W6f7/D/js5HQm/kwMAQMcTsd/JAQAAaA+IHAAAYCQiBwAAGInIAQAARiJyAACAkYgcAABgJCIHAAAYicgBAABGInIAAICRiBwAAGAkIgcAABiJyAEAAEYicgAAgJGIHAAAYCQiBwAAGInIAQAARiJyAACAkYgcAABgJCIHAAAYicgBAABGInIAAICRiBwAAGAkIgcAABiJyAEAAEYicgAAgJGIHAAAYCQiBwAAGInIAQAARiJyAACAkYgcAABgJCIHAAAYicgBAABGInIAAICRiBwAAGAkIgcAABiJyAEAAEYicgAAgJGIHAAAYCQiBwAAGInIAQAARiJyAACAkYgcAABgJCIHAAAYicgBAABGInIAAICRYiI9gKn6FZVHeoQO46OZWZEeAQBgIF7JAQAARiJyAACAkYgcAABgJCIHAAAYicgBAABGInIAAICRiBwAAGAkIgcAABiJyAEAAEYicgAAgJGIHAAAYCQiBwAAGInIAQAARiJyAACAkYgcAABgJCIHAAAYicgBAABGapPI+eSTT3TTTTepZ8+eio+P16BBg7Rt2zb7uGVZmj59unr16qX4+HhlZGTogw8+CDnHvn37lJOTI6fTqYSEBOXm5urQoUMha9555x1dcskliouLU0pKikpLS9vicgAAQAcU9sjZv3+/Ro0apbPOOktr1qzRu+++q9mzZ6tHjx72mtLSUs2bN09lZWWqqalR165dlZmZqSNHjthrcnJyVF9fr4qKCq1evVpVVVWaMmWKfTwYDGrs2LHq27evamtrNWvWLD300ENatGhRuC8JAAB0QFGWZVnhPGFRUZE2btyov//976c8blmWkpOTdeedd+quu+6SJAUCAbndbi1evFjZ2dl67733lJaWpq1bt2rEiBGSpLVr1+qqq67Sxx9/rOTkZC1cuFD33XeffD6fYmNj7cdeuXKldu3adVqzBoNBuVwuBQIBOZ3OMFz9/9evqDys5zPZRzOzIj0CAKADOd2/32F/JWfVqlUaMWKEfvWrXykpKUkXXHCBnn32Wfv47t275fP5lJGRYe9zuVxKT09XdXW1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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "x_axis = np.arange(len(orig_schedule.path_frequency))\n", "y_axis = list(orig_schedule.path_frequency.values())\n", "\n", "plt.bar(x_axis, y_axis)\n", "plt.xticks(x_axis)\n", "plt.ylim(0, n)\n", "# plt.yscale(\"log\")\n", "# plt.yticks([10,100,1000,10000])\n", "plt;" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:44.105893Z", "iopub.status.busy": "2024-01-18T17:14:44.105764Z", "iopub.status.idle": "2024-01-18T17:14:44.108237Z", "shell.execute_reply": "2024-01-18T17:14:44.107930Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " path id 'p' : path frequency 'f(p)'\n" ] }, { "data": { "text/plain": [ "{'e014b68ad4f3bc2daf207e2498d14cbf': 6581,\n", " '0a1008773804033d8a4c0e3aba4b96a0': 2379,\n", " 'eae4df5b039511eac56625f47c337d24': 737,\n", " 'b14f545c3b39716a455034d9a0c61b8c': 241,\n", " '11529f85aaa30be08110f3076748e420': 62}" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "print(\" path id 'p' : path frequency 'f(p)'\")\n", "orig_schedule.path_frequency" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The exponential power schedule shaves some of the executions of the \"high-frequency path\" off and adds them to the lower-frequency paths. The path executed least often is either not at all exercised using the traditional power schedule or it is exercised much less often.\n", "\n", "Let's have a look at the energy that is assigned to the discovered seeds." ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:44.109925Z", "iopub.status.busy": "2024-01-18T17:14:44.109800Z", "iopub.status.idle": "2024-01-18T17:14:44.112060Z", "shell.execute_reply": "2024-01-18T17:14:44.111792Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "'e014b68ad4f3bc2daf207e2498d14cbf', 0.20000, 'good'\n", "'0a1008773804033d8a4c0e3aba4b96a0', 0.20000, 'bgI/d'\n", "'eae4df5b039511eac56625f47c337d24', 0.20000, 'baI/dt'\n", "'b14f545c3b39716a455034d9a0c61b8c', 0.20000, 'badtuS'\n", "'11529f85aaa30be08110f3076748e420', 0.20000, 'bad!`tuS'\n" ] } ], "source": [ "orig_energy = orig_schedule.normalizedEnergy(orig_fuzzer.population)\n", "\n", "for (seed, norm_energy) in zip(orig_fuzzer.population, orig_energy):\n", " print(\"'%s', %0.5f, %s\" % (getPathID(seed.coverage), # type: ignore\n", " norm_energy, repr(seed.data)))" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:44.113893Z", "iopub.status.busy": "2024-01-18T17:14:44.113771Z", "iopub.status.idle": "2024-01-18T17:14:44.115925Z", "shell.execute_reply": "2024-01-18T17:14:44.115668Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "'e014b68ad4f3bc2daf207e2498d14cbf', 0.00000, 'good'\n", "'0a1008773804033d8a4c0e3aba4b96a0', 0.00000, 'bnd'\n", "'eae4df5b039511eac56625f47c337d24', 0.00030, 'ba.'\n", "'b14f545c3b39716a455034d9a0c61b8c', 0.02464, 'bad.'\n", "'11529f85aaa30be08110f3076748e420', 0.97506, 'bad!\\\\.'\n" ] } ], "source": [ "fast_energy = fast_schedule.normalizedEnergy(fast_fuzzer.population)\n", "\n", "for (seed, norm_energy) in zip(fast_fuzzer.population, fast_energy):\n", " print(\"'%s', %0.5f, %s\" % (getPathID(seed.coverage), # type: ignore\n", " norm_energy, repr(seed.data)))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Exactly. Our new exponential power schedule assigns most energy to the seed exercising the lowest-frequency path.\n", "\n", "Let's compare them in terms of coverage achieved over time for our simple [example](#Runner-and-Sample-Program)." ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:44.117540Z", "iopub.status.busy": "2024-01-18T17:14:44.117435Z", "iopub.status.idle": "2024-01-18T17:14:44.280678Z", "shell.execute_reply": "2024-01-18T17:14:44.280326Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "_, orig_coverage = population_coverage(orig_fuzzer.inputs, crashme)\n", "_, fast_coverage = population_coverage(fast_fuzzer.inputs, crashme)\n", "line_orig, = plt.plot(orig_coverage, label=\"Original Greybox Fuzzer\")\n", "line_fast, = plt.plot(fast_coverage, label=\"Boosted Greybox Fuzzer\")\n", "plt.legend(handles=[line_orig, line_fast])\n", "plt.title('Coverage over time')\n", "plt.xlabel('# of inputs')\n", "plt.ylabel('lines covered');" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "As expected, the boosted greybox fuzzer (with the exponential power schedule) achieves coverage much faster.\n", "\n", "***Summary***. By fuzzing seeds more often that exercise low-frequency paths, we can explore program paths in a much more efficient manner.\n", "\n", "***Try it***. You can try other exponents for the fast power schedule, or change the power schedule entirely. Note that a large exponent can lead to overflows and imprecisions in the floating point arithmetic producing unexpected results. You can execute your own code by opening this chapter as Jupyter notebook.\n", "\n", "***Read***. You can find out more about fuzzer boosting in the paper \"[Coverage-based Greybox Fuzzing as Markov Chain](https://mboehme.github.io/paper/CCS16.pdf)\" \\cite{boehme2018greybox} and check out the implementation into AFL at [http://github.com/mboehme/aflfast]." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## A Complex Example: HTMLParser\n", "\n", "Let's compare the three fuzzers on a more realistic example, the Python [HTML parser](https://docs.python.org/3/library/html.parser.html). We run all three fuzzers $n=5k$ times on the HTMLParser, starting with the \"empty\" seed." ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:44.282386Z", "iopub.status.busy": "2024-01-18T17:14:44.282266Z", "iopub.status.idle": "2024-01-18T17:14:44.284388Z", "shell.execute_reply": "2024-01-18T17:14:44.284125Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from html.parser import HTMLParser" ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:44.285879Z", "iopub.status.busy": "2024-01-18T17:14:44.285757Z", "iopub.status.idle": "2024-01-18T17:14:44.287518Z", "shell.execute_reply": "2024-01-18T17:14:44.287267Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# create wrapper function\n", "def my_parser(inp: str) -> None:\n", " parser = HTMLParser() # resets the HTMLParser object for every fuzz input\n", " parser.feed(inp)" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:44.289042Z", "iopub.status.busy": "2024-01-18T17:14:44.288934Z", "iopub.status.idle": "2024-01-18T17:14:44.291361Z", "shell.execute_reply": "2024-01-18T17:14:44.291018Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "n = 5000\n", "seed_input = \" \" # empty seed\n", "blackbox_fuzzer = AdvancedMutationFuzzer([seed_input], Mutator(), PowerSchedule())\n", "greybox_fuzzer = GreyboxFuzzer([seed_input], Mutator(), PowerSchedule())\n", "boosted_fuzzer = CountingGreyboxFuzzer([seed_input], Mutator(), AFLFastSchedule(5))" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:44.292922Z", "iopub.status.busy": "2024-01-18T17:14:44.292806Z", "iopub.status.idle": "2024-01-18T17:14:56.390320Z", "shell.execute_reply": "2024-01-18T17:14:56.390042Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "'It took all three fuzzers 12.09 seconds to generate and execute 5000 inputs.'" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "start = time.time()\n", "blackbox_fuzzer.runs(FunctionCoverageRunner(my_parser), trials=n)\n", "greybox_fuzzer.runs(FunctionCoverageRunner(my_parser), trials=n)\n", "boosted_fuzzer.runs(FunctionCoverageRunner(my_parser), trials=n)\n", "end = time.time()\n", "\n", "\"It took all three fuzzers %0.2f seconds to generate and execute %d inputs.\" % (end - start, n)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "How do the fuzzers compare in terms of coverage over time?" ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:56.392078Z", "iopub.status.busy": "2024-01-18T17:14:56.391954Z", "iopub.status.idle": "2024-01-18T17:14:57.060744Z", "shell.execute_reply": "2024-01-18T17:14:57.060386Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "image/png": "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/jvE3NzcHFlZWTmOa2lpia5du2LVqlU4evQoQkNDcfny5eJ5E0SUA2t2iEhrvLy8EBsbi4cPH6J79+6QyWS4evUqevXqBTc3t9e+ftKkSZg1axYqVaqEevXqYdOmTYiIiMAPP/ygg+iBNWvWoEqVKvD19cXy5cvx9OlTDB48GADQtGlTWFlZYfr06RgzZgzCwsIQHBys9npvb29ER0cjIiICFSpUgK2tLX766SdkZWVJr9+8eTMsLS3zHTpPRNrFmh0i0qqjR4+icePGsLCwwJkzZ1ChQoUCJToAMGbMGEyYMAEfffQRateujf3792PXrl2oUqVKMUedbeHChVi4cCHq1q2LkydPYteuXVIfI0dHR2zevBl79+5F7dq18dNPP+UY+t6rVy907NgRbdu2Rbly5fDTTz/BwcEBGzduhL+/P+rUqYM///wTv//+e4FGeBGRdnAGZSIq9W7fvg0fHx9cuHAhzyUoiKjkYs0OERERGTUmO0RERGTU2IxFRERERo01O0RERGTUmOwQERGRUWOyQ0REREaNyQ4REREZNSY7REREZNSY7BAREZFRY7JDRERERo3JDhERERk1JjtERERk1P4PTpk74CmSCPcAAAAASUVORK5CYII=\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "_, black_coverage = population_coverage(blackbox_fuzzer.inputs, my_parser)\n", "_, grey_coverage = population_coverage(greybox_fuzzer.inputs, my_parser)\n", "_, boost_coverage = population_coverage(boosted_fuzzer.inputs, my_parser)\n", "line_black, = plt.plot(black_coverage, label=\"Blackbox Fuzzer\")\n", "line_grey, = plt.plot(grey_coverage, label=\"Greybox Fuzzer\")\n", "line_boost, = plt.plot(boost_coverage, label=\"Boosted Greybox Fuzzer\")\n", "plt.legend(handles=[line_boost, line_grey, line_black])\n", "plt.title('Coverage over time')\n", "plt.xlabel('# of inputs')\n", "plt.ylabel('lines covered');" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Both greybox fuzzers clearly outperform the blackbox fuzzer. The reason is that the greybox fuzzer \"discovers\" interesting inputs along the way. Let's have a look at the last 10 inputs generated by the greybox versus blackbox fuzzer." ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:57.062523Z", "iopub.status.busy": "2024-01-18T17:14:57.062389Z", "iopub.status.idle": "2024-01-18T17:14:57.064871Z", "shell.execute_reply": "2024-01-18T17:14:57.064557Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "[' H', '', '', '`', ' i', '', '(', 'j ', '', '0']" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "blackbox_fuzzer.inputs[-10:]" ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:57.066491Z", "iopub.status.busy": "2024-01-18T17:14:57.066360Z", "iopub.status.idle": "2024-01-18T17:14:57.068628Z", "shell.execute_reply": "2024-01-18T17:14:57.068325Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "['m*\\x08',\n", " 'r.5<)h',\n", " 'iq',\n", " '\\x11G',\n", " '5, [, ]`). Yet, many important keywords, such as `` are still missing. \n", "\n", "To inform the fuzzer about these important keywords, we will need [grammars](Grammars.ipynb); in the section on [smart greybox fuzzing](LangFuzzer.ipynb), we combine them with the techniques above.\n", "\n", "***Try it***. You can re-run these experiments to understand the variance of fuzzing experiments. Sometimes, the fuzzer that we claim to be superior does not seem to outperform the inferior fuzzer. In order to do this, you just need to open this chapter as Jupyter notebook." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Directed Greybox Fuzzing\n", "\n", "Sometimes, you just want the fuzzer to reach some dangerous location in the source code. This could be a location where you expect a buffer overflow. Or you want to test a recent change in your code base. How do we direct the fuzzer towards these locations?\n", "\n", "In this chapter, we introduce directed greybox fuzzing as an optimization problem." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Solving the Maze\n", "\n", "To provide a meaningful example where you can easily change the code complexity and target location, we generate the maze source code from the maze provided as string. This example is loosely based on an old [blog post](https://feliam.wordpress.com/2010/10/07/the-symbolic-maze/) on symbolic execution by Felipe Andres Manzano (Quick shout-out!).\n", "\n", "You simply specify the maze as a string. Like so." ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:57.070330Z", "iopub.status.busy": "2024-01-18T17:14:57.070214Z", "iopub.status.idle": "2024-01-18T17:14:57.071926Z", "shell.execute_reply": "2024-01-18T17:14:57.071603Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "maze_string = \"\"\"\n", "+-+-----+\n", "|X| |\n", "| | --+ |\n", "| | | |\n", "| +-- | |\n", "| |#|\n", "+-----+-+\n", "\"\"\"" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The code is generated using the function `generate_maze_code()`. We'll hide the implementation and instead explain what it does. If you are interested in the coding, go [here](ControlFlow.ipynb#Example:-Maze)." ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:57.073668Z", "iopub.status.busy": "2024-01-18T17:14:57.073534Z", "iopub.status.idle": "2024-01-18T17:14:57.143839Z", "shell.execute_reply": "2024-01-18T17:14:57.143522Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from ControlFlow import generate_maze_code" ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:57.145678Z", "iopub.status.busy": "2024-01-18T17:14:57.145557Z", "iopub.status.idle": "2024-01-18T17:14:57.147323Z", "shell.execute_reply": "2024-01-18T17:14:57.147062Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# ignore\n", "def maze(s: str) -> str:\n", " return \"\" # Will be overwritten by exec()" ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:57.148752Z", "iopub.status.busy": "2024-01-18T17:14:57.148654Z", "iopub.status.idle": "2024-01-18T17:14:57.150152Z", "shell.execute_reply": "2024-01-18T17:14:57.149923Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "# ignore\n", "def target_tile() -> str:\n", " return ' ' # Will be overwritten by exec()" ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:57.151624Z", "iopub.status.busy": "2024-01-18T17:14:57.151524Z", "iopub.status.idle": "2024-01-18T17:14:57.153222Z", "shell.execute_reply": "2024-01-18T17:14:57.152966Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "maze_code = generate_maze_code(maze_string)" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:57.154722Z", "iopub.status.busy": "2024-01-18T17:14:57.154640Z", "iopub.status.idle": "2024-01-18T17:14:57.158546Z", "shell.execute_reply": "2024-01-18T17:14:57.158212Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "exec(maze_code)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The objective is to get the \"X\" to the \"#\" by providing inputs `D` for down, `U` for up, `L` for left, and `R` for right." ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:57.160189Z", "iopub.status.busy": "2024-01-18T17:14:57.160072Z", "iopub.status.idle": "2024-01-18T17:14:57.161800Z", "shell.execute_reply": "2024-01-18T17:14:57.161513Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "SOLVED\n", "\n", "+-+-----+\n", "| | |\n", "| | --+ |\n", "| | | |\n", "| +-- | |\n", "| |X|\n", "+-----+-+\n", "\n" ] } ], "source": [ "print(maze(\"DDDDRRRRUULLUURRRRDDDD\")) # Appending one more 'D', you have reached the target." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Each character in `maze_string` represents a tile. For each tile, a tile-function is generated. \n", "* If the current tile is \"benign\" (` `), the tile-function corresponding to the next input character (D, U, L, R) is called. Unexpected input characters are ignored. If no more input characters are left, it returns \"VALID\" and the current maze state.\n", "* If the current tile is a \"trap\" (`+`,`|`,`-`), it returns \"INVALID\" and the current maze state.\n", "* If the current tile is the \"target\" (`#`), it returns \"SOLVED\" and the current maze state.\n", "\n", "***Try it***. You can test other sequences of input characters, or even change the maze entirely. In order to execute your own code, you just need to open this chapter as Jupyter notebook.\n", "\n", "To get an idea of the generated code, lets look at the static [call graph](https://en.wikipedia.org/wiki/Call_graph). A call graph shows the order in which functions can be executed." ] }, { "cell_type": "code", "execution_count": 64, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:57.163466Z", "iopub.status.busy": "2024-01-18T17:14:57.163357Z", "iopub.status.idle": "2024-01-18T17:14:57.164829Z", "shell.execute_reply": "2024-01-18T17:14:57.164574Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from ControlFlow import callgraph" ] }, { "cell_type": "code", "execution_count": 65, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:57.166266Z", "iopub.status.busy": "2024-01-18T17:14:57.166180Z", "iopub.status.idle": "2024-01-18T17:14:57.754004Z", "shell.execute_reply": "2024-01-18T17:14:57.753576Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "G\n", "\n", "\n", "cluster_G\n", "\n", "\n", "\n", "cluster_callgraphX\n", "\n", "callgraph\n", "\n", "\n", "\n", "callgraphX\n", "\n", "callgraph\n", "\n", "\n", "\n", "callgraphX__maze\n", "\n", "maze\n", "(callgraph.py:84)\n", "\n", "\n", "\n", "callgraphX->callgraphX__maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__print_maze\n", "\n", "print_maze\n", "(callgraph.py:2)\n", "\n", "\n", "\n", "callgraphX->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__target_tile\n", "\n", "target_tile\n", "(callgraph.py:358)\n", "\n", "\n", "\n", "callgraphX->callgraphX__target_tile\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_1_0\n", "\n", "tile_1_0\n", "(callgraph.py:26)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_1_0\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_1_1\n", "\n", "tile_1_1\n", "(callgraph.py:31)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_1_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_1_2\n", "\n", "tile_1_2\n", "(callgraph.py:36)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_1_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_1_3\n", "\n", "tile_1_3\n", "(callgraph.py:41)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_1_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_1_4\n", "\n", "tile_1_4\n", "(callgraph.py:46)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_1_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_1_5\n", "\n", "tile_1_5\n", "(callgraph.py:51)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_1_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_1_6\n", "\n", "tile_1_6\n", "(callgraph.py:56)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_1_6\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_1_7\n", "\n", "tile_1_7\n", "(callgraph.py:61)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_1_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_1_8\n", "\n", "tile_1_8\n", "(callgraph.py:66)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_1_8\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_0\n", "\n", "tile_2_0\n", "(callgraph.py:71)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_2_0\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_1\n", "\n", "tile_2_1\n", "(callgraph.py:76)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_2_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_2\n", "\n", "tile_2_2\n", "(callgraph.py:87)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_2_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_3\n", "\n", "tile_2_3\n", "(callgraph.py:92)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_2_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_4\n", "\n", "tile_2_4\n", "(callgraph.py:100)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_2_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_5\n", "\n", "tile_2_5\n", "(callgraph.py:108)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_2_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_6\n", "\n", "tile_2_6\n", "(callgraph.py:116)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_2_6\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_7\n", "\n", "tile_2_7\n", "(callgraph.py:124)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_2_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_8\n", "\n", "tile_2_8\n", "(callgraph.py:132)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_2_8\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_0\n", "\n", "tile_3_0\n", "(callgraph.py:137)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_3_0\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_1\n", "\n", "tile_3_1\n", "(callgraph.py:142)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_3_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_2\n", "\n", "tile_3_2\n", "(callgraph.py:150)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_3_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_3\n", "\n", "tile_3_3\n", "(callgraph.py:155)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_3_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_4\n", "\n", "tile_3_4\n", "(callgraph.py:163)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_3_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_5\n", "\n", "tile_3_5\n", "(callgraph.py:168)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_3_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_6\n", "\n", "tile_3_6\n", "(callgraph.py:173)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_3_6\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_7\n", "\n", "tile_3_7\n", "(callgraph.py:178)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_3_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_8\n", "\n", "tile_3_8\n", "(callgraph.py:186)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_3_8\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_0\n", "\n", "tile_4_0\n", "(callgraph.py:191)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_4_0\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_1\n", "\n", "tile_4_1\n", "(callgraph.py:196)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_4_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_2\n", "\n", "tile_4_2\n", "(callgraph.py:204)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_4_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_3\n", "\n", "tile_4_3\n", "(callgraph.py:209)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_4_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_4\n", "\n", "tile_4_4\n", "(callgraph.py:217)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_4_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_5\n", "\n", "tile_4_5\n", "(callgraph.py:225)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_4_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_6\n", "\n", "tile_4_6\n", "(callgraph.py:233)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_4_6\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_7\n", "\n", "tile_4_7\n", "(callgraph.py:238)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_4_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_8\n", "\n", "tile_4_8\n", "(callgraph.py:246)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_4_8\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_0\n", "\n", "tile_5_0\n", "(callgraph.py:251)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_5_0\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_1\n", "\n", "tile_5_1\n", "(callgraph.py:256)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_5_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_2\n", "\n", "tile_5_2\n", "(callgraph.py:264)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_5_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_3\n", "\n", "tile_5_3\n", "(callgraph.py:269)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_5_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_4\n", "\n", "tile_5_4\n", "(callgraph.py:274)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_5_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_5\n", "\n", "tile_5_5\n", "(callgraph.py:279)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_5_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_6\n", "\n", "tile_5_6\n", "(callgraph.py:287)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_5_6\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_7\n", "\n", "tile_5_7\n", "(callgraph.py:292)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_5_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_8\n", "\n", "tile_5_8\n", "(callgraph.py:300)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_5_8\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_0\n", "\n", "tile_6_0\n", "(callgraph.py:305)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_6_0\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_1\n", "\n", "tile_6_1\n", "(callgraph.py:310)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_6_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_2\n", "\n", "tile_6_2\n", "(callgraph.py:318)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_6_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_3\n", "\n", "tile_6_3\n", "(callgraph.py:326)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_6_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_4\n", "\n", "tile_6_4\n", "(callgraph.py:334)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_6_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_5\n", "\n", "tile_6_5\n", "(callgraph.py:342)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_6_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_6\n", "\n", "tile_6_6\n", "(callgraph.py:350)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_6_6\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_7\n", "\n", "tile_6_7\n", "(callgraph.py:355)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_6_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_8\n", "\n", "tile_6_8\n", "(callgraph.py:361)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_6_8\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_7_0\n", "\n", "tile_7_0\n", "(callgraph.py:366)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_7_0\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_7_1\n", "\n", "tile_7_1\n", "(callgraph.py:371)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_7_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_7_2\n", "\n", "tile_7_2\n", "(callgraph.py:376)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_7_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_7_3\n", "\n", "tile_7_3\n", "(callgraph.py:381)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_7_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_7_4\n", "\n", "tile_7_4\n", "(callgraph.py:386)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_7_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_7_5\n", "\n", "tile_7_5\n", "(callgraph.py:391)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_7_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_7_6\n", "\n", "tile_7_6\n", "(callgraph.py:396)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_7_6\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_7_7\n", "\n", "tile_7_7\n", "(callgraph.py:401)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_7_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_7_8\n", "\n", "tile_7_8\n", "(callgraph.py:406)\n", "\n", "\n", "\n", "callgraphX->callgraphX__tile_7_8\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__maze->callgraphX__tile_2_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_1_0->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_1_1->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_1_2->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_1_3->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_1_4->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_1_5->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_1_6->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_1_7->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_1_8->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_0->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_1->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_1->callgraphX__tile_1_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_1->callgraphX__tile_2_0\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_1->callgraphX__tile_2_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_1->callgraphX__tile_2_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_1->callgraphX__tile_3_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_2->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_3->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_3->callgraphX__tile_1_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_3->callgraphX__tile_2_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_3->callgraphX__tile_2_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_3->callgraphX__tile_2_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_3->callgraphX__tile_3_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_4->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_4->callgraphX__tile_1_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_4->callgraphX__tile_2_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_4->callgraphX__tile_2_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_4->callgraphX__tile_2_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_4->callgraphX__tile_3_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_5->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_5->callgraphX__tile_1_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_5->callgraphX__tile_2_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_5->callgraphX__tile_2_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_5->callgraphX__tile_2_6\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_5->callgraphX__tile_3_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_6->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_6->callgraphX__tile_1_6\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_6->callgraphX__tile_2_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_6->callgraphX__tile_2_6\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_6->callgraphX__tile_2_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_6->callgraphX__tile_3_6\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_7->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_7->callgraphX__tile_1_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_7->callgraphX__tile_2_6\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_7->callgraphX__tile_2_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_7->callgraphX__tile_2_8\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_7->callgraphX__tile_3_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_2_8->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_0->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_1->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_1->callgraphX__tile_2_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_1->callgraphX__tile_3_0\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_1->callgraphX__tile_3_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_1->callgraphX__tile_3_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_1->callgraphX__tile_4_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_2->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_3->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_3->callgraphX__tile_2_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_3->callgraphX__tile_3_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_3->callgraphX__tile_3_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_3->callgraphX__tile_3_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_3->callgraphX__tile_4_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_4->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_5->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_6->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_7->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_7->callgraphX__tile_2_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_7->callgraphX__tile_3_6\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_7->callgraphX__tile_3_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_7->callgraphX__tile_3_8\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_7->callgraphX__tile_4_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_3_8->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_0->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_1->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_1->callgraphX__tile_3_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_1->callgraphX__tile_4_0\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_1->callgraphX__tile_4_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_1->callgraphX__tile_4_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_1->callgraphX__tile_5_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_2->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_3->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_3->callgraphX__tile_3_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_3->callgraphX__tile_4_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_3->callgraphX__tile_4_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_3->callgraphX__tile_4_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_3->callgraphX__tile_5_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_4->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_4->callgraphX__tile_3_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_4->callgraphX__tile_4_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_4->callgraphX__tile_4_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_4->callgraphX__tile_4_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_4->callgraphX__tile_5_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_5->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_5->callgraphX__tile_3_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_5->callgraphX__tile_4_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_5->callgraphX__tile_4_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_5->callgraphX__tile_4_6\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_5->callgraphX__tile_5_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_6->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_7->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_7->callgraphX__tile_3_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_7->callgraphX__tile_4_6\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_7->callgraphX__tile_4_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_7->callgraphX__tile_4_8\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_7->callgraphX__tile_5_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_4_8->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_0->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_1->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_1->callgraphX__tile_4_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_1->callgraphX__tile_5_0\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_1->callgraphX__tile_5_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_1->callgraphX__tile_5_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_1->callgraphX__tile_6_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_2->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_3->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_4->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_5->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_5->callgraphX__tile_4_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_5->callgraphX__tile_5_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_5->callgraphX__tile_5_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_5->callgraphX__tile_5_6\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_5->callgraphX__tile_6_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_6->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_7->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_7->callgraphX__tile_4_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_7->callgraphX__tile_5_6\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_7->callgraphX__tile_5_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_7->callgraphX__tile_5_8\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_7->callgraphX__tile_6_7\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_5_8->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_0->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_1->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_1->callgraphX__tile_5_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_1->callgraphX__tile_6_0\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_1->callgraphX__tile_6_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_1->callgraphX__tile_6_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_1->callgraphX__tile_7_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_2->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_2->callgraphX__tile_5_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_2->callgraphX__tile_6_1\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_2->callgraphX__tile_6_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_2->callgraphX__tile_6_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_2->callgraphX__tile_7_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_3->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_3->callgraphX__tile_5_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_3->callgraphX__tile_6_2\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_3->callgraphX__tile_6_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_3->callgraphX__tile_6_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_3->callgraphX__tile_7_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_4->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_4->callgraphX__tile_5_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_4->callgraphX__tile_6_3\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_4->callgraphX__tile_6_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_4->callgraphX__tile_6_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_4->callgraphX__tile_7_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_5->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_5->callgraphX__tile_5_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_5->callgraphX__tile_6_4\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_5->callgraphX__tile_6_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_5->callgraphX__tile_6_6\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_5->callgraphX__tile_7_5\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_6->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_7->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_6_8->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_7_0->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_7_1->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_7_2->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_7_3->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_7_4->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_7_5->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_7_6->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_7_7->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n", "callgraphX__tile_7_8->callgraphX__print_maze\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "callgraph(maze_code)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### A First Attempt\n", "\n", "We introduce a `DictMutator` class which mutates strings by inserting a keyword from a given dictionary:" ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:57.756388Z", "iopub.status.busy": "2024-01-18T17:14:57.756257Z", "iopub.status.idle": "2024-01-18T17:14:57.759325Z", "shell.execute_reply": "2024-01-18T17:14:57.758896Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class DictMutator(Mutator):\n", " \"\"\"Variant of `Mutator` inserting keywords from a dictionary\"\"\"\n", "\n", " def __init__(self, dictionary: Sequence[str]) -> None:\n", " \"\"\"Constructor.\n", " `dictionary` - a list of strings that can be used as keywords\n", " \"\"\"\n", " super().__init__()\n", " self.dictionary = dictionary\n", " self.mutators.append(self.insert_from_dictionary)\n", "\n", " def insert_from_dictionary(self, s: str) -> str:\n", " \"\"\"Returns `s` with a keyword from the dictionary inserted\"\"\"\n", " pos = random.randint(0, len(s))\n", " random_keyword = random.choice(self.dictionary)\n", " return s[:pos] + random_keyword + s[pos:]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "To fuzz the maze, we extend the `DictMutator` class to append dictionary keywords to the end of the seed and to remove a character from the end of the seed." ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:57.761103Z", "iopub.status.busy": "2024-01-18T17:14:57.760997Z", "iopub.status.idle": "2024-01-18T17:14:57.763821Z", "shell.execute_reply": "2024-01-18T17:14:57.763487Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class MazeMutator(DictMutator):\n", " def __init__(self, dictionary: Sequence[str]) -> None:\n", " super().__init__(dictionary)\n", " self.mutators.append(self.delete_last_character)\n", " self.mutators.append(self.append_from_dictionary)\n", "\n", " def append_from_dictionary(self, s: str) -> str:\n", " \"\"\"Returns s with a keyword from the dictionary appended\"\"\"\n", " random_keyword = random.choice(self.dictionary)\n", " return s + random_keyword\n", "\n", " def delete_last_character(self, s: str) -> str:\n", " \"\"\"Returns s without the last character\"\"\"\n", " if len(s) > 0:\n", " return s[:-1]\n", " return s" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Let's try a standard greybox fuzzer with the classic power schedule and our extended maze mutator (n=20k)." ] }, { "cell_type": "code", "execution_count": 68, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:14:57.765553Z", "iopub.status.busy": "2024-01-18T17:14:57.765448Z", "iopub.status.idle": "2024-01-18T17:15:04.531093Z", "shell.execute_reply": "2024-01-18T17:15:04.530766Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'It took the fuzzer 6.76 seconds to generate and execute 20000 inputs.'" ] }, "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ "n = 20000\n", "seed_input = \" \" # empty seed\n", "\n", "maze_mutator = MazeMutator([\"L\", \"R\", \"U\", \"D\"])\n", "maze_schedule = PowerSchedule()\n", "maze_fuzzer = GreyboxFuzzer([seed_input], maze_mutator, maze_schedule)\n", "\n", "start = time.time()\n", "maze_fuzzer.runs(FunctionCoverageRunner(maze), trials=n)\n", "end = time.time()\n", "\n", "\"It took the fuzzer %0.2f seconds to generate and execute %d inputs.\" % (end - start, n)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "We will need to print statistics for several fuzzers. Why don't we define a function for that?" ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:04.532766Z", "iopub.status.busy": "2024-01-18T17:15:04.532652Z", "iopub.status.idle": "2024-01-18T17:15:04.535092Z", "shell.execute_reply": "2024-01-18T17:15:04.534833Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "def print_stats(fuzzer: GreyboxFuzzer) -> None:\n", " total = len(fuzzer.population)\n", " solved = 0\n", " invalid = 0\n", " valid = 0\n", " for seed in fuzzer.population:\n", " s = maze(str(seed.data))\n", " if \"INVALID\" in s:\n", " invalid += 1\n", " elif \"VALID\" in s:\n", " valid += 1\n", " elif \"SOLVED\" in s:\n", " solved += 1\n", " if solved == 1:\n", " print(\"First solution: %s\" % repr(seed))\n", " else:\n", " print(\"??\")\n", "\n", " print(\"\"\"Out of %d seeds,\n", "* %4d solved the maze,\n", "* %4d were valid but did not solve the maze, and\n", "* %4d were invalid\"\"\" % (total, solved, valid, invalid))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "How well does our good, old greybox fuzzer do?" ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:04.536547Z", "iopub.status.busy": "2024-01-18T17:15:04.536442Z", "iopub.status.idle": "2024-01-18T17:15:04.551396Z", "shell.execute_reply": "2024-01-18T17:15:04.551135Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Out of 1398 seeds,\n", "* 0 solved the maze,\n", "* 270 were valid but did not solve the maze, and\n", "* 1128 were invalid\n" ] } ], "source": [ "print_stats(maze_fuzzer)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "It probably didn't solve the maze a single time. How can we make the fuzzer aware how \"far\" a seed is from reaching the target? If we know that, we can just assign more energy to that seed.\n", "\n", "***Try it***. Print the statistics for the boosted fuzzer using the `AFLFastSchedule` and the `CountingGreyboxFuzzer`. It will likely perform much better than the unboosted greybox fuzzer: The lowest-probablity path happens to be also the path which reaches the target. You can execute your own code by opening this chapter as Jupyter notebook." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Computing Function-Level Distance\n", "\n", "Using the static call graph for the maze code and the target function, we can compute the distance of each function $f$ to the target $t$ as the length of the shortest path between $f$ and $t$.\n", "\n", "Fortunately, the generated maze code includes a function called `target_tile` which returns the name of the target-function." ] }, { "cell_type": "code", "execution_count": 71, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:04.552920Z", "iopub.status.busy": "2024-01-18T17:15:04.552835Z", "iopub.status.idle": "2024-01-18T17:15:04.554813Z", "shell.execute_reply": "2024-01-18T17:15:04.554586Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'tile_6_7'" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "target = target_tile()\n", "target" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Now, we need to find the corresponding function in the call graph. The function `get_callgraph` returns the call graph for the maze code as [networkx](https://networkx.github.io/) graph. Networkx provides some useful functions for graph analysis." ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:04.556295Z", "iopub.status.busy": "2024-01-18T17:15:04.556212Z", "iopub.status.idle": "2024-01-18T17:15:04.557817Z", "shell.execute_reply": "2024-01-18T17:15:04.557589Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import networkx as nx # type: ignore" ] }, { "cell_type": "code", "execution_count": 73, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:04.559207Z", "iopub.status.busy": "2024-01-18T17:15:04.559127Z", "iopub.status.idle": "2024-01-18T17:15:04.560675Z", "shell.execute_reply": "2024-01-18T17:15:04.560439Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from ControlFlow import get_callgraph" ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:04.562094Z", "iopub.status.busy": "2024-01-18T17:15:04.562006Z", "iopub.status.idle": "2024-01-18T17:15:04.579210Z", "shell.execute_reply": "2024-01-18T17:15:04.578950Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "cg = get_callgraph(maze_code)\n", "for node in cg.nodes():\n", " if target in node:\n", " target_node = node\n", " break" ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:04.580695Z", "iopub.status.busy": "2024-01-18T17:15:04.580594Z", "iopub.status.idle": "2024-01-18T17:15:04.582606Z", "shell.execute_reply": "2024-01-18T17:15:04.582371Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'callgraphX__tile_6_7'" ] }, "execution_count": 75, "metadata": {}, "output_type": "execute_result" } ], "source": [ "target_node" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "We can now generate the function-level distance. The dictionary `distance` contains for each function the distance to the target-function. If there is no path to the target, we assign a maximum distance (`0xFFFF`).\n", "\n", "The function `nx.shortest_path_length(CG, node, target_node)` returns the length of the shortest path from function `node` to function `target_node` in the call graph `CG`." ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:04.583989Z", "iopub.status.busy": "2024-01-18T17:15:04.583910Z", "iopub.status.idle": "2024-01-18T17:15:04.586340Z", "shell.execute_reply": "2024-01-18T17:15:04.586106Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "distance = {}\n", "for node in cg.nodes():\n", " if \"__\" in node:\n", " name = node.split(\"__\")[-1]\n", " else:\n", " name = node\n", " try:\n", " distance[name] = nx.shortest_path_length(cg, node, target_node)\n", " except:\n", " distance[name] = 0xFFFF" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "These are the distance values for all tile-functions on the path to the target function." ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:04.587850Z", "iopub.status.busy": "2024-01-18T17:15:04.587762Z", "iopub.status.idle": "2024-01-18T17:15:04.590228Z", "shell.execute_reply": "2024-01-18T17:15:04.589982Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "{'callgraphX': 1,\n", " 'maze': 23,\n", " 'tile_2_1': 22,\n", " 'tile_2_3': 8,\n", " 'tile_2_4': 7,\n", " 'tile_2_5': 6,\n", " 'tile_2_6': 5,\n", " 'tile_2_7': 4,\n", " 'tile_3_1': 21,\n", " 'tile_3_3': 9,\n", " 'tile_3_7': 3,\n", " 'tile_4_1': 20,\n", " 'tile_4_3': 10,\n", " 'tile_4_4': 11,\n", " 'tile_4_5': 12,\n", " 'tile_4_7': 2,\n", " 'tile_5_1': 19,\n", " 'tile_5_5': 13,\n", " 'tile_5_7': 1,\n", " 'tile_6_1': 18,\n", " 'tile_6_2': 17,\n", " 'tile_6_3': 16,\n", " 'tile_6_4': 15,\n", " 'tile_6_5': 14,\n", " 'tile_6_7': 0}" ] }, "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ "{k: distance[k] for k in list(distance) if distance[k] < 0xFFFF}" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "***Summary***. Using the static call graph and the target function $t$, we have shown how to compute the function-level distance of each function $f$ to the target $t$.\n", "\n", "***Try it***. You can try and execute your own code by opening this chapter as Jupyter notebook.\n", "\n", "* How do we compute distance if there are multiple targets? (Hint: [Geometric Mean](https://en.wikipedia.org/wiki/Geometric_mean)).\n", "* Given the call graph (CG) and the control-flow graph (CFG$_f$) for each function $f$, how do we compute basic-block (BB)-level distance? (Hint: In CFG$_f$, measure the BB-level distance to *calls* of functions on the path to the target function. Remember that BB-level distance in functions with higher function-level distance is higher, too.)\n", "\n", "***Read***. If you are interested in other aspects of search, you can follow up by reading the chapter on [Search-based Fuzzing](SearchBasedFuzzer.ipynb). If you are interested, how to solve the problems above, you can have a look at our paper on \"[Directed Greybox Fuzzing](https://mboehme.github.io/paper/CCS17.pdf)\"." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Directed Power Schedule\n", "Now that we know how to compute the function-level distance, let's try to implement a power schedule that assigns *more energy to seeds with a lower average distance* to the target function. Notice that the distance values are all *pre-computed*. These values are injected into the program binary, just like the coverage instrumentation. In practice, this makes the computation of the average distance *extremely efficient*.\n", "\n", "If you really want to know. Given the function-level distance $d_f(s,t)$ of a function $s$ to a function $t$ in call graph $CG$, our directed power schedule computes the seed distance $d(i,t)$ for a seed $i$ to function $t$ as $d(i,t)=\\dfrac{\\sum_{s\\in CG} d_f(s,t)}{|CG|}$ where $|CG|$ is the number of nodes in the call graph $CG$." ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:04.591731Z", "iopub.status.busy": "2024-01-18T17:15:04.591648Z", "iopub.status.idle": "2024-01-18T17:15:04.594482Z", "shell.execute_reply": "2024-01-18T17:15:04.594248Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class DirectedSchedule(PowerSchedule):\n", " \"\"\"Assign high energy to seeds close to some target\"\"\"\n", "\n", " def __init__(self, distance: Dict[str, int], exponent: float) -> None:\n", " self.distance = distance\n", " self.exponent = exponent\n", "\n", " def __getFunctions__(self, coverage: Set[Location]) -> Set[str]:\n", " functions = set()\n", " for f, _ in set(coverage):\n", " functions.add(f)\n", " return functions\n", "\n", " def assignEnergy(self, population: Sequence[Seed]) -> None:\n", " \"\"\"Assigns each seed energy inversely proportional\n", " to the average function-level distance to target.\"\"\"\n", " for seed in population:\n", " if seed.distance < 0:\n", " num_dist = 0\n", " sum_dist = 0\n", " for f in self.__getFunctions__(seed.coverage):\n", " if f in list(self.distance):\n", " sum_dist += self.distance[f]\n", " num_dist += 1\n", " seed.distance = sum_dist / num_dist\n", " seed.energy = (1 / seed.distance) ** self.exponent" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Let's see how the directed schedule performs against the good, old greybox fuzzer." ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:04.595939Z", "iopub.status.busy": "2024-01-18T17:15:04.595839Z", "iopub.status.idle": "2024-01-18T17:15:13.308418Z", "shell.execute_reply": "2024-01-18T17:15:13.308087Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'It took the fuzzer 8.71 seconds to generate and execute 20000 inputs.'" ] }, "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ "directed_schedule = DirectedSchedule(distance, 3)\n", "directed_fuzzer = GreyboxFuzzer([seed_input], maze_mutator, directed_schedule)\n", "\n", "start = time.time()\n", "directed_fuzzer.runs(FunctionCoverageRunner(maze), trials=n)\n", "end = time.time()\n", "\n", "\"It took the fuzzer %0.2f seconds to generate and execute %d inputs.\" % (end - start, n)" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:13.310203Z", "iopub.status.busy": "2024-01-18T17:15:13.310090Z", "iopub.status.idle": "2024-01-18T17:15:13.337847Z", "shell.execute_reply": "2024-01-18T17:15:13.337568Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Out of 2708 seeds,\n", "* 0 solved the maze,\n", "* 1020 were valid but did not solve the maze, and\n", "* 1688 were invalid\n" ] } ], "source": [ "print_stats(directed_fuzzer)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "It probably didn't solve a single maze either, but we have more valid solutions. So, there is definitely progress.\n", "\n", "Let's have a look at the distance values for each seed." ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:13.339610Z", "iopub.status.busy": "2024-01-18T17:15:13.339459Z", "iopub.status.idle": "2024-01-18T17:15:13.451424Z", "shell.execute_reply": "2024-01-18T17:15:13.450024Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "image/png": "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JCy8pjimbXAGKPOvKnLlO1IqJMy/qsjjfbnLalQKzJ5QGHCdsycU+szXJHe9PbWBiIgoHAya4tSU4hwsmVkCvUYpq7xWpUCKXu3xmr9YIceow71lhcgx+r9slW3U+Xxcf0pxDhbPLEF2CMvOm1aEe8sKu7VLIQVONxBovf764q8NREREoWKepgiJRnJLgBnBA62XGcGJiChcTG4ZA9EKmoiIiCh6mNySiIiIKMIYNBERERHJwKCJiIiISIaYBk2LFi3CBRdcgOTkZGRmZmLGjBnYu3evR5lLL70UkiR5/Nx3330eZQ4fPozp06dDr9cjMzMTjz76KLq6ujzKrFmzBiUlJdBqtRg2bBiWLl3arT0vv/wyCgoKoNPpMH78eGzevDnifSYiIqK+KaZB01dffYU5c+Zg48aNWLlyJTo7OzFp0iS0trZ6lJs9ezaqq6tdP88995zrPZvNhunTp8NqtWLDhg148803sXTpUjz55JOuMlVVVZg+fTouu+wybN++HQ8++CDuvvtufPbZZ64y//znP/Hwww9j/vz52LZtG8aMGYPJkyejrq4u+gNBREREcS+unp47ceIEMjMz8dVXX6GsrAyA45umc889F7///e99LvPpp5/ihz/8IY4fP46srCwAwJIlSzB37lycOHECGo0Gc+fOxSeffIKKigrXcjfddBOampqwYsUKAMD48eNxwQUX4I9//CMAwG63Y/DgwfjJT36Cn//85z22nU/PERER9T199uk5k8kEAEhL85z24u9//zsyMjJQXFyMefPmoa3t9KSu5eXlGDVqlCtgAoDJkyfDbDZj165drjITJ070qHPy5MkoLy8HAFitVmzdutWjjEKhwMSJE11lvFksFpjNZo8fIiIi6r9UsW6Ak91ux4MPPoiLL74YxcWnpxD50Y9+hPz8fOTm5mLnzp2YO3cu9u7di/feew8AUFNT4xEwAXD9XlNTE7CM2WxGe3s7GhsbYbPZfJbZs2ePz/YuWrQICxcuDK/TRERE1GfETdA0Z84cVFRUYP369R6v33PPPa7/jxo1Cjk5Objiiiuwf/9+DB06tLeb6TJv3jw8/PDDrt/NZjMGDx4cs/YQERFRdMVF0PTAAw/g448/xtq1azFo0KCAZcePHw8A+P777zF06FBkZ2d3e8qttrYWAJCdne361/maexmDwYCEhAQolUoolUqf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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "y = [seed.distance for seed in directed_fuzzer.population] # type: ignore\n", "x = range(len(y))\n", "plt.scatter(x, y)\n", "plt.ylim(0, max(y))\n", "plt.xlabel(\"Seed ID\")\n", "plt.ylabel(\"Distance\");" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Let's normalize the y-axis and improve the importance of the small distance seeds." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Improved Directed Power Schedule\n", "\n", "The improved directed schedule normalizes seed distance between the minimal and maximal distance.\n", "Again, if you really want to know. Given the seed distance $d(i,t)$ of a seed $i$ to a function $t$, our improved power schedule computes the new seed distance $d'(i,t)$ as \n", "$$\n", "d'(i,t)=\\begin{cases}\n", "1 & \\text{if } d(i,t) = \\text{minD} = \\text{maxD}\\\\\n", "\\text{maxD} - \\text{minD} & \\text{if } d(i,t) = \\text{minD} \\neq \\text{maxD}\\\\\n", "\\frac{\\text{maxD} - \\text{minD}}{d(i,t)-\\text{minD}} & \\text{otherwise}\n", "\\end{cases}\n", "$$\n", "where \n", "$$\\text{minD}=\\min_{i\\in T}[d(i,t)]$$\n", "and\n", "$$\\text{maxD}=\\max_{i\\in T}[d(i,t)]$$\n", "where $T$ is the set of seeds (i.e., the population)." ] }, { "cell_type": "code", "execution_count": 82, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:13.458579Z", "iopub.status.busy": "2024-01-18T17:15:13.457317Z", "iopub.status.idle": "2024-01-18T17:15:13.467750Z", "shell.execute_reply": "2024-01-18T17:15:13.466645Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class AFLGoSchedule(DirectedSchedule):\n", " \"\"\"Assign high energy to seeds close to the target\"\"\"\n", "\n", " def assignEnergy(self, population: Sequence[Seed]):\n", " \"\"\"Assigns each seed energy inversely proportional\n", " to the average function-level distance to target.\"\"\"\n", " min_dist: Union[int, float] = 0xFFFF\n", " max_dist: Union[int, float] = 0\n", "\n", " for seed in population:\n", " if seed.distance < 0:\n", " num_dist = 0\n", " sum_dist = 0\n", " for f in self.__getFunctions__(seed.coverage):\n", " if f in list(self.distance):\n", " sum_dist += self.distance[f]\n", " num_dist += 1\n", " seed.distance = sum_dist / num_dist\n", " if seed.distance < min_dist:\n", " min_dist = seed.distance\n", " if seed.distance > max_dist:\n", " max_dist = seed.distance\n", "\n", " for seed in population:\n", " if seed.distance == min_dist:\n", " if min_dist == max_dist:\n", " seed.energy = 1\n", " else:\n", " seed.energy = max_dist - min_dist\n", " else:\n", " seed.energy = (max_dist - min_dist) / (seed.distance - min_dist)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Let's see how the improved power schedule performs." ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:13.475923Z", "iopub.status.busy": "2024-01-18T17:15:13.475225Z", "iopub.status.idle": "2024-01-18T17:15:29.650719Z", "shell.execute_reply": "2024-01-18T17:15:29.650416Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'It took the fuzzer 16.17 seconds to generate and execute 20000 inputs.'" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "aflgo_schedule = AFLGoSchedule(distance, 3)\n", "aflgo_fuzzer = GreyboxFuzzer([seed_input], maze_mutator, aflgo_schedule)\n", "\n", "start = time.time()\n", "aflgo_fuzzer.runs(FunctionCoverageRunner(maze), trials=n)\n", "end = time.time()\n", "\n", "\"It took the fuzzer %0.2f seconds to generate and execute %d inputs.\" % (end - start, n)" ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:29.652670Z", "iopub.status.busy": "2024-01-18T17:15:29.652473Z", "iopub.status.idle": "2024-01-18T17:15:29.707292Z", "shell.execute_reply": "2024-01-18T17:15:29.707013Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "First solution: DP{\u00049DsDmD)$gPRROR\"jRUUHtLFLPUU=DURRR/RDDDDD\n", "Out of 3918 seeds,\n", "* 680 solved the maze,\n", "* 348 were valid but did not solve the maze, and\n", "* 2890 were invalid\n" ] } ], "source": [ "print_stats(aflgo_fuzzer)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "In contrast to all previous power schedules, this one generates hundreds of solutions. It has generated many solutions. \n", "\n", "Let's filter out all ignored input characters from the first solution. The function `filter(f, seed.data)` returns a list of elements `e` in `seed.data` where the function `f` applied on `e` returns True." ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:29.709286Z", "iopub.status.busy": "2024-01-18T17:15:29.709085Z", "iopub.status.idle": "2024-01-18T17:15:29.717506Z", "shell.execute_reply": "2024-01-18T17:15:29.717246Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "DDDDRRRRUULLUUDURRRRDDDDD\n" ] } ], "source": [ "for seed in aflgo_fuzzer.population:\n", " s = maze(str(seed.data))\n", " if \"SOLVED\" in s:\n", " filtered = \"\".join(list(filter(lambda c: c in \"UDLR\", seed.data)))\n", " print(filtered)\n", " break" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "This is definitely a solution for the maze specified at the beginning!\n", "\n", "***Summary***. After pre-computing the function-level distance to the target, we can develop a power schedule that assigns more energy to a seed with a smaller average function-level distance to the target. By normalizing seed distance values between the minimum and maximum seed distance, we can further boost the directed power schedule.\n", "\n", "***Try it***. Implement and evaluate a simpler directed power that uses the minimal (rather than average) function-level distance. What is the downside of using the minimal distance? In order to execute your code, you just need to open this chapter as Jupyter notebook.\n", "\n", "***Read***. You can find out more about directed greybox fuzzing in the equally-named paper \"[Directed Greybox Fuzzing](https://mboehme.github.io/paper/CCS17.pdf)\" \\cite{boehme2017greybox} and check out the implementation into AFL at [http://github.com/aflgo/aflgo](http://github.com/aflgo/aflgo)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Synopsis\n", "\n", "This chapter introduces advanced methods for grey-box fuzzing inspired by the popular AFL fuzzer. The `GreyboxFuzzer` class has three arguments. First, a list of seed inputs:" ] }, { "cell_type": "code", "execution_count": 86, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:29.719330Z", "iopub.status.busy": "2024-01-18T17:15:29.719199Z", "iopub.status.idle": "2024-01-18T17:15:29.720838Z", "shell.execute_reply": "2024-01-18T17:15:29.720550Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "seed_input = \"http://www.google.com/search?q=fuzzing\"\n", "seeds = [seed_input]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Second, a _mutator_ that changes individual parts of the input." ] }, { "cell_type": "code", "execution_count": 87, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:29.722649Z", "iopub.status.busy": "2024-01-18T17:15:29.722528Z", "iopub.status.idle": "2024-01-18T17:15:29.724315Z", "shell.execute_reply": "2024-01-18T17:15:29.723991Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "mutator = Mutator()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Third, a _power schedule_ that assigns fuzzing effort across the population:" ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:29.726300Z", "iopub.status.busy": "2024-01-18T17:15:29.726160Z", "iopub.status.idle": "2024-01-18T17:15:29.727855Z", "shell.execute_reply": "2024-01-18T17:15:29.727596Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "schedule = PowerSchedule()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "These three go into the `GreyboxFuzzer` constructor:" ] }, { "cell_type": "code", "execution_count": 89, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:29.729377Z", "iopub.status.busy": "2024-01-18T17:15:29.729273Z", "iopub.status.idle": "2024-01-18T17:15:29.731429Z", "shell.execute_reply": "2024-01-18T17:15:29.731184Z" }, "slideshow": { "slide_type": "fragment" }, "tags": [] }, "outputs": [], "source": [ "greybox_fuzzer = GreyboxFuzzer(seeds=seeds, mutator=mutator, schedule=schedule)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The `GreyboxFuzzer` class is used in conjunction with a `FunctionCoverageRunner`:" ] }, { "cell_type": "code", "execution_count": 90, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:29.732965Z", "iopub.status.busy": "2024-01-18T17:15:29.732841Z", "iopub.status.idle": "2024-01-18T17:15:30.669175Z", "shell.execute_reply": "2024-01-18T17:15:30.668886Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "http_runner = FunctionCoverageRunner(http_program)\n", "outcomes = greybox_fuzzer.runs(http_runner, trials=10000)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "After fuzzing, we can inspect the population:" ] }, { "cell_type": "code", "execution_count": 91, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:30.671192Z", "iopub.status.busy": "2024-01-18T17:15:30.671066Z", "iopub.status.idle": "2024-01-18T17:15:30.673517Z", "shell.execute_reply": "2024-01-18T17:15:30.673218Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "[http://www.google.com/search?q=fuzzing,\n", " htpp://w.gfoogne.com/seaRchw?q=fuzzng,\n", " http://ww.google.com/search?q=furzing,\n", " xvtp:{/www.foogle.com/seapch=q=uzzing,\n", " ht:w/ww.goog\\l*#m/seaXrceh?bq=rzilgl,\n", " http:/wwwgoogne.coe/seazch?q=uzzin&,\n", " h|t4p://.PgoolL.com/sdrh?Qq=Fuzi,\n", " htzBjQex/c&*ieq=08T,\n", " vpe7pJ5/nb/nUc{jICo-m /gacw0qmfQuzz#ng,\n", " htpp://w.gf/ogn.#m/seaRchw?q=fuzzng,\n", " J;w?G/&!7jwA,j7/o!fL\u0003mX\\gh?2-bz@\"i,\n", " ;?p8.gnoox\u0007n%ncooms'aRhqW=f\"Dzg]" ] }, "execution_count": 91, "metadata": {}, "output_type": "execute_result" } ], "source": [ "greybox_fuzzer.population[:20]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Besides the simple `PowerSchedule`, we can have advanced power schedules.\n", "\n", "* `AFLFastSchedule` assigns high energy to \"unusual\" paths not taken very often.\n", "* `AFLGoSchedule` assigns high energy to paths close to uncovered program locations. \n", "\n", "The `AFLGoSchedule` class constructor requires a `distance` metric from each node towards target locations, as determined via analysis of the program code. See the chapter for details." ] }, { "cell_type": "code", "execution_count": 92, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:30.675141Z", "iopub.status.busy": "2024-01-18T17:15:30.675034Z", "iopub.status.idle": "2024-01-18T17:15:30.676480Z", "shell.execute_reply": "2024-01-18T17:15:30.676226Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# ignore\n", "from ClassDiagram import display_class_hierarchy" ] }, { "cell_type": "code", "execution_count": 93, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:30.677973Z", "iopub.status.busy": "2024-01-18T17:15:30.677890Z", "iopub.status.idle": "2024-01-18T17:15:31.162783Z", "shell.execute_reply": "2024-01-18T17:15:31.162410Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "CountingGreyboxFuzzer\n", "\n", "\n", "CountingGreyboxFuzzer\n", "\n", "\n", "\n", "run()\n", "\n", "\n", "\n", "reset()\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "GreyboxFuzzer\n", "\n", "\n", "GreyboxFuzzer\n", "\n", "\n", "\n", "run()\n", "\n", "\n", "\n", "reset()\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "CountingGreyboxFuzzer->GreyboxFuzzer\n", "\n", "\n", "\n", "\n", "\n", "AdvancedMutationFuzzer\n", "\n", "\n", "AdvancedMutationFuzzer\n", "\n", "\n", "\n", "__init__()\n", "\n", "\n", "\n", "fuzz()\n", "\n", "\n", "\n", "create_candidate()\n", "\n", "\n", "\n", "reset()\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "GreyboxFuzzer->AdvancedMutationFuzzer\n", "\n", "\n", "\n", "\n", "\n", "Fuzzer\n", "\n", "\n", "Fuzzer\n", "\n", "\n", "\n", "__init__()\n", "\n", "\n", "\n", "fuzz()\n", "\n", "\n", "\n", "run()\n", "\n", "\n", "\n", "runs()\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "AdvancedMutationFuzzer->Fuzzer\n", "\n", "\n", "\n", "\n", "\n", "AFLFastSchedule\n", "\n", "\n", "AFLFastSchedule\n", "\n", "\n", "\n", "__init__()\n", "\n", "\n", "\n", "assignEnergy()\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "PowerSchedule\n", "\n", "\n", "PowerSchedule\n", "\n", "\n", "\n", "__init__()\n", "\n", "\n", "\n", "assignEnergy()\n", "\n", "\n", "\n", "choose()\n", "\n", "\n", "\n", "normalizedEnergy()\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "AFLFastSchedule->PowerSchedule\n", "\n", "\n", "\n", "\n", "\n", "AFLGoSchedule\n", "\n", "\n", "AFLGoSchedule\n", "\n", "\n", "\n", "assignEnergy()\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "DirectedSchedule\n", "\n", "\n", "DirectedSchedule\n", "\n", "\n", "\n", "__init__()\n", "\n", "\n", "\n", "__getFunctions__()\n", "\n", "\n", "\n", "assignEnergy()\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "AFLGoSchedule->DirectedSchedule\n", "\n", "\n", "\n", "\n", "\n", "DirectedSchedule->PowerSchedule\n", "\n", "\n", "\n", "\n", "\n", "DictMutator\n", "\n", "\n", "DictMutator\n", "\n", "\n", "\n", "__init__()\n", "\n", "\n", "\n", "insert_from_dictionary()\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Mutator\n", "\n", "\n", "Mutator\n", "\n", "\n", "\n", "__init__()\n", "\n", "\n", "\n", "delete_random_character()\n", "\n", "\n", "\n", "flip_random_character()\n", "\n", "\n", "\n", "insert_random_character()\n", "\n", "\n", "\n", "mutate()\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "DictMutator->Mutator\n", "\n", "\n", "\n", "\n", "\n", "Seed\n", "\n", "\n", "Seed\n", "\n", "\n", "\n", "__init__()\n", "\n", "\n", "\n", "__repr__()\n", "\n", "\n", "\n", "__str__()\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Legend\n", "Legend\n", "• \n", "public_method()\n", "• \n", "private_method()\n", "• \n", "overloaded_method()\n", "Hover over names to see doc\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 93, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# ignore\n", "display_class_hierarchy([CountingGreyboxFuzzer, AFLFastSchedule, AFLGoSchedule,\n", " DictMutator, Seed],\n", " public_methods=[\n", " Fuzzer.run,\n", " Fuzzer.__init__,\n", " Fuzzer.runs,\n", " Fuzzer.fuzz,\n", " AdvancedMutationFuzzer.__init__,\n", " AdvancedMutationFuzzer.fuzz,\n", " GreyboxFuzzer.run,\n", " CountingGreyboxFuzzer.run,\n", " PowerSchedule.__init__,\n", " DirectedSchedule.__init__,\n", " AFLGoSchedule.__init__,\n", " AFLFastSchedule.__init__,\n", " Seed.__init__,\n", " Mutator.__init__,\n", " DictMutator.__init__,\n", " ],\n", " types={'Location': Location},\n", " project='fuzzingbook')" ] }, { "cell_type": "markdown", "metadata": { "button": false, "jp-MarkdownHeadingCollapsed": true, "new_sheet": true, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" }, "tags": [] }, "source": [ "## Lessons Learned\n", "\n", "* A *greybox fuzzer* generates thousands of inputs per second. Pre-processing and lightweight instrumentation \n", " * allows maintaining the efficiency *during* the fuzzing campaign, and \n", " * still provides enough information to control progress and slightly steer the fuzzer.\n", "* The *power schedule* allows _steering and controlling_ the fuzzer. For instance,\n", " * Our [boosted greybox fuzzer](#Fuzzer-Boosting) spends more energy on seeds that exercise \"unlikely\" paths. The hope is that the generated inputs exercise even more unlikely paths. This in turn increases the number of paths explored per unit time.\n", " * Our [directed greybox fuzzer](#Directed-Greybox-Fuzzing) spends more energy on seeds that are \"closer\" to a target location. The hope is that the generated inputs get even closer to the target.\n", "* The *mutator* defines the fuzzer's search space. [Customizing the mutator](GreyboxFuzzer.ipynb#A-First-Attempt) for the given program allows reducing the search space to only relevant inputs. In a couple of chapters, we'll learn about [dictionary-based, and grammar-based mutators](GreyboxGrammarFuzzer.ipynb) to increase the ratio of valid inputs generated." ] }, { "cell_type": "markdown", "metadata": { "jp-MarkdownHeadingCollapsed": true, "slideshow": { "slide_type": "slide" }, "tags": [] }, "source": [ "## Background\n", "\n", "* **Find out more about AFL**: http://lcamtuf.coredump.cx/afl/\n", "* **Learn about LibFuzzer** (another famous greybox fuzzer): http://llvm.org/docs/LibFuzzer.html\n", "* **How quickly must a whitebox fuzzer exercise each path to remain more efficient than a greybox fuzzer?** Marcel Böhme and Soumya Paul. 2016. [A Probabilistic Analysis of the Efficiency of Automated Software Testing](https://mboehme.github.io/paper/TSE15.pdf), IEEE TSE, 42:345-360 \\cite{boehme2016efficiency}" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## Next Steps\n", "\n", "Our aim is still to sufficiently cover functionality, such that we can trigger as many bugs as possible. To this end, we focus on two classes of techniques:\n", "\n", "1. Try to cover as much _specified_ functionality as possible. Here, we would need a _specification of the input format,_ distinguishing between individual input elements such as (in our case) numbers, operators, comments, and strings – and attempting to cover as many of these as possible. We will explore this as it comes to [grammar-based testing](GrammarFuzzer.ipynb), and especially in [grammar-based mutations](GreyboxGrammarFuzzer.ipynb).\n", "\n", "2. Try to cover as much _implemented_ functionality as possible. The concept of a \"population\" that is systematically \"evolved\" through \"mutations\" will be explored in depth when discussing [search-based testing](SearchBasedFuzzer.ipynb). Furthermore, [symbolic testing](SymbolicFuzzer.ipynb) introduces how to systematically reach program locations by solving the conditions that lie on their paths.\n", "\n", "These two techniques make up the gist of the book; and, of course, they can also be combined with each other. As usual, we provide runnable code for all. Enjoy!" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "We're done, so we clean up:" ] }, { "cell_type": "code", "execution_count": 94, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:31.164566Z", "iopub.status.busy": "2024-01-18T17:15:31.164447Z", "iopub.status.idle": "2024-01-18T17:15:31.166332Z", "shell.execute_reply": "2024-01-18T17:15:31.166063Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import os" ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:31.167877Z", "iopub.status.busy": "2024-01-18T17:15:31.167766Z", "iopub.status.idle": "2024-01-18T17:15:31.170286Z", "shell.execute_reply": "2024-01-18T17:15:31.170053Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "if os.path.exists('callgraph.dot'):\n", " os.remove('callgraph.dot')\n", "\n", "if os.path.exists('callgraph.py'):\n", " os.remove('callgraph.py')" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Compatibility" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "In previous version of the chapter, the `AdvancedMutationFuzzer` class was named simply `MutationFuzzer`, causing name confusion with the `MutationFuzzer` class in the [introduction to mutation-based fuzzing](MutationFuzzer.ipynb). The following declaration introduces a backward-compatible alias." ] }, { "cell_type": "code", "execution_count": 96, "metadata": { "execution": { "iopub.execute_input": "2024-01-18T17:15:31.171708Z", "iopub.status.busy": "2024-01-18T17:15:31.171603Z", "iopub.status.idle": "2024-01-18T17:15:31.173237Z", "shell.execute_reply": "2024-01-18T17:15:31.172980Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "class MutationFuzzer(AdvancedMutationFuzzer):\n", " pass" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": true, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## Exercises\n", "\n", "To be added. \\todo{}" ] } ], "metadata": { "ipub": { "bibliography": "fuzzingbook.bib", "toc": true }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.2" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": true, "title_cell": "", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": true }, "toc-autonumbering": false, "varInspector": { "cols": { "lenName": 16, "lenType": 16, "lenVar": 40 }, "kernels_config": { "python": { "delete_cmd_postfix": "", "delete_cmd_prefix": "del ", "library": "var_list.py", "varRefreshCmd": "print(var_dic_list())" }, "r": { "delete_cmd_postfix": ") ", "delete_cmd_prefix": "rm(", "library": "var_list.r", "varRefreshCmd": "cat(var_dic_list()) " } }, "types_to_exclude": [ "module", "function", "builtin_function_or_method", "instance", "_Feature" ], "window_display": false }, "vscode": { "interpreter": { "hash": "4185989cf89c47c310c2629adcadd634093b57a2c49dffb5ae8d0d14fa302f2b" } } }, "nbformat": 4, "nbformat_minor": 4 }