During testing of different forwarding-HTTP-proxies at Opera Software, we heavily edited Privoxy so that it could be fuzzed with nearly complete coverage for a single input.
While normally it is preferable for a fuzzer to concentrate on specific sub-functionality such as response handling or input handling, we decided that Privoxy's codebase is small enough (and robust enough!) that it could handle an input which acts as both a client's request and a client's response.
By implementing a 'magic-byte' [\xF8 AKA ø], a single input is able to act as an HTTP request and an HTTP response all in the same file, and thus a more realistic codeflow from Privoxy is obtained. In practise, this meant that an unlimited request<-->server file could be created by continuously separating each segment with the ø character. For example, a file could contain GET / HTTP/1.1\r\nHost: host\r\n\r\nøHTTP/1.1 200 OK\r\n\r\n which would successfully be parsed as both a request and a response by Privoxy. No hard limit was set, and interestingly, afl++ was able to create a file which correctly acted as hundreds of requests and responses. Amazingly, corpus 1228 has a total of 323 completely valid HTTP requests and responses generated by the fuzzer.
There were various caveats that needed to be resolved while implementing this system, namely due to how I/O streams work. More information about the actions we took can be found here: blog.
We implemented leak-checking into AFL++, which we then used to identify X vulnerabilities which could cause memory exhaustion via Privoxy.
The diffs between privoxy-3.0.31-stable and our fuzzing code can be found here: there are a couple of patches (clearly labelled) to check out. We also include some corpuses.
Finally, we include a coverage report which outlines exactly which lines of code and functions that the fuzzer (in this case, AFL++) was able to uncover. Nearly every function which was not covered is completely unable to be fuzzed with a single input of 'request->response', and in some cases manual fuzzing of these functions were done (such as config file parsing).
For any adventurous readers out there, I would recommend taking a look at Privoxy's SSL[1] handling[2] and network[1] handling[2], as we did not take an in-depth look at these sections and there may be vulnerabilities to be found.
In total, 12 bugs were found:
- CVE-2021-20217 --- DoS
- CVE-2021-20272 --- DoS
- CVE-2021-20275 --- Buffer Overflow
- CVE-2021-20274 --- Null Pointer Dereferencing
- CVE-2021-20273 --- DoS
- CVE-2021-20276 --- Buffer Overflow
- 647353d3e6621b32696a08d66656b7c6bb8fb0a3 --- Undefined memcpy behavior
- 9788f97d68076acffc11da82a9c65f06cd661b15 --- Undefined left shift
- f018685d622080d08641471be338e5e2b698d8df --- Uninitialized byte reading
- c78a3a27b574d049a155542724134f01648d7aac --- DoS & Buffer Overflow & Null Pointer Dereferencing (only in fuzzing mode)
- 88d5b57a4a639f88c937e7b22f6fe976b4380ce9 -- DoS (only in fuzzing mode)
- bc7cc845a82203f50e3fc5c1bd0dca01571749b9 --- Incorrect config loading
A second round of fuzzing occured, looking for memory leaks. 3 issues were found:
- CVE-2021-44540
- CVE-2021-44541
- CVE-2021-44542
Most of these bugs were exploitable by remote webpages, and more information about them can be found on our our post #1 and post #2.
A big thank you to Fabian Keil for his incredibly quick patches and continued communication during this testing.