{"id":268,"date":"2024-06-24T12:48:18","date_gmt":"2024-06-24T19:48:18","guid":{"rendered":"https:\/\/blog.mozilla.org\/attack-and-defense\/?p=268"},"modified":"2024-06-24T12:48:18","modified_gmt":"2024-06-24T19:48:18","slug":"ipc-fuzzing-with-snapshots","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/attack-and-defense\/2024\/06\/24\/ipc-fuzzing-with-snapshots\/","title":{"rendered":"IPC Fuzzing with Snapshots"},"content":{"rendered":"<p>Process separation remains one of the most important parts of the Firefox security model and securing our IPC (Inter-Process Communication) interfaces is crucial to keep privileges in the different processes separated. Today, we will take a more detailed look at our newest tool for finding vulnerabilities in these interfaces &#8211; snapshot fuzzing.<\/p>\n<h2>Snapshot Fuzzing<\/h2>\n<p>One of the challenges\u00a0 when fuzzing the IPC Layer is that isolating the interfaces that are to be tested isn\u2019t easily doable. Instead, one needs to run an entire Firefox instance to effectively fuzz these interfaces. However, having to run a Firefox instance for fuzzing comes with another set of downsides: First, we cannot easily reset the system back into a known-good state other than restarting the entire browser. This causes issues with reproducibility and breaks determinism required by coverage-guided fuzzing. And second, many errors in the parent process are still handled by crashing, again forcing a full and time consuming restart of the browser. Both cases are essentially a performance problem &#8211; restarting the browser is simply too slow to allow for efficient and productive\u00a0 fuzzing. This is where snapshot fuzzing comes into play &#8211; it allows us to take a snapshot at the point where we are \u201cready\u201d to perform fuzzing and reset to that snapshot point after each fuzzing iteration at practically no cost. This snapshot technique even works when we find a bug in the parent process which would normally force us to restart the browser.<\/p>\n<h2>Technical Implementation<\/h2>\n<p>As Firefox consists of multiple processes that need to be kept in sync, we decided to use <a href=\"https:\/\/github.com\/nyx-fuzz\">Nyx<\/a>, <i>a full-vm snapshot fuzzing tool<\/i>. In this setup, Firefox runs in a guest operating system (usually Linux) and the snapshot taken is a snapshot of the whole guest including all of its processes. Nyx is also compatible with <a href=\"https:\/\/github.com\/AFLplusplus\/AFLplusplus\">AFL++<\/a> as a frontend, a tool we already employ for other fuzzing targets.<\/p>\n<p>To facilitate communication between Firefox and Nyx, we use a <a href=\"https:\/\/github.com\/MozillaSecurity\/snapshot-fuzzing\">custom agent<\/a>, essentially glue-code that is preloaded into Firefox. This code handles the low-level communication with Nyx and is also responsible for providing the trace buffer (for coverage measurements) to the AFL++ runtime linked to Firefox as well as passing through fuzzing data from AFL++. Both of these tasks are more complex in this configuration as AFL++ is not directly launching and communicating with the target binary. The agent further exposes <a href=\"https:\/\/searchfox.org\/mozilla-central\/source\/tools\/fuzzing\/nyx\/Nyx.h\">a clean interface<\/a> to Firefox that can be used to implement the actual fuzzer in Firefox itself without having to worry about the low-level details.<\/p>\n<div id=\"attachment_269\" style=\"width: 1974px\" class=\"wp-caption aligncenter\"><img aria-describedby=\"caption-attachment-269\" decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-269\" src=\"http:\/\/blog.mozilla.org\/attack-and-defense\/files\/2024\/06\/snapshot-stack.png\" alt=\"The snapshot fuzzing technology stack depicted from bottom to top: AFL++, QEMU-Nyx, Linux Guest with Firefox, Preloader Code, Mozilla Nyx Interface and on top of this multiple fuzzing targets.QEMU-Nyx is launched by AFL++, which then launches the Linux guest with Firefox in a fuzzing configuration. The preloader code is injected with LD_PRELOAD and manages low-level tasks as well as providing the communication interface to the Mozilla Nyx interface.\" width=\"1964\" height=\"1126\" srcset=\"https:\/\/blog.mozilla.org\/attack-and-defense\/files\/2024\/06\/snapshot-stack.png 1964w, https:\/\/blog.mozilla.org\/attack-and-defense\/files\/2024\/06\/snapshot-stack-300x172.png 300w, https:\/\/blog.mozilla.org\/attack-and-defense\/files\/2024\/06\/snapshot-stack-600x344.png 600w, https:\/\/blog.mozilla.org\/attack-and-defense\/files\/2024\/06\/snapshot-stack-768x440.png 768w, https:\/\/blog.mozilla.org\/attack-and-defense\/files\/2024\/06\/snapshot-stack-1536x881.png 1536w, https:\/\/blog.mozilla.org\/attack-and-defense\/files\/2024\/06\/snapshot-stack-1000x573.png 1000w\" sizes=\"(max-width: 1964px) 100vw, 1964px\" \/><p id=\"caption-attachment-269\" class=\"wp-caption-text\">Technology stack for snapshot fuzzing at a glance.<\/p><\/div>\n<p>On top of this interface, we have implemented <a href=\"https:\/\/searchfox.org\/mozilla-central\/source\/tools\/fuzzing\/ipc\/IPCFuzzController.cpp\">multiple IPC fuzzing targets<\/a>, the simplest one being <a href=\"https:\/\/searchfox.org\/mozilla-central\/rev\/159929cd10b8fba135c72a497d815ab2dd5a521c\/tools\/fuzzing\/ipc\/IPCFuzzController.cpp#1171-1323\">IPC_SingleMessage<\/a>, which we will look at in more detail now.<\/p>\n<h2>Fuzzing a single IPC message<\/h2>\n<p>Modifying a single IPC message in transit is one of the rudimentary approaches\u00a0 for IPC fuzzing in general. It is especially useful if the message type being targeted is in itself complex (lots of data contained in a single message rather than a complex interface being composed of a large number of simpler messages).<\/p>\n<p>For this purpose, we intercept messages in the parent process on the target thread <a href=\"https:\/\/searchfox.org\/mozilla-central\/rev\/893f350260faac2ee6bf2b14c627d55eb2babfb0\/ipc\/glue\/MessageChannel.cpp#1713-1718\">before they are dispatched<\/a> to the generated IPC code that ultimately calls the IPC method. Most of the logic is then contained in IPCFuzzController::replaceIPCMessage which primarily does either of these two things:<\/p>\n<ol>\n<li aria-level=\"1\">If the message type does not match our configured target message, we can optionally dump it to a file (this is useful to create seeds for different types of single message fuzzing), but otherwise we pass the message through.<\/li>\n<li aria-level=\"1\">If the message matches our target specification, take the snapshot, replace the payload of the original message with our fuzzing data and return the new (fuzzed) message to be dispatched.<\/li>\n<\/ol>\n<p>Once the fuzzed message is dispatched (most commonly to a different thread), we face an important challenge\u00a0 of multi-threaded snapshot fuzzing: synchronization. Coverage-guided fuzzing generally operates under the assumption that we know when our fuzzing data has been processed. Depending on the fuzzing target, it can be fairly difficult to tell when we are \u201cdone\u201d but in our case, because we are already on the target thread that is running the actual IPC method. So unless that method again performs an asynchronous dispatch, we can just wait for the dispatch to return and we do so <a href=\"https:\/\/searchfox.org\/mozilla-central\/rev\/893f350260faac2ee6bf2b14c627d55eb2babfb0\/ipc\/glue\/MessageChannel.cpp#1755\">at the end of DispatchMessage() where we call back into IPCFuzzController<\/a> to release (revert back to the snapshot).<\/p>\n<p>By combining this target with a CI test\u00b9, we are now able to find implementation flaws like for example a <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1820389\">vulnerability in the accessibility code<\/a> that involved the <a href=\"https:\/\/searchfox.org\/mozilla-central\/rev\/f60bb10a5fe6936f9e9f9e8a90d52c18a0ffd818\/accessible\/ipc\/PDocAccessible.ipdl#59\">ShowEvent<\/a> message. This message contains an array of serialized AccessibleData, making this message type a good target for single message fuzzing.<\/p>\n<h2>Measuring Code Coverage in Snapshot Fuzzing<\/h2>\n<p>Code coverage is probably the most important metric for long-term fuzzing campaigns as it highlights potential shortcomings of the fuzzing. While for most fuzzing, it is rather straightforward to generate code coverage, doing so in snapshot fuzzing is less trivial. Traditional source code coverage provided by tools like <a href=\"https:\/\/gcc.gnu.org\/onlinedocs\/gcc\/Gcov.html\">gcov<\/a> which find usage with other fuzzing, aren\u2019t easily deployable because the data would have to be pulled out of the VM on every iteration so it can be saved before the snapshot revert resets the data. Doing so would make the process of obtaining code coverage unfeasibly slow.<\/p>\n<p>Instead, we decided to build our own code coverage measurement on top of the existing instrumentation. For this purpose, we added a <a href=\"https:\/\/github.com\/AFLplusplus\/AFLplusplus\/pull\/1720\">new AFL++ instrumentation type<\/a> that instruments all basic blocks and then <a href=\"https:\/\/github.com\/AFLplusplus\/AFLplusplus\/pull\/2129\">creates a second, permanent trace buffer in AFL++<\/a> that accumulates the coverage of the regular trace buffer. Finally, we create a third buffer called the <i>pcmap<\/i> which maps every entry in the trace buffer to an address in the binary that can later be resolved to a source code location using debug information. As this information is <a href=\"https:\/\/github.com\/AFLplusplus\/AFLplusplus\/blob\/36db3428ab16156dd72196213d2a02a5eadaed11\/instrumentation\/afl-compiler-rt.o.c#L1616\">contained<\/a> in the AFL++ runtime, we need to obtain it within our custom Nyx agent and <a href=\"https:\/\/github.com\/MozillaSecurity\/snapshot-fuzzing\/blob\/953c67882ecf61905683b290d6c2065e4e94d357\/preload\/harness\/src\/moz_interface.c#L57\">write it out to the host<\/a>. The same holds for <a href=\"https:\/\/github.com\/MozillaSecurity\/snapshot-fuzzing\/blob\/953c67882ecf61905683b290d6c2065e4e94d357\/preload\/harness\/src\/afl_runtime.c#L20\"><i>module information<\/i><\/a> that denotes at which addresses Firefox modules were loaded. By combining these three sources of information, we can map the progress of Nyx fuzzing onto actual source code. We also built <a href=\"https:\/\/github.com\/MozillaSecurity\/snapshot-fuzzing\/tree\/main\/code-coverage\">additional tooling<\/a> to turn this basic block coverage into line-based coverage using information from a gcov build\u00b2. As a result, we can generate metrics like percentage of code covered to evaluate the overall effectiveness of snapshot-based fuzzing.<\/p>\n<h2>Conclusion<\/h2>\n<p>While snapshot fuzzing is a rather complex technology with many moving parts, it allows us to effectively stress code regions\u00a0 of the browser that would otherwise\u00a0 remain beyond the\u00a0 capabilities\u00a0 of traditional fuzzing techniques,\u00a0 but are critical for providing adequate security guarantees. We are happy to report that this new fuzzing technology is becoming the norm and is now an essential part of our security testing strategy. We would like to thank the authors of Nyx and AFL++ for making this technology available and hope that our combined efforts will help others to adapt snapshot fuzzing for their projects.<\/p>\n<hr \/>\n<p>\u00b9 <small>Firefox runs many <b>C<\/b>ontinuous <b>I<\/b>ntegration tests to ensure every functionality of the browser is automatically tested.<\/small><\/p>\n<p>\u00b2 <small>Unfortunately, gcov and debug information deviate in some cases, so the result is not a 100% accurate mapping yet and can\u2019t be seamlessly merged with other gcov data. This could likely be improved using LLVM annotations for additional basic block information.<\/small><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Process separation remains one of the most important parts of the Firefox security model and securing our IPC (Inter-Process Communication) interfaces is crucial to keep privileges in the different processes &hellip; <a class=\"go\" href=\"https:\/\/blog.mozilla.org\/attack-and-defense\/2024\/06\/24\/ipc-fuzzing-with-snapshots\/\">Read more<\/a><\/p>\n","protected":false},"author":409,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[449530,449525],"tags":[542,465201,465200,465203,465202,465199],"coauthors":[45537],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/posts\/268"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/users\/409"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/comments?post=268"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/posts\/268\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/media?parent=268"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/categories?post=268"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/tags?post=268"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mozilla.org\/attack-and-defense\/wp-json\/wp\/v2\/coauthors?post=268"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}