Merge pull request #2800 from SpaceWhite/CWE-643

CWE-643 XPathInjection on java
This commit is contained in:
Anders Schack-Mulligen
2020-03-13 13:40:17 +01:00
committed by GitHub
3 changed files with 161 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
final String xmlStr = "<users>" +
" <user name=\"aaa\" pass=\"pass1\"></user>" +
" <user name=\"bbb\" pass=\"pass2\"></user>" +
"</users>";
try {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
//Document doc = builder.parse("user.xml");
Document doc = builder.parse(new InputSource(new StringReader(xmlStr)));
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
// Injectable data
String user = request.getParameter("user");
String pass = request.getParameter("pass");
if (user != null && pass != null) {
boolean isExist = false;
// Bad expression
String expression1 = "/users/user[@name='" + user + "' and @pass='" + pass + "']";
isExist = (boolean)xpath.evaluate(expression1, doc, XPathConstants.BOOLEAN);
System.out.println(isExist);
// Bad expression
XPathExpression expression2 = xpath.compile("/users/user[@name='" + user + "' and @pass='" + pass + "']");
isExist = (boolean)expression2.evaluate(doc, XPathConstants.BOOLEAN);
System.out.println(isExist);
// Bad expression
StringBuffer sb = new StringBuffer("/users/user[@name=");
sb.append(user);
sb.append("' and @pass='");
sb.append(pass);
sb.append("']");
String query = sb.toString();
XPathExpression expression3 = xpath.compile(query);
isExist = (boolean)expression3.evaluate(doc, XPathConstants.BOOLEAN);
System.out.println(isExist);
// Good expression
String expression4 = "/users/user[@name=$user and @pass=$pass]";
xpath.setXPathVariableResolver(v -> {
switch (v.getLocalPart()) {
case "user":
return user;
case "pass":
return pass;
default:
throw new IllegalArgumentException();
}
});
isExist = (boolean)xpath.evaluate(expression4, doc, XPathConstants.BOOLEAN);
System.out.println(isExist);
// Bad Dom4j
org.dom4j.io.SAXReader reader = new org.dom4j.io.SAXReader();
org.dom4j.Document document = reader.read(new InputSource(new StringReader(xmlStr)));
isExist = document.selectSingleNode("/users/user[@name='" + user + "' and @pass='" + pass + "']").hasContent();
// or document.selectNodes
System.out.println(isExist);
}
} catch (ParserConfigurationException e) {
} catch (SAXException e) {
} catch (XPathExpressionException e) {
} catch (org.dom4j.DocumentException e) {
}

View File

@@ -0,0 +1,44 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
If an XPath expression is built using string concatenation, and the components of the concatenation
include user input, a user is likely to be able to create a malicious XPath expression.
</p>
</overview>
<recommendation>
<p>
If user input must be included in an XPath expression, pre-compile the query and use variable
references to include the user input.
</p>
<p>
XPath injection can also be prevented by using XQuery.
</p>
</recommendation>
<example>
<p>
In the first, second, and third example, the code accepts a name and password specified by the user, and uses this
unvalidated and unsanitized value in an XPath expression. This is vulnerable to the user providing
special characters or string sequences that change the meaning of the XPath expression to search
for different values.
</p>
<p>
In the fourth example, the code utilizes setXPathVariableResolver which prevents XPath Injection.
</p>
<p>
The fifth example is a dom4j XPath injection example.
</p>
<sample src="XPathInjection.java" />
</example>
<references>
<li>OWASP: <a href="https://www.owasp.org/index.php?title=Testing_for_XPath_Injection_(OTG-INPVAL-010)">Testing for XPath Injection</a>.</li>
<li>OWASP: <a href="https://www.owasp.org/index.php/XPATH_Injection">XPath Injection</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,44 @@
/**
* @name XPath injection
* @description Building an XPath expression from user-controlled sources is vulnerable to insertion of
* malicious code by the user.
* @kind path-problem
* @problem.severity error
* @precision high
* @id java/xml/xpath-injection
* @tags security
* external/cwe/cwe-643
*/
import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.security.XmlParsers
import DataFlow::PathGraph
class XPathInjectionConfiguration extends TaintTracking::Configuration {
XPathInjectionConfiguration() { this = "XPathInjection" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof XPathInjectionSink }
}
class XPathInjectionSink extends DataFlow::ExprNode {
XPathInjectionSink() {
exists(Method m, MethodAccess ma | ma.getMethod() = m |
m.getDeclaringType().hasQualifiedName("javax.xml.xpath", "XPath") and
(m.hasName("evaluate") or m.hasName("compile")) and
ma.getArgument(0) = this.getExpr()
or
m.getDeclaringType().hasQualifiedName("org.dom4j", "Node") and
(m.hasName("selectNodes") or m.hasName("selectSingleNode")) and
ma.getArgument(0) = this.getExpr()
)
}
}
from DataFlow::PathNode source, DataFlow::PathNode sink, XPathInjectionConfiguration c
where c.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "$@ flows to here and is used in an XPath expression.",
source.getNode(), "User-provided value"