This repository has been archived by the owner on Aug 11, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 28
/
double_eval_final.ql
136 lines (120 loc) · 4.99 KB
/
double_eval_final.ql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/**
* @name Double Ognl Evaluation
* @description Fields that are initialized from configuration that may get evaluated twice as Ognl expression.
* @kind path-problem
* @problem.severity error
* @id double_eval
*/
import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.frameworks.Servlets
import semmle.code.java.dataflow.internal.DataFlowUtil
import lib.dataflow_extra.CollectionsEdges
import lib.dataflow_extra.ExtraEdges
import lib.struts.Sanitizers
import lib.struts.DataFlow
import lib.dataflow_extra.Sanitizers
import DataFlow::PathGraph
import lib.struts.OGNL
/** A `Field` whose value is a compile time constant.*/
class ConstantField extends Field {
ConstantField() {
exists(CompileTimeConstantExpr e | this.getAnAssignedValue() = e and
this.isFinal()
)
}
}
/** A `Class` whose fields may be initialized from a config file.*/
class InputClass extends RefType {
InputClass() {
hasQualifiedName("org.apache.struts2.views.jsp", "StrutsBodyTagSupport") or
hasQualifiedName("org.apache.struts2.components", "Component") or
hasQualifiedName("com.opensymphony.xwork2", "Result") or
hasQualifiedName("org.apache.struts.action", "Action") or
hasQualifiedName("com.opensymphony.xwork2.interceptor", "Interceptor")
}
}
/** A `Field` that may take value from a config file.*/
class ConfigurableField extends Field {
ConfigurableField() {
getDeclaringType().getASupertype*() instanceof InputClass
}
}
/** Tracks from a field that may take value from configuration file into the an ognl evaluation method.*/
class InputCfg extends DataFlow::Configuration {
InputCfg() {
this = "input"
}
override predicate isSource(DataFlow::Node source) {
exists(ConfigurableField f | f.getAnAccess() = source.asExpr() and
not f instanceof ConstantField)
or
//`ActionConfig` params are also taken from configuration.
exists(MethodAccess ma, Method m | ma.getMethod() = m and
m.getDeclaringType().hasName("ActionConfig") and
m.hasName("getParams") and source.asExpr() = ma
)
}
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma, OgnlEntryPointMethod entry, Method m | ma.getMethod() = m and
ma.getAnArgument() = sink.asExpr() and entry.overridesOrInstantiates*(m)
)
}
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
standardExtraEdges(node1, node2) or
collectionsPutEdge(node1, node2) or
actionMapperEdge(node1, node2)
}
override predicate isBarrier(DataFlow::Node node) {
node instanceof StrutsTestSanitizer
or
ognlSanitizers(node)
or
node instanceof ToStringSanitizer
or
node instanceof MapMethodSanitizer
}
}
class DoubleEvalConfig extends DataFlow::Configuration {
DoubleEvalConfig() {
this = "doubleEvalConfig"
}
override predicate isSource(DataFlow::Node source) {
exists(MethodAccess ma, OgnlEntryPointMethod entry, Method m | ma.getMethod() = m and
source.asExpr() = ma and entry.overridesOrInstantiates*(m)
)
}
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma, OgnlEntryPointMethod entry, Method m, int i | ma.getMethod() = m and
sink.asExpr() = ma.getArgument(i) and entry.overridesOrInstantiates*(m) and
m.getParameter(i).getType() instanceof TypeString
) or
isOgnlSink(sink)
}
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
standardExtraEdges(node1, node2) or
collectionsPutEdge(node1, node2) or
actionMapperEdge(node1, node2)
}
override predicate isBarrier(DataFlow::Node node) {
ognlSanitizers(node) or
node instanceof StrutsTestSanitizer or
node instanceof ToStringSanitizer or
node instanceof MapMethodSanitizer or
//Stops when we reach an `OgnlEntryPointMethod` to cut down on duplicate results.
exists(OgnlEntryPointMethod m, DataFlow::Node n |TaintTracking::localTaint(n, node) and m.getAParameter() = n.asParameter()) or
exists(ReturnStmt stmt, DataFlow::Node rtn | stmt.getResult() = rtn.asExpr() and TaintTracking::localTaint(node, rtn) and
stmt.getEnclosingCallable() instanceof OgnlEntryPointMethod)
}
}
from DoubleEvalConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink,
InputCfg input, DataFlow::Node inputSrc, DataFlow::Node inputSink
where cfg.hasFlowPath(source, sink) and input.hasFlow(inputSrc, inputSink) and
//use call site of `InputCfg` as source of `DoubleEvalConfig`
exists(MethodAccess ma | ma = source.getNode().asExpr() and ma.getAnArgument() = inputSink.asExpr()) and
//Remove `circular` evaluations
not exists(MethodAccess ma | source.getNode().asExpr() = ma and sink.getNode().asExpr() = ma.getAnArgument()) and
//Cut down on paths that contains one another
not exists(DataFlow::Node s | TaintTracking::localTaintStep+(s, inputSrc))
select source, source, sink, "$@ from configuration first $@ and then evaluated $@.", inputSrc, "Input", source, "evaluated", sink, "here"