Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4c7d476
[JAVA] Partial Path Traversal Vuln Query
smehta23 Jun 28, 2022
7122f29
Finish Partial Path Traversal Query
smehta23 Jun 28, 2022
b5ca2c3
Add additional tests from real world query run
smehta23 Jun 28, 2022
955e614
Add documentation of the Partial Path Traversal vuln
smehta23 Jun 29, 2022
7ab8f02
Fix duplicate class header and better fix using toPath()
smehta23 Jun 29, 2022
1681407
Fix typo in .qhelp
smehta23 Jun 29, 2022
c6f2f61
Update java/ql/src/Security/CWE/CWE-023/PartialPathTraversalBad.java
smehta23 Jul 1, 2022
209a216
Update java/ql/src/Security/CWE/CWE-023/PartialPathTraversalGood.java
smehta23 Jul 1, 2022
300a14c
Add ESAPI reference
smehta23 Jul 1, 2022
1a41d4c
Add CVE number
smehta23 Jul 1, 2022
48e16e5
Update java/ql/src/Security/CWE/CWE-023/PartialPathTraversal.qhelp
smehta23 Jul 1, 2022
ebe48ec
Update java/ql/src/Security/CWE/CWE-023/PartialPathTraversal.qhelp
smehta23 Jul 1, 2022
391dd5b
Update java/ql/src/Security/CWE/CWE-023/PartialPathTraversalGood.java
smehta23 Jul 1, 2022
39f8854
Change log
smehta23 Jul 1, 2022
781a2a7
Merge branch 'main' into feat/SM/java_partial_path_traversal_vulnerab…
smehta23 Jul 12, 2022
65b9947
Incorporate jksco's feedback
smehta23 Jul 12, 2022
01cec04
Abbreviate qhelp
smowton Jul 13, 2022
a697063
Improve description
smowton Jul 13, 2022
b7e5227
Apply suggestions from code review
smehta23 Jul 20, 2022
09ec379
Partial Path Traversal split into 2 queries
smehta23 Jul 20, 2022
76cecc1
Fix documentation
smehta23 Aug 3, 2022
4f1bc30
Update java/ql/src/Security/CWE/CWE-023/PartialPathTraversalFromRemot…
smehta23 Aug 8, 2022
9d3e8ec
Update PartialPathTraversalFromRemote.qhelp
smehta23 Aug 8, 2022
50b4df5
Fixed precision labels
smehta23 Aug 8, 2022
af92fc3
Update PartialPathTraversalFromRemote.qhelp
smehta23 Aug 8, 2022
c2b670e
Update java/ql/src/Security/CWE/CWE-023/PartialPathTraversalFromRemot…
smehta23 Aug 9, 2022
7da0740
Update java/ql/src/Security/CWE/CWE-023/PartialPathTraversalFromRemot…
smehta23 Aug 9, 2022
4d80fd0
Update java/ql/src/Security/CWE/CWE-023/PartialPathTraversal.qhelp
smehta23 Aug 9, 2022
cf68a11
Update java/ql/src/Security/CWE/CWE-023/PartialPathTraversal.qhelp
smehta23 Aug 9, 2022
2ca0b0c
Inline qhelp overview
smowton Aug 9, 2022
09e4c6b
Add dataflow path-graph
smowton Aug 10, 2022
e9df675
Autoformat ql
smowton Aug 11, 2022
3cf871e
Apply docs suggestions
smowton Aug 15, 2022
5677e38
Style edit
smowton Aug 15, 2022
1a3dc1d
Remove extra closing tag
smowton Aug 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions java/ql/lib/semmle/code/java/security/PartialPathTraversal.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/** Provides classes to reason about partial path traversal vulnerabilities. */

import java
private import semmle.code.java.dataflow.DataFlow
private import semmle.code.java.environment.SystemProperty

private class MethodStringStartsWith extends Method {
MethodStringStartsWith() {
this.getDeclaringType() instanceof TypeString and
this.hasName("startsWith")
}
}

private class MethodFileGetCanonicalPath extends Method {
MethodFileGetCanonicalPath() {
this.getDeclaringType() instanceof TypeFile and
this.hasName("getCanonicalPath")
}
}

private class MethodAccessFileGetCanonicalPath extends MethodAccess {
MethodAccessFileGetCanonicalPath() { this.getMethod() instanceof MethodFileGetCanonicalPath }
}

abstract private class FileSeparatorExpr extends Expr { }

private class SystemPropFileSeparatorExpr extends FileSeparatorExpr {
SystemPropFileSeparatorExpr() { this = getSystemProperty("file.separator") }
}

private class StringLiteralFileSeparatorExpr extends FileSeparatorExpr, StringLiteral {
StringLiteralFileSeparatorExpr() {
this.getValue().matches("%/") or this.getValue().matches("%\\")
}
}

private class CharacterLiteralFileSeparatorExpr extends FileSeparatorExpr, CharacterLiteral {
CharacterLiteralFileSeparatorExpr() { this.getValue() = "/" or this.getValue() = "\\" }
}

private class FileSeparatorAppend extends AddExpr {
FileSeparatorAppend() { this.getRightOperand() instanceof FileSeparatorExpr }
}

private predicate isSafe(Expr expr) {
DataFlow::localExprFlow(any(Expr e |
e instanceof FileSeparatorAppend or e instanceof FileSeparatorExpr
), expr)
}

/**
* A method access that returns a boolean that incorrectly guards against Partial Path Traversal.
*/
class PartialPathTraversalMethodAccess extends MethodAccess {
PartialPathTraversalMethodAccess() {
this.getMethod() instanceof MethodStringStartsWith and
DataFlow::localExprFlow(any(MethodAccessFileGetCanonicalPath gcpma), this.getQualifier()) and
not isSafe(this.getArgument(0))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/** Provides taint tracking configurations to be used in partial path traversal queries. */

import java
import semmle.code.java.security.PartialPathTraversal
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.ExternalFlow
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.FlowSources

/**
* A taint-tracking configuration for unsafe user input
* that is used to validate against path traversal, but is insufficient
* and remains vulnerable to Partial Path Traversal.
*/
class PartialPathTraversalFromRemoteConfig extends TaintTracking::Configuration {
PartialPathTraversalFromRemoteConfig() { this = "PartialPathTraversalFromRemoteConfig" }

override predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource }

override predicate isSink(DataFlow::Node node) {
any(PartialPathTraversalMethodAccess ma).getQualifier() = node.asExpr()
}
}
15 changes: 15 additions & 0 deletions java/ql/src/Security/CWE/CWE-023/PartialPathTraversal.qhelp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>A common way to check that a user-supplied path <code>SUBDIR</code> falls inside a directory <code>DIR</code>
is to use <code>getCanonicalPath()</code> to remove any path-traversal elements and then check that <code>DIR</code>
is a prefix. However, if <code>DIR</code> is not slash-terminated, this can unexpectedly allow access to siblings of <code>DIR</code>.</p>

<p>See also <code>java/partial-path-traversal-from-remote</code>, which is similar to this query but only flags instances with evidence of remote exploitability.</p>
</overview>

<include src="PartialPathTraversalRemainder.inc.qhelp"/>
Comment thread
mchammer01 marked this conversation as resolved.

</qhelp>
16 changes: 16 additions & 0 deletions java/ql/src/Security/CWE/CWE-023/PartialPathTraversal.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* @name Partial path traversal vulnerability
* @description A prefix used to check that a canonicalised path falls within another must be slash-terminated.
* @kind problem
* @problem.severity error
* @security-severity 9.3
* @precision medium
* @id java/partial-path-traversal
* @tags security
* external/cwe/cwe-023
*/

import semmle.code.java.security.PartialPathTraversal

from PartialPathTraversalMethodAccess ma
select ma, "Partial Path Traversal Vulnerability due to insufficient guard against path traversal"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
public class PartialPathTraversalBad {
public void example(File dir, File parent) throws IOException {
if (!dir.getCanonicalPath().startsWith(parent.getCanonicalPath())) {
throw new IOException("Invalid directory: " + dir.getCanonicalPath());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>A common way to check that a user-supplied path <code>SUBDIR</code> falls inside a directory <code>DIR</code>
is to use <code>getCanonicalPath()</code> to remove any path-traversal elements and then check that <code>DIR</code>
is a prefix. However, if <code>DIR</code> is not slash-terminated, this can unexpectedly allow accessing siblings of <code>DIR</code>.</p>

<p>See also <code>java/partial-path-traversal</code>, which is similar to this query,
but may also flag non-remotely-exploitable instances of partial path traversal vulnerabilities.</p>
</overview>

<include src="PartialPathTraversalRemainder.inc.qhelp"/>

</qhelp>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* @name Partial path traversal vulnerability from remote
* @description A prefix used to check that a canonicalised path falls within another must be slash-terminated.
* @kind path-problem
* @problem.severity error
* @security-severity 9.3
* @precision high
Comment thread
smehta23 marked this conversation as resolved.
* @id java/partial-path-traversal-from-remote
* @tags security
* external/cwe/cwe-023
*/

import semmle.code.java.security.PartialPathTraversalQuery
import DataFlow::PathGraph

from DataFlow::PathNode source, DataFlow::PathNode sink
where any(PartialPathTraversalFromRemoteConfig config).hasFlowPath(source, sink)
select sink.getNode(), source, sink,
"Partial Path Traversal Vulnerability due to insufficient guard against path traversal from user-supplied data"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
public class PartialPathTraversalGood {
public void example(File dir, File parent) throws IOException {
if (!dir.getCanonicalPath().toPath().startsWith(parent.getCanonicalPath().toPath())) {
throw new IOException("Invalid directory: " + dir.getCanonicalPath());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>

<recommendation>

<p>If the user should only access items within a certain directory <code>DIR</code>, ensure that <code>DIR</code> is slash-terminated
before checking that <code>DIR</code> is a prefix of the user-provided path, <code>SUBDIR</code>. Note, Java's <code>getCanonicalPath()</code>
returns a <b>non</b>-slash-terminated path string, so a slash must be added to <code>DIR</code> if that method is used.</p>

</recommendation>
<example>

<p>


In this example, the <code>if</code> statement checks if <code>parent.getCanonicalPath()</code>
is a prefix of <code>dir.getCanonicalPath()</code>. However, <code>parent.getCanonicalPath()</code> is
not slash-terminated. This means that users that supply <code>dir</code> may be also allowed to access siblings of <code>parent</code>
and not just children of <code>parent</code>, which is a security issue.

</p>

<sample src="PartialPathTraversalBad.java" />

<p>

In this example, the <code>if</code> statement checks if <code>parent.getCanonicalPath() + File.separator </code>
is a prefix of <code>dir.getCanonicalPath()</code>. Because <code>parent.getCanonicalPath().toPath()</code> is
indeed slash-terminated, the user supplying <code>dir</code> can only access children of
<code>parent</code>, as desired.

</p>

<sample src="PartialPathTraversalGood.java" />

</example>
<references>

<li>OWASP:
<a href="https://owasp.org/www-community/attacks/Path_Traversal">Partial Path Traversal</a>.</li>
<li>CVE-2022-23457:
<a href="https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/GHSL-2022-008_The_OWASP_Enterprise_Security_API.md"> ESAPI Vulnerability Report</a>.</li>

</references>


</qhelp>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
category: newQuery
---
* A new query `java/partial-path-traversal` finds partial path traversal vulnerabilities resulting from incorrectly using
`String#startsWith` to compare canonical paths.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
| PartialPathTraversalTest.java:10:14:10:73 | startsWith(...) | Partial Path Traversal Vulnerability due to insufficient guard against path traversal |
| PartialPathTraversalTest.java:17:9:17:72 | startsWith(...) | Partial Path Traversal Vulnerability due to insufficient guard against path traversal |
| PartialPathTraversalTest.java:29:14:29:58 | startsWith(...) | Partial Path Traversal Vulnerability due to insufficient guard against path traversal |
| PartialPathTraversalTest.java:35:14:35:63 | startsWith(...) | Partial Path Traversal Vulnerability due to insufficient guard against path traversal |
| PartialPathTraversalTest.java:42:14:42:64 | startsWith(...) | Partial Path Traversal Vulnerability due to insufficient guard against path traversal |
| PartialPathTraversalTest.java:49:14:49:64 | startsWith(...) | Partial Path Traversal Vulnerability due to insufficient guard against path traversal |
| PartialPathTraversalTest.java:53:14:53:65 | startsWith(...) | Partial Path Traversal Vulnerability due to insufficient guard against path traversal |
| PartialPathTraversalTest.java:61:14:61:64 | startsWith(...) | Partial Path Traversal Vulnerability due to insufficient guard against path traversal |
| PartialPathTraversalTest.java:64:14:64:65 | startsWith(...) | Partial Path Traversal Vulnerability due to insufficient guard against path traversal |
| PartialPathTraversalTest.java:75:14:75:64 | startsWith(...) | Partial Path Traversal Vulnerability due to insufficient guard against path traversal |
| PartialPathTraversalTest.java:94:14:94:63 | startsWith(...) | Partial Path Traversal Vulnerability due to insufficient guard against path traversal |
| PartialPathTraversalTest.java:102:14:102:63 | startsWith(...) | Partial Path Traversal Vulnerability due to insufficient guard against path traversal |
| PartialPathTraversalTest.java:105:14:105:64 | startsWith(...) | Partial Path Traversal Vulnerability due to insufficient guard against path traversal |
| PartialPathTraversalTest.java:173:14:173:63 | startsWith(...) | Partial Path Traversal Vulnerability due to insufficient guard against path traversal |
| PartialPathTraversalTest.java:191:18:191:87 | startsWith(...) | Partial Path Traversal Vulnerability due to insufficient guard against path traversal |
| PartialPathTraversalTest.java:209:14:209:64 | startsWith(...) | Partial Path Traversal Vulnerability due to insufficient guard against path traversal |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Security/CWE/CWE-023/PartialPathTraversal.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import java
import TestUtilities.InlineFlowTest
import semmle.code.java.security.PartialPathTraversalQuery

class TestRemoteSource extends RemoteFlowSource {
TestRemoteSource() { this.asParameter().hasName(["dir", "path"]) }

override string getSourceType() { result = "TestSource" }
}

class Test extends InlineFlowTest {
override DataFlow::Configuration getValueFlowConfig() { none() }

override TaintTracking::Configuration getTaintFlowConfig() {
result instanceof PartialPathTraversalFromRemoteConfig
}
}
Loading