-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Java: Arbitrary user-controlled read/write on user-controlled path #3794
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
| def.getName() = "startsWith" or | ||
| def.getName() = "endsWith" or | ||
| def.getName() = "isEmpty" or | ||
| def.getName() = "equals" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /** 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe also apply this change to #3881?
Still early work, but posting nevertheless to prevent further bounty collisions.