
In the past, it was really difficult to fuzz JavaScript engines (those that enable you to add Snowfall to your browser or develop Node.js backends). Mutations in JS code caused syntax errors, which slowed down the process significantly. Samples were discarded by the engine, and you had to generate new ones. Over time, grammar-based fuzzers came to help, but they are not easy-to-use either.
In 2019, security researcher saelo unveiled his project – Fuzzilli. The idea of this fuzzer was to generate bytecode-like output (instead of JavaScript) that would be easier to mutate. The fuzzer name refers to a pasta variety, but, in fact, it originates from FuzzIL (Fuzzing Intermediate Language).

Test system
First of all, you’ll need a Linux virtual machine. A ready-made VM with your favorite distribution can be downloaded from osboxes.org. In my tests, I will use Ubuntu 22.
The more resources you allocate to the test VM, the better. The fuzzer shows code coverage, and, depending on the computation power, fuzzing the entire JS engine can take from a single day to several weeks.
The accessory tools include Git, the Swift programming language (not to be confused with the songstress), and the toolchain required to build JS engines (to be addressed in more detail later).
Start the VM and enter your password.
Password=osboxes.org
echo $Password | sudo -S apt update
sudo apt upgrade -y

Fuzzers
Fuzzing is a testing technique: incorrect, unexpected, or randomized data are fed to a software product as input, and the fuzzer monitors it for crashes, assertion triggers, and memory leaks.
An important fuzzing parameter is code coverage. In essence, this is the proportion of program code covered in the course of a certain set of tests.
Fuzzing can be either ‘dumb’ (or unstructured) or ‘smart’ (i.e. structured). If the fuzzer has no idea about the structure of input data expected by the tested program, this is dumb fuzzing. If it knows this structure, this is smart fuzzing.
Input data can be created using generation or mutation. Generation creates data from scratch; while mutation modifies existing data.
Depending on your knowledge about the source code, fuzzers can be divided into black-box and white-box ones.
Black-box fuzzing implies that you have no information about the program structure; accordingly, the fuzzer creates randomized input data.
White-box fuzzing involves program analysis with the purpose to improve code coverage (e.g. symbolic execution to bypass various parts of the program). However, program analysis takes longer than black-box fuzzing.
In addition, the tested ‘box’ can be gray. In such a case, code instrumentation is used instead of program analysis. This makes it possible to collect information about the program without analysis (i.e. something between a white and a black box). Input data are generated faster, and concurrently you receive code coverage information.
Fuzzilli belongs to the third type. In terms of input data creation, it combines generation and mutation. In terms of fuzzing technology, it’s rather ‘smart’.
JavaScript engines
The main components of a JavaScript engine are parser, interpreter, and compiler.

First, JavaScript source code is parsed. An abstract syntax tree (AST) is built, and bytecode is created on its basis. Then the interpreter executes the bytecode.
During the execution, various information is recorded (data profiling) and subsequently used when the bytecode is compiled into machine code. This operation is performed by the compiler.
Machine code is generated in situations when some code section is used frequently (e.g. a function is executed in a loop). Usually, it makes sense to spend time on its compilation and subsequently gain in execution time (since interpretation is always slower).
Typically, several compilers are used, and the optimal one is selected depending on the code optimization level.
www
More information on JS engines is available in the presentation “JavaScript engines: The Good Parts” (PDF, WebArchive).
Preparing fuzzer
Fuzzilli is available in the form of source code written in Swift.
To download the source code, you’ll need Git; to build Fuzzilli, GCC and Binutils packages (and, of course, the fuzzer source code). Install the dependencies and clone the Fuzzilli repository.
Password=osboxes.org
echo $Password | sudo -S apt update
sudo apt install git binutils gcc -ygit clone https://github.com/googleprojectzero/fuzzilli

Now go to the Swift website and look for a release suitable for your distribution in the Download section. For Ubuntu 22, download the release Ubuntu 22.04 x86_64.

Unpack the archive and copy the usr
folder to install Swift. After that, make sure everything is configured correctly.
Below is a mini-script for lazy hackers. If you are reading this article many years later, replace the SwiftUrl
variable with the respective URL from the Swift page.
# Installing SwiftPassword=osboxes.org
SwiftUrl=https://download.swift.org/swift-5.8.1-release/ubuntu2204/swift-5.8.1-RELEASE/swift-5.8.1-RELEASE-ubuntu22.04.tar.gz
SwiftTar=$(echo $SwiftUrl | sed 's:.*/::')SwiftFolder=${SwiftTar%.tar.gz}# Go to HOME directorycd $HOME# Download archivewget $SwiftUrl# Extracttar -xzf $SwiftTar# Installecho $Password | sudo -S cp -r $SwiftFolder/usr /
# Delete archiverm $SwiftTar# Delete folderrm -rf $SwiftFolder# Test runswift --version

Now go to the Fuzzilli folder and build the fuzzer.
cd fuzzilli && swift build -c release

The fuzzer is ready. You can read its Help section if necessary.
swift run -c release FuzzilliCli --help
Time to prepare JS engines. The instruction on the repository main page says: “Download the source code for one of the supported JavaScript engines. See the Targets/ directory for the list of supported JavaScript engines”. For each engine, there is a separate folder containing an instruction that specifies how to build this particular engine for fuzzing.
Building and fuzzing V8
Theory
Let’s start with the Google Chrome browser engine called V8. It’s a JavaScript and WebAssembly open-source engine developed by Google and written in C++. It’s used in Chrome, Node.js, and numerous Chrome derivatives.
This engine was developed in Aarhus, Denmark, and its lead developer is Lars Bak. Bak was also involved in the development of the Self language and HotSpot Java virtual machine. Therefore, many of the Self developments migrated to V8 (e.g. JIT compilation and object maps).
www
For more detail, see the paper that formulates the basis of Self: “An Efficient Implementation of SELF, a Dynamically-Typed Object-Oriented Language Based on Prototypes” (PDF, WebArchive).
The engine includes the following components: Ignition interpreter, non-optimizing JavaScript compiler, and TurboFan optimizing compiler.

Building V8
The explanatory note in the Targets/V8 folder suggests to follow the instructions provided at https://v8.dev/docs/build. But I compiled all the required steps into a mini-script.
# Preparing and building V8Password=osboxes.org
echo $Password | sudo -S apt update
# Go to HOME directorycd $HOME# Download depot_tools repositorygit clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
# Add to PATHecho "PATH=$HOME/depot_tools:$PATH" >> ~/.bashrc
source ~/.bashrc
# Download source codegclient
fetch v8
cd v8/
gclient sync# Install build dependencies (in fact, update since the script asks to enter password)echo $Password | sudo -S apt update
./build/install-build-deps.sh
# Build V8 using script in the fuzzer folder$HOME/fuzzilli/Targets/V8/fuzzbuild.sh

The script goes to the user home directory, installs depot_tools
(to download and build V8), and downloads and compiles the V8 source code. This process can take some time depending on your PC’s horsepower.
Fuzzing
To start fuzzing, run the following command:
# Run Fuzzilli# Disable core dumpsecho $Password | sudo -S sysctl -w 'kernel.core_pattern=|/bin/false'# Go to Fuzzilli foldercd $HOME/fuzzilli
# Runsswift run -c release FuzzilliCli --profile=v8 --resume --storagePath=$HOME/fuzzilli-storage-v8 $HOME/v8/out/fuzzbuild/d8

The main settings are as follows: engine profile (--profile=v8
), resume the previous fuzzing session (--resume
), and path to the storage where the fuzzer will save its data (--storagePath
): found crashes, sample corpus, and other information.
Fuzzing statistics will be displayed on a regular basis. The most interesting parameters are Crashes Found and Coverage.

Building and fuzzing SpiderMonkey
Theory
Let’s move on to Firefox JavaScript engine.
SpiderMonkey originates from the world’s first JavaScript engine released back in 1995. Originally developed by Brendan Eich at Netscape, its first versions were written in C, but later the code was rewritten in C++.
The SpiderMonkey structure is shown below. The parser produces bytecode. The JavaScript interpreter executes this bytecode. The baseline interpreter creates inline cache. The baseline compiler creates non-optimized machine code, and WarpMonkey creates optimized machine code.

Building SpiderMonkey
The instruction suggests to clone the Gecko repository and run the fuzzbuild.sh script.
But in fact, you have to apply patches from the Patches folder first – and only then build the engine. Also, fuzzbuild should be run not from js/
, but from the gecko-dev
root.

To build the engine, you’ll need curl and the Rust compiler.
# Preparing and building SpiderMonkeyPassword=osboxes.org
cd $HOME# Install curlecho $Password | sudo -S apt install curl -y# Install Rustcurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o install.sh
sh ./install.sh -ysource "$HOME/.cargo/env"# Clone repositorygit clone https://github.com/mozilla/gecko-dev.git
# Go to foldercd gecko-dev/js/src
# Apply patchgit apply $HOME/fuzzilli/Targets/Spidermonkey/Patches/*cd $HOME/gecko-dev
# Build$HOME/fuzzilli/Targets/Spidermonkey/fuzzbuild.sh


Fuzzing
After building the engine, you can start fuzzing it from the fuzzilli
folder. The startup commands and fuzzer parameters are the same, except for the profile and executable file.
# Starting Fuzzilli# Disable core dumpsecho $Password | sudo -S sysctl -w 'kernel.core_pattern=|/bin/false'# Go to Fuzzilli foldercd $HOME/fuzzilli
# Runswift run -c release FuzzilliCli --profile=spidermonkey --resume --storagePath=$HOME/fuzzilli-storage-sm $HOME/gecko-dev/obj-fuzzbuild/dist/bin/js

Building and fuzzing JavaScriptCore
And finally, the JavaScriptCore engine that powers the Safari browser.
Theory
JavaScriptCore originates from the KJS JavaScript engine used in the Konqueror browser, which is part of KDE. The WebKit project started in 2001 as a fork of KHTML and KJS.
It features the most complex four-level pipeline:
- LLInt (Low Level Interpreter) interprets bytecode generated based on the JavaScript source code;
- Then slightly optimizing Baseline compiler goes into play. Both of them collect information required for further machine code optimizations.
- The next component is DFG JIT (Data Flow Graph Just In Time). It enhances machine code optimization; and
- Finally, FTL JIT (Faster Than Light) generates the most optimized machine code.

Building JavaScriptCore
Let’s see what the instruction in the Targets folder says about JavaScriptCore.

You have to clone the code from the WebKit mirror, apply patches, and run the fuzzbuild.sh script. Also, you need to install Clang and dependencies. The Tools
folder contains ready-made scripts for this. Download and install all the required stuff, apply patches, and build the engine. As usual, below is the script:
# Preparing and building JavaScriptCorePassword=osboxes.org
cd $HOME# Download source codegit clone https://github.com/WebKit/WebKit.git
# Install dependenciescd WebKit
echo $Password | sudo -S apt update
sudo apt install clang -yTools/gtk/install-dependencies
# Apply patchesgit apply ../fuzzilli/Targets/JavaScriptCore/Patches/*# Build JSC$HOME/fuzzilli/Targets/JavaScriptCore/fuzzbuild.sh

Fuzzing
After building the engine, you can proceed to fuzzing.
# Running Fuzzilli# Disable core dumpsecho $Password | sudo -S sysctl -w 'kernel.core_pattern=|/bin/false'# Go to Fuzzilli foldercd $HOME/fuzzilli
# Start fuzzingswift run -c release FuzzilliCli --profile=jsc --resume --storagePath=$HOME/fuzzilli-storage-jsc $HOME/WebKit/FuzzBuild/Debug/bin/jsc

Conclusions
Now you are familiar with the fuzzing theory and have a fuzzing platform suitable for the three main JavaScript engines. However, Fuzzilli supports other engines as well (just check the Targets folder), including:
- JerryScript;
- QuickJS;
- Qt QJSEngine;
- XS; and
- duktape.
Your newly acquired knowledge and skills enable you to fuzz them on your own.
Good luck!
Useful links
- Coverage Guided Fuzzing for JavaScript Engines Thesis (PDF)
- OffensiveCon19 video
- OffensiveCon19 slides (PDF)
- How Fuzzilli Works
- Fuzzilli repository on GitHub

2022.02.15 — First contact: How hackers steal money from bank cards
Network fraudsters and carders continuously invent new ways to steal money from cardholders and card accounts. This article discusses techniques used by criminals to bypass security…
Full article →
2022.01.13 — Bug in Laravel. Disassembling an exploit that allows RCE in a popular PHP framework
Bad news: the Ignition library shipped with the Laravel PHP web framework contains a vulnerability. The bug enables unauthorized users to execute arbitrary code. This article examines…
Full article →
2023.07.07 — Evil Ethernet. BadUSB-ETH attack in detail
If you have a chance to plug a specially crafted device to a USB port of the target computer, you can completely intercept its traffic, collect cookies…
Full article →
2022.06.01 — Routing nightmare. How to pentest OSPF and EIGRP dynamic routing protocols
The magic and charm of dynamic routing protocols can be deceptive: admins trust them implicitly and often forget to properly configure security systems embedded in these protocols. In this…
Full article →
2023.02.13 — First Contact: Attacks on Google Pay, Samsung Pay, and Apple Pay
Electronic wallets, such as Google Pay, Samsung Pay, and Apple Pay, are considered the most advanced and secure payment tools. However, these systems are also…
Full article →
2023.02.12 — Gateway Bleeding. Pentesting FHRP systems and hijacking network traffic
There are many ways to increase fault tolerance and reliability of corporate networks. Among other things, First Hop Redundancy Protocols (FHRP) are used for this…
Full article →
2023.07.29 — Invisible device. Penetrating into a local network with an 'undetectable' hacker gadget
Unauthorized access to someone else's device can be gained not only through a USB port, but also via an Ethernet connection - after all, Ethernet sockets…
Full article →
2022.01.12 — First contact. Attacks against contactless cards
Contactless payment cards are very convenient: you just tap the terminal with your card, and a few seconds later, your phone rings indicating that…
Full article →
2023.02.21 — SIGMAlarity jump. How to use Sigma rules in Timesketch
Information security specialists use multiple tools to detect and track system events. In 2016, a new utility called Sigma appeared in their arsenal. Its numerous functions will…
Full article →
2022.04.04 — Elephants and their vulnerabilities. Most epic CVEs in PostgreSQL
Once a quarter, PostgreSQL publishes minor releases containing vulnerabilities. Sometimes, such bugs make it possible to make an unprivileged user a local king superuser. To fix them,…
Full article →