Add additional tests from real world query run

This commit is contained in:
Shyam Mehta
2022-06-28 17:32:20 -04:00
parent 7122f29296
commit b5ca2c3d9d
3 changed files with 72 additions and 2 deletions

View File

@@ -0,0 +1,46 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Accessing paths controlled by users can allow an attacker to access unexpected resources. This
can result in sensitive information being revealed or deleted, or an attacker being able to influence
behavior by modifying unexpected files.</p>
<p>Paths that are naively constructed from data controlled by a user may contain unexpected special characters,
such as "..". Such a path may potentially point to any directory on the file system.</p>
</overview>
<recommendation>
<p>Validate user input before using it to construct a file path. Ideally, follow these rules:</p>
<ul>
<li>Do not allow more than a single "." character.</li>
<li>Do not allow directory separators such as "/" or "\" (depending on the file system).</li>
<li>Do not rely on simply replacing problematic sequences such as "../". For example, after applying this filter to
".../...//" the resulting string would still be "../".</li>
<li>Ideally use a whitelist of known good patterns.</li>
</ul>
</recommendation>
<example>
<p>In this example, a file name is read from a <code>java.net.Socket</code> and then used to access a file in the
user's home directory and send it back over the socket. However, a malicious user could enter a file name which contains special
characters. For example, the string "../../etc/passwd" will result in the code reading the file located at
"/home/[user]/../../etc/passwd", which is the system's password file. This file would then be sent back to the user,
giving them access to all the system's passwords.</p>
<sample src="PartialPathTraversal.java" />
</example>
<references>
<li>
OWASP:
<a href="https://owasp.org/www-community/attacks/Path_Traversal">Path Traversal</a>.
</li>
</references>
</qhelp>

View File

@@ -39,14 +39,24 @@ class SystemPropFileSeparatorExpr extends FileSeparatorExpr {
}
class StringLiteralFileSeparatorExpr extends FileSeparatorExpr, StringLiteral {
StringLiteralFileSeparatorExpr() { this.getValue() = "/" }
StringLiteralFileSeparatorExpr() {
this.getValue().matches("%/") or this.getValue().matches("%\\")
}
}
class CharacterLiteralFileSeparatorExpr extends FileSeparatorExpr, CharacterLiteral {
CharacterLiteralFileSeparatorExpr() { this.getValue() = "/" or this.getValue() = "\\" }
}
class FileSeparatorAppend extends AddExpr {
FileSeparatorAppend() { this.getRightOperand() instanceof FileSeparatorExpr }
}
predicate isSafe(Expr expr) { DataFlow::localExprFlow(any(FileSeparatorAppend fsa), expr) }
predicate isSafe(Expr expr) {
DataFlow::localExprFlow(any(Expr e |
e instanceof FileSeparatorAppend or e instanceof FileSeparatorExpr
), expr)
}
from MethodAccess ma
where

View File

@@ -218,8 +218,22 @@ public class PartialPathTraversalTest {
}
}
void foo24(File dir, File parent) throws IOException {
String parentCanonical = parent.getCanonicalPath();
if (!dir.getCanonicalPath().startsWith(parentCanonical + '/')) {
throw new IOException("Invalid directory: " + dir.getCanonicalPath());
}
}
public void doesNotFlag() {
"hello".startsWith("goodbye");
}
public void doesNotFlagBackslash(File file) throws IOException {
// https://github.com/jenkinsci/jenkins/blob/be3cf6bffe7aa2fe2307c424fa418519f3bbd73b/core/src/main/java/hudson/util/jna/Kernel32Utils.java#L77-L77
if (!file.getCanonicalPath().startsWith("\\\\")) {
throw new RuntimeException("Boom");
}
}
}