Java: Calling openStream on URLs created from remote source can lead to local file disclosure.

This commit is contained in:
Peter Stöckli
2020-02-21 17:51:15 +01:00
parent 2df3fe8f36
commit b622e2ae06
3 changed files with 97 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
public class TestServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// BAD: a URL from a remote source is opened with URL#openStream()
URL url = new URL(request.getParameter("url"));
InputStream inputStream = new URL(url).openStream();
}
}

View File

@@ -0,0 +1,39 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Calling <code>openStream</code> on URLs created from remote source can lead to local file disclosure.</p>
<p>If <code>openStream</code> is called on a <code>java.net.URL</code>, that was created from a remote source
an attacker can try to pass absolute URLs starting with <code>file://</code> or <code>jar://</code> to access
local resources in addition to remote ones.</p>
</overview>
<recommendation>
<p>When you construct a URL using <code>java.net.URL</code> from a remote source, make sure
to not call openStream on it. Instead fetch the URL with a HTTP Client to access its content.
Also validate that the URL uses the correct protocol and host combination.</p>
</recommendation>
<example>
<p>The following example shows an URL that is constructed from a request parameter. Afterwards <code>openStream</code>
is called on the URL, potentially leading to a local file access.</p>
<sample src="OpenStream.java" />
</example>
<references>
<li>Java Platform, Standard Edition 11, API Specification:
<a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/URL.html">
Class URL</a>.</li>
<li>
<!-- LocalWords: CWE
-->
</references>
</qhelp>

View File

@@ -0,0 +1,50 @@
import java
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.FlowSources
import DataFlow::PathGraph
class URLConstructor extends ClassInstanceExpr {
URLConstructor() { this.getConstructor().getDeclaringType().getQualifiedName() = "java.net.URL" }
Expr stringArg() {
// Query only in URL's that were constructed by calling the single parameter string constructor.
if
this.getConstructor().getNumberOfParameters() = 1 and
this.getConstructor().getParameter(0).getType().getName() = "String"
then result = this.getArgument(0)
else none()
}
}
class URLOpenStreamMethod extends Method {
URLOpenStreamMethod() {
this.getDeclaringType().getQualifiedName() = "java.net.URL" and
this.getName() = "openStream"
}
}
class RemoteURLToOpenStreamFlowConfig extends TaintTracking::Configuration {
RemoteURLToOpenStreamFlowConfig() { this = "OpenStream::RemoteURLToOpenStreamFlowConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess m |
sink.asExpr() = m.getQualifier() and m.getMethod() instanceof URLOpenStreamMethod
)
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(URLConstructor u |
node1.asExpr() = u.stringArg() and
node2.asExpr() = u
)
}
}
from DataFlow::PathNode source, DataFlow::PathNode sink, MethodAccess call
where
sink.getNode().asExpr() = call.getQualifier() and
any(RemoteURLToOpenStreamFlowConfig c).hasFlowPath(source, sink)
select call, source, sink,
"URL on which openStream is called may have been constructed from remote source"