This repository was archived by the owner on Jan 5, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 128
Expand file tree
/
Copy pathCommandInjection.qll
More file actions
121 lines (105 loc) · 4.2 KB
/
CommandInjection.qll
File metadata and controls
121 lines (105 loc) · 4.2 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
/**
* Provides a taint tracking configuration for reasoning about command
* injection vulnerabilities.
*
* Note, for performance reasons: only import this file if
* `CommandInjection::Configuration` is needed, otherwise
* `CommandInjectionCustomizations` should be imported instead.
*/
import go
/**
* Provides a taint tracking configuration for reasoning about command
* injection vulnerabilities.
*/
module CommandInjection {
import CommandInjectionCustomizations::CommandInjection
/**
* A taint-tracking configuration for reasoning about command-injection vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "CommandInjection" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node sink) {
exists(Sink s | sink = s | not s.doubleDashIsSanitizing())
}
override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node) or
node instanceof Sanitizer
}
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
guard instanceof SanitizerGuard
}
}
private class ArgumentArrayWithDoubleDash extends DataFlow::Node {
int doubleDashIndex;
ArgumentArrayWithDoubleDash() {
// Call whose argument list contains a "--"
exists(DataFlow::CallNode c |
this = c and
(c = Builtin::append().getACall() or c = any(SystemCommandExecution sce)) and
c.getArgument(doubleDashIndex).getStringValue() = "--"
)
or
// array/slice literal containing a "--"
exists(ArrayOrSliceLit litExpr |
this = DataFlow::exprNode(litExpr) and
litExpr.getElement(doubleDashIndex).getStringValue() = "--"
)
or
// call consuming an existing an array with a "--"
exists(ArgumentArrayWithDoubleDash alreadyHasDoubleDash, DataFlow::CallNode userCall |
(
alreadyHasDoubleDash.getType().getUnderlyingType() instanceof ArrayType or
alreadyHasDoubleDash.getType() instanceof SliceType
) and
this = userCall and
DataFlow::localFlow(alreadyHasDoubleDash, userCall.getArgument(doubleDashIndex))
)
}
DataFlow::Node getASanitizedElement() {
exists(int sanitizedIndex |
sanitizedIndex > doubleDashIndex and
(
result = this.(DataFlow::CallNode).getArgument(sanitizedIndex) or
result = DataFlow::exprNode(this.asExpr().(ArrayOrSliceLit).getElement(sanitizedIndex))
)
)
}
}
class DoubleDashSanitizingConfiguration extends TaintTracking::Configuration {
DoubleDashSanitizingConfiguration() { this = "CommandInjectionWithDoubleDashSanitizer" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node sink) {
exists(Sink s | sink = s | s.doubleDashIsSanitizing())
}
override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node) or
node instanceof Sanitizer or
node = any(ArgumentArrayWithDoubleDash array).getASanitizedElement()
}
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
guard instanceof SanitizerGuard
}
// Hack: with use-use flow, we might have x (use at line 1) -> x (use at line 2),
// x (use at line 1) -> array at line 1 and x (use at line 2) -> array at line 2,
// in the context
//
// array1 := {"--", x}
// array2 := {x, "--"}
//
// We want to taint array2 but not array1, which suggests excluding the edge x (use 1) -> array1
// However isSanitizer only allows us to remove nodes (isSanitizerIn/Out permit removing all outgoing
// or incoming edges); we can't remove an individual edge, so instead we supply extra edges connecting
// the definition with the next use.
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(
ArgumentArrayWithDoubleDash array, DataFlow::InstructionNode sanitized,
DataFlow::SsaNode defn
|
sanitized = array.getASanitizedElement() and sanitized = defn.getAUse()
|
pred = defn and succ = sanitized.getASuccessor()
)
}
}
}