Repository navigation
Expand file tree
/
Copy pathMvelInjection.qll
More file actions
210 lines (186 loc) · 7.21 KB
/
Copy pathMvelInjection.qll
File metadata and controls
210 lines (186 loc) · 7.21 KB
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/** Provides classes to reason about MVEL injection attacks. */
overlay[local?]
module;
import java
private import semmle.code.java.dataflow.DataFlow
private import semmle.code.java.dataflow.ExternalFlow
/** A data flow sink for unvalidated user input that is used to construct MVEL expressions. */
abstract class MvelEvaluationSink extends DataFlow::Node { }
/** A sanitizer that prevents MVEL injection attacks. */
abstract class MvelInjectionSanitizer extends DataFlow::Node { }
/**
* A unit class for adding additional taint steps.
*
* Extend this class to add additional taint steps that should apply to the `MvelInjectionFlowConfig`.
*/
class MvelInjectionAdditionalTaintStep extends Unit {
/**
* Holds if the step from `node1` to `node2` should be considered a taint
* step for the `MvelInjectionFlowConfig` configuration.
*/
abstract predicate step(DataFlow::Node n1, DataFlow::Node n2);
}
/** Default sink for MVEL injection vulnerabilities. */
private class DefaultMvelEvaluationSink extends MvelEvaluationSink {
DefaultMvelEvaluationSink() { sinkNode(this, "mvel-injection") }
}
/** A default sanitizer that considers numeric and boolean typed data safe for building MVEL expressions */
private class DefaultMvelInjectionSanitizer extends MvelInjectionSanitizer {
DefaultMvelInjectionSanitizer() {
this.getType() instanceof NumericType or this.getType() instanceof BooleanType
}
}
/** A set of additional taint steps to consider when taint tracking MVEL related data flows. */
private class DefaultMvelInjectionAdditionalTaintStep extends MvelInjectionAdditionalTaintStep {
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
expressionCompilationStep(node1, node2) or
createExpressionCompilerStep(node1, node2) or
expressionCompilerCompileStep(node1, node2) or
createCompiledAccExpressionStep(node1, node2) or
scriptCompileStep(node1, node2) or
createMvelCompiledScriptStep(node1, node2) or
templateCompileStep(node1, node2) or
createTemplateCompilerStep(node1, node2)
}
}
/**
* Holds if `node1` to `node2` is a dataflow step that compiles a MVEL expression
* by callilng `MVEL.compileExpression(tainted)`.
*/
private predicate expressionCompilationStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(StaticMethodCall ma, Method m | ma.getMethod() = m |
m.getDeclaringType() instanceof MVEL and
m.hasName("compileExpression") and
ma.getAnArgument() = node1.asExpr() and
node2.asExpr() = ma
)
}
/**
* Holds if `node1` to `node2` is a dataflow step that creates `ExpressionCompiler`
* by calling `new ExpressionCompiler(tainted)`.
*/
private predicate createExpressionCompilerStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(ConstructorCall cc |
cc.getConstructedType() instanceof ExpressionCompiler and
cc = node2.asExpr() and
cc.getArgument(0) = node1.asExpr()
)
}
/**
* Holds if `node1` to `node2` is a dataflow step creates `CompiledAccExpression`
* by calling `new CompiledAccExpression(tainted, ...)`.
*/
private predicate createCompiledAccExpressionStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(ConstructorCall cc |
cc.getConstructedType() instanceof CompiledAccExpression and
cc = node2.asExpr() and
cc.getArgument(0) = node1.asExpr()
)
}
/**
* Holds if `node1` to `node2` is a dataflow step that compiles a MVEL expression
* by calling `ExpressionCompiler.compile()`.
*/
private predicate expressionCompilerCompileStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(MethodCall ma, Method m | ma.getMethod() = m |
m.getDeclaringType() instanceof ExpressionCompiler and
m.hasName("compile") and
ma = node2.asExpr() and
ma.getQualifier() = node1.asExpr()
)
}
/**
* Holds if `node1` to `node2` is a dataflow step that compiles a script via `MvelScriptEngine`
* by calling `engine.compile(tainted)` or `engine.compiledScript(tainted)`.
*/
private predicate scriptCompileStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(MethodCall ma, Method m | ma.getMethod() = m |
m instanceof MvelScriptEngineCompilationMethod and
ma = node2.asExpr() and
ma.getArgument(0) = node1.asExpr()
)
}
/**
* Holds if `node1` to `node2` is a dataflow step that creates `MvelCompiledScript`
* by calling `new MvelCompiledScript(engine, tainted)`.
*/
private predicate createMvelCompiledScriptStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(ConstructorCall cc |
cc.getConstructedType() instanceof MvelCompiledScript and
cc = node2.asExpr() and
cc.getArgument(1) = node1.asExpr()
)
}
/**
* Holds if `node1` to `node2` is a dataflow step creates `TemplateCompiler`
* by calling `new TemplateCompiler(tainted)`.
*/
private predicate createTemplateCompilerStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(ConstructorCall cc |
cc.getConstructedType() instanceof TemplateCompiler and
cc = node2.asExpr() and
cc.getArgument(0) = node1.asExpr()
)
}
/**
* Holds if `node1` to `node2` is a dataflow step that compiles a script via `TemplateCompiler`
* by calling `compiler.compile()` or `TemplateCompiler.compileTemplate(tainted)`.
*/
private predicate templateCompileStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(MethodCall ma, Method m | ma.getMethod() = m |
m instanceof TemplateCompilerCompileMethod and
ma.getQualifier() = node1.asExpr() and
ma = node2.asExpr()
)
or
exists(StaticMethodCall ma, Method m | ma.getMethod() = m |
m instanceof TemplateCompilerCompileTemplateMethod and
ma = node2.asExpr() and
ma.getArgument(0) = node1.asExpr()
)
}
/**
* Methods in `MvelScriptEngine` that compile a MVEL expression.
*/
private class MvelScriptEngineCompilationMethod extends Method {
MvelScriptEngineCompilationMethod() {
this.getDeclaringType() instanceof MvelScriptEngine and
this.hasName(["compile", "compiledScript"])
}
}
/**
* `TemplateCompiler.compile()` method that compiles a MVEL template.
*/
private class TemplateCompilerCompileMethod extends Method {
TemplateCompilerCompileMethod() {
this.getDeclaringType() instanceof TemplateCompiler and
this.hasName("compile")
}
}
/**
* `TemplateCompiler.compileTemplate(tainted)` static method that compiles a MVEL template.
*/
private class TemplateCompilerCompileTemplateMethod extends Method {
TemplateCompilerCompileTemplateMethod() {
this.getDeclaringType() instanceof TemplateCompiler and
this.hasName("compileTemplate")
}
}
private class MVEL extends RefType {
MVEL() { this.hasQualifiedName("org.mvel2", "MVEL") }
}
private class ExpressionCompiler extends RefType {
ExpressionCompiler() { this.hasQualifiedName("org.mvel2.compiler", "ExpressionCompiler") }
}
private class CompiledAccExpression extends RefType {
CompiledAccExpression() { this.hasQualifiedName("org.mvel2.compiler", "CompiledAccExpression") }
}
private class MvelScriptEngine extends RefType {
MvelScriptEngine() { this.hasQualifiedName("org.mvel2.jsr223", "MvelScriptEngine") }
}
private class MvelCompiledScript extends RefType {
MvelCompiledScript() { this.hasQualifiedName("org.mvel2.jsr223", "MvelCompiledScript") }
}
private class TemplateCompiler extends RefType {
TemplateCompiler() { this.hasQualifiedName("org.mvel2.templates", "TemplateCompiler") }
}