You may not have heard of cURL, but you’ve probably used software that uses it.
It’s an open-source programming toolkit that helps you deal with writing client-side code that deals with URLs.
In the words of the project itself, “cURL groks those URLs.”
It’s popular because it’s a URL Swiss Army knife, making it easy to handle popular protocols like HTTP, SMTP, POP3 and many more. It also supports uploads, downloads, authentication, proxies, cookies and SSL/TLS.
It even supports Gopher, if you remember that far back.
One risk with an all-singing, all-dancing library, of course, is that there’s more code to go wrong.
And sometimes, even obscure bits of code you thought you’d never use might get triggered. Worse still, they might be triggerable by external circumstances you never predicted.
That’s the curly problem here.
The vulnerable code was introduced in the 7.26.0 release, when support for DIGEST_MD5 authentication was added to the cURL software.
DIGEST_MD5 is an rudimentary way of allowing you to login over an unencrypted connection, for example to an HTTP or POP3 server, without sending your actual password.
The server sends a random challenge string, or nonce, together with a bunch of other authentication-related data; you reply with a cryptographic hash of your password mixed up with that server-supplied data:
So a cracker who sniffs your reply can’t directly recover your password from it, and since the challenge is random and varies every time you log in, the cracker can’t re-use your reply later.
As an aside: avoid using DIGEST_MD5 authentication. Encrypt the entire session using TLS instead.
A cracker who sniffs a DIGEST_MD5 reply can’t re-use it directly, but he can use it to try to recover your password offline using a dictionary attack.
TLS not only prevents this, but also keeps the entire transaction secret, including the contents of your web session or email. That’s a much better security outcome.
Here’s the buggy code from a vulnerable version of cURL:
Don’t worry if you aren’t familiar with C. I’ll explain.
In C, the management and use of memory is left up to the programmer. You can use library code to help you deal safely with variable-length data, such as user-supplied text strings, or you can deal directly with memory yourself.
Above, the programmer has done the latter.
Firstly, he allocates a series of fixed-length memory blocks on the stack.
Then he copies text strings supplied by the caller of the function into those blocks, but be uses the system functions strcpy() and strcat(), which stand for “string copy” and “string concatentate” (tack one string on the end of another) respectively.
In modern code, you should never use those functions, because you can’t limit how much data they copy.
They simply duplicate every byte from the input string into the output string, until a NUL (zero) byte has been found. A NUL is how the end of a text string is denoted in C.
So, if the server sends too much data in its authentication challenge, for example an overly-long realm string (the contents of which can be whatever the server chooses), this function will stuff too much data into the buffer it uses to compute the authentication response.
A buffer overflow will result, and in this case, since the destination data blocks were allocated automatically on the stack, the function will crash when it ends.
That’s because the stack also stores the address in memory from which the function was called, so the cURL software can return there when it’s finished. The return address is overwritten in the above code if the string response get over-filled.
The fix was a simple one.
The uncontrollable strcpy() and strcat() functions have been replaced with the function snprintf(), which stands for “formatted print of string into at most n bytes”:
You can still make mistakes with snprintf, since it’s up to you to specify n, and if you aren’t careful, you may get it wrong.
But the point is that is is at least possible to restrict the output of snprintf to a known buffer size, which you simply can’t do with the old-fashioned strcpy() and strcat().
→ The updated cURL code above still isn’t perfect. The programmer should really check the return value of snprintf(), which reports how many bytes it wanted to write. If your buffer wasn’t big enough, then the output will be incomplete and therefore incorrect. You ought not to use it: increase the size of your buffer and try again, or report an error instead.
You’re probably thinking, at this point, that exploiting this vulnerability would be hard because most programs that use cURL do so in the background. They aren’t interactive.
Autoupdating software, which might use the cURL library (known as libcurl) typically comes pre-configured with a list of known-good URLs, or asks you to enter a URL at install time, and that’s that.
An attacker who could talk you unto switching your known-good autoupdate URL for a dodgy one, or who could persuade you to change from the POP3 email server you’ve always used to one you’ve never heard of, would surely find it easier to infect you simply by getting you to run his malware directly.
That’s true, but there’s still a risk.
If an attacker can redirect the requests from your autoupdater or your POP3 client, for example by fiddling with your DNS settings, or by hacking a server at the edge of your service provider’s network, he could send you off to an imposter site and attempt to exploit you from there.
→ As @kyprizel points out in the comments below, cURL only actually calls the vulnerable function from its POP3 and SMTP protocol handlers, so an HTTP request cannot directly put you in harm’s way. But cURL follows HTTP redirects, even unusual ones that send you off to a POP3 server, so a crook who can modify your HTTP replies may be able to attack you nevertheless.
Because of the buffer overflow, this could lead to a drive-by download, where cURL itself is tricked into misbehaving, cutting your informed consent out of the loop altogether.
- Don’t use strcpy() or strcat().
- Use snprintf (or strlcpy() and strlcat(), or similar) instead.
- Always check the return value of string-handling functions so you don’t end up using incorrect results.
Your next problem is to find out which software you are using, if any, includes cURL code with these bugs. (Versions of cURL from 7.26.0 to 7.28.1 inclusive are affected.)
Best start asking around…
NB. Although several Sophos products use libcurl, none of them use code from the vulnerable versions.
9 comments on “Anatomy of a vulnerability – cURL web download toolkit holed by authentication bug”
This pales in comparison to the buffer overrun, but all those strcats take O(n^2) time.
buffer overrun is the root of dds. I believe we should change all memory architecture to defend buffer overrun. I remember the day I used to 8088, I try to find the way to "not overrun" including using compression method which takes my 8088 a min to decry-pt it. Then last, I give up and redesign my code.
Vulnerability has been originally found by Volema, you should at least have decency to mention it.
Sure. But I did link to cURL's security bulletins page, which in turn links to cURL's own official commentary on this specific bug, to the volema.com page, and to a third-party bulletin announcement site (Symantec's, as it happens).
I think that's a better route to guide people down: visit the cURL site and take the whole story from there, since cURL tells it clearly.
With that in mind, I suggest that implying "indecency" might be taking your critique of my article a bit far 🙂 But if you need to visit the Volema link without visiting the cURL site first, see @tracer0tong's comment below…
Vulnerable function Curl_sasl_create_digest_md5_message() is only used in SASL DIGEST_MD5 of POP3 and SMTP, not HTTP.
Good point. That function is called only from lib/pop3.c and lib/smtp.c.
However, you can end up at a POP3-style DIGEST_MD5 connection even though you started at an HTTP URL because cURL will follow redirects from HTTP to POP3.
I have added a clarification in the article itself….thanks!
Still confused about it and what to do.
That's part of the problem when a widely-used library is found to have a hole. Lots of programs might inherit the vulnerability.
Furthermore, how to fix the vulnerability then depends on whether they statically link to the library, i.e. compile in their own version, or dynamically link to a DLL or shared library provided elsewhere that can centrally be upgraded.
(That's why I explicitly mentioned above that those Sophos products that use libcurl include code from non-vulnerable versions, so you can cross us off your list 🙂
Mac OS X users: your OS includes curl/libcurl as its standard command-line download tool and library; from what I can see, OS X 10.8.2 has curl 7.24.0, so the OS-provided version is OK.
But if you use macports (are there OS X users who don't?) and have its curl installed, you need to do a "sudo port upgrade curl" to bump yourself up to curl 7.29.0.
Sorry I can't be more helpful and give you a representative list…I suppose you could start by scanning your filing system for files named '*curl*' and seeing what turns up.
Vulnerability was discovered by http://blog.volema.com/