Skip to content

Commit 01157e4

Browse files
authored
Merge pull request github#2899 from p-/cwe-036
Java: Calling openStream on URLs created from remote source can lead to file disclosure
2 parents 6757924 + ca80bfd commit 01157e4

File tree

4 files changed

+100
-0
lines changed

4 files changed

+100
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
public class TestServlet extends HttpServlet {
2+
protected void doGet(HttpServletRequest request, HttpServletResponse response)
3+
throws ServletException, IOException {
4+
// BAD: a URL from a remote source is opened with URL#openStream()
5+
URL url = new URL(request.getParameter("url"));
6+
InputStream inputStream = new URL(url).openStream();
7+
}
8+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>Calling <code>openStream</code> on URLs created from remote source can lead to local file disclosure.</p>
8+
<p>If <code>openStream</code> is called on a <code>java.net.___URL</code>, that was created from a remote source,
9+
an attacker can try to pass absolute URLs starting with <code>file://</code> or <code>jar://</code> to access
10+
local resources in addition to remote ones.</p>
11+
</overview>
12+
13+
<recommendation>
14+
<p>When you construct a URL using <code>java.net.___URL</code> from a remote source,
15+
don't call <code>openStream</code> on it. Instead, use an HTTP Client to fetch the URL and access its content.
16+
You should also validate the URL to check that it uses the correct protocol and host combination.</p>
17+
</recommendation>
18+
19+
<example>
20+
<p>The following example shows an URL that is constructed from a request parameter. Afterwards <code>openStream</code>
21+
is called on the URL, potentially leading to a local file access.</p>
22+
<sample src="OpenStream.java" />
23+
</example>
24+
25+
<references>
26+
<li>Java Platform, Standard Edition 11, API Specification:
27+
<a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/URL.html">
28+
Class URL</a>.
29+
</li>
30+
<!-- LocalWords: CWE -->
31+
</references>
32+
33+
</qhelp>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* @name openStream called on URLs created from remote source
3+
* @description Calling openStream on URLs created from remote source
4+
* can lead to local file disclosure.
5+
* @kind path-problem
6+
*/
7+
8+
import java
9+
import semmle.code.java.dataflow.TaintTracking
10+
import semmle.code.java.dataflow.FlowSources
11+
import DataFlow::PathGraph
12+
13+
class URLConstructor extends ClassInstanceExpr {
14+
URLConstructor() { this.getConstructor().getDeclaringType() instanceof TypeUrl }
15+
16+
Expr stringArg() {
17+
// Query only in URL's that were constructed by calling the single parameter string constructor.
18+
this.getConstructor().getNumberOfParameters() = 1 and
19+
this.getConstructor().getParameter(0).getType() instanceof TypeString and
20+
result = this.getArgument(0)
21+
}
22+
}
23+
24+
class URLOpenStreamMethod extends Method {
25+
URLOpenStreamMethod() {
26+
this.getDeclaringType() instanceof TypeUrl and
27+
this.getName() = "openStream"
28+
}
29+
}
30+
31+
class RemoteURLToOpenStreamFlowConfig extends TaintTracking::Configuration {
32+
RemoteURLToOpenStreamFlowConfig() { this = "OpenStream::RemoteURLToOpenStreamFlowConfig" }
33+
34+
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
35+
36+
override predicate isSink(DataFlow::Node sink) {
37+
exists(MethodAccess m |
38+
sink.asExpr() = m.getQualifier() and m.getMethod() instanceof URLOpenStreamMethod
39+
)
40+
}
41+
42+
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
43+
exists(URLConstructor u |
44+
node1.asExpr() = u.stringArg() and
45+
node2.asExpr() = u
46+
)
47+
}
48+
}
49+
50+
from DataFlow::PathNode source, DataFlow::PathNode sink, MethodAccess call
51+
where
52+
sink.getNode().asExpr() = call.getQualifier() and
53+
any(RemoteURLToOpenStreamFlowConfig c).hasFlowPath(source, sink)
54+
select call, source, sink,
55+
"URL on which openStream is called may have been constructed from remote source"

java/ql/src/semmle/code/java/frameworks/Networking.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ class TypeSocket extends RefType {
1212
TypeSocket() { hasQualifiedName("java.net", "Socket") }
1313
}
1414

15+
class TypeUrl extends RefType {
16+
TypeUrl() { hasQualifiedName("java.net", "URL") }
17+
}
18+
1519
class URLConnectionGetInputStreamMethod extends Method {
1620
URLConnectionGetInputStreamMethod() {
1721
getDeclaringType() instanceof TypeUrlConnection and

0 commit comments

Comments
 (0)