Java: Arbitrary user-controlled read/write on user-controlled path#3794
Java: Arbitrary user-controlled read/write on user-controlled path#3794intrigus-lgtm wants to merge 11 commits intogithub:mainfrom
Conversation
| def.getName() = "startsWith" or | ||
| def.getName() = "endsWith" or | ||
| def.getName() = "isEmpty" or | ||
| def.getName() = "equals" |
There was a problem hiding this comment.
Isn't an equals check strong? E.g.:
if (s.equals("C:/test")) {
Path.of(s);
}
| TypeFileInputStream() { this.hasQualifiedName("java.io", "FileInputStream") } | ||
| } | ||
|
|
||
| /** Models additional taint steps like `file.toPath()`, `path.toFile()`, `new FileInputStream(..)`, `Files.readAll{Bytes|Lines}(...)`, and `new File(...)`. */ |
There was a problem hiding this comment.
| /** Models additional taint steps like `file.toPath()`, `path.toFile()`, `new FileInputStream(..)`, `Files.readAll{Bytes|Lines}(...)`, and `new File(...)`. */ | |
| /** Models additional taint steps like `file.toPath()`, `path.toFile()`, `new FileInputStream(...)`, `Files.readAll{Bytes|Lines}(...)`, and `new File(...)`. */ |
| private predicate readsAllFromPath(DataFlow::Node node1, DataFlow::Node node2) { | ||
| exists(MethodAccess call | | ||
| call.getReceiverType() instanceof TypeFiles and | ||
| call.getMethod().hasName(["readAllBytes", "readAllLines"]) and |
There was a problem hiding this comment.
Java 11 added Files.readString(Path) and Files.readString(Path, Charset), would probably good to cover them as well
And Files.lines(Path) and Files.lines(Path, Charset) might be relevant as well
Edit: Though note that this predicate duplicates logic of FileReadWrite.qll
| } | ||
|
|
||
| /** Holds if `node1` is read by `node2` via a call to `Files.readAllBytes(node1)` or `Files.readAllLines(node1)`. */ | ||
| private predicate readsAllFromPath(DataFlow::Node node1, DataFlow::Node node2) { |
There was a problem hiding this comment.
Should this maybe also cover the Files.new... methods? I.e.:
Files.newBufferedReaderFiles.newByteChannelFiles.newInputStream
| } | ||
|
|
||
| /** Holds if `node1` is read by `node2` via a call to `Files.readAllBytes(node1)` or `Files.readAllLines(node1)`. */ | ||
| private predicate readsAllFromPath(DataFlow::Node node1, DataFlow::Node node2) { |
There was a problem hiding this comment.
Would it make sense to consider methods which access the contents of the folder?
I.e.:
Files.findFiles.listFiles.newDirectoryStreamFiles.walkFileTree
Though I am not sure if that would taint objects retrieved from the return values as well (e.g. Files.list(...).forEach(p -> Files.delete(p))).
Maybe it would make sense to include the java.io.File listing files as well.
| ) | ||
| } | ||
|
|
||
| class SensitiveFileOperationSink extends DataFlow::ExprNode { |
There was a problem hiding this comment.
Maybe consider the following as well?
Files.setAttributesFiles.setOwnerFiles.setPosixFilePermissions
And same for respective java.io.File methods.
| } | ||
|
|
||
| /** Models additional taint steps like `file.toPath()`, `path.toFile()`, `new FileInputStream(..)`, `Files.readAll{Bytes|Lines}(...)`, and `new File(...)`. */ | ||
| class PathAdditionalTaintStep extends TaintTracking::AdditionalTaintStep { |
There was a problem hiding this comment.
Path.resolve(Path) / Path.resolveSibling(Path) is probably a taint step as well, right?
Maybe also Path.relativize(Path)
And the other Path methods returning a Path as well?
And same for java.io.File methods returning File or String representation of file?
| ) | ||
| } | ||
|
|
||
| class ContainsDotDotSanitizer extends DataFlow::BarrierGuard { |
There was a problem hiding this comment.
This might cause false negatives, e.g.
String s = ...;
Path ROOT = Path.of("/my-app/data);
if (!s.contains("..")) {
Files.delete(ROOT.resolve(Path.of(s));
// Or
Files.delete(ROOT.resolve(s));
)Would still be vulnerable if s is absolute.
|
Thank you for the early review @Marcono1234 :) I'll have to merge the common Path stuff somewhere. I don't know whether I want to add the additional path creations like The other suggestions I will have a look at probably next week. |
Marcono1234
left a comment
There was a problem hiding this comment.
Does this depend on #3881 now?
java/ql/src/experimental/Security/CWE/CWE-706/UserControlledArbitraryWrite.ql
Show resolved
Hide resolved
java/ql/src/experimental/Security/CWE/CWE-706/UserControlledArbitraryWrite.ql
Show resolved
Hide resolved
| } | ||
|
|
||
| /** The class `java.nio.file.Files`. */ | ||
| class TypeFiles extends Class { |
Still early work, but posting nevertheless to prevent further bounty collisions.