I tend to find ways of occupying myself with new exciting things. It might be a new language I try to learn to speak (not much success there), a new instrument I try to learn to play (I have no musical talents) or perhaps just a new game I lose myself in for weeks (Red Dead Online, I’m coming for you in a few minutes!). So it was just a matter of time before I started to experiment with fuzzing.
In this post, I don’t aim to give any high level introduction into what fuzzing is. I will this time give the description for fuzzing as mutilating the input, and seeing how much the process throws up from it. I will aim to write a post oversimplifying fuzzing in the future. But this time, I will instead tell you the short story of how I almost discovered a security issue in curl.
As my skillset mainly focuses on network related things, I wanted to start my fuzzing path on something related. What was the very first thing that came to my mind? curl!
Curl is a magnificent tool, so simple and yet so powerful. You give curl a URL, and it gives you what lies behind it. I also admit harbouring a feeling of slight pride from the fact that curl has been created by our neighbours, the Swedes. And, at the moment of writing, curl officially supports 26 different protocols. That’s 26 different things to fuzz right there!
As I needed to start somewhere, I decided to not start by having the most epic set of testing samples, and instead collected a modest set of traffic samples I could use. I wanted to settle in to my new fuzzy life, learn what I need to do, make a few mistakes first, and only then build up that most epic set of testing samples.
Initially, I experimented with using Radamsa as my fuzzer, but I quickly moved onto another fuzzer my friend had made. I made a process which fuzzes the samples I had and makes curl fetch them. At first, I picked the easiest two protocols I could think of: HTTP and HTTPS. But as I realized these two must be the most stable two protocols there are, I decided to randomly pick a less familiar protocol. I ended up picking MQTT. Turns out MQTT support in curl is quite recent: it was added there in 2020.
I got a few MQTT traffic samples and started the fuzzer. I left it running for the night. When I came back in the morning, I found my test stuck and curl eating close to 100% of my CPU.
I manually tried the sample which curl had gotten stuck on a few times, and sure enough, close to 100% CPU for curl immediately every time. Strace showed that the process was clearly in a busyloop. I checked what my sample looked like: It was a very modest message consisting of just 5 bytes. A DoS with just 5 bytes! This must be an interesting finding!
Out of curiosity, I also checked what the traffic looks like in wireshark when the busyloop happens. The client makes a connect command, the server sends its 5 bytes (which is a malformed Publish message), and the server closes the connection. The client, however, does not close its side of the connection, as it has entered the loop. That’s even a very cheap DoS, I thought, as the server just needs to deliver the 5 bytes and it can then just close the connection!
In my mind, I parallelized this infinite loop issue with a recent infinite loop vulnerability in OpenSSL. As an infinite loop in OpenSSL was considered a high severity security issue, I was certain that an infinite loop in curl would at least warrant medium severity.
I rolled up my sleeves and opened up the Hacker One page for curl. I entered the details and provided the poc.
The initial acknowledgement came almost immediately, with a polite thank you for my submission. The initial triage came the next day: this does not seem like a security issue, and can instead be compared to the server never closing the connection. Anyone using curl should always know that an infinitely long connection is possible, and thus write their scripts so that they finish in time. This can for example be done in curl with the –m option. In addition, this does not starve the system out – it will give way for other processes.
I gave my counter-arguments: infinite loop is different from an infinitely long transfer, as the process remains in the loop even when the other endpoint has already closed the connection. I also argued that this should not be dismissed due to the existence of options in the tool that make the issue go away: the default behaviour of the process is to remain in the loop. And finally I highlighted that this is a very “cheap” DoS, as the server only needs to send out five bytes, after which it can close the connection.
The developers gave it a good deliberation, and ended up deciding that this is not a security issue due to the above mentioned reasons. They did acknowledge that it is indeed an annoying bug which needs to be fixed as soon as possible. I made sure that this meant that I can post about it, and they confirmed it. You can find the disclosed issue here.
If you are interested, you can check out the poc from this github repo!
And that was the story of how I almost found a security issue in curl! Despite it ending up being nothing more but an annoying bug, I valued the experience I got from it, and the expertise I encountered from the curl team. I hope my next discovery will be a bit more exciting.
Thank you again for reading, and see you next time!
Edit 29th March: Here is the same issue discussed from the perspective of Daniel Stenberg, the founder and lead developer of curl. To quote him: Security is hard.