XXE query

This commit is contained in:
Arthur Baars
2021-09-02 16:34:01 +02:00
parent 0ea228e86f
commit 4268d9c565
11 changed files with 357 additions and 0 deletions

View File

@@ -503,3 +503,38 @@ module CodeExecution {
abstract DataFlow::Node getCode();
}
}
/**
* A data-flow node that parses XML content.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `XmlParserCall::Range` instead.
*/
class XmlParserCall extends DataFlow::Node {
XmlParserCall::Range range;
XmlParserCall() { this = range }
/** Gets the argument that specifies the XML content to be parsed. */
DataFlow::Node getInput() { result = range.getInput() }
/** Holds if this XML parser call is configured to process external entities */
predicate externalEntitiesEnabled() { range.externalEntitiesEnabled() }
}
/** Provides a class for modeling new XML parsing APIs. */
module XmlParserCall {
/**
* A data-flow node that parses XML content.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `class XmlParserCall` instead.
*/
abstract class Range extends DataFlow::Node {
/** Gets the argument that specifies the XML content to be parsed. */
abstract DataFlow::Node getInput();
/** Holds if this XML parser call is configured to process external entities */
abstract predicate externalEntitiesEnabled();
}
}

View File

@@ -8,3 +8,4 @@ private import codeql.ruby.frameworks.ActionView
private import codeql.ruby.frameworks.StandardLibrary
private import codeql.ruby.frameworks.Files
private import codeql.ruby.frameworks.HttpClients
private import codeql.ruby.frameworks.XmlParsing

View File

@@ -0,0 +1,103 @@
private import codeql.ruby.Concepts
private import codeql.ruby.AST
private import codeql.ruby.DataFlow
private import codeql.ruby.typetracking.TypeTracker
private import codeql.ruby.ApiGraphs
private import codeql.ruby.controlflow.CfgNodes as CfgNodes
private class NokogiriXmlParserCall extends XmlParserCall::Range, DataFlow::CallNode {
NokogiriXmlParserCall() {
this =
[
API::getTopLevelMember("Nokogiri").getMember("XML"),
API::getTopLevelMember("Nokogiri").getMember("XML").getMember("Document"),
API::getTopLevelMember("Nokogiri")
.getMember("XML")
.getMember("SAX")
.getMember("Parser")
.getInstance()
].getAMethodCall("parse")
}
override DataFlow::Node getInput() { result = this.getArgument(0) }
override predicate externalEntitiesEnabled() {
this.getArgument(3) = trackNoEnt()
or
this.asExpr()
.getExpr()
.(MethodCall)
.getBlock()
.getAStmt()
.getAChild*()
.(MethodCall)
.getMethodName() = "noent"
}
}
private class LibXmlRubyXmlParserCall extends XmlParserCall::Range, DataFlow::CallNode {
LibXmlRubyXmlParserCall() {
this =
[API::getTopLevelMember("LibXML").getMember("XML"), API::getTopLevelMember("XML")]
.getMember(["Document", "Parser"])
.getAMethodCall(["file", "io", "string"])
}
override DataFlow::Node getInput() { result = this.getArgument(0) }
override predicate externalEntitiesEnabled() {
exists(Pair pair |
pair = this.getArgument(1).asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
pair.getKey().(Literal).getValueText() = "options" and
trackNoEnt().asExpr().getExpr() = pair.getValue()
)
}
}
private DataFlow::LocalSourceNode trackNoEnt(TypeTracker t) {
t.start() and
(
result.asExpr().getExpr().(IntegerLiteral).getValue().bitAnd(2) = 2
or
result =
API::getTopLevelMember("Nokogiri")
.getMember("XML")
.getMember("ParseOptions")
.getMember("NOENT")
.getAUse()
or
result =
[API::getTopLevelMember("LibXML").getMember("XML"), API::getTopLevelMember("XML")]
.getMember("Options")
.getMember("NOENT")
.getAUse()
or
result.asExpr().getExpr() instanceof BitwiseOrExpr and
result.asExpr().(CfgNodes::ExprNodes::OperationCfgNode).getAnOperand() = trackNoEnt().asExpr()
or
result =
API::getTopLevelMember("Nokogiri")
.getMember("XML")
.getMember("ParseOptions")
.getAnInstantiation() and
result.asExpr().(CfgNodes::ExprNodes::CallCfgNode).getArgument(0) = trackNoEnt().asExpr()
or
exists(CfgNodes::ExprNodes::CallCfgNode call |
call.getExpr().(MethodCall).getMethodName() = "noent" and
(
result.asExpr() = call
or
result.flowsTo(any(DataFlow::Node n | n.asExpr() = call.getReceiver()))
)
)
or
exists(CfgNodes::ExprNodes::CallCfgNode call |
trackNoEnt().asExpr() = call.getReceiver() and
result.asExpr() = call
)
)
or
exists(TypeTracker t2 | result = trackNoEnt(t2).track(t2, t))
}
private DataFlow::Node trackNoEnt() { trackNoEnt(TypeTracker::end()).flowsTo(result) }