Skip to content

Commit 7cdf997

Browse files
committed
Java: CWE-200: Temp directory local information disclosure vulnerability
1 parent 768e519 commit 7cdf997

File tree

11 files changed

+251
-3
lines changed

11 files changed

+251
-3
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* @name Temporary Directory Local information disclosure
3+
* @description Detect local information disclosure via the java temporary directory
4+
* @kind problem
5+
* @problem.severity warning
6+
* @precision very-high
7+
* @id java/local-information-disclosure
8+
* @tags security
9+
* external/cwe/cwe-200
10+
*/
11+
12+
import TempDirUtils
13+
14+
/**
15+
* All `java.io.File::createTempFile` methods.
16+
*/
17+
class MethodFileCreateTempFile extends Method {
18+
MethodFileCreateTempFile() {
19+
this.getDeclaringType() instanceof TypeFile and
20+
this.hasName("createTempFile")
21+
}
22+
}
23+
24+
class TempDirSystemGetPropertyToAnyConfig extends TaintTracking::Configuration {
25+
TempDirSystemGetPropertyToAnyConfig() { this = "TempDirSystemGetPropertyToAnyConfig" }
26+
27+
override predicate isSource(DataFlow::Node source) {
28+
source.asExpr() instanceof MethodAccessSystemGetPropertyTempDir
29+
}
30+
31+
override predicate isSink(DataFlow::Node source) { any() }
32+
33+
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
34+
isAdditionalFileTaintStep(node1, node2)
35+
}
36+
}
37+
38+
abstract class MethodAccessInsecureFileCreation extends MethodAccess { }
39+
40+
/**
41+
* Insecure calls to `java.io.File::createTempFile`.
42+
*/
43+
class MethodAccessInsecureFileCreateTempFile extends MethodAccessInsecureFileCreation {
44+
MethodAccessInsecureFileCreateTempFile() {
45+
this.getMethod() instanceof MethodFileCreateTempFile and
46+
(
47+
this.getNumArgument() = 2 or
48+
getArgument(2) instanceof NullLiteral or
49+
// There exists a flow from the 'java.io.tmpdir' system property to this argument
50+
exists(TempDirSystemGetPropertyToAnyConfig config |
51+
config.hasFlowTo(DataFlow::exprNode(getArgument(2)))
52+
)
53+
)
54+
}
55+
}
56+
57+
class MethodGuavaFilesCreateTempFile extends Method {
58+
MethodGuavaFilesCreateTempFile() {
59+
getDeclaringType().hasQualifiedName("com.google.common.io", "Files") and
60+
hasName("createTempDir")
61+
}
62+
}
63+
64+
class MethodAccessInsecureGuavaFilesCreateTempFile extends MethodAccessInsecureFileCreation {
65+
MethodAccessInsecureGuavaFilesCreateTempFile() {
66+
getMethod() instanceof MethodGuavaFilesCreateTempFile
67+
}
68+
}
69+
70+
from MethodAccessInsecureFileCreation methodAccess
71+
select methodAccess,
72+
"Local information disclosure vulnerability due to use of file or directory readable by other local users."
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* @name Temporary Directory Local information disclosure
3+
* @description Detect local information disclosure via the java temporary directory
4+
* @kind path-problem
5+
* @problem.severity warning
6+
* @precision very-high
7+
* @id java/local-information-disclosure
8+
* @tags security
9+
* external/cwe/cwe-200
10+
*/
11+
12+
import TempDirUtils
13+
import DataFlow::PathGraph
14+
15+
private class MethodFileSystemCreation extends Method {
16+
MethodFileSystemCreation() {
17+
getDeclaringType() instanceof TypeFile and
18+
(
19+
hasName("mkdir") or
20+
hasName("createNewFile")
21+
)
22+
}
23+
}
24+
25+
private class TempDirSystemGetPropertyToCreateConfig extends TaintTracking::Configuration {
26+
TempDirSystemGetPropertyToCreateConfig() { this = "TempDirSystemGetPropertyToAnyConfig" }
27+
28+
override predicate isSource(DataFlow::Node source) {
29+
source.asExpr() instanceof MethodAccessSystemGetPropertyTempDir
30+
}
31+
32+
override predicate isSink(DataFlow::Node sink) {
33+
exists (MethodAccess ma |
34+
ma.getMethod() instanceof MethodFileSystemCreation and
35+
ma.getQualifier() = sink.asExpr()
36+
)
37+
}
38+
39+
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
40+
isAdditionalFileTaintStep(node1, node2)
41+
}
42+
}
43+
44+
45+
from DataFlow::PathNode source, DataFlow::PathNode sink, TempDirSystemGetPropertyToCreateConfig conf
46+
where conf.hasFlowPath(source, sink)
47+
select source.getNode(), source, sink,
48+
"Local information disclosure vulnerability from $@ due to use of file or directory readable by other local users.", source.getNode(),
49+
"system temp directory"
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import java
2+
import semmle.code.java.dataflow.FlowSources
3+
4+
class MethodAccessSystemGetPropertyTempDir extends MethodAccessSystemGetProperty {
5+
MethodAccessSystemGetPropertyTempDir() { this.hasPropertyName("java.io.tmpdir") }
6+
}
7+
8+
/**
9+
* Find dataflow from the temp directory system property to the `File` constructor.
10+
* Examples:
11+
* - `new File(System.getProperty("java.io.tmpdir"))`
12+
* - `new File(new File(System.getProperty("java.io.tmpdir")), "/child")`
13+
*/
14+
private predicate isTaintedFileCreation(Expr expSource, Expr exprDest) {
15+
exists(ConstructorCall construtorCall |
16+
construtorCall.getConstructedType() instanceof TypeFile and
17+
construtorCall.getArgument(0) = expSource and
18+
construtorCall = exprDest
19+
)
20+
}
21+
22+
private class TaintFollowingFileMethod extends Method {
23+
TaintFollowingFileMethod() {
24+
getDeclaringType() instanceof TypeFile and
25+
(
26+
hasName("getAbsoluteFile") or
27+
hasName("getCanonicalFile")
28+
)
29+
}
30+
}
31+
32+
private predicate isTaintFollowingFileTransformation(Expr expSource, Expr exprDest) {
33+
exists(MethodAccess fileMethodAccess |
34+
fileMethodAccess.getMethod() instanceof TaintFollowingFileMethod and
35+
fileMethodAccess.getQualifier() = expSource and
36+
fileMethodAccess = exprDest
37+
)
38+
}
39+
40+
predicate isAdditionalFileTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
41+
isTaintedFileCreation(node1.asExpr(), node2.asExpr()) or
42+
isTaintFollowingFileTransformation(node1.asExpr(), node2.asExpr())
43+
}

java/ql/src/semmle/code/java/JDK.qll

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,16 @@ class MethodSystemGetProperty extends Method {
211211
}
212212
}
213213

214+
class MethodAccessSystemGetProperty extends MethodAccess {
215+
MethodAccessSystemGetProperty() {
216+
getMethod() instanceof MethodSystemGetProperty
217+
}
218+
219+
predicate hasPropertyName(string propertyName) {
220+
this.getArgument(0).(CompileTimeConstantExpr).getStringValue() = propertyName
221+
}
222+
}
223+
214224
/**
215225
* Any method named `exit` on class `java.lang.Runtime` or `java.lang.System`.
216226
*/

java/ql/src/semmle/code/java/StringFormat.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ private predicate formatStringValue(Expr e, string fmtvalue) {
307307
or
308308
exists(Field f |
309309
e = f.getAnAccess() and
310-
f.getDeclaringType().hasQualifiedName("java.io", "File") and
310+
f.getDeclaringType() instanceof TypeFile and
311311
fmtvalue = "x" // dummy value
312312
|
313313
f.hasName("pathSeparator") or

java/ql/src/semmle/code/java/frameworks/javaee/ejb/EJBRestrictions.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ class SecurityManagerClass extends Class {
244244
/** A class involving file input or output. */
245245
class FileInputOutputClass extends Class {
246246
FileInputOutputClass() {
247-
this.hasQualifiedName("java.io", "File") or
247+
this instanceof TypeFile or
248248
this.hasQualifiedName("java.io", "FileDescriptor") or
249249
this.hasQualifiedName("java.io", "FileInputStream") or
250250
this.hasQualifiedName("java.io", "FileOutputStream") or

java/ql/src/semmle/code/java/security/FileWritable.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ private VarAccess getFileForPathConversion(Expr pathExpr) {
6767
fileToPath = pathExpr and
6868
result = fileToPath.getQualifier() and
6969
fileToPath.getMethod().hasName("toPath") and
70-
fileToPath.getMethod().getDeclaringType().hasQualifiedName("java.io", "File")
70+
fileToPath.getMethod().getDeclaringType() instanceof TypeFile
7171
)
7272
or
7373
// Look for the pattern `Paths.get(file.get*Path())` for converting between a `File` and a `Path`.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.google.common.io;
2+
3+
import java.io.File;
4+
5+
public class Files {
6+
/** Maximum loop count when creating temp directories. */
7+
private static final int TEMP_DIR_ATTEMPTS = 10000;
8+
9+
public static File createTempDir() {
10+
File baseDir = new File(System.getProperty("java.io.tmpdir"));
11+
String baseName = System.currentTimeMillis() + "-";
12+
13+
for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
14+
File tempDir = new File(baseDir, baseName + counter);
15+
if (tempDir.mkdir()) {
16+
return tempDir;
17+
}
18+
}
19+
throw new IllegalStateException("Failed to create directory within " + TEMP_DIR_ATTEMPTS + " attempts (tried "
20+
+ baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')');
21+
}
22+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Security/CWE/CWE-200/TempDirLocalInformationDisclosure1.ql
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Security/CWE/CWE-200/TempDirLocalInformationDisclosure2.ql

0 commit comments

Comments
 (0)