XPATH Injection Java

Last revision (mm/dd/yy): 03/06/2012

Injection context
Some applications use XML based datastore and sometimes XPATH query language to retrieve data from theses stores. The application can construct XPATH query expression from user input in order to select user dedicated data.

Injection objective
The objective of the injection is to submit a piece of XPATH language that will change the normal behavior of the target expression in order to retrieve more or differents data than expected.

Injection examples
For the examples we will take a case of an application that store employees informations using XML store with this structure:    

The XPATH expression to select an employee node used by application is: /employees/employee[@id='EMPLOYEE_ID']

The code (JavaEE6 Servlet for example) used to perform selection is: package org.owasp.javaproject.xpathinjection;

import java.io.IOException; import java.io.StringReader; import java.util.List;

import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory;

import org.jaxen.XPath; import org.jaxen.dom.DOMXPath; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.InputSource;

/** * Sample service to retrieve employees salary */ @SuppressWarnings("serial") @WebServlet("/EmployeesSalaryService") public class EmployeesSalaryService extends HttpServlet {

private static final String DATASOURCE_XML = "Put XML Structure above here";

/**	 * {@inheritDoc} * 	 * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */	@SuppressWarnings("rawtypes") @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { // For the sample we load the XML Document at each request but this not a good way for real application..... DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance; DocumentBuilder builder = factory.newDocumentBuilder; Document doc = builder.parse(new InputSource(new StringReader(DATASOURCE_XML)));

// Retrieve employee ID from the input HTTP request String eID = request.getParameter("employeeID"); if (eID == null) { eID = ""; }

// Create XPATH expression String xpathExpr = "/employees/employee[@id='" + eID + "']"; XPath expression = new DOMXPath(xpathExpr);

// Apply expression on XML document List nodes = expression.selectNodes(doc); for (int i = 0; i < nodes.size; i++) { Element employee = (Element) nodes.get(i); response.getWriter.print(employee.getAttribute("lastname") 				+ " " 				+ employee.getAttribute("firstname") 				+ " : " 				+ employee.getAttribute("annualsalary") 				+ " "); }		} catch (Exception e) { response.sendError(HttpServletResponse.SC_BAD_REQUEST); e.printStackTrace; }	}

}

Here the sensitive information is the annual salary then it's will be the target of the injection.

The application expect to receive, for the employee ID, an value like "AS789" but what is the application behavior if a user submit another value pattern ?

Sample value n°1: '%20or%20'1'='1 Result: All employees nodes are selected (in this cases the user do not known the XML structure).



Sample value n°2: '%20or%20fn:contains(fn:lower-case(@lastname),'dobora')%20or%20' Result: Employee where the last name contains "dobora" is selected (in this cases the user has guessed the XML structure).



Injection countermeasure
Input used into an XPATH expression must not contains any of the characters below: = ' [ ] :, * / WHITESPACE

According to our example context, the modification to apply can be to create an application transversal utility method that check the presence of characters above and reject the value submitted if it's contains any one.

Checking utility method example: public boolean checkValueForXpathInjection(String value) throws Exception { boolean isValid = true; if ((value != null) && !"".equals(value)) { String xpathCharList = "='[]:,*/ "; // Always to avoid encoding evading.... String decodedValue = URLDecoder.decode(value, Charset.defaultCharset.name); for (char c : decodedValue.toCharArray) { if (xpathCharList.indexOf(c) != -1) { isValid = false; break; }		}	}	return isValid; }

Checking utility use example: /** * {@inheritDoc} * * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @SuppressWarnings("rawtypes") @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try {

// Check input if (!checkValueForXpathInjection(request.getParameter("employeeID"))) { response.sendError(HttpServletResponse.SC_BAD_REQUEST); // Trace injection // Exit return; }

.....

}	} catch (Exception e) { response.sendError(HttpServletResponse.SC_BAD_REQUEST); e.printStackTrace; } }

Note
See into your web framework if validation content methods exists or use OWASP dedicated API ;o)