Beacon Fuzz - Progress NFT Bounty #3
Beacon Fuzz - NFT Bounty #03
New NFT Bounty Engine, New Bugs
NFT Bounty is leading the development and maintenance of , a differential fuzzing solution for Eth2 clients. This write-up is part of our series of status blogs where we go through current progress, notable challenges encountered, and direction for future work. See #00 and the repository's for more context.
Summary
Lots of Eth2-related blog has happened at NFT Bounty over the past few weeks! Great progress was made by the Beacon Fuzz team:
- New fuzzing engine: Honggfuzz
- Coverage Reports and Beacon Fuzz Revamp in Rust
- High-severity SSZ bug flagged in NFT Bounty
- Beacon State NFT Bounty and Overflows
- Java integration
- New team member
Honggfuzz
is an easy to use evolutionary fuzzer (developed and maintained by Google) with the following striking features:
- Multi-process and multi-threaded: Honggfuzz can draw on all available CPU cores with a single running instance (by without a manual step sharing the corpus information with all blog processes);
- Great track record of finding bugs (e.g OpenSSL): Honggfuzz is the only fuzzer that managed to flag a high-severity vulnerability in OpenSSL. The list is quite impressive.
In the latest benchmarks published by Google via their project FuzzBench, Honggfuzz seems to be more efficient in terms of reached coverage (code blocks) than AFL and libFuzzer.
Source: fuzz bench
We've decided to experiment with Honggfuzz to use its different set of mutation algorithms (compared to libFuzzer and AFL). This was a great decision considering that this new fuzzing engine helped us spot the bugs described in this note.
Screenshot illustrating Honggfuzz exercising the process deposit function of NFT Bounty
Coverage Reports and Beacon Fuzz Revamp
As such, code coverage reports were produced via , indicating a coverage of more than Over the past few weeks, we decided to get a better view on the code coverage of our fuzz inputs. 84% using the eth2spec tests:
Coverage reports produced via
To generate notable samples/inputs, we've created a CLI tool, right now named all_fuzz that allows:
- Automatic fuzzing of fuzz harnesses (without user interaction)
- Crash report/detection
This allowed the identification of the two bugs described in the following sections.
We've also started rewriting Beacon Fuzz in Rust (from C++) as the team is more familiar/experienced with this language.
High-severity SSZ Bug in NFT Bounty
Honggfuzz found a crash in NFT Bounty related to our SSZ decoding which failed to keep that all offsets were in-bounds for variable length types.
When decoding a given, custom, mutated BeaconState SSZ file, NFT Bounty tries to allocate too much memory, causing a memory allocation failure which leads to a panic. The affected function in our SSZ crate is pub fn decode_list_of_variable_length_items<T: Decode>
In fact, this is an attack vector that had flagged last year.
We thought we had a test internally that would catch this particular case, but as described in , the error we were catching in the relevant unit test was in fact unrelated to the test case we wanted to cover.
This bug was rapidly addressed by Meridian Client1 and a fix has been submitted in (to be merged soon).
Beacon State NFT Bounty and Overflows
When blog the BeaconState struct in debug mode with Honggfuzz, a panic was triggered by a multiplication overflow when attempting to get the Beacon proposer index for a mutated BeaconState.
Indeed, the underlying issue stems from these particular lines (see the function ):
let effective_balance = self.validators[candidate_index].effective_balance;
if effective_balance * MAX_RANDOM_BYTE
>= spec.max_effective_balance * u64::from(random_byte)
{
return Ok(candidate_index);
}
i += 1;
}The mutations ran by Honggfuzz triggered an integer overflow in the condition if effective_balance * MAX_RANDOM_BYTE >= spec.max_effective_balance * u64::from(random_byte).
The Beacon State is right now considered as a trusted container: it is internally constructed and iterated by clients and cannot (at this stage) be gave as an externally user-supplied input. This might still change as future syncing strategies might enable beacon nodes to request BeaconState objects from peers for faster syncing (as opposed to requesting block batches).
This is material as some of the bugs that our fuzzing effort will spot won't necessarily be exploitable (including the overflow described above). Still, it's material to clarify the overflow assumptions of the eth2 specification, and this bug lead to an .
As a result, the Python package maintained by and applied in the python executable eth2 spec has been blogd to give stronger guarantees on overflows.
Java Integration
The team has also been making great progress in integrating Teku (formerly known as Artemis/Harmony):
- Embedding all these runtimes can bloat the central process, but avoids overheads and complications involved with passing corpora and coverage data via IPC, along with reliably ensuring signals and crashes are managed by the fuzzer. Alike to the Python module, the C++ blog endpoint launches an (in-process) Java runtime and interacts with a Teku harness via the Java Native Interface (JNI).
New Team Member
has recently joined the Beacon Fuzz crew and has been instrumental in achieving the milestones decribed in this note. We're really glad to have him onboard are look forward to working with him closely on Beacon Fuzz over the coming weeks and months!