forked from github/codeql
/
TempDirHijackingVulnerability.ql
111 lines (96 loc) · 3.79 KB
/
TempDirHijackingVulnerability.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
/**
* @name Temporary Directory Hijacking Vulnerability disclosure
* @description Detect temporary directory hijack vulnerability
* @kind path-problem
* @problem.severity error
* @precision very-high
* @id java/temp-directory-hijacking
*/
import java
import semmle.code.java.controlflow.Guards
import semmle.code.java.dataflow.FlowSources
import DataFlow::PathGraph
/**
* All `java.io.File::createTempFile` methods.
*/
private class MethodFileCreateTempFile extends Method {
MethodFileCreateTempFile() {
this.getDeclaringType() instanceof TypeFile and
this.hasName("createTempFile")
}
}
private class MethodFileMkdir extends Method {
MethodFileMkdir() {
getDeclaringType() instanceof TypeFile and
hasName("mkdir")
or
hasName("mkdirs")
}
}
/**
* An expression that will create a directory without throwing an exception if a file/directory already exists.
*/
private predicate isNonThrowingDirectoryCreationExpression(Expr expr, MethodAccess creationCall) {
creationCall.getMethod() instanceof MethodFileMkdir and creationCall.getQualifier() = expr
}
private class MethodFileDelete extends Method {
MethodFileDelete() {
getDeclaringType() instanceof TypeFile and
hasName("delete")
}
}
predicate isDeleteFileExpr(Expr expr) {
exists(MethodAccess ma |
expr = ma.getQualifier() and
ma.getMethod() instanceof MethodFileDelete
)
}
private class TempDirHijackingToDeleteConfig extends DataFlow::Configuration {
TempDirHijackingToDeleteConfig() { this = "TempDirHijackingToDeleteConfig" }
override predicate isSource(DataFlow::Node source) {
exists(MethodAccess ma |
ma.getMethod() instanceof MethodFileCreateTempFile and
source.asExpr() = ma
)
}
override predicate isSink(DataFlow::Node sink) { isDeleteFileExpr(sink.asExpr()) }
}
private class TempDirHijackingFromDeleteConfig extends DataFlow2::Configuration {
TempDirHijackingFromDeleteConfig() { this = "TempDirHijackingFromDeleteConfig" }
override predicate isSource(DataFlow::Node source) { isDeleteFileExpr(source.asExpr()) }
override predicate isSink(DataFlow::Node sink) {
isNonThrowingDirectoryCreationExpression(sink.asExpr(), _)
}
}
predicate isUnsafeUseUnconstrainedByIfCheck(DataFlow::Node sink, Expr unsafeUse) {
exists(Guard g, MethodAccess ma |
any(TempDirHijackingFromDeleteConfig c).isSink(sink) and // Sink is a call to delete
sink.asExpr() = ma.getQualifier() and // The method access is on the same object as the sink
g = ma and // The guard is the method access
DataFlow::localExprFlow(sink.asExpr(), unsafeUse) and // There is some flow from the sink to an unsafe use of the File
unsafeUse != sink.asExpr() and // The unsafe use is not the sink itself
not safeUse(unsafeUse) and // The unsafe use is not a safe use
not g.controls(unsafeUse.getBasicBlock(), true)
)
}
private predicate safeUse(Expr e) {
exists(AndLogicalExpr andExp |
andExp.getType() instanceof BooleanType and andExp.getAnOperand() = e
)
or
exists(AssignAndExpr assignAndExp |
assignAndExp.getType() instanceof BooleanType and assignAndExp.getSource() = e
)
}
from
DataFlow::PathNode source, DataFlow::PathNode deleteCheckpoint, DataFlow2::Node deleteCheckpoint2,
DataFlow2::Node sink, MethodAccess creationCall, TempDirHijackingToDeleteConfig toDeleteConfig,
TempDirHijackingFromDeleteConfig fromDeleteConfig
where
toDeleteConfig.hasFlowPath(source, deleteCheckpoint) and
fromDeleteConfig.hasFlow(deleteCheckpoint2, sink) and
deleteCheckpoint.getNode().asExpr() = deleteCheckpoint2.asExpr() and
isUnsafeUseUnconstrainedByIfCheck(sink, _) and
isNonThrowingDirectoryCreationExpression(sink.asExpr(), creationCall)
select deleteCheckpoint.getNode(), source, deleteCheckpoint,
"Local temporary directory hijacking race condition $@", creationCall, "here"