How to fuzz the Lightning Network Daemon's wire protocol using go-fuzz
This document will describe how to use the fuzz-testing library
lnd wire protocol.
Lnd uses its own wire protocol to send and receive messages of all types. There
are 22 different message types, each with their own specific format. If a
message is not in the correct format,
lnd should logically reject the message
and throw an error. But what if it doesn't? What if we could sneakily craft a
custom message that could pass all the necessary checks and cause an error to
go undetected? Chaos would ensue. However, crafting such a message would require
an in-depth understanding of the many different cogs that make the wire protocol
A better solution is fuzz-testing. Fuzz-testing or fuzzing is when a program
known as a fuzzer generates many, many inputs to a function or program in an
attempt to cause it to crash. Fuzzing is surprisingly effective at finding bugs
and a particular fuzzing program
AFL is well-known for the amount of bugs it
has found with its learned approach. The library we will be using,
AFL and has quite a track record of finding bugs in a diverse set of
go-fuzz takes a coverage-guided approach in an attempt to cover
as many code paths as possible on an attack surface. We give
valid inputs and it will essentially change bits until it achieves a crash!
After reading this document, you too may be able to find errors in
Setup and Installation
This section will cover setup and installation of
- First, we must get
$ go get github.com/dvyukov/go-fuzz/go-fuzz $ go get github.com/dvyukov/go-fuzz/go-fuzz-build
- Next, create a folder in the
lnwirepackage. You can name it whatever.
$ mkdir lnwire/<folder name here>
docs/go-fuzzfolder and move it to the folder you just made.
$ tar -xzf docs/go-fuzz/corpus.tar.gz $ mv corpus lnwire/<folder name here>
- Now, move
wirefuzz.goto the same folder you just created.
$ mv docs/go-fuzz/wirefuzz.go lnwire/<folder name here>
- Change the package name in
<folder name here>.
- Build the test program - this produces a
<folder name here>-fuzz.zip(archive) file.
$ go-fuzz-build github.com/lightningnetwork/lnd/lnwire/<folder name here>
- Now, run
$ go-fuzz -bin=<.zip archive here> -workdir=lnwire/<folder name here>
go-fuzz will print out log lines every couple of seconds. Example output:
2017/09/19 17:44:23 slaves: 8, corpus: 23 (3s ago), crashers: 1, restarts: 1/748, execs: 400690 (16694/sec), cover: 394, uptime: 24s
Corpus is the number of items in the corpus.
go-fuzz may add valid inputs to
the corpus in an attempt to gain more coverage. Crashers is the number of inputs
resulting in a crash. The inputs, and their outputs are logged in:
<folder name here>/crashers.
go-fuzz also creates a
of stacktraces to ignore so that it doesn't create duplicate stacktraces.
Cover is a number representing coverage of the program being fuzzed. When I ran
go-fuzz found two bugs (#310 and #312) within minutes!
You may wonder how I made the corpus that you unzipped in the previous step.
It's quite simple really. For every message type that
TestLightningWireProtocol, I logged it (in
byte format) to
a .txt file. Within minutes, I had a corpus of valid
lnwire messages that
I could use with
go-fuzz will alter these valid messages to create
the sneakily crafted message that I described in the introduction that manages
to bypass validation checks and crash the program. I ran
go-fuzz for several
hours on the corpus I generated and found two bugs. I believe I have exhausted
the current corpus, but there are still perhaps possible malicious inputs that
go-fuzz has not yet reached and could reach with a slightly different generated
If you take a look at the test harness that I used,
wirefuzz.go, you will see
that it consists of one function:
func Fuzz(data byte) int.
that each input in the corpus is in
byte format. The test harness is also
quite simple. It reads in
byte messages into
serializes them into a buffer, deserializes them back into
and asserts their equality. If the pre-serialization and post-deserialization
lnwire.Message objects are not equal, the wire protocol has encountered a bug.
0 is returned,
go-fuzz will ignore that input as it has reached
an unimportant code path caused by the parser catching the error. If a
byte input was parsed successfully and the two
objects were indeed equal. This
byte input is then added to the corpus as
a valid message. If a
panic is reached, serialization or deserialization failed
go-fuzz may have found a bug.
Fuzzing is a powerful and quick way to find bugs in programs that works especially
well with protocols where there is a strict format with validation rules. Fuzzing
is important as an automated security tool and can find real bugs in real-world
software. The fuzzing of
lnd is by no means complete and there exist probably
many more bugs in the software that may
go undetected if left unfuzzed. Citizens,
do your part and