-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Java: Arbitrary user-controlled read/write on user-controlled path #3794
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.newBufferedReader
Files.newByteChannel
Files.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.find
Files.list
Files.newDirectoryStream
Files.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.setAttributes
Files.setOwner
Files.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
@@ -165,6 +165,11 @@ class TypeFileSystem extends Class { | |||
TypeFileSystem() { this.hasQualifiedName("java.nio.file", "FileSystem") } | |||
} | |||
|
|||
/** 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.