-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathCommandInjectionRuntimeExec.qll
More file actions
107 lines (92 loc) · 3.44 KB
/
Copy pathCommandInjectionRuntimeExec.qll
File metadata and controls
107 lines (92 loc) · 3.44 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
deprecated module;
import java
import semmle.code.java.frameworks.javaee.ejb.EJBRestrictions
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.FlowSources
private import semmle.code.java.security.Sanitizers
module ExecCmdFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source.asExpr().(CompileTimeConstantExpr).getStringValue() instanceof UnSafeExecutable
}
predicate isSink(DataFlow::Node sink) {
exists(MethodCall call |
call.getMethod() instanceof RuntimeExecMethod and
sink.asExpr() = call.getArgument(0) and
sink.asExpr().getType() instanceof Array
)
}
predicate isBarrier(DataFlow::Node node) {
node instanceof AssignToNonZeroIndex or
node instanceof ArrayInitAtNonZeroIndex or
node instanceof StreamConcatAtNonZeroIndex or
node instanceof SimpleTypeSanitizer
}
}
/** Tracks flow of unvalidated user input that is used in Runtime.Exec */
module ExecCmdFlow = TaintTracking::Global<ExecCmdFlowConfig>;
abstract class Source extends DataFlow::Node { }
module ExecUserFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) {
exists(MethodCall call |
call.getMethod() instanceof RuntimeExecMethod and
sink.asExpr() = call.getArgument(_) and
sink.asExpr().getType() instanceof Array
)
}
predicate isBarrier(DataFlow::Node node) { node instanceof SimpleTypeSanitizer }
}
/** Tracks flow of unvalidated user input that is used in Runtime.Exec */
module ExecUserFlow = TaintTracking::Global<ExecUserFlowConfig>;
// array[3] = node
class AssignToNonZeroIndex extends DataFlow::Node {
AssignToNonZeroIndex() {
exists(AssignExpr assign, ArrayAccess access |
assign.getDest() = access and
access.getIndexExpr().(IntegerLiteral).getValue().toInt() != 0 and
assign.getSource() = this.asExpr()
)
}
}
// String[] array = {"a", "b, "c"};
class ArrayInitAtNonZeroIndex extends DataFlow::Node {
ArrayInitAtNonZeroIndex() {
exists(ArrayInit init, int index |
init.getInit(index) = this.asExpr() and
index != 0
)
}
}
// Stream.concat(Arrays.stream(array_1), Arrays.stream(array_2))
class StreamConcatAtNonZeroIndex extends DataFlow::Node {
StreamConcatAtNonZeroIndex() {
exists(MethodCall call, int index |
call.getMethod().hasQualifiedName("java.util.stream", "Stream", "concat") and
call.getArgument(index) = this.asExpr() and
index != 0
)
}
}
// list of executables that execute their arguments
// TODO: extend with data extensions
class UnSafeExecutable extends string {
bindingset[this]
UnSafeExecutable() {
this.regexpMatch("^(|.*/)([a-z]*sh|javac?|python.*|perl|[Pp]ower[Ss]hell|php|node|deno|bun|ruby|osascript|cmd|Rscript|groovy)(\\.exe)?$") and
not this = "netsh.exe"
}
}
predicate callIsTaintedByUserInputAndDangerousCommand(
ExecUserFlow::PathNode source, ExecUserFlow::PathNode sink, DataFlow::Node sourceCmd,
DataFlow::Node sinkCmd
) {
exists(MethodCall call |
call.getMethod() instanceof RuntimeExecMethod and
// this is a command-accepting call to exec, e.g. rt.exec(new String[]{"/bin/sh", ...})
ExecCmdFlow::flow(sourceCmd, sinkCmd) and
sinkCmd.asExpr() = call.getArgument(0) and
// it is tainted by untrusted user input
ExecUserFlow::flowPath(source, sink) and
sink.getNode().asExpr() = call.getArgument(0)
)
}