mirror of
https://github.com/github/codeql.git
synced 2025-12-24 12:46:34 +01:00
Java: CWE-200: Temp directory local information disclosure vulnerability
This commit is contained in:
@@ -236,14 +236,14 @@ class MethodSystemGetProperty extends Method {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An access to a method named `getProperty` on class `java.lang.System`.
|
* Any method access to a method named `getProperty` on class `java.lang.System`.
|
||||||
*/
|
*/
|
||||||
class MethodAccessSystemGetProperty extends MethodAccess {
|
class MethodAccessSystemGetProperty extends MethodAccess {
|
||||||
MethodAccessSystemGetProperty() { this.getMethod() instanceof MethodSystemGetProperty }
|
MethodAccessSystemGetProperty() { this.getMethod() instanceof MethodSystemGetProperty }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this call has a compile-time constant first argument with the value `propertyName`.
|
* Holds true if this is a compile-time constant call for the specified `propertyName`.
|
||||||
* For example: `System.getProperty("user.dir")`.
|
* Eg. `System.getProperty("user.dir")`.
|
||||||
*/
|
*/
|
||||||
predicate hasCompileTimeConstantGetPropertyName(string propertyName) {
|
predicate hasCompileTimeConstantGetPropertyName(string propertyName) {
|
||||||
this.getArgument(0).(CompileTimeConstantExpr).getStringValue() = propertyName
|
this.getArgument(0).(CompileTimeConstantExpr).getStringValue() = propertyName
|
||||||
|
|||||||
@@ -325,7 +325,7 @@ private predicate formatStringValue(Expr e, string fmtvalue) {
|
|||||||
or
|
or
|
||||||
exists(Field f |
|
exists(Field f |
|
||||||
e = f.getAnAccess() and
|
e = f.getAnAccess() and
|
||||||
f.getDeclaringType().hasQualifiedName("java.io", "File") and
|
f.getDeclaringType() instanceof TypeFile and
|
||||||
fmtvalue = "x" // dummy value
|
fmtvalue = "x" // dummy value
|
||||||
|
|
|
|
||||||
f.hasName("pathSeparator") or
|
f.hasName("pathSeparator") or
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ class SecurityManagerClass extends Class {
|
|||||||
/** A class involving file input or output. */
|
/** A class involving file input or output. */
|
||||||
class FileInputOutputClass extends Class {
|
class FileInputOutputClass extends Class {
|
||||||
FileInputOutputClass() {
|
FileInputOutputClass() {
|
||||||
this.hasQualifiedName("java.io", "File") or
|
this instanceof TypeFile or
|
||||||
this.hasQualifiedName("java.io", "FileDescriptor") or
|
this.hasQualifiedName("java.io", "FileDescriptor") or
|
||||||
this.hasQualifiedName("java.io", "FileInputStream") or
|
this.hasQualifiedName("java.io", "FileInputStream") or
|
||||||
this.hasQualifiedName("java.io", "FileOutputStream") or
|
this.hasQualifiedName("java.io", "FileOutputStream") or
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ private VarAccess getFileForPathConversion(Expr pathExpr) {
|
|||||||
fileToPath = pathExpr and
|
fileToPath = pathExpr and
|
||||||
result = fileToPath.getQualifier() and
|
result = fileToPath.getQualifier() and
|
||||||
fileToPath.getMethod().hasName("toPath") and
|
fileToPath.getMethod().hasName("toPath") and
|
||||||
fileToPath.getMethod().getDeclaringType().hasQualifiedName("java.io", "File")
|
fileToPath.getMethod().getDeclaringType() instanceof TypeFile
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// Look for the pattern `Paths.get(file.get*Path())` for converting between a `File` and a `Path`.
|
// Look for the pattern `Paths.get(file.get*Path())` for converting between a `File` and a `Path`.
|
||||||
|
|||||||
@@ -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."
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* @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 = "TempDirSystemGetPropertyToCreateConfig" }
|
||||||
|
|
||||||
|
override predicate isSource(DataFlow::Node source) {
|
||||||
|
source.asExpr() instanceof MethodAccessSystemGetPropertyTempDir
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||||
|
isAdditionalFileTaintStep(node1, node2)
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate isSink(DataFlow::Node sink) {
|
||||||
|
exists (MethodAccess ma |
|
||||||
|
ma.getMethod() instanceof MethodFileSystemCreation and
|
||||||
|
ma.getQualifier() = sink.asExpr()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
46
java/ql/src/Security/CWE/CWE-200/TempDirUtils.qll
Normal file
46
java/ql/src/Security/CWE/CWE-200/TempDirUtils.qll
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import java
|
||||||
|
import semmle.code.java.dataflow.FlowSources
|
||||||
|
|
||||||
|
class MethodAccessSystemGetPropertyTempDir extends MethodAccessSystemGetProperty {
|
||||||
|
MethodAccessSystemGetPropertyTempDir() { this.hasCompileTimeConstantGetPropertyName("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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any `File` methods that
|
||||||
|
*/
|
||||||
|
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())
|
||||||
|
}
|
||||||
@@ -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) + ')');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Security/CWE/CWE-200/TempDirLocalInformationDisclosure1.ql
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Security/CWE/CWE-200/TempDirLocalInformationDisclosure2.ql
|
||||||
@@ -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 vulnerableFileCreateTempFileMkdirTainted() {
|
||||||
|
File tempDirChild = new File(System.getProperty("java.io.tmpdir"), "/child");
|
||||||
|
tempDirChild.mkdir();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user