Skip to content

Commit c319ee4

Browse files
committed
Add TempDirLocalInformationDisclosureQuery
1 parent b087cf9 commit c319ee4

File tree

4 files changed

+259
-223
lines changed

4 files changed

+259
-223
lines changed

java/ql/lib/change-notes/2023-03-30-add-libraries-for-query-configurations.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ category: minorAnalysis
1919
* Added the `ArithmeticCommon.qll` library to provide predicates for reasoning about arithmetic operations.
2020
* Added the `ArithmeticTaintedQuery.qll` library to provide the `RemoteUserInputOverflow` and `RemoteUserInputUnderflow` taint-tracking modules to reason about arithmetic with unvalidated user input.
2121
* Added the `ArithmeticUncontrolledQuery.qll` library to provide the `ArithmeticUncontrolledOverflowFlow` and `ArithmeticUncontrolledUnderflowFlow` taint-tracking modules to reason about arithmetic with uncontrolled user input.
22-
* Added the `ArithmeticWithExtremeValuesQuery.qll` library to provide the `MaxValueFlow` and `MinValueFlow` dataflow modules to reason about arithmetic with extreme values.
22+
* Added the `ArithmeticWithExtremeValuesQuery.qll` library to provide the `MaxValueFlow` and `MinValueFlow` dataflow modules to reason about arithmetic with extreme values.
23+
* Added the `TempDirLocalInformationDisclosureQuery.qll` library to provide the `TempDirSystemGetPropertyToCreate` taint-tracking module to reason about local information disclosure vulnerabilities caused by local data flow.
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
/** Provides classes to reason about local information disclosure in a temporary directory. */
2+
3+
import java
4+
import semmle.code.java.dataflow.TaintTracking
5+
private import semmle.code.java.os.OSCheck
6+
private import semmle.code.java.security.TempDirUtils
7+
8+
/**
9+
* A method which creates a file or directory in the file system.
10+
*/
11+
abstract private class MethodFileSystemFileCreation extends Method {
12+
MethodFileSystemFileCreation() { this.getDeclaringType() instanceof TypeFile }
13+
}
14+
15+
/**
16+
* A method which creates a directory in the file system.
17+
*/
18+
private class MethodFileDirectoryCreation extends MethodFileSystemFileCreation {
19+
MethodFileDirectoryCreation() { this.hasName(["mkdir", "mkdirs"]) }
20+
}
21+
22+
/**
23+
* A method which creates a file in the file system.
24+
*/
25+
private class MethodFileFileCreation extends MethodFileSystemFileCreation {
26+
MethodFileFileCreation() { this.hasName("createNewFile") }
27+
}
28+
29+
/**
30+
* A dataflow node that creates a file or directory in the file system.
31+
*/
32+
abstract private class FileCreationSink extends DataFlow::Node { }
33+
34+
/**
35+
* The qualifier of a call to one of `File`'s file-creating or directory-creating methods,
36+
* treated as a sink by `TempDirSystemGetPropertyToCreateConfig`.
37+
*/
38+
private class FileFileCreationSink extends FileCreationSink {
39+
FileFileCreationSink() {
40+
exists(MethodAccess ma |
41+
ma.getMethod() instanceof MethodFileSystemFileCreation and
42+
ma.getQualifier() = this.asExpr()
43+
)
44+
}
45+
}
46+
47+
/**
48+
* The argument to a call to one of `Files` file-creating or directory-creating methods,
49+
* treated as a sink by `TempDirSystemGetPropertyToCreateConfig`.
50+
*/
51+
private class FilesFileCreationSink extends FileCreationSink {
52+
FilesFileCreationSink() {
53+
exists(FilesVulnerableCreationMethodAccess ma | ma.getArgument(0) = this.asExpr())
54+
}
55+
}
56+
57+
/**
58+
* A call to a `Files` method that create files/directories without explicitly
59+
* setting the newly-created file or directory's permissions.
60+
*/
61+
private class FilesVulnerableCreationMethodAccess extends MethodAccess {
62+
FilesVulnerableCreationMethodAccess() {
63+
exists(Method m |
64+
m = this.getMethod() and
65+
m.getDeclaringType().hasQualifiedName("java.nio.file", "Files")
66+
|
67+
m.hasName(["write", "newBufferedWriter", "newOutputStream"])
68+
or
69+
m.hasName(["createFile", "createDirectory", "createDirectories"]) and
70+
this.getNumArgument() = 1
71+
or
72+
m.hasName("newByteChannel") and
73+
this.getNumArgument() = 2
74+
)
75+
}
76+
}
77+
78+
/**
79+
* A call to a `File` method that create files/directories with a specific set of permissions explicitly set.
80+
* We can safely assume that any calls to these methods with explicit `PosixFilePermissions.asFileAttribute`
81+
* contains a certain level of intentionality behind it.
82+
*/
83+
private class FilesSanitizingCreationMethodAccess extends MethodAccess {
84+
FilesSanitizingCreationMethodAccess() {
85+
exists(Method m |
86+
m = this.getMethod() and
87+
m.getDeclaringType().hasQualifiedName("java.nio.file", "Files")
88+
|
89+
m.hasName(["createFile", "createDirectory", "createDirectories"]) and
90+
this.getNumArgument() = 2
91+
)
92+
}
93+
}
94+
95+
/**
96+
* The temp directory argument to a call to `java.io.File::createTempFile`,
97+
* treated as a sink by `TempDirSystemGetPropertyToCreateConfig`.
98+
*/
99+
private class FileCreateTempFileSink extends FileCreationSink {
100+
FileCreateTempFileSink() {
101+
exists(MethodAccess ma |
102+
ma.getMethod() instanceof MethodFileCreateTempFile and ma.getArgument(2) = this.asExpr()
103+
)
104+
}
105+
}
106+
107+
/**
108+
* A sanitizer that holds when the program is definitely running under some version of Windows.
109+
*/
110+
abstract private class WindowsOsSanitizer extends DataFlow::Node { }
111+
112+
private class IsNotUnixSanitizer extends WindowsOsSanitizer {
113+
IsNotUnixSanitizer() { any(IsUnixGuard guard).controls(this.asExpr().getBasicBlock(), false) }
114+
}
115+
116+
private class IsWindowsSanitizer extends WindowsOsSanitizer {
117+
IsWindowsSanitizer() { any(IsWindowsGuard guard).controls(this.asExpr().getBasicBlock(), true) }
118+
}
119+
120+
private class IsSpecificWindowsSanitizer extends WindowsOsSanitizer {
121+
IsSpecificWindowsSanitizer() {
122+
any(IsSpecificWindowsVariant guard).controls(this.asExpr().getBasicBlock(), true)
123+
}
124+
}
125+
126+
/**
127+
* A taint tracking configuration tracking the access of the system temporary directory
128+
* flowing to the creation of files or directories.
129+
*/
130+
module TempDirSystemGetPropertyToCreateConfig implements DataFlow::ConfigSig {
131+
predicate isSource(DataFlow::Node source) {
132+
source.asExpr() instanceof ExprSystemGetPropertyTempDirTainted
133+
}
134+
135+
predicate isSink(DataFlow::Node sink) {
136+
sink instanceof FileCreationSink and
137+
not TempDirSystemGetPropertyDirectlyToMkdir::flowTo(sink)
138+
}
139+
140+
predicate isBarrier(DataFlow::Node sanitizer) {
141+
exists(FilesSanitizingCreationMethodAccess sanitisingMethodAccess |
142+
sanitizer.asExpr() = sanitisingMethodAccess.getArgument(0)
143+
)
144+
or
145+
sanitizer instanceof WindowsOsSanitizer
146+
}
147+
}
148+
149+
/**
150+
* Taint-tracking flow which tracks the access of the system temporary directory
151+
* flowing to the creation of files or directories.
152+
*/
153+
module TempDirSystemGetPropertyToCreate =
154+
TaintTracking::Global<TempDirSystemGetPropertyToCreateConfig>;
155+
156+
/**
157+
* Configuration that tracks calls to to `mkdir` or `mkdirs` that are are directly on the temp directory system property.
158+
* Examples:
159+
* - `File tempDir = new File(System.getProperty("java.io.tmpdir")); tempDir.mkdir();`
160+
* - `File tempDir = new File(System.getProperty("java.io.tmpdir")); tempDir.mkdirs();`
161+
*
162+
* These are examples of code that is simply verifying that the temp directory exists.
163+
* As such, this code pattern is filtered out as an explicit vulnerability in
164+
* `TempDirSystemGetPropertyToCreateConfig::isSink`.
165+
*/
166+
module TempDirSystemGetPropertyDirectlyToMkdirConfig implements DataFlow::ConfigSig {
167+
predicate isSource(DataFlow::Node node) {
168+
exists(ExprSystemGetPropertyTempDirTainted propertyGetExpr, DataFlow::Node callSite |
169+
DataFlow::localFlow(DataFlow::exprNode(propertyGetExpr), callSite)
170+
|
171+
isFileConstructorArgument(callSite.asExpr(), node.asExpr(), 1)
172+
)
173+
}
174+
175+
predicate isSink(DataFlow::Node node) {
176+
exists(MethodAccess ma | ma.getMethod() instanceof MethodFileDirectoryCreation |
177+
ma.getQualifier() = node.asExpr()
178+
)
179+
}
180+
181+
predicate isBarrier(DataFlow::Node sanitizer) {
182+
isFileConstructorArgument(sanitizer.asExpr(), _, _)
183+
}
184+
}
185+
186+
/**
187+
* Taint-tracking flow that tracks calls to to `mkdir` or `mkdirs` that are are directly on the temp directory system property.
188+
* Examples:
189+
* - `File tempDir = new File(System.getProperty("java.io.tmpdir")); tempDir.mkdir();`
190+
* - `File tempDir = new File(System.getProperty("java.io.tmpdir")); tempDir.mkdirs();`
191+
*
192+
* These are examples of code that is simply verifying that the temp directory exists.
193+
* As such, this code pattern is filtered out as an explicit vulnerability in
194+
* `TempDirSystemGetPropertyToCreateConfig::isSink`.
195+
*/
196+
module TempDirSystemGetPropertyDirectlyToMkdir =
197+
TaintTracking::Global<TempDirSystemGetPropertyDirectlyToMkdirConfig>;
198+
199+
//
200+
// Begin configuration for tracking single-method calls that are vulnerable.
201+
//
202+
/**
203+
* A `MethodAccess` against a method that creates a temporary file or directory in a shared temporary directory.
204+
*/
205+
abstract class MethodAccessInsecureFileCreation extends MethodAccess {
206+
/**
207+
* Gets the type of entity created (e.g. `file`, `directory`, ...).
208+
*/
209+
abstract string getFileSystemEntityType();
210+
211+
/**
212+
* Gets the dataflow node representing the file system entity created.
213+
*/
214+
DataFlow::Node getNode() { result.asExpr() = this }
215+
}
216+
217+
/**
218+
* An insecure call to `java.io.File.createTempFile`.
219+
*/
220+
class MethodAccessInsecureFileCreateTempFile extends MethodAccessInsecureFileCreation {
221+
MethodAccessInsecureFileCreateTempFile() {
222+
this.getMethod() instanceof MethodFileCreateTempFile and
223+
(
224+
// `File.createTempFile(string, string)` always uses the default temporary directory
225+
this.getNumArgument() = 2
226+
or
227+
// The default temporary directory is used when the last argument of `File.createTempFile(string, string, File)` is `null`
228+
DataFlow::localExprFlow(any(NullLiteral n), this.getArgument(2))
229+
)
230+
}
231+
232+
override string getFileSystemEntityType() { result = "file" }
233+
}
234+
235+
/**
236+
* The `com.google.common.io.Files.createTempDir` method.
237+
*/
238+
class MethodGuavaFilesCreateTempFile extends Method {
239+
MethodGuavaFilesCreateTempFile() {
240+
this.getDeclaringType().hasQualifiedName("com.google.common.io", "Files") and
241+
this.hasName("createTempDir")
242+
}
243+
}
244+
245+
/**
246+
* A call to the `com.google.common.io.Files.createTempDir` method.
247+
*/
248+
class MethodAccessInsecureGuavaFilesCreateTempFile extends MethodAccessInsecureFileCreation {
249+
MethodAccessInsecureGuavaFilesCreateTempFile() {
250+
this.getMethod() instanceof MethodGuavaFilesCreateTempFile
251+
}
252+
253+
override string getFileSystemEntityType() { result = "directory" }
254+
}

0 commit comments

Comments
 (0)