XPath Injection
XPath Injection
Description
XPath injection is a type of attack that can change the intent of an XPath query that is executed on an application’s backend. An application might be vulnerable to this attack if special characters are injected into a user-supplied input value, that input is not filtered and is concatenated with other strings to construct an XPath query, which is executed against an XML document.
Impacts of this attack can include bypassing authentication logic, or the disclosure of sensitive data within the XML document being queried.
Examples
Java
String xmlInput = "<users>" +
"<user><username>admin</username><password>admin@123</password></user>" +
"<user><username>guest</username><password>guest@123</password></user>" +
"</users>";
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(xmlInput)));
//Input example :
String inputUsername = "admin' or '1'='1' or '";
String inputPassword = "randomPassword";
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
String query = String.format("//user[username/text()='%s' and password/text()='%s']", inputUsername, inputPassword);
XPathExpression expr = xpath.compile(query);
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
System.out.println(nodes.getLength() > 0 ? "Authenticated" : "Authentication Failed");
Javascript
const express = require('express');
const libxml = require('libxmljs');
const app = express();
const xmlInput = `<users>
<user><username>admin</username><password>admin@123</password></user>
<user><username>guest</username><password>guest@123</password></user>
</users>`;
app.post('/authenticate', (req, res) => {
const { username, password } = req.body;
const xmlDoc = libxml.parseXml(xmlInput);
const query = `//user[username/text()='${username}' and password/text()='${password}']`;
const nodes = xmlDoc.find(query);
res.send(nodes.length > 0 ? 'Authenticated' : 'Authentication Failed');
});
Php
<?php
$xmlInput = "<users>" .
"<user><username>admin</username><password>admin@123</password></user>" .
"<user><username>guest</username><password>guest@123</password></user>" .
"</users>";
//Input example :
$inputUsername = "admin' or '1'='1' or '";
$inputPassword = "randomPassword";
$doc = new DOMDocument();
$doc->loadXML($xmlInput);
$xpath = new DOMXPath($doc);
$query = "//user[username/text()='$inputUsername' and password/text()='$inputPassword']";
$nodes = $xpath->query($query);
echo ($nodes->length > 0) ? "Authenticated" : "Authentication Failed";
?>
Recommendation
User input should be strictly validated before being incorporated into XPath queries. In most cases, it will be appropriate to accept input containing only short alphanumeric strings. At the very least, input containing any XPath metacharacters such as " ' / @ = * [ ] ( and ) should be rejected.
Subsequently, encoding user input using HTML entities can provide an additional layer of defense against potential attacks by rendering special characters inert. However, a superior approach, if available, is to utilize parameterized XPath queries. This method, akin to SQL's parameterized queries, involves inserting user input as a variable into the query. In doing so, any special characters are either handled to avoid query syntax modification or cause the query to fail, thereby enhancing security."
Examples
Java
String xmlInput = "<users>" +
"<user><username>admin</username><password>admin@123</password></user>" +
"<user><username>guest</username><password>guest@123</password></user>" +
"</users>";
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(xmlInput)));
//Input example :
String inputUsername = "admi'n";
String inputPassword = "admin@//'123";
// Sanitize inputs
inputUsername = inputUsername.replaceAll("[^a-zA-Z0-9]", "");
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
// Using parameterized queries
String query = "//user[username/text()=? and password/text()=?]";
XPathExpression expr = xpath.compile(query);
expr.setXPathVariableResolver((varName) -> {
if (varName.equals("username")) {
return inputUsername;
} else if (varName.equals("password")) {
return inputPassword;
}
return null;
});
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
System.out.println(nodes.getLength() > 0 ? "Authenticated" : "Authentication Failed");
Javascript
const express = require('express');
const libxml = require('libxmljs');
const app = express();
const xmlInput = `<users>
<user><username>admin</username><password>admin@123</password></user>
<user><username>guest</username><password>guest@123</password></user>
</users>`;
app.post('/authenticate', (req, res) => {
let { username, password } = req.body;
const xmlDoc = libxml.parseXml(xmlInput);
const query = `//user[username/text()=$username and password/text()=$password]`;
const nodes = xmlDoc.find(query, { $username: username, $password: password });
res.send(nodes.length > 0 ? 'Authenticated' : 'Authentication Failed');
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Php
<?php
$xmlInput = "<users>" .
"<user><username>admin</username><password>admin@123</password></user>" .
"<user><username>guest</username><password>guest@123</password></user>" .
"</users>";
$inputUsername = "admin";
$inputPassword = "admin@123";
// Sanitize inputs to avoid ' and "
$inputUsername = htmlspecialchars($inputUsername, ENT_QUOTES, 'UTF-8');
$inputPassword = htmlspecialchars($inputPassword, ENT_QUOTES, 'UTF-8');
$doc = new DOMDocument();
$doc->loadXML($xmlInput);
$xpath = new DOMXPath($doc);
// Using parameterized queries
$query = "//user[username/text()='" . $inputUsername . "' and password/text()='" . $inputPassword . "']";
$nodes = $xpath->query($query);
echo ($nodes->length > 0) ? "Authenticated" : "Authentication Failed";
?>
Links
Standards
- GDPR:
- ART_32
- PCI_STANDARDS:
- REQ_6_4
- REQ_6_5