This very simple routine is used for logging in the HTTPd. As you can easily see, depending on the log type (first argument), it either prints the given string to ‘server->access_log’ or ‘server->error_log’. However, during the ‘format’ parsing it uses a simple sequence of va_start(3), vsprintf(3) and va_end(3) that could easily result in a buffer overrun since ‘buf[]’ is statically allocated with size of ‘BUFSIZE’ (which is 4096 as we can read from orzhttpd.h) and vsprintf(3) doesn’t perform any bound checks.
The second vulnerability appears in the actual printing using fprintf(3) that does not use a format specifier and since the user can have some control over the passed string for logging, he could inject format string specifiers and result in a remotely exploitable format string.
To fix this, the following patch was applied:

The insecure vsprintf(3) was replaced with vsnprintf(3) that performs some bound checks given the correct size limit and fprintf(3) now includes a format string specifier.
Patroklos Argyroudis released a PoC code that triggers the vulnerability which you can find here…
Let’s have a look at this code

This is a simple check for the passed arguments along with a usage message on error and initialization of the appropriate variables in case of 4 arguments passed to it. Nothing really important here, let’s move on…

This try/except block will attempt to connect to the given server using urllib‘s urlopen() routine and if it succeeds, just close it using close(), otherwise print the error returned to the user and exit the script using exit(). Assuming that the server is fine this is the code that will be executed:

It will sleep() and then initialize the ‘fmtstr’ with struct.pack() using ‘<LL' format, meaning that 'addr+2' and 'addr' will be packed as little-endian unsigned long integers and then, the format string that exploits the bug (probably the most important part of the code) is appended to the 'fmtstr'. Next, 'payload' is initialized with the 'GET' define that is used to set the HTTP request to GET and the malicious format string 'fmtstr' is appended to it. At last…

An AF_INET socket is created, and the Python script uses connect() and send() to connect to the remote server and send the malicious request. Finally, it sends an empty GET request using the same functions. This will trigger the vulnerability in serverlog() and result in passing the 'fmtstr' to fprintf(3).

2 Responses

I immediately saw the format string bug. The overflow took a few seconds. And all the time I’ve been saying to myself: “no way!” :) Given the triviality I consider your analysis a bit too long. Afterall it highlighted the issues a reader should already be familiar with. For certain kernel bugs I really enjoy all the context, but here it seemed to me like bloat around the fact those bugs were found in OrzHTTPd.