First of all, Happy new hacking year everyone 😉
I got asked multiple time if fuzzing WebAssembly APIs of Javascript engines is complicated, so here is a short tutorial using Dharma (but you can use Domato if you prefer).
In this blogpost, I will first detailed which WebAssembly Javascript APIs are supported by major browsers. Then, I’ll explains how to use Dharma to generate valid Javascript file to fuzz WebAssembly APIs. Finally, I’ll show an easy way to execute those generated testcases over ASAN build of Chrome/V8 engine.
Just a quick reminder before we start, if you are interested about WebAssembly security and fuzzing WebAssembly Browsers/VMs, I decided to convert my 4-day live training into recorded courses (check here).
Long story short, WebAssembly (abbreviated Wasm) is a new portable, size- and load-time-efficient binary instruction format for the web. It has been designed by members of people representing the four browsers, Chrome, Edge, Firefox, and WebKit. WebAssembly is now supported/enabled by default by every major browsers (both on Desktop and Mobile) and WebAssembly module are executed through their respective javascript engines such as v8, spidermonkey, jsc, etc.
As you can see on caniuse.com, in January 2020, around 88% of all internet users can run WebAssembly modules on their browsers.
Adding supports of a new binary format designed to be loaded and executed by Javascript engines implied of course some huge modification on the source code i.e. from a researcher point of view, a new attack surface to discover 😉
Notably, some security issues related to WebAssembly APIs has been found in V8, WebKit and Firefox by Natalie Silvanovich (@natashenka) but also in the closed-source Xiaomi Mi6 Browser by @fluoroacetate with CVE-2019-6743.
If you want to discover more about existing WebAssembly browser CVEs, take a look at those links:
WebAssembly JavaScript APIs are composed of multiple methods and WebAssembly object constructors used to created and instantiate wasm modules, such as:
WebAssembly.instantiate()
method.WebAssembly.compile()
method.WebAssembly.validate()
method.WebAssembly.Global()
object.WebAssembly.Module()
object.WebAssembly.Instance()
object.WebAssembly.Memory()
object.WebAssembly.Table()
object.Those APIs are our first targets for fuzzing since they are involved in multiple CVEs and they are common to every browser/JavaScript engine implementation. More details about those APIs and their syntaxes can be found here:
Dharma is a generation-based, context-free grammar fuzzer created in 2015 by Christoph Diehl from Mozilla Security team. The goal of this tool is to generate files (like Javascript and/or HTML) based on a given grammar description. This concept look maybe familiar to you if you already played with Domato by Ivan Fratric from Google Project Zero.
Personally, I prefer the grammar syntax of Dharma but if you are a Domato adept, converting the following grammar to Domato shouldn’t be difficult 😉
The most time consuming part is to read all the specification and/or APIs descriptions in order to create valid wasm objects and methods calls. I will not detailed Dharma grammar syntax in this blogpost but you can find a complete grammar cheatsheet on the official github repository of Dharma. Once your grammar seems acceptable, you can generate multiple files using this command:
dharma -grammars dharma/wasm.dg -count 100 -format js -seed 1337 -storage output_folder
Your output folder should now contain multiple JavaScript files similar to the following picture.
I just published a simple WebAssembly grammar in this github repository if you need something to start 😉 I also invited you to read the following blogposts to discover how other researchers are using Dharma or Domato 😉
In this blogpost, I will only target Chrome/V8 because it’s the best way for you (readers) to reproduce blogpost’s steps at home without spending hours in compilation/debugging. Just a quick reminder, AddressSanitizer (ASan) is a memory error detector based on compiler instrumentation (LLVM) and used to detect multiple kind vulnerabilities (UaF, HBoF, etc.).
Here is the main reasons why I choose fuzzing V8 engine/Chrome as a first choice:
gsutil cp $(gsutil ls "gs://chromium-browser-asan/linux-release/asan-linux-release-*.zip" | tail -1) .
Last but not least, we need to provide our JS files to d8. An easy way to start can be to create a simple bash script (like this one) that will loop and:
Honggfuzz is really easy-to-use and awesome fuzzer developed and maintained by Robert Swiecki from Google. During honggfuzz “dry-run”, all given files will be executed in different threads, monitored for crashes and stored if relevants. That also mean that only using the following command-line, you let honggfuzz handle everything and can go to sleep 😉
honggfuzz -t 5 -n 4 -i input_wasm_js/ -- ./d8 ___FILE___
Note: I noticed during my research that someone also integrate dharma directly into honggfuzz. I let you the check here, but personally I was not able to make it work :s
Congratz, you are now able to fuzz V8 WebAssembly APIs using generation-based fuzzers.
The previous dharma grammar is really specific to WebAssembly APIs and should be improved to handle more generic JavaScript methods/objects (Array, UIntArray, Number, etc.). Also, another grammar should be created to generate valid WebAssembly module bytecode, stored inside ArrayBuffer or TypedBuffer and provided to the WebAssembly.Module() constructor.
Grammar and script shown in this blogpost are available in this github repository.
Final reminder, If you want to learn/discover more about WebAssembly security (both reversing and fuzzing), I converted my 4-day live training into recorded courses. More details here.
Patrick Ventuzelo / @Pat_Ventuzelo
Cookie | Duration | Description |
---|---|---|
cookielawinfo-checkbox-analytics | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Analytics". |
cookielawinfo-checkbox-functional | 11 months | The cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional". |
cookielawinfo-checkbox-necessary | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary". |
cookielawinfo-checkbox-others | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Other. |
cookielawinfo-checkbox-performance | 11 months | This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Performance". |
viewed_cookie_policy | 11 months | The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data. |