Skip to content

Commit 311c9e4

Browse files
committed
Query to detect unsafe resource loading in Java Spring applications
1 parent 342c876 commit 311c9e4

File tree

10 files changed

+475
-5
lines changed

10 files changed

+475
-5
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//BAD: no path validation in Spring resource loading
2+
@GetMapping("/file")
3+
public String getFileContent(@RequestParam(name="fileName") String fileName) {
4+
ClassPathResource clr = new ClassPathResource(fileName);
5+
6+
File file = ResourceUtils.getFile(fileName);
7+
8+
Resource resource = resourceLoader.getResource(fileName);
9+
}
10+
11+
//GOOD: check for a trusted prefix, ensuring path traversal is not used to erase that prefix in Spring resource loading:
12+
@GetMapping("/file")
13+
public String getFileContent(@RequestParam(name="fileName") String fileName) {
14+
if (!fileName.contains("..") && fileName.hasPrefix("/public-content")) {
15+
ClassPathResource clr = new ClassPathResource(fileName);
16+
17+
File file = ResourceUtils.getFile(fileName);
18+
19+
Resource resource = resourceLoader.getResource(fileName);
20+
}
21+
}

java/ql/src/experimental/Security/CWE/CWE-552/UnsafeUrlForward.qhelp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ file exposure attacks. It also shows how to remedy the problem by validating the
4343

4444
<sample src="UnsafeResourceGet.java" />
4545

46+
<p>The following examples show an HTTP request parameter being used directly to retrieve a resource
47+
of a Java Spring application without validating the input, which allows sensitive file exposure
48+
attacks. It also shows how to remedy the problem by validating the user input.
49+
</p>
50+
51+
<sample src="UnsafeLoadSpringResource.java" />
4652
</example>
4753
<references>
4854
<li>File Disclosure:
@@ -57,5 +63,8 @@ file exposure attacks. It also shows how to remedy the problem by validating the
5763
<li>CVE-2015-5174:
5864
<a href="https://vuldb.com/?id.81084">Apache Tomcat 6.0/7.0/8.0/9.0 Servletcontext getResource/getResourceAsStream/getResourcePaths Path Traversal</a>
5965
</li>
66+
<li>CVE-2019-3799:
67+
<a href="https://github.com/mpgn/CVE-2019-3799">CVE-2019-3799 - Spring-Cloud-Config-Server Directory Traversal &lt; 2.1.2, 2.0.4, 1.4.6</a>
68+
</li>
6069
</references>
6170
</qhelp>

java/ql/src/experimental/Security/CWE/CWE-552/UnsafeUrlForward.ql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/**
2-
* @name Unsafe URL forward or dispatch from remote source
3-
* @description URL forward or dispatch based on unvalidated user-input
2+
* @name Unsafe URL forward, dispatch, or load from remote source
3+
* @description URL forward, dispatch, or load based on unvalidated user-input
44
* may cause file information disclosure.
55
* @kind path-problem
66
* @problem.severity error
77
* @precision high
8-
* @id java/unsafe-url-forward-dispatch
8+
* @id java/unsafe-url-forward-dispatch-load
99
* @tags security
1010
* external/cwe-552
1111
*/

java/ql/src/experimental/Security/CWE/CWE-552/UnsafeUrlForward.qll

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ private import semmle.code.java.dataflow.ExternalFlow
44
private import semmle.code.java.dataflow.FlowSources
55
private import semmle.code.java.dataflow.StringPrefixes
66
private import semmle.code.java.frameworks.javaee.ejb.EJBRestrictions
7+
private import experimental.semmle.code.java.frameworks.SpringResource
78

89
/** A sink for unsafe URL forward vulnerabilities. */
910
abstract class UnsafeUrlForwardSink extends DataFlow::Node { }
@@ -99,6 +100,22 @@ private class GetResourceSink extends UnsafeUrlForwardSink {
99100
}
100101
}
101102

103+
/** Sink of Spring resource loading. */
104+
private class SpringResourceSink extends UnsafeUrlForwardSink {
105+
SpringResourceSink() {
106+
exists(MethodAccess ma |
107+
(
108+
ma.getMethod() instanceof GetClassPathResourceInputStreamMethod or
109+
ma.getMethod() instanceof GetResourceMethod
110+
) and
111+
ma.getQualifier() = this.asExpr()
112+
or
113+
ma.getMethod() instanceof GetResourceUtilsMethod and
114+
ma.getArgument(0) = this.asExpr()
115+
)
116+
}
117+
}
118+
102119
/** An argument to `new ModelAndView` or `ModelAndView.setViewName`. */
103120
private class SpringModelAndViewSink extends UnsafeUrlForwardSink {
104121
SpringModelAndViewSink() {
@@ -175,3 +192,17 @@ private class FilePathFlowStep extends SummaryModelCsv {
175192
]
176193
}
177194
}
195+
196+
/** Taint model related to resource loading in Spring. */
197+
private class LoadSpringResourceFlowStep extends SummaryModelCsv {
198+
override predicate row(string row) {
199+
row =
200+
[
201+
"org.springframework.core.io;ClassPathResource;false;ClassPathResource;;;Argument[0];Argument[-1];taint",
202+
"org.springframework.core.io;ClassPathResource;true;" +
203+
["getFilename", "getPath", "getURL", "resolveURL"] + ";;;Argument[-1];ReturnValue;value",
204+
"org.springframework.core.io;ResourceLoader;true;getResource;;;Argument[0];ReturnValue;taint",
205+
"org.springframework.core.io;Resource;true;createRelative;;;Argument[0];ReturnValue;taint"
206+
]
207+
}
208+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* Provides classes for working with resource loading in Spring.
3+
*/
4+
5+
import java
6+
private import semmle.code.java.dataflow.ExternalFlow
7+
private import semmle.code.java.dataflow.FlowSources
8+
9+
/** A class for class path resources in the Spring framework. */
10+
class ClassPathResource extends Class {
11+
ClassPathResource() { this.hasQualifiedName("org.springframework.core.io", "ClassPathResource") }
12+
}
13+
14+
/** An interface for objects that are sources for an InputStream in the Spring framework. */
15+
class InputStreamResource extends RefType {
16+
InputStreamResource() {
17+
this.hasQualifiedName("org.springframework.core.io", "InputStreamSource")
18+
}
19+
}
20+
21+
/** An interface that abstracts from the underlying resource, such as a file or class path resource in the Spring framework. */
22+
class Resource extends RefType {
23+
Resource() { this.hasQualifiedName("org.springframework.core.io", "Resource") }
24+
}
25+
26+
/** A utility class for resolving resource locations to files in the file system in the Spring framework. */
27+
class ResourceUtils extends Class {
28+
ResourceUtils() { this.hasQualifiedName("org.springframework.util", "ResourceUtils") }
29+
}
30+
31+
/**
32+
* The method `getInputStream()` declared in Spring `ClassPathResource`.
33+
*/
34+
class GetClassPathResourceInputStreamMethod extends Method {
35+
GetClassPathResourceInputStreamMethod() {
36+
this.getDeclaringType().getASupertype*() instanceof ClassPathResource and
37+
this.hasName("getInputStream")
38+
}
39+
}
40+
41+
/**
42+
* Resource loading method declared in Spring `Resource` with `getInputStream` inherited from the parent interface.
43+
*/
44+
class GetResourceMethod extends Method {
45+
GetResourceMethod() {
46+
(
47+
this.getDeclaringType() instanceof InputStreamResource or
48+
this.getDeclaringType() instanceof Resource
49+
) and
50+
this.hasName(["getFile", "getFilename", "getInputStream", "getURI", "getURL"])
51+
}
52+
}
53+
54+
/**
55+
* Resource loading method declared in Spring `ResourceUtils`.
56+
*/
57+
class GetResourceUtilsMethod extends Method {
58+
GetResourceUtilsMethod() {
59+
this.getDeclaringType().getASupertype*() instanceof ResourceUtils and
60+
this.hasName(["extractArchiveURL", "extractJarFileURL", "getFile", "getURL", "toURI"])
61+
}
62+
}

0 commit comments

Comments
 (0)