Friday, October 3, 2014 At 9:20AM
Amongst the chaos of trying to patch and the whirlwind of information on the Shellshock vulnerability, several of our clients raised the question – what level of exposure, if any, do our enterprise Java web applications have to Shellshock?
In case you’ve been living under a shell, here are a few links to get up to speed on CVE-2014-6271, dubbed Shellshock. Various web-based software products were proven to be vulnerable. Custom web apps running on Apache using mod_cgi or mod_cgid where CGI scripts are either written in Bash, or spawning subshells are also easy targets (if a vulnerable version of Bash is still installed). Web apps written in PHP, Python, or Java could be vulnerable if they make calls to functions such as popen() or system(), as these are backed by calls to /bin/sh -c. Since it is common for systems to create symbolic links from /bin/sh to /bin/bash it could still lead to these applications being susceptible to the Shellshock vulnerability.
Despite Java apps being implicated as potentially vulnerable, our gut reaction was the exposure for a typical Java web app would likely not be critical or systemic, but rather on a case by case basis. Further, we always advise our clients that any Java web app spawning external processes be closely audited and better more secure alternatives be considered irrespective of this Bash vulnerability. With that said, we still wanted to do a little bit of research to back this up and this blog post shares some of these results. On to the details …
Two main conditions must be met to exploit the Shellshock vulnerability:
- The system runs a vulnerable version of Bash shell
- An attacker injects a persistent or volatile malicious environment variable into directly or indirectly invoked Bash shell
Applications running Apache ‘mod_cgi’ are remotely vulnerable to Shellshock due to HTTP request headers (e.g. User-Agent header) being set as environment variables. During the request processing Apache receives an HTTP request header and sets the values as environment variables that are visible within the script handler provided by mod_cgi. The combination of user controllable environment variables through HTTP data and the behavior of command executions spawning shells creates a high potential for exploitable Shellshock scenarios within CGI applications to be a likely scenario.
However, Java web application containers like Apache Tomcat, handle the request parameters and headers without using the environment variables as a pipeline. To verify the previous statement GDS performed a set of tests on Apache Tomcat (v8.0.12,7.0.55) and JBoss Application Server (v7.4). The tests included using a python script that injects a Shellshock payload into HTTP request headers and query parameters on a selected endpoint. A simple JSP page was built to iterate through the environment variables to confirm that the request headers did not lead to an environment variable being tainted with user input. As expected, this was confirmed and significantly reduces the potential for Shellshock to be exploited in a Java web app via the typical HTTP header payloads.
POC.JSP:
… snip …
Enumeration<String> HeaderName = request.getHeaderNames(); %>
<H1> Headers</h1>
<%
String headerName , headerValue ;
while(HeaderName.hasMoreElements()){
headerName = HeaderName.nextElement();
headerValue = request.getHeader(headerName);
// Save Header
headerMap.put(headerName, headerValue);
%><H1> Env Variables </h1><%
for(Map.Entry<String,String> entry : System.getenv().entrySet()){ %>
<%=entry.getKey()%> <%=entry.getValue()%><br>
<%
for(Map.Entry<String,String> headerEntry : headerMap.entrySet()){
if(entry.getValue().contains(headerEntry.getValue())){
finding.put(entry.getKey(), headerEntry.getKey() +":"+ headerEntry.getValue());
}}}
// Register Logs
for(Map.Entry<String,String> entry : finding.entrySet()){
writer.append(entry.getKey()+":"+"{"+entry.getValue()+"}");
}
…snip …
Tester.py:
…snip …
def start:
request_headers = {}
for header_key in INPUT.HEADERS:
for reuqest_method in INPUT.METHODS:
request_headers = copy.copy(INPUT.HEADERS)
request_headers[header_key] = INPUT.HEADERS[header_key]+INPUT.PAYLOAD
self.__client.request( request_method,"/ POCs_Headers.jsp",headers=request_headers )
self.__logfile.write(self.__client.getresponse().read())
… snip …
Java’s ‘Runtime.exec’ method seemed like the most obvious place to start as a high-risk function that could work as a stepping stone for exploitable scenarios of Shellshock. ‘Runtime.exec’ uses the ProcessBuilder class to execute the command passed to it. The following outlines the function execution flow for executing the submitted command.
Runtime.exec function flow :
– > Runtime.exec(String command)
– > Runtime.exec(String command, String envp, File dir)
– > ProcessBuilder.start(String[] cmdArray, Map<String,String> env, String dir, …)
– > UnixProcess( final byte[] prog, …. )
– > forkAndExec( … )
Unlike command execution APIs found on PHP or Perl where a shell will be invoked regardless of the string passed in, Java’s Runtime.exec does not work in this manner. Runtime exec will perform an exec of the submitted string value and therefore a Shellshock vulnerability could only occur if Bash is invoked by the running process by calling ‘/bin/bash’, symbolic links to ‘/bin/bash’ or any executable that may invoke Bash at some point.
In the event that an environment variable within the application server process is user controllable it would cause the application invoking the following code to be vulnerable to the Shellshock vulnerability (as discussed above, this is not the case for the application servers we looked at):
Runtime.getRuntime().exec( "/bin/bash" ) ; Runtime.getRuntime().exec( "/script.sh" ) ; # Where script.sh uses !#/bin/bash
Another exploitable scenario but also very unlikely would be if a malicious environment variable is passed as the second parameter within the ‘Runtime.exec’ method.
Runtime.getRuntime().exec(
new String[]{"/bin/bash"},
new String[]{"P=() { :;}; echo 'vulnerable' > message"}
);
Runtime.getRuntime().exec(
new String[]{"/script.sh"},
new String[]{"P=() { :;}; echo 'vulnerable' > message"}
);
What follows is a sample code snippet that if present in a web app would be remotely exploitable via Shellshock:
… snip … Enumeration<String> HeaderName = request.getHeaderNames(); String headerName , headerValue ; while(HeaderName.hasMoreElements()) { if ( headerName.nextElement().equals(“HOST”)){ headerValue = request.getHeader(headerName); Runtime.getRuntime().exec( new String[]{“/bin/bash”,”-c”,”/bin/process_host”}, new String[] {headerValue}); } }
Although not impossible, it’s unlikely that a developer would pass the environment variable intentionally via a HTTP header.
In conclusion, while there are certain scenarios where a Java web app could be vulnerable to Shellshock, these are likely to be one off edge cases. This is primarily due to application servers not setting environment variables with HTTP request data and also the fact that Runtime.exec does not invoke a shell unless explicitly set by a developer or indireclty used by any invoked script. Organizations should ensure that Bash is patched to the latest version and not vulnerable to Shellshock. Additionally, identify if any of the following methods or functions are called using a shell (e.g. /bin/bash/, /bin/sh, shell scripts) and ensure user input is not passed in as any parameters unless it undergoes stringent input validation.
- Runtime.exec
- ProcessBuilder.start
- UnixProcess
- forkAndExec
It is also recommended to pass in a empty String array as the environment to these functions in order to prevent the possibility of the application server’s environment being tainted with user input. By passing an empty array as the environment it will provide an extra layer of protection in the event the application server’s environment variables can be compromised.
Author: Chaddy Huussin
©Aon plc 2023