Skip to content

Commit b622e2a

Browse files
committed
Java: Calling openStream on URLs created from remote source can lead to local file disclosure.
1 parent 2df3fe8 commit b622e2a

3 files changed

Lines changed: 97 additions & 0 deletions

File tree

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: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>Calling <code>openStream</code> on URLs created from remote source can lead to local file disclosure.</p>
7+
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+
12+
</overview>
13+
<recommendation>
14+
15+
<p>When you construct a URL using <code>java.net.URL</code> from a remote source, make sure
16+
to not call openStream on it. Instead fetch the URL with a HTTP Client to access its content.
17+
Also validate that the URL uses the correct protocol and host combination.</p>
18+
19+
</recommendation>
20+
<example>
21+
22+
<p>The following example shows an URL that is constructed from a request parameter. Afterwards <code>openStream</code>
23+
is called on the URL, potentially leading to a local file access.</p>
24+
25+
<sample src="OpenStream.java" />
26+
27+
</example>
28+
<references>
29+
30+
<li>Java Platform, Standard Edition 11, API Specification:
31+
<a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/URL.html">
32+
Class URL</a>.</li>
33+
<li>
34+
35+
<!-- LocalWords: CWE
36+
-->
37+
38+
</references>
39+
</qhelp>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import java
2+
import semmle.code.java.dataflow.TaintTracking
3+
import semmle.code.java.dataflow.FlowSources
4+
import DataFlow::PathGraph
5+
6+
class URLConstructor extends ClassInstanceExpr {
7+
URLConstructor() { this.getConstructor().getDeclaringType().getQualifiedName() = "java.net.URL" }
8+
9+
Expr stringArg() {
10+
// Query only in URL's that were constructed by calling the single parameter string constructor.
11+
if
12+
this.getConstructor().getNumberOfParameters() = 1 and
13+
this.getConstructor().getParameter(0).getType().getName() = "String"
14+
then result = this.getArgument(0)
15+
else none()
16+
}
17+
}
18+
19+
class URLOpenStreamMethod extends Method {
20+
URLOpenStreamMethod() {
21+
this.getDeclaringType().getQualifiedName() = "java.net.URL" and
22+
this.getName() = "openStream"
23+
}
24+
}
25+
26+
class RemoteURLToOpenStreamFlowConfig extends TaintTracking::Configuration {
27+
RemoteURLToOpenStreamFlowConfig() { this = "OpenStream::RemoteURLToOpenStreamFlowConfig" }
28+
29+
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
30+
31+
override predicate isSink(DataFlow::Node sink) {
32+
exists(MethodAccess m |
33+
sink.asExpr() = m.getQualifier() and m.getMethod() instanceof URLOpenStreamMethod
34+
)
35+
}
36+
37+
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
38+
exists(URLConstructor u |
39+
node1.asExpr() = u.stringArg() and
40+
node2.asExpr() = u
41+
)
42+
}
43+
}
44+
45+
from DataFlow::PathNode source, DataFlow::PathNode sink, MethodAccess call
46+
where
47+
sink.getNode().asExpr() = call.getQualifier() and
48+
any(RemoteURLToOpenStreamFlowConfig c).hasFlowPath(source, sink)
49+
select call, source, sink,
50+
"URL on which openStream is called may have been constructed from remote source"

0 commit comments

Comments
 (0)