Skip to content

Commit

Permalink
Java: CWE-200: Temp directory local information disclosure vulnerability
Browse files Browse the repository at this point in the history
  • Loading branch information
JLLeitschuh committed Oct 2, 2020
1 parent 768e519 commit 7cdf997
Show file tree
Hide file tree
Showing 11 changed files with 251 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* @name Temporary Directory Local information disclosure
* @description Detect local information disclosure via the java temporary directory
* @kind problem
* @problem.severity warning
* @precision very-high
* @id java/local-information-disclosure
* @tags security
* external/cwe/cwe-200
*/

import TempDirUtils

/**
* All `java.io.File::createTempFile` methods.
*/
class MethodFileCreateTempFile extends Method {
MethodFileCreateTempFile() {
this.getDeclaringType() instanceof TypeFile and
this.hasName("createTempFile")
}
}

class TempDirSystemGetPropertyToAnyConfig extends TaintTracking::Configuration {
TempDirSystemGetPropertyToAnyConfig() { this = "TempDirSystemGetPropertyToAnyConfig" }

override predicate isSource(DataFlow::Node source) {
source.asExpr() instanceof MethodAccessSystemGetPropertyTempDir
}

override predicate isSink(DataFlow::Node source) { any() }

override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
isAdditionalFileTaintStep(node1, node2)
}
}

abstract class MethodAccessInsecureFileCreation extends MethodAccess { }

/**
* Insecure calls to `java.io.File::createTempFile`.
*/
class MethodAccessInsecureFileCreateTempFile extends MethodAccessInsecureFileCreation {
MethodAccessInsecureFileCreateTempFile() {
this.getMethod() instanceof MethodFileCreateTempFile and
(
this.getNumArgument() = 2 or
getArgument(2) instanceof NullLiteral or
// There exists a flow from the 'java.io.tmpdir' system property to this argument
exists(TempDirSystemGetPropertyToAnyConfig config |
config.hasFlowTo(DataFlow::exprNode(getArgument(2)))
)
)
}
}

class MethodGuavaFilesCreateTempFile extends Method {
MethodGuavaFilesCreateTempFile() {
getDeclaringType().hasQualifiedName("com.google.common.io", "Files") and
hasName("createTempDir")
}
}

class MethodAccessInsecureGuavaFilesCreateTempFile extends MethodAccessInsecureFileCreation {
MethodAccessInsecureGuavaFilesCreateTempFile() {
getMethod() instanceof MethodGuavaFilesCreateTempFile
}
}

from MethodAccessInsecureFileCreation methodAccess
select methodAccess,
"Local information disclosure vulnerability due to use of file or directory readable by other local users."
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* @name Temporary Directory Local information disclosure
* @description Detect local information disclosure via the java temporary directory
* @kind path-problem
* @problem.severity warning
* @precision very-high
* @id java/local-information-disclosure
* @tags security
* external/cwe/cwe-200
*/

import TempDirUtils
import DataFlow::PathGraph

private class MethodFileSystemCreation extends Method {
MethodFileSystemCreation() {
getDeclaringType() instanceof TypeFile and
(
hasName("mkdir") or
hasName("createNewFile")
)
}
}

private class TempDirSystemGetPropertyToCreateConfig extends TaintTracking::Configuration {
TempDirSystemGetPropertyToCreateConfig() { this = "TempDirSystemGetPropertyToAnyConfig" }

override predicate isSource(DataFlow::Node source) {
source.asExpr() instanceof MethodAccessSystemGetPropertyTempDir
}

override predicate isSink(DataFlow::Node sink) {
exists (MethodAccess ma |
ma.getMethod() instanceof MethodFileSystemCreation and
ma.getQualifier() = sink.asExpr()
)
}

override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
isAdditionalFileTaintStep(node1, node2)
}
}


from DataFlow::PathNode source, DataFlow::PathNode sink, TempDirSystemGetPropertyToCreateConfig conf
where conf.hasFlowPath(source, sink)
select source.getNode(), source, sink,
"Local information disclosure vulnerability from $@ due to use of file or directory readable by other local users.", source.getNode(),
"system temp directory"
43 changes: 43 additions & 0 deletions java/ql/src/Security/CWE/CWE-200/TempDirUtils.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import java
import semmle.code.java.dataflow.FlowSources

class MethodAccessSystemGetPropertyTempDir extends MethodAccessSystemGetProperty {
MethodAccessSystemGetPropertyTempDir() { this.hasPropertyName("java.io.tmpdir") }
}

/**
* Find dataflow from the temp directory system property to the `File` constructor.
* Examples:
* - `new File(System.getProperty("java.io.tmpdir"))`
* - `new File(new File(System.getProperty("java.io.tmpdir")), "/child")`
*/
private predicate isTaintedFileCreation(Expr expSource, Expr exprDest) {
exists(ConstructorCall construtorCall |
construtorCall.getConstructedType() instanceof TypeFile and
construtorCall.getArgument(0) = expSource and
construtorCall = exprDest
)
}

private class TaintFollowingFileMethod extends Method {
TaintFollowingFileMethod() {
getDeclaringType() instanceof TypeFile and
(
hasName("getAbsoluteFile") or
hasName("getCanonicalFile")
)
}
}

private predicate isTaintFollowingFileTransformation(Expr expSource, Expr exprDest) {
exists(MethodAccess fileMethodAccess |
fileMethodAccess.getMethod() instanceof TaintFollowingFileMethod and
fileMethodAccess.getQualifier() = expSource and
fileMethodAccess = exprDest
)
}

predicate isAdditionalFileTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
isTaintedFileCreation(node1.asExpr(), node2.asExpr()) or
isTaintFollowingFileTransformation(node1.asExpr(), node2.asExpr())
}
10 changes: 10 additions & 0 deletions java/ql/src/semmle/code/java/JDK.qll
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,16 @@ class MethodSystemGetProperty extends Method {
}
}

class MethodAccessSystemGetProperty extends MethodAccess {
MethodAccessSystemGetProperty() {
getMethod() instanceof MethodSystemGetProperty
}

predicate hasPropertyName(string propertyName) {
this.getArgument(0).(CompileTimeConstantExpr).getStringValue() = propertyName
}
}

/**
* Any method named `exit` on class `java.lang.Runtime` or `java.lang.System`.
*/
Expand Down
2 changes: 1 addition & 1 deletion java/ql/src/semmle/code/java/StringFormat.qll
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ private predicate formatStringValue(Expr e, string fmtvalue) {
or
exists(Field f |
e = f.getAnAccess() and
f.getDeclaringType().hasQualifiedName("java.io", "File") and
f.getDeclaringType() instanceof TypeFile and
fmtvalue = "x" // dummy value
|
f.hasName("pathSeparator") or
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ class SecurityManagerClass extends Class {
/** A class involving file input or output. */
class FileInputOutputClass extends Class {
FileInputOutputClass() {
this.hasQualifiedName("java.io", "File") or
this instanceof TypeFile or
this.hasQualifiedName("java.io", "FileDescriptor") or
this.hasQualifiedName("java.io", "FileInputStream") or
this.hasQualifiedName("java.io", "FileOutputStream") or
Expand Down
2 changes: 1 addition & 1 deletion java/ql/src/semmle/code/java/security/FileWritable.qll
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ private VarAccess getFileForPathConversion(Expr pathExpr) {
fileToPath = pathExpr and
result = fileToPath.getQualifier() and
fileToPath.getMethod().hasName("toPath") and
fileToPath.getMethod().getDeclaringType().hasQualifiedName("java.io", "File")
fileToPath.getMethod().getDeclaringType() instanceof TypeFile
)
or
// Look for the pattern `Paths.get(file.get*Path())` for converting between a `File` and a `Path`.
Expand Down
22 changes: 22 additions & 0 deletions java/ql/test/query-tests/security/CWE-200/semmle/tests/Files.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.google.common.io;

import java.io.File;

public class Files {
/** Maximum loop count when creating temp directories. */
private static final int TEMP_DIR_ATTEMPTS = 10000;

public static File createTempDir() {
File baseDir = new File(System.getProperty("java.io.tmpdir"));
String baseName = System.currentTimeMillis() + "-";

for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
File tempDir = new File(baseDir, baseName + counter);
if (tempDir.mkdir()) {
return tempDir;
}
}
throw new IllegalStateException("Failed to create directory within " + TEMP_DIR_ATTEMPTS + " attempts (tried "
+ baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Security/CWE/CWE-200/TempDirLocalInformationDisclosure1.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Security/CWE/CWE-200/TempDirLocalInformationDisclosure2.ql
50 changes: 50 additions & 0 deletions java/ql/test/query-tests/security/CWE-200/semmle/tests/Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

import java.io.File;
import com.google.common.io.Files;

public class Test {

void vulnerableFileCreateTempFile() {
File temp = File.createTempFile("random", "file");
}

void vulnerableFileCreateTempFileNull() {
File temp = File.createTempFile("random", "file", null);
}

void vulnerableFileCreateTempFileTainted() {
File tempDir = new File(System.getProperty("java.io.tmpdir"));
File temp = File.createTempFile("random", "file", tempDir);
}

void vulnerableFileCreateTempFileChildTainted() {
File tempDirChild = new File(new File(System.getProperty("java.io.tmpdir")), "/child");
File temp = File.createTempFile("random", "file", tempDirChild);
}

void vulnerableFileCreateTempFileCanonical() {
File tempDir = new File(System.getProperty("java.io.tmpdir")).getCanonicalFile();
File temp = File.createTempFile("random", "file", tempDir);
}

void vulnerableFileCreateTempFileAbsolute() {
File tempDir = new File(System.getProperty("java.io.tmpdir")).getAbsoluteFile();
File temp = File.createTempFile("random", "file", tempDir);
}

void safeFileCreateTempFileTainted() {
/* Creating a temporary directoy in the current user directory is not a vulnerability. */
File currentDirectory = new File(System.getProperty("user.dir"));
File temp = File.createTempFile("random", "file", currentDirectory);
}

void vulnerableGuavaFilesCreateTempDir() {
File tempDir = Files.createTempDir();
}

void vulnerableFileCreateTempFileTainted() {
File tempDirChild = new File(System.getProperty("java.io.tmpdir"), "/child");
tempDirChild.mkdir();
}

}

0 comments on commit 7cdf997

Please sign in to comment.