To be done…
@source https://lidaxian121.github.io/posts/fuzz/

Example

1
2
3
4
5
6
7
8
9
10
11
12
// gcc test.c -o test
#include <unistd.h>

int main() {
char input[8] = {0};
read(STDIN_FILENO, input, 8);
if (input[0] == 'A' && input[1] == 'B') {
*((unsigned int *)0) = 0xdeadbeef; // crash
}
write(STDOUT_FILENO, input, 8);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
# fuzzer.py
import subprocess

target = './test'
inps = ['AA', 'BB', 'BA', 'AB']

for inp in inps:
try:
subprocess.run([target], input=inp.encode(), capture_output=True, check=True)
except subprocess.CalledProcessError:
print(f"bug found with input: {inp}")

>>> bug found with input: 'AB'

fuzzing process

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// gcc test.c -o test
#include <unistd.h>
#include <stdio.h>

int main() {
char input[8] = {0};
read(STDIN_FILENO, input, 8);

if (input[0] == 'A') {
puts("AAA");
if (input[1] == 'B') {
puts("BBB");
if (input[2] == 'C') {
*((unsigned int*)0) = 0xdeadbeef; // crash
}
}
}
return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# fuzzer.py
# python3 fuzzer.py >> output.txt
import subprocess
import random

target = './test'
inps = ['A', 'B'] # corpus
count = 1

while True:
inp = inps[0] # select a seed from corpus
inp += random.choice(['A', 'B', 'C']) # mutate
del inps[0]
count += 1

try:
comp = subprocess.run([target], input=inp.encode(), capture_output=True, check=True)
if comp.stdout != b'': # has some output
inps.append(inp) # add to corpus
print(f"inp: {inp}, inps: {inps}", end='\n', flush=True)
except subprocess.CalledProcessError:
print(f"inp: {inp}, inps: {inps}", end='\n', flush=True)
print(f"bug found with input: '{inp}'")
break

if count % 100 == 0 or len(inps) == 0: # periodically reset corpus
inps = ['A', 'B']

1
2
3
4
inp: AB, inps: ['B', 'AB']
inp: BC, inps: ['AB']
inp: ABC, inps: []
bug found with input: 'ABC'

The output can be very long (a thousand lines) or very short (3 lines). WOW

The quality of a fuzzer depends on:

  1. Selection of seeds: the quality of corpus

  2. Mutation: the efficiency of random variation

  3. Coverage: the overhead of the implementation method


AFL (American Fuzz Loop)

Two fuzz approaches of AFL

  1. For open-source software, instrument it directly during compilation
  2. For close-source software, use QEMU to fuzz the byte code

Instrumentation (插桩)

While ensuring the logical integrity of the program, make the program output some logs to collect the status during runtime

1
2
3
4
5
6
7
8
9
int test_var = 0;

// original
void b() { ...; }
void a() { ...; }

// instrumented
void b() { printf("test_var: %d\n", test_var); ...; }
void a() { printf("test_var: %d\n", test_var); ...; }

example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int had_exec[100] = {0};

void a() {
had_exec[0] = 1;
// ...
}

void b() {
had_exec[1] = 1;
// ...
}

void c() {
had_exec[2] = 1;
// ...
}

int main() {
// ...
if (had_exec[0]) {
puts("function `a` had been called");
}
}

demo

image-20251004171318202

In addition to the target program, you also need to prepare the copus folder fuzz_in and the folder fuzz_out for storing the fuzz results. Put some random text in the former, and leave the latter empty.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// afl-gcc -g -o ./afl-test/afl-test ./afl-test/testcase.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
int vuln(char *str) {
int len = strlen(str);
// crash case 1
if(str[0] == 'A' && len == 66) {
raise(SIGSEGV);
}
// crash case 2
else if(str[0] == 'F' && len == 6) {
raise(SIGSEGV);
} else {
printf("it is good!\n");
}
return 0;
}

int main(int argc, char *argv[]) {
char buf[100]={0};
// crash case 3
gets(buf); // stack overflow vulnerability
// crash case 4
printf(buf); // format string vulnerability
vuln(buf);
return 0;
}

Use command afl-fuzz -i fuzz_in -o fuzz_out ./afl-test/afl-test to fuzz。

image-20251004171859706

太黑客了。

Check the seed that caused the program to crash under fuzz_out/default/crashes

  1. crash case 2

image-20251004173249791

  1. crash case 3

image-20251004173302271

  1. crash case 3

image-20251004172127854

  1. crash case 1

image-20251004173331488

cool