Skip to content

Commit 64001cc

Browse files
authored
Merge pull request github#5587 from smowton/smowton/admin/promote-ssrf-query
Promote SSRF query from experimental
2 parents 200126b + 11b7032 commit 64001cc

File tree

61 files changed

+2471
-771
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+2471
-771
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
lgtm,codescanning
2+
* The query "Server-side request forgery (SSRF)" (`java/ssrf`) has been promoted from experimental to the main query pack. Its results will now appear by default. This query was originally [submitted as an experimental query by @porcupineyhairs](https://github.com/github/codeql/pull/3454).
3+
* Models for `URI` and `HttpRequest` in the `java.net` package have been improved. This may lead to more results from any query where these types' methods are relevant.
4+
* Models for Apache HttpComponents' `RequestLine` and `BasicRequestLine` types. This may lead to more results from any query where these types' methods are relevant.

java/ql/src/experimental/Security/CWE/CWE-918/RequestForgery.qhelp renamed to java/ql/src/Security/CWE/CWE-918/RequestForgery.qhelp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,24 @@
55

66

77
<overview>
8-
<p>Directly incorporating user input into a HTTP request without validating the input
9-
can facilitate Server Side Request Forgery (SSRF) attacks. In these attacks, the server
10-
may be tricked into making a request and interacting with an attacker-controlled server.
8+
<p>Directly incorporating user input into an HTTP request without validating the input
9+
can facilitate server-side request forgery (SSRF) attacks. In these attacks, the server
10+
may be tricked into making a request and interacting with an attacker-controlled server.
1111
</p>
1212

1313
</overview>
1414
<recommendation>
1515

16-
<p>To guard against SSRF attacks, it is advisable to avoid putting user input
17-
directly into the request URL. Instead, maintain a list of authorized
18-
URLs on the server; then choose from that list based on the user input provided.</p>
16+
<p>To guard against SSRF attacks, you should avoid putting user-provided input
17+
directly into a request URL. Instead, maintain a list of authorized
18+
URLs on the server; then choose from that list based on the input provided.
19+
Alternatively, ensure requests constructed from user input are limited to
20+
a particular host or more restrictive URL prefix.</p>
1921

2022
</recommendation>
2123
<example>
2224

23-
<p>The following example shows an HTTP request parameter being used directly in a forming a
25+
<p>The following example shows an HTTP request parameter being used directly to form a
2426
new request without validating the input, which facilitates SSRF attacks.
2527
It also shows how to remedy the problem by validating the user input against a known fixed string.
2628
</p>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @name Server-side request forgery
3+
* @description Making web requests based on unvalidated user-input
4+
* may cause the server to communicate with malicious servers.
5+
* @kind path-problem
6+
* @problem.severity error
7+
* @precision high
8+
* @id java/ssrf
9+
* @tags security
10+
* external/cwe/cwe-918
11+
*/
12+
13+
import java
14+
import semmle.code.java.security.RequestForgeryConfig
15+
import DataFlow::PathGraph
16+
17+
from DataFlow::PathNode source, DataFlow::PathNode sink, RequestForgeryConfiguration conf
18+
where conf.hasFlowPath(source, sink)
19+
select sink.getNode(), source, sink, "Potential server-side request forgery due to $@.",
20+
source.getNode(), "a user-provided value"

java/ql/src/experimental/Security/CWE/CWE-522/InsecureBasicAuth.ql

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -194,15 +194,6 @@ predicate urlOpen(DataFlow::Node node1, DataFlow::Node node2) {
194194
)
195195
}
196196

197-
/** Constructor of `BasicRequestLine` */
198-
predicate basicRequestLine(DataFlow::Node node1, DataFlow::Node node2) {
199-
exists(ConstructorCall mcc |
200-
mcc.getConstructedType().hasQualifiedName("org.apache.http.message", "BasicRequestLine") and
201-
mcc.getArgument(1) = node1.asExpr() and // `BasicRequestLine(String method, String uri, ProtocolVersion version)
202-
node2.asExpr() = mcc
203-
)
204-
}
205-
206197
class BasicAuthFlowConfig extends TaintTracking::Configuration {
207198
BasicAuthFlowConfig() { this = "InsecureBasicAuth::BasicAuthFlowConfig" }
208199

@@ -236,7 +227,6 @@ class BasicAuthFlowConfig extends TaintTracking::Configuration {
236227
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
237228
apacheHttpRequest(node1, node2) or
238229
createURI(node1, node2) or
239-
basicRequestLine(node1, node2) or
240230
createURL(node1, node2) or
241231
urlOpen(node1, node2)
242232
}

java/ql/src/experimental/Security/CWE/CWE-918/RequestForgery.ql

Lines changed: 0 additions & 33 deletions
This file was deleted.

java/ql/src/experimental/Security/CWE/CWE-918/RequestForgery.qll

Lines changed: 0 additions & 192 deletions
This file was deleted.

java/ql/src/semmle/code/java/StringFormat.qll

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,19 @@ class FormattingCall extends Call {
175175
)
176176
}
177177

178+
/** Gets the `i`th argument to be formatted. The index `i` is one-based. */
179+
Expr getArgumentToBeFormatted(int i) {
180+
i >= 1 and
181+
if this.hasExplicitVarargsArray()
182+
then
183+
result =
184+
this.getArgument(1 + this.getFormatStringIndex())
185+
.(ArrayCreationExpr)
186+
.getInit()
187+
.getInit(i - 1)
188+
else result = this.getArgument(this.getFormatStringIndex() + i)
189+
}
190+
178191
/** Holds if the varargs argument is given as an explicit array. */
179192
private predicate hasExplicitVarargsArray() {
180193
this.getNumArgument() = this.getFormatStringIndex() + 2 and
@@ -353,6 +366,11 @@ class FormatString extends string {
353366
* is not referred by any format specifier.
354367
*/
355368
/*abstract*/ int getASkippedFmtSpecIndex() { none() }
369+
370+
/**
371+
* Gets an offset (zero-based) in this format string where argument `argNo` (1-based) will be interpolated, if any.
372+
*/
373+
int getAnArgUsageOffset(int argNo) { none() }
356374
}
357375

358376
private class PrintfFormatString extends FormatString {
@@ -425,6 +443,22 @@ private class PrintfFormatString extends FormatString {
425443
result > count(int i | fmtSpecRefersToSequentialIndex(i)) and
426444
not result = fmtSpecRefersToSpecificIndex(_)
427445
}
446+
447+
private int getFmtSpecRank(int specOffset) {
448+
rank[result](int i | this.fmtSpecIsRef(i)) = specOffset
449+
}
450+
451+
override int getAnArgUsageOffset(int argNo) {
452+
argNo = fmtSpecRefersToSpecificIndex(result)
453+
or
454+
result = rank[argNo](int i | fmtSpecRefersToSequentialIndex(i))
455+
or
456+
fmtSpecRefersToPrevious(result) and
457+
exists(int previousOffset |
458+
getFmtSpecRank(previousOffset) = getFmtSpecRank(result) - 1 and
459+
previousOffset = getAnArgUsageOffset(argNo)
460+
)
461+
}
428462
}
429463

430464
private class LoggerFormatString extends FormatString {
@@ -449,4 +483,6 @@ private class LoggerFormatString extends FormatString {
449483
}
450484

451485
override int getMaxFmtSpecIndex() { result = count(int i | fmtPlaceholder(i)) }
486+
487+
override int getAnArgUsageOffset(int argNo) { result = rank[argNo](int i | fmtPlaceholder(i)) }
452488
}

java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ private module Frameworks {
8282
private import semmle.code.java.frameworks.guava.Guava
8383
private import semmle.code.java.frameworks.jackson.JacksonSerializability
8484
private import semmle.code.java.frameworks.JaxWS
85+
private import semmle.code.java.frameworks.spring.SpringHttp
86+
private import semmle.code.java.frameworks.spring.SpringWebClient
8587
private import semmle.code.java.security.ResponseSplitting
8688
private import semmle.code.java.security.InformationLeak
8789
private import semmle.code.java.security.XSS
@@ -209,6 +211,8 @@ private predicate sinkModelCsv(string row) {
209211
// Open URL
210212
"java.net;URL;false;openConnection;;;Argument[-1];open-url",
211213
"java.net;URL;false;openStream;;;Argument[-1];open-url",
214+
"java.net.http;HttpRequest;false;newBuilder;;;Argument[0];open-url",
215+
"java.net.http;HttpRequest$Builder;false;uri;;;Argument[0];open-url",
212216
// Create file
213217
"java.io;FileOutputStream;false;FileOutputStream;;;Argument[0];create-file",
214218
"java.io;RandomAccessFile;false;RandomAccessFile;;;Argument[0];create-file",
@@ -248,6 +252,8 @@ private predicate summaryModelCsv(string row) {
248252
"javax.xml.transform.stream;StreamSource;false;getInputStream;;;Argument[-1];ReturnValue;taint",
249253
"java.nio;ByteBuffer;false;get;;;Argument[-1];ReturnValue;taint",
250254
"java.net;URI;false;toURL;;;Argument[-1];ReturnValue;taint",
255+
"java.net;URI;false;toString;;;Argument[-1];ReturnValue;taint",
256+
"java.net;URI;false;toAsciiString;;;Argument[-1];ReturnValue;taint",
251257
"java.io;File;false;toURI;;;Argument[-1];ReturnValue;taint",
252258
"java.io;File;false;toPath;;;Argument[-1];ReturnValue;taint",
253259
"java.nio.file;Path;false;toFile;;;Argument[-1];ReturnValue;taint",

0 commit comments

Comments
 (0)