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 #839

Closed
1 of 2 tasks
intrigus-lgtm opened this issue Jun 25, 2024 · 2 comments
Closed
1 of 2 tasks
Labels
The Bug Slayer Submissions to The Bug Slayer bounty

Comments

@intrigus-lgtm
Copy link
Contributor

CVE(s) ID list

  • CVE-2020-15097 (9.1 CRITICAL)
  • CVE-2020-4039 (9.1 CRITICAL)

All For One submission

#136

Details

This has been originally found on lgtm.com
As it has been shut down, I can not provide links to it, so I have to create new dbs.
This will take some time.

Are you planning to discuss this vulnerability submission publicly? (Blog Post, social networks, etc).

  • Yes
  • No

Blog post link

https://intrigus.org/advisories/2021/07/19/ISL-2020-002-loklak-loklak_server/ https://intrigus.org/advisories/2021/07/19/ISL-2020-001-fossasia-susiserver/

@intrigus-lgtm intrigus-lgtm added the The Bug Slayer Submissions to The Bug Slayer bounty label Jun 25, 2024
@intrigus-lgtm
Copy link
Contributor Author

susi_server_db.zip
loklak_server_db.zip

Here is the slightly cleaned and modernized query from github/codeql#3794 (I did not update the query there, because it will likely not be merged).
Changes include using the new flow states and query restructuring.

/**
 * @name User-controlled content written on user-controlled path expression.
 * @description Writing user-controlled data to an user-controlled paths can allow an attacker to write arbitrary files.
 * @kind path-problem
 * @problem.severity error
 * @precision medium
 * @id java/tainted-file-write
 * @tags security
 *       external/cwe/cwe-706
 */

import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking2
import semmle.code.java.security.XSS
import TaintedPathFlow::PathGraph
import semmle.code.java.security.PathCreation

/** Holds if `node1` is used in the creation of `node2` and not guarded. */
predicate usedInPathCreation(DataFlow::Node node1, DataFlow::Node node2) {
  exists(Expr e | e = node1.asExpr() | e = node2.asExpr().(PathCreation).getAnInput())
}

module TaintedPathConfig implements DataFlow::StateConfigSig {
  class FlowState = string;

  predicate isSource(DataFlow::Node source, FlowState state) {
    source instanceof RemoteFlowSource and state = "remote-file-name"
  }

  predicate isSink(DataFlow::Node sink, FlowState state) {
    sink instanceof FileWriteSink and state = "file-write-on-tainted-file-instance"
  }

  predicate isBarrier(DataFlow::Node node) {
    exists(Type t | t = node.getType() | t instanceof BoxedType or t instanceof PrimitiveType)
  }

  // override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
  //   guard instanceof ContainsDotDotSanitizer
  //   // TODO add guards from zipslip.ql
  // }
  predicate isAdditionalFlowStep(
    DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
  ) {
    usedInPathCreation(node1, node2) and state1 = "remote-file-name" and state2 = "remote-file-name"
    or
    node1 instanceof TaintedPathSink and
    state1 = "remote-file-name" and
    node2.asExpr() = node1.(TaintedPathSink).getTaintedFile() and
    state2 = "tainted-file-instance"
    or
    exists(NewClassExpr fos | fos.getConstructedType().hasQualifiedName(_, "FileOutputStream") |
      fos.getAnArgument() = node1.asExpr() and node2.asExpr() = fos
    ) and
    state1 = "tainted-file-instance" and
    state2 = "file-write-on-tainted-file-instance"
  }
}

module TaintedPathFlow = TaintTracking::GlobalWithState<TaintedPathConfig>;

private class TaintedPathSink extends DataFlow::Node {
  Expr path;

  TaintedPathSink() {
    exists(Expr e, PathCreation p | e = asExpr() | e = p.getAnInput() and path = p)
  }

  Expr getTaintedFile() { result = path }
}

module UserControlledWrite implements DataFlow::ConfigSig {
  predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }

  predicate isSink(DataFlow::Node sink) {
    exists(FileWriteSink fws | fws.getWrittenContent() = sink)
  }
}

module UserControlledWriteFlow = TaintTracking::Global<UserControlledWrite>;

/**
 * Modles the `write` method of `java.io.OutputStream`
 */
private class WriteMethod extends Method {
  WriteMethod() {
    this.hasName("write") and this.getDeclaringType().hasQualifiedName("java.io", "OutputStream")
  }
}

abstract private class FileWriteSink extends DataFlow::Node {
  abstract DataFlow::Node getWrittenContent();
}

/**
 * Models a write to a file via a direct or indirect call to the `write` method of `java.io.OutputStream`.
 */
private class OutputStreamWriteSink extends FileWriteSink {
  Expr content;

  OutputStreamWriteSink() {
    exists(MethodCall mc | mc.getMethod().overrides*(any(WriteMethod wm)) |
      mc.getQualifier() = this.asExpr() and mc.getAnArgument() = content
    )
  }

  override DataFlow::Node getWrittenContent() { result.asExpr() = content }
}

from
  TaintedPathFlow::PathNode remoteFileCreationSource, TaintedPathFlow::PathNode taintedFile,
  DataFlow::Node remoteContentSource
where
  TaintedPathFlow::flowPath(remoteFileCreationSource, taintedFile) and
  UserControlledWriteFlow::flow(remoteContentSource,
    taintedFile.getNode().(FileWriteSink).getWrittenContent())
select taintedFile.getNode(), remoteFileCreationSource, taintedFile,
  "Potential $@ written to a file derived from $@.", taintedFile, "user-controlled content",
  remoteFileCreationSource.getNode(), "a remote source"

@xcorail
Copy link
Contributor

xcorail commented Jul 18, 2024

Closing as per this discussion

@xcorail xcorail closed this as completed Jul 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
The Bug Slayer Submissions to The Bug Slayer bounty
Projects
None yet
Development

No branches or pull requests

2 participants