diff --git a/fuzzing-ping.org b/fuzzing-ping.org index b601a41..512f181 100644 --- a/fuzzing-ping.org +++ b/fuzzing-ping.org @@ -29,20 +29,19 @@ that's just rude, uncalled for, and generally boring and pointless. Technically I'm on vacation and I had resolved to only do fun things this week. So let's have some fun. -Step two: FreeBSD had a problem in =pr_pack()= because that function -handles data from the network. The data is untrusted and needs to be -validated. Now is a good time as any to check OpenBSD's implementation -of =pr_pack()=. I wanted to try fuzzing something, anything, with [[https://en.wikipedia.org/wiki/American_fuzzy_lop_(fuzzer)][afl]] -for a few years, but never got around to it. I thought I might as well -do it now, might be fun. +Step two: Did we mess something else up? FreeBSD had a problem in +=pr_pack()= because that function handles data from the network. The +data is untrusted and needs to be validated. Now is a good a time as +any to check OpenBSD's implementation of =pr_pack()=. I wanted to try +fuzzing something, anything, with [[https://en.wikipedia.org/wiki/American_fuzzy_lop_(fuzzer)][afl]] for a few years, but never got +around to it. I thought I might as well do it now, might be fun. * Make sure you are not holding it wrong. -I installed =afl++= from packages and glanced at "[[https://aflplus.plus/docs/tutorials/libxml2_tutorial/][Fuzzing libxml2 with -AFL++]]". -Here is what we need: +I installed =afl++= from packages and glanced at +"[[https://aflplus.plus/docs/tutorials/libxml2_tutorial/][Fuzzing libxml2 with AFL++]]". Here is what we need: + A program to test. Something with a know bug so that we can tell the fuzzing works. -+ A file as input, that does not trigger the bug. ++ An input file, that does not trigger the bug. + Compile the program with =afl-clang-fast=. + Run =afl-fuzz=. @@ -85,12 +84,12 @@ Here is what we need: } #+end_src This program has a trivial buffer overflow. It figures out how big a -file is on disk and stores this in =fsize=. Then it goes ahead and -reads the whole file into a buffer. It interprets the first byte as -the length of data (=len=) and allocates a new buffer (=dbuf=) of this -size. It skips the length byte and copies =fsize - 1= bytes into the -new buffer. So it trusts that the amount of data it read from disk is -the same as indicated by the length byte. +file is on disk and stores this in =fsize=. It allocates a buffer of +this size and then reads the whole file into it. It interprets the +first byte as the length of the data (=len=) and allocates a new +buffer (=dbuf=) of this size. It skips the length byte and copies +=fsize - 1= bytes into the new buffer. So it trusts that the amount of +data it read from disk is the same as indicated by the length byte. While this might seem silly, this is how real world buffer overflows look like. @@ -120,16 +119,17 @@ At this point we are facing a few problems. What does it mean to fuzz =ping(8)=, where are we getting the sample input from and how do we feed it to =ping(8)=. -From a high level point of view =ping(8)= parses arguments, initializes -a bunch of stuff and then enters an infinite loop sending ICMP echo -packets and waiting for a reply. It then parses and prints the reply. +From a high level point of view =ping(8)= parses arguments, +initializes a bunch of stuff and then enters an infinite loop sending +ICMP echo request packets and waiting for a reply. It parses and +prints each reply. Parsing the reply is the interesting thing. The reply comes from the -network and is untrusted. This is where things go wrong. The parsing -is handled by =pr_pack()=, so that's what we should fuzz. +network and is untrusted. This is where things can go wrong. The +parsing is handled by =pr_pack()=, so that's what we should fuzz. ** =in/= for =ping(8)= -Now we need sample data. An ICMP package is binary data +We need some sample data. An ICMP package is binary data on-wire. Crafting it by hand is annoying. So let's just hack =ping(8)= to dump the packet to disk. #+begin_src diff @@ -280,7 +280,7 @@ single ping and wait for one second. The ICMP reply is written to ** Fuzzing =pr_pack()= At this point I wrote a =main()= function that accepts a file name as -argument, and reads it into a buffer. I then ripped =pr_pack()= out of +argument and reads it into a buffer. I then ripped =pr_pack()= out of =ping(8)= and fed it the file contents. Of course compiling fails quite spectacularly at this point. So I @@ -292,19 +292,20 @@ is that the data does not validate and =SipHash= would short circuit. Oh yeah, and the thing is legacy IP only at this point. -So [[file:fuzzing-ping/afl_ping.c][here (=afl_ping.c=)]] it is, it is quite terrible. It would probably make more sense -to copy all of =ping(8)= and slap on a new =main()= function. Maybe. +So [[file:fuzzing-ping/afl_ping.c][here (=afl_ping.c=)]] it is, it is quite terrible. It would probably +make more sense to copy all of =ping(8)= and slap on a new =main()= +function. Maybe. Anyway, at this point I was 30 minutes in, from reading about afl for the first time until firing up =afl-fuzz= on my hacked -=pr_pack()=. Not too bad. It was now time for dinner and I left the -thing running. +=pr_pack()=. Not too bad. It was time for dinner and I left the thing +running. ** The promised bug I came back after dinner and afl found zero crashes. That's -disappointing. Or good. Depending on how you look at it. But it found +disappointing. Or good. Depending on how you look at it. But it found hangs. Running =afl_ping= on one of the reproducers, it printed -=unknown option 20= forever. +"=unknown option 20=" forever. The problem is in this part of the code: #+begin_src C