In this article we'll talk about the CVE-2013-1862 vulnerability, which exists in Apache versions 2.2.x before 2.2.25, where the data written to the log is not sanitized for non-printing characters. Therefore, an escape sequence characters sent by attacker in a request will get logged to the Apache log file and consequentially executed in the administrator's terminal when viewing the log file with standard tools like tail/cat/sed. The description of the whole vulnerability is as follows:
mod_rewrite.c in the mod_rewrite module in the Apache HTTP Server 2.2.x before 2.2.25 writes data to a log file without sanitizing non-printable characters, which might allow remote attackers to execute arbitrary commands via an HTTP request containing an escape sequence for a terminal emulator.
The vulnerability details are shown below, where it's evident that code execution should be possible through the vulnerability. There's also high access complexity, where multiple conditions needs to be satisfied for a successful exploitation of the vulnerability, which makes the vulnerability hard to exploit.
Without much further ado, we'll be using two Apache modules for vulnerability exploitation:
- mod_rewrite: the module is used by the Apache to rewrite URLs on the fly by using regular expression rules. Basically when a pattern in an URL is detected it is rewritten in runtime by using the provided rules. The module can be configured to redirect us to a different URL globally in httpd.conf or locally in .htaccess.
- mod_auth_basic: the module is used by Apache to provide basic authentication to restrict access to certain resources.
Before starting to dive into the Apache code, we can take a look at the patch , which will give us all the information about the changes made to fix the bug. Notice that the vulnerability is fixed by wrapping the values rhost, rname and r->user into the ap_escape_logitem function prior to writing them to the file. That function ensures the special non-printable characters are not allowed to be printed to the log file.
Obtain Vulnerable Application
A vulnerable version of the application has to be available in order to be able to explore the code-base and consequentially exploit it. From the vulnerability description we've determined that Apache versions prior to 2.2.25 are vulnerable, which is why it's best to download the latest still vulnerable version 2.2.24. In order to do that we can issue the following commands, which will download, extract and compile the vulnerable Apache.
# wget http://archive.apache.org/dist/httpd/httpd-2.2.24.tar.bz2 # tar xvjf httpd-2.2.24.tar.bz2 # cd httpd-2.2.24 # ./configure # make # make install
Build Dependency Graph
All dependencies need to be satisfied, so the vulnerable code could be reached with the input data sent to the Apache server. The real trick is finding the dependencies that we need in order for the vulnerable code to be reached. Dependency resolution is an iterative process and additional dependencies will need to be satisfied when going to the rabbit hole deeper and deeper into the code towards vulnerable code. Therefore it's imperative to understand that dependency resolution is an ongoing process that lasts throughout the whole process of exploiting a vulnerability, since all dependencies are known right before the vulnerability is successfully exploited. We should also be aware of the fact that vulnerabilities can sometimes be exploited through different input vectors, which makes the dependency graph more diversified.
From the vulnerability description we can infer that mod_rewrite module is needed, which we have to enable when installing Apache. Therefore, the first thing is recompiling Apache with additional flags:
# ./configure --enable-rewrite # make # make install
Afterwards we need to enable the rewrite_mod module by placing the following into the /usr/local/apache2/conf/httpd.conf:
RewriteEngine On RewriteLog "/var/log/apache2/rewrite.log" RewriteLogLevel 3
Since specifying the /var/log/apache2/rewrite.log log file, we also need to create the directory and assign it enough privileges, so Apache will be able to write into the directory. To find out under which username/group Apache is running, we should look into the httpd.conf and search for User/Group directives – in this case it was the daemon user and group.
# mkdir /var/log/apache2/ # chown daemon:daemon /var/log/apache2 -R
Then, we need to take a look at the do_rewritelog function, which contains the code presented below, where the vulnerability happens. The first line creates the messages that will be displayed in the terminal by assigning it to the logline variable. Then the message length is determined and the message is written to a log file by using the apr_file_write function.
Since the values rhost, rname and r->user are not checked for non-printable characters, such characters could be included and written to the log file. In order to trigger the vulnerability, we have to send a specifically crafted HTTP request to the server, which must reach the vulnerable code, where the special characters are saved to the log file. Thus, at the time of forming the logline message that will be written to the file, the variables rhost, rname or r->user need to contain special escape sequences that will trigger the vulnerability. Therefore, if we can influence those variables to contain user-supplied input, the vulnerability could be exploited.
The next step is determining how the three variables rhost, rname or r->user can be influenced with the input data. Usually we only have one variable to choose from, so the vulnerability depends on finding how we can influence that one variable to trigger the vulnerability. Since we now have three possible variables that will all trigger the vulnerability, we have to research them all, which results in more work, but possibly increases the chances of successful vulnerability exploitation, since either of the variables can be used for exploitation. In the following we'll analyze how we can influence each of the three variables.
In the do_rewritelog function, the rname is set through by calling the ap_get_remote_logname function as presented below.
Next, we have to find the ap_get_remote_logname function in order to see how the value is obtained. We can find the function code by simple grepping for the function name throughout the source code. We can quickly identify the function is located in the server/core.c and is defined as follows.
The function accepts the parameter r, which is the request_rec structure from where the remote_logname variable is read and returned. Therefore, we need to find the structure in question and view the connection parameter, which is most likely just another structure. By using grepping, we identified that the request_rec structure is present in include/httpd.h header file and looks like presented below.
Since we're interested in the connection parameter, we didn't present the rest of the structure; the connection parameter is of type conn_rec, which is another structure contained in the same header file.
The conn_rec structure contains the remote_logname variable that we're interested in. The comments above the variable say that the field is set only when doing a RFC1413 lookups. With a little bit of googling, we can quickly figure out that RFC 1413 is used for finding more information about the owner of the connection. To use the RFC 1413, we have to enable the mod_indent Apache module, which has the functionality of querying for RFC 1413 information. At first we have to add an additional option --enable-ident to the configure script when building Apache:
# ./configure --enable-rewrite --enable-ident
We also have to add additional configuration lines to httpd.conf to enable the module – note that the module is disabled by default.
IdentityCheck On IdentityCheckTimeout 10
Whenever enabling the identity check module, each time a connection will be established, the mod_indent module will query the identd server for additional information about the connection, which can cause degraded performance of the Apache server. The mod_indent module sends a query to the TCP port 113, so we have to install an identd program that provides identify check capabilities:
# apt-get install oidentd # netstat -luntp | grep 113 tcp 0 0 0.0.0.0:113 0.0.0.0:* LISTEN 20141/oidentd tcp6 0 0 :::113 :::* LISTEN 20141/oidentd
We can read more about the RFC 1413 here , but to recap what the server does is accept messages on the TCP port 113 and it will automatically determine the information about the owner of the connection and respond with the information. The Apache server can depending on the returned information, terminate the connection or continue interacting with the connection.
From all the information we gathered, we can conclude that it's fairly difficult if not impossible to convince the identd server to return the information including the user-supplied escape sequences that will be assigned to the remote_logname variable and consequentially stored in the log file. Therefore, for the time being we can move on to the other two variables and determine whether there's an easier way of exploiting the vulnerability. One of the benefits of having three input variables to choose from, is the option of ditching one of the hard-to-exploit variables and go for the easier option. If we can influence any of the three variables, the attack will be successful.
The next thing that we're going to check is whether we can store an arbitrary string into the rhost variable to cause an arbitrary string being written to the log file. The rhost variable is set by calling the ap_get_remote_host function as can be seen below.
The ap_get_remote_host function is defined in the ./server/core.c file. The function is rather long, so it doesn't fit on one picture, which is why we're presenting just the bits of the function. Since we're passing the REMOTE_NOLOOKUP to the function call, we're effectively saying that we don't want to issue a DNS lookup request, which is normally done to enable logging of host names. The code below presents the interesting part where the actual lookup is done, but the if statement in the beginning of the code section is specifically checking whether the type parameter is not set to REMOTE_NOLOOKUP, which it is in our case.
Therefore, we cannot store an arbitrary value in the rhost variable, which could be possible through a DNS system by registering a specifically crafted domain name including an escape sequence. Even if there was a possibility of setting that variable, an exploitation is unlikely, since we cannot use special character when registering a domain name, plus the domain length limits would probably cause some issues, since the buffer for the attack would be limited. This is why it's best to move on to the last variable r->user, which is our last hope in exploiting the vulnerability.
The r->user variable is a member of the request_rec structure, which we've already seen. It's defined in the httpd.h header file.
The comments in front of the members give us enough information to gather that the member variables are used when authentication is in place. Since the basic authentication module is enabled by default, we don't have to recompile Apache in order to get the necessary functionality. If we run the “./configure --help”, only the “--disable-auth-basic” option is printed, which confirms that the option can only be disabled, but is enabled by default.
In order to use the functionality we should add the following into the httpd.conf to instruct Apache that the user must first authenticate prior to using a web page:
<Location /> AuthType Basic AuthName "Private Area" AuthUserFile /usr/local/apache2/conf/.digestpw Require valid-user </Location>
Since the web page required authentication, we also have to create a valid user by using the htpasswd command.
# ./htpasswd -c /usr/local/apache2/conf/.digestpw admin
After creating the user, we need to restart Apache in order for changes to take effect. Then we can send the following request to the web server to authentication with basic authentication mechanism. If you decrypt the base64 encoded string, you'll see that admin:admin123 user account is used.
With trial and error, we can infer that do_rewritelog (in mod_rewrite.c source file) is called before get_basic_auth (in mod_auth_basic.c source file), which sets the r->user value to the username specified in the Authorization HTTP header. The get_basic_auth function gets the value of the Authorization HTTP header, base64 decrypts it and parses the username/password pair, then saves the username to the r->user member. To print the diagnosing messages, I've included a special lines in the Apache sources code to see how the input data is handled. In get_basic_auth function of mod_auth_basic.c source file I've included the following line:
In do_rewritelog function of mod_rewrite.c source file I've included the following line:
Then we can resend the request to the web server, which will cause the following to be displayed in the /usr/local/apache2/logs/error_log log file – we can continually display the changes of the content of the file by using the “tail -f <file>” command. Notice that the get_basic_auth function is not even called, which means the resources we've requested doesn't require authentication.
Additionally we also have to have at least one RewriteRule that will be matched by our request. The simplest rule is taking the URL request and adding an ending slash '/' to the end of the directory when one is not provided by the user. In order to use that rule, we should edit the /usr/local/apache2/htdocs/.htaccess file and include the following into the file. This will effectively cause the do_rewritelog function to be called at the time when the r->user was previously set by the get_basic_auth function.
RewriteRule ^(([a-z0-9-]+/)*[a-z0-9-]+)$ $1a/ [NC,R=301,L]
In order to cause authentication function to be called, we have to send the Authorization HTTP header to a protected resource. If we have protected the whole DocumentRoot /, then the HTTP request needs to be sent by using “GET / HTTP/1.1” request header. After correcting the request appropriately and resending the request, the following will be displayed in the terminal.
The first there lines present the do_rewritelog function calls, where the r->user hasn't been set yet, since the get_basic_auth function hasn't been called at that point; therefore the r->user is marked as NULL. Next, the get_basic_auth function gets called setting the r->user variable to the username part of the base64 encoded Authorization HTTP header. After which, we can observe that the do_rewritelog correctly displays the r->user value as “ admin ”, since it was previously set by the get_basic_auth function. Therefore, we've been able to set the r->user value with arbitrary data, but there's a catch: the r->user only gets set when sending an authentication request for an existing username. If we send the same request to the server, but changing the “ admin ” username to “ administrator ” username, the following is displayed in the terminal.
The problem now is that only the name of an existing username could be written to a log file, therefore preventing us to save an arbitrary escape sequence to the log file. At this point we've discovered that we can't write arbitrary string into the log file by using r->user, because an existing user must already exist in the AuthUserFile for the vulnerable code to be reached.
In this article we've seen how an attacker might analyze a vulnerability in Apache in order to determine whether it is executable. Such skill comes in handy during the penetration test, where a client is running a vulnerable version of Apache server, because we can analyze the vulnerability and if possible write an exploit to use the vulnerability to our advantage. If vulnerability is exploitable we can gain unauthorized access to the system or possibly execute arbitrary code, which wouldn't be possible otherwise.