Write-up: Source-code disclosure via an SOH byte in Tomcat routing

Archived article from 27 March 2016. Its content reflects the state of things at the time. Reconstructed from the web archive of secalert.net - view the original.

Summary. A target running Apache httpd in front of Apache Tomcat could be tricked into disclosing the source of server-side files. By appending the SOH control character (start of heading, 0x01) to a .jsp or .inc URL, Tomcat treated the byte as the start of a new header rather than part of the extension and returned the file, while httpd, not recognising the resulting .jsp%01 extension, served that content as plain text. The issue was reported through a bug bounty program, triaged at the highest severity, and fixed within 48 hours. The affected company is anonymised throughout, as required by the program's terms.

Background

While taking part in bug bounty programs, I recently came across an interesting issue that was classified at the highest severity and earned a bounty. Because of the program's terms, I cannot name the company.

Information gathering

I started with some information gathering and footprinting. I noticed that the files ended in .jsp, which often indicates Apache Tomcat. First I reviewed the HTTP response headers to learn more about the target system:

response headers
HTTP/1.1 200 OK
Date: 16 Mar 2016 15:15:33 GMT

When the status line is followed by the Date header on the second line, it usually means an Apache httpd server is in front. Here I assumed httpd was sitting in front of a Tomcat server. If that were the case, they were probably using a module to dispatch requests between httpd and Tomcat, which meant I might be able to trick the routing into exposing the source of any .jsp or .inc file by appending specific low-ASCII characters, depending on whether a connector or a handler was in use.

Connector, handler, file descriptor

1) The Apache Tomcat connectors: if httpd and Tomcat are configured to serve content from the same filesystem location, care must be taken to ensure httpd cannot serve inappropriate content such as the contents of the WEB-INF directory or JSP source. This can happen if the httpd DocumentRoot overlaps with a Tomcat host's appBase or the docBase of any context, or when using the httpd Alias directive in combination with them.

2) Now consider the httpd handler. A "handler" is Apache's internal representation of the action to perform when a file is requested. Files usually have implicit handlers based on their type. Most files are simply served, while certain types are "handled" separately. To handle .jsp files, you might use the Apache module mod_mime to associate a filename extension with the file's behaviour (handlers and filters) and content (MIME type, language, character set and encoding).

What does httpd do with a file that is not explicitly associated with a handler or filter? It serves the file as plain text with no further processing, which is exactly the behaviour that can be exploited here.

Analysis

From the information gathering I knew the target handled .jsp files, so I assumed httpd was in front with Tomcat or a similar server behind it. I tried appending a character to the file extension to provoke an uncaught exception or some abnormal behaviour:

null byte appended
https://www.victim.tld/password.jsp%00

This did not work as expected. Rather than a stack trace or a web application firewall, I got:

response to %00
HTTP ERROR: 400
Problem accessing /password.jsp%00. Reason:
    The request contains an illegal URL

From past pentests I knew that httpd would normally strip the %00 and return something like:

the response httpd would normally return
Not Found
The requested URL /password.jsp was not found on this server.

So I concluded that this error did not come from httpd but from a connector. I also knew from experience that some connectors behaved unusually when passed low-ASCII characters.

Researching Tomcat connectors, I found that the routing of the data stream could be manipulated using the SOH (start of heading, 0x01) transmission control character. SOH was originally used to mark a non-data section of a stream, the part containing addresses and other housekeeping data.

Having had success with this trick in the past against modules such as mod_proxy_ajp, mod_jk, some Spring Boot setups and a few others, I tried:

SOH control character appended
https://www.victim.tld/password.jsp%01

What I assumed was happening

The whole trick is a routing confusion between the two servers. The animation below follows a single request through the chain. The precise steps follow underneath.

Routing confusion %01 (SOH) between Apache httpd and Tomcat Client GET /password.jsp%01 source disclosed <%@ page %> String pw="…"; <% out.print(pw) %> browser Apache httpd HANDLER LIST .jsp → Tomcat .jsp%01 → none ⇒ serve as plain text reverse proxy Tomcat password.jsp %01 %01 starts a new header opens password.jsp servlet container GET /password.jsp%01 .jsp source String pw="…"; <% out.print(pw) %> 1 · Client requests /password.jsp with a trailing %01 byte 2 · httpd matches the .jsp handler and forwards to Tomcat 3 · Tomcat reads %01 as a new header, so it opens password.jsp 4 · Tomcat returns the file content under the .jsp%01 extension 5 · httpd has no .jsp%01 handler, so it serves the source as plain text
Cross-server routing confusion: the SOH byte (%01) splits the extension, so Tomcat returns the raw source and httpd serves it as text. The same works for .inc files.
assumed request flow
1) Request is sent to Apache httpd
2) httpd uses its file handler/filter to pass the request to Tomcat for processing
3) Tomcat opens the ".jsp" file because it treats %01 as the start of a new
   header rather than part of the file extension
4) Tomcat returns the content of the requested file to httpd, which now holds
   the ".jsp" content under the requested extension ".jsp%01"
5) httpd does not find the ".jsp%01" extension in its handler list and therefore
   serves the file as plain text
6) The same also works for ".inc" files on the target system

Proof of concept and reporting

I could potentially have accessed the entire source tree, but I limited myself to a few .jsp and .inc files as proof of concept. I reported the issue immediately. Within three hours the company confirmed it and triaged it at the highest severity, and they deployed a hotfix within 48 hours. Well handled.

References

documentation
http://httpd.apache.org/docs/current/handler.html
http://httpd.apache.org/docs/current/mod/mod_mime.html
https://tomcat.apache.org/connectors-doc/reference/apache.html

Web server handlers, filters and modules with similar issues in the past:

prior art
CVE-2007-1860: mod_jk double-decoding:
http://www.cvedetails.com/cve/CVE-2007-1860/

WebLogic: http://www.example.com/index.js%70
(via: http://www.securityfocus.com/bid/2527/exploit)

Tomcat: http://www.example.com/examples/snp/snoop%252ejsp
(via: http://www.securityfocus.com/bid/2527/exploit)

IBM Websphere: http://www.example.com/login.JsP
(via: http://www.securityfocus.com/bid/1328/info)

Netscape Web Server: http://www.example.com/login.jsp%20
(via: http://www.securityfocus.com/bid/273/discuss)

Allaire JRun root directory disclosure:
http://server/%3f.jsp
(via: http://www.securityfocus.com/bid/3592/discuss)

Apache httpd artificially long slash path directory listing vulnerability:
http://www.example.com///[1-4096 slashes here]/admin/*
(via: http://www.securityfocus.com/bid/2503/discuss)

BEA WebLogic directory traversal with %00, %2e, %2f and %5c:
http://www.example.com/%5cadmin/
(via: http://www.securityfocus.com/bid/2513/discuss)