Skip to content
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

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

intrigus-lgtm
Copy link
Contributor

@intrigus-lgtm intrigus-lgtm commented Jun 24, 2020

Still early work, but posting nevertheless to prevent further bounty collisions.

def.getName() = "startsWith" or
def.getName() = "endsWith" or
def.getName() = "isEmpty" or
def.getName() = "equals"
Copy link
Contributor

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(...)`. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/** 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
Copy link
Contributor

@Marcono1234 Marcono1234 Jun 26, 2020

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) {
Copy link
Contributor

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) {
Copy link
Contributor

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 {
Copy link
Contributor

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 {
Copy link
Contributor

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 {
Copy link
Contributor

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.

@intrigus-lgtm
Copy link
Contributor Author

Thank you for the early review @Marcono1234 :)
The biggest part of https://github.com/github/codeql/pull/3794/files#diff-babd1a1e86909a409ee3a0ff2ee648f2R1-R82 is not my code, but code copied from a similar query that only looks for path injections.
The ContainsDotDotSanitizer class is also from there.

I'll have to merge the common Path stuff somewhere.

I don't know whether I want to add the additional path creations like Path.of() in this query.
That would probably make evaluation harder, but I'll prepare a separate PR that adds them.

The other suggestions I will have a look at probably next week.

Copy link
Contributor

@Marcono1234 Marcono1234 left a 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?

@@ -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 {
Copy link
Contributor

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?

@intrigus-lgtm intrigus-lgtm changed the title Arbitrary user-controlled read/write on user-controlled path Java: Arbitrary user-controlled read/write on user-controlled path Jul 8, 2020
@adityasharad adityasharad changed the base branch from master to main August 14, 2020 18:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants