To be done… @source https://lidaxian121.github.io/posts/fuzz/
Example 1 2 3 4 5 6 7 8 9 10 11 12 #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 ; } write(STDOUT_FILENO, input, 8 ); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 import subprocesstarget = './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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #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 ; } } } 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 import subprocessimport randomtarget = './test' inps = ['A' , 'B' ] count = 1 while True : inp = inps[0 ] inp += random.choice(['A' , 'B' , 'C' ]) del inps[0 ] count += 1 try : comp = subprocess.run([target], input =inp.encode(), capture_output=True , check=True ) if comp.stdout != b'' : inps.append(inp) 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 : 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:
Selection of seeds: the quality of corpus
Mutation: the efficiency of random variation
Coverage: the overhead of the implementation method
AFL (American Fuzz Loop) Two fuzz approaches of AFL
For open-source software, instrument it directly during compilation
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 ;void b () { ...; }void a () { ...; }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
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 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <signal.h> int vuln (char *str) { int len = strlen (str); if (str[0 ] == 'A' && len == 66 ) { raise(SIGSEGV); } 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 }; gets(buf); printf (buf); vuln(buf); return 0 ; }
Use command afl-fuzz -i fuzz_in -o fuzz_out ./afl-test/afl-test to fuzz。
太黑客了。
Check the seed that caused the program to crash under fuzz_out/default/crashes
crash case 2
crash case 3
crash case 3
crash case 1
cool