XPath Injection

MRunal
MRunal
Jul 4 · 3 min read

The root cause of XPath injection vulnerability is the ability of an attacker to change context in the XPath query, causing a value that the programmer intended to be interpreted as data to be interpreted as a command instead. When an XPath query is constructed, the programmer knows what should be interpreted as part of the command and what should be interpreted as data.

To prevent an attacker from violating the programmer’s expectations, use a whitelist to ensure that user-controlled values used in an XPath query are composed from only the expected set of characters and do not contain any XPath metacharacters given the context in which they are used. If a user-controlled value requires that it contain XPath metacharacters, use an appropriate encoding mechanism to remove their significance within the XPath query.

Example 1: The following code dynamically constructs and executes an XPath query that retrieves an e-mail address for a given account ID. The account ID is read from an HTTP request, and is therefore untrusted.

...
string acctID = Request["acctID"];
string query = null;
if(acctID != null) {
StringBuffer sb = new StringBuffer("/accounts/account[acctID='");
sb.append(acctID);
sb.append("']/email/text()");
query = sb.toString();
}
XPathDocument docNav = new XPathDocument(myXml);
XPathNavigator nav = docNav.CreateNavigator();
nav.Evaluate(query);
...

Under normal conditions, such as searching for an e-mail address that belongs to the account number 1, the query that this code executes will look like the following:

/accounts/account[acctID='1']/email/text()

However, because the query is constructed dynamically by concatenating a constant base query string and a user input string, the query only behaves correctly if acctID does not contain a single-quote character. If an attacker enters the string 1' or '1' = '1 for acctID, then the query becomes the following:

/accounts/account[acctID='1' or '1' = '1']/email/text()

The addition of the 1' or '1' = '1 condition causes the where clause to always evaluate to true, so the query becomes logically equivalent to the much simpler query:

//email/text()

This simplification of the query allows the attacker to bypass the requirement that the query only return items owned by the authenticated user; the query now returns all e-mail addresses stored in the document, regardless of their specified owner.

Summary

On line 28 of XPathInjection.aspx.cs, the method FindSalesPerson() invokes an XPath query built using unvalidated input. This call could allow an attacker to modify the statement’s meaning or to execute arbitrary XPath queries.Constructing a dynamic XPath query with user input may allow an attacker to modify the statement’s meaning.

Explanation

XPath injection occurs when:

1. Data enters a program from an untrusted source.

In this case the data enters at get_QueryString() in XPathInjection.aspx.cs at line 20.

2. The data used to dynamically construct an XPath query.

In this case the query is passed to SelectNodes() in XPathInjection.aspx.cs at line 28.

code :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;
using System.Xml.XPath;

namespace OWASP.WebGoat.NET
{
public partial class XPathInjection : System.Web.UI.Page
{
// Make into actual lesson
private string xml = “<?xml version=\”1.0\” encoding=\”ISO-8859–1\”?><sales><salesperson><name>David Palmer</name><city>Portland</city><state>or</state><ssn>123–45–6789</ssn></salesperson><salesperson><name>Jimmy Jones</name><city>San Diego</city><state>ca</state><ssn>555–45–6789</ssn></salesperson><salesperson><name>Tom Anderson</name><city>New York</city><state>ny</state><ssn>444–45–6789</ssn></salesperson><salesperson><name>Billy Moses</name><city>Houston</city><state>tx</state><ssn>333–45–6789</ssn></salesperson></sales>”;
protected void Page_Load(object sender, EventArgs e)
{
if (Request.QueryString[“state”] != null)
{
FindSalesPerson(Request.QueryString[“state”]);
}
}

private void FindSalesPerson(string state)
{
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(xml);
XmlNodeList list = xDoc.SelectNodes(“//salesperson[state=’” + state + “‘]”);
if (list.Count > 0)
{

}

}
}
}

XPathInjection.aspx.cs:28 — SelectNodes(0)