Refactor to CSV sink model

This commit is contained in:
Tony Torralba
2021-05-11 15:33:49 +02:00
parent 549c9eee1a
commit 53da3b661a
2 changed files with 59 additions and 77 deletions

View File

@@ -12,10 +12,28 @@
import java
import semmle.code.java.dataflow.FlowSources
import DataFlow
import DataFlow::PathGraph
import OgnlInjectionLib
/**
* A taint-tracking configuration for unvalidated user input that is used in OGNL EL evaluation.
*/
class OgnlInjectionFlowConfig extends TaintTracking::Configuration {
OgnlInjectionFlowConfig() { this = "OgnlInjectionFlowConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof OgnlInjectionSink }
override predicate isSanitizer(DataFlow::Node node) {
node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
any(OgnlInjectionAdditionalTaintStep c).step(node1, node2)
}
}
from DataFlow::PathNode source, DataFlow::PathNode sink, OgnlInjectionFlowConfig conf
where conf.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "OGNL expression might include input from $@.",

View File

@@ -1,101 +1,59 @@
import java
import semmle.code.java.dataflow.FlowSources
import DataFlow
import DataFlow::PathGraph
import semmle.code.java.dataflow.ExternalFlow
/**
* A taint-tracking configuration for unvalidated user input that is used in OGNL EL evaluation.
* A data flow sink for unvalidated user input that is used in OGNL EL evaluation.
*
* Extend this class to add your own OGNL injection sinks.
*/
class OgnlInjectionFlowConfig extends TaintTracking::Configuration {
OgnlInjectionFlowConfig() { this = "OgnlInjectionFlowConfig" }
abstract class OgnlInjectionSink extends DataFlow::Node { }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
/**
* A unit class for adding additional taint steps.
*
* Extend this class to add additional taint steps that should apply to the `OgnlInjectionFlowConfig`.
*/
class OgnlInjectionAdditionalTaintStep extends Unit {
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
}
override predicate isSink(DataFlow::Node sink) { sink instanceof OgnlInjectionSink }
override predicate isSanitizer(DataFlow::Node node) {
node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType
private class DefaultOgnlInjectionSinkModel extends SinkModelCsv {
override predicate row(string row) {
row =
[
"org.apache.commons.ognl;Ognl;false;getValue;;;Argument[-1..0];ognl-injection",
"org.apache.commons.ognl;Ognl;false;setValue;;;Argument[-1..0];ognl-injection",
"ognl;Ognl;false;getValue;;;Argument[-1..0];ognl-injection",
"ognl;Ognl;false;setValue;;;Argument[-1..0];ognl-injection",
"org.apache.commons.ognl;Node;false;getValue;;;Argument[-1..0];ognl-injection",
"org.apache.commons.ognl;Node;false;setValue;;;Argument[-1..0];ognl-injection",
"ognl;Node;false;getValue;;;Argument[-1..0];ognl-injection",
"ognl;Node;false;setValue;;;Argument[-1..0];ognl-injection",
"com.opensymphony.xwork2.ognl;OgnlUtil;false;getValue;;;Argument[-1..0];ognl-injection",
"com.opensymphony.xwork2.ognl;OgnlUtil;false;setValue;;;Argument[-1..0];ognl-injection",
"com.opensymphony.xwork2.ognl;OgnlUtil;false;callMethod;;;Argument[-1..0];ognl-injection"
]
}
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
parseCompileExpressionStep(node1, node2)
}
private class DefaultOgnlInjectionSink extends OgnlInjectionSink {
DefaultOgnlInjectionSink() { sinkNode(this, "ognl-injection") }
}
/** The class `org.apache.commons.ognl.Ognl` or `ognl.Ognl`. */
class TypeOgnl extends Class {
private class TypeOgnl extends Class {
TypeOgnl() {
this.hasQualifiedName("org.apache.commons.ognl", "Ognl") or
this.hasQualifiedName("ognl", "Ognl")
}
}
/** The interface `org.apache.commons.ognl.Node` or `ognl.Node`. */
class TypeNode extends Interface {
TypeNode() {
this.hasQualifiedName("org.apache.commons.ognl", "Node") or
this.hasQualifiedName("ognl", "Node")
}
}
/** The class `com.opensymphony.xwork2.ognl.OgnlUtil`. */
class TypeOgnlUtil extends Class {
TypeOgnlUtil() { this.hasQualifiedName("com.opensymphony.xwork2.ognl", "OgnlUtil") }
}
/**
* OGNL sink for OGNL injection vulnerabilities, i.e. 1st argument to `getValue` or `setValue`
* method from `Ognl` or `getValue` or `setValue` method from `Node`.
*/
predicate ognlSinkMethod(Method m, int index) {
(
m.getDeclaringType() instanceof TypeOgnl
or
m.getDeclaringType().getAnAncestor*() instanceof TypeNode
) and
(
m.hasName("getValue") or
m.hasName("setValue")
) and
index = 0
}
/**
* Struts sink for OGNL injection vulnerabilities, i.e. 1st argument to `getValue`, `setValue` or
* `callMethod` method from `OgnlUtil`.
*/
predicate strutsSinkMethod(Method m, int index) {
m.getDeclaringType() instanceof TypeOgnlUtil and
(
m.hasName("getValue") or
m.hasName("setValue") or
m.hasName("callMethod")
) and
index = 0
}
/** Holds if parameter at index `index` in method `m` is OGNL injection sink. */
predicate ognlInjectionSinkMethod(Method m, int index) {
ognlSinkMethod(m, index) or
strutsSinkMethod(m, index)
}
/** A data flow sink for unvalidated user input that is used in OGNL EL evaluation. */
class OgnlInjectionSink extends DataFlow::ExprNode {
OgnlInjectionSink() {
exists(MethodAccess ma, Method m, int index |
ma.getMethod() = m and
(ma.getArgument(index) = this.getExpr() or ma.getQualifier() = this.getExpr()) and
ognlInjectionSinkMethod(m, index)
)
}
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `String` and `Object` or `Node`,
* i.e. `Ognl.parseExpression(tainted)` or `Ognl.compileExpression(tainted)`.
*/
predicate parseCompileExpressionStep(ExprNode n1, ExprNode n2) {
private predicate parseCompileExpressionStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(MethodAccess ma, Method m, int index |
n1.asExpr() = ma.getArgument(index) and
n2.asExpr() = ma and
@@ -107,3 +65,9 @@ predicate parseCompileExpressionStep(ExprNode n1, ExprNode n2) {
m.hasName("compileExpression") and index = 2
)
}
private class DefaultOgnlInjectionAdditionalTaintStep extends OgnlInjectionAdditionalTaintStep {
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
parseCompileExpressionStep(node1, node2)
}
}