Skip to content

Commit 9f02ca0

Browse files
authored
Merge pull request #8357 from p0wn4j/jdbc-url-ssrf-sink
Java: Add JDBC connection SSRF sinks
2 parents 0bf4ce7 + b351d5b commit 9f02ca0

File tree

20 files changed

+1110
-1
lines changed

20 files changed

+1110
-1
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added support for detection of SSRF via JDBC database URLs, including connections made using the standard library (`java.sql`), Hikari Connection Pool, JDBI and Spring JDBC.

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ private module Frameworks {
131131
private import semmle.code.java.security.XPath
132132
private import semmle.code.java.security.XsltInjection
133133
private import semmle.code.java.frameworks.Jdbc
134+
private import semmle.code.java.frameworks.Jdbi
135+
private import semmle.code.java.frameworks.HikariCP
134136
private import semmle.code.java.frameworks.SpringJdbc
135137
private import semmle.code.java.frameworks.MyBatis
136138
private import semmle.code.java.frameworks.Hibernate
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Definitions of sinks in the Hikari Connection Pool library.
3+
*/
4+
5+
import java
6+
import semmle.code.java.dataflow.ExternalFlow
7+
8+
private class SsrfSinkCsv extends SinkModelCsv {
9+
override predicate row(string row) {
10+
row =
11+
[
12+
//"package;type;overrides;name;signature;ext;spec;kind"
13+
"com.zaxxer.hikari;HikariConfig;false;HikariConfig;(Properties);;Argument[0];jdbc-url",
14+
"com.zaxxer.hikari;HikariConfig;false;setJdbcUrl;(String);;Argument[0];jdbc-url"
15+
]
16+
}
17+
}

java/ql/lib/semmle/code/java/frameworks/Jdbc.qll

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,16 @@ private class SqlSinkCsv extends SinkModelCsv {
5252
]
5353
}
5454
}
55+
56+
private class SsrfSinkCsv extends SinkModelCsv {
57+
override predicate row(string row) {
58+
row =
59+
[
60+
//"package;type;overrides;name;signature;ext;spec;kind"
61+
"java.sql;DriverManager;false;getConnection;(String);;Argument[0];jdbc-url",
62+
"java.sql;DriverManager;false;getConnection;(String,Properties);;Argument[0];jdbc-url",
63+
"java.sql;DriverManager;false;getConnection;(String,String,String);;Argument[0];jdbc-url",
64+
"java.sql;Driver;false;connect;(String,Properties);;Argument[0];jdbc-url"
65+
]
66+
}
67+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* Definitions of sinks in the JDBI library.
3+
*/
4+
5+
import java
6+
import semmle.code.java.dataflow.ExternalFlow
7+
8+
private class SsrfSinkCsv extends SinkModelCsv {
9+
override predicate row(string row) {
10+
row =
11+
[
12+
//"package;type;overrides;name;signature;ext;spec;kind"
13+
"org.jdbi.v3.core;Jdbi;false;create;(String);;Argument[0];jdbc-url",
14+
"org.jdbi.v3.core;Jdbi;false;create;(String,Properties);;Argument[0];jdbc-url",
15+
"org.jdbi.v3.core;Jdbi;false;create;(String,String,String);;Argument[0];jdbc-url",
16+
"org.jdbi.v3.core;Jdbi;false;open;(String);;Argument[0];jdbc-url",
17+
"org.jdbi.v3.core;Jdbi;false;open;(String,Properties);;Argument[0];jdbc-url",
18+
"org.jdbi.v3.core;Jdbi;false;open;(String,String,String);;Argument[0];jdbc-url"
19+
]
20+
}
21+
}

java/ql/lib/semmle/code/java/frameworks/SpringJdbc.qll

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,17 @@ private class SqlSinkCsv extends SinkModelCsv {
3737
]
3838
}
3939
}
40+
41+
private class SsrfSinkCsv extends SinkModelCsv {
42+
override predicate row(string row) {
43+
row =
44+
[
45+
//"package;type;overrides;name;signature;ext;spec;kind"
46+
"org.springframework.boot.jdbc;DataSourceBuilder;false;url;(String);;Argument[0];jdbc-url",
47+
"org.springframework.jdbc.datasource;AbstractDriverBasedDataSource;false;setUrl;(String);;Argument[0];jdbc-url",
48+
"org.springframework.jdbc.datasource;DriverManagerDataSource;false;DriverManagerDataSource;(String);;Argument[0];jdbc-url",
49+
"org.springframework.jdbc.datasource;DriverManagerDataSource;false;DriverManagerDataSource;(String,String,String);;Argument[0];jdbc-url",
50+
"org.springframework.jdbc.datasource;DriverManagerDataSource;false;DriverManagerDataSource;(String,Properties);;Argument[0];jdbc-url"
51+
]
52+
}
53+
}

java/ql/lib/semmle/code/java/security/RequestForgery.qll

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import semmle.code.java.frameworks.spring.Spring
77
import semmle.code.java.frameworks.JaxWS
88
import semmle.code.java.frameworks.javase.Http
99
import semmle.code.java.dataflow.DataFlow
10+
import semmle.code.java.frameworks.Properties
1011
private import semmle.code.java.dataflow.StringPrefixes
1112
private import semmle.code.java.dataflow.ExternalFlow
1213

@@ -33,13 +34,31 @@ private class DefaultRequestForgeryAdditionalTaintStep extends RequestForgeryAdd
3334
}
3435
}
3536

37+
private class TypePropertiesRequestForgeryAdditionalTaintStep extends RequestForgeryAdditionalTaintStep {
38+
override predicate propagatesTaint(DataFlow::Node pred, DataFlow::Node succ) {
39+
exists(MethodAccess ma |
40+
// Properties props = new Properties();
41+
// props.setProperty("jdbcUrl", tainted);
42+
// Propagate tainted value to the qualifier `props`
43+
ma.getMethod() instanceof PropertiesSetPropertyMethod and
44+
ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "jdbcUrl" and
45+
pred.asExpr() = ma.getArgument(1) and
46+
succ.asExpr() = ma.getQualifier()
47+
)
48+
}
49+
}
50+
3651
/** A data flow sink for server-side request forgery (SSRF) vulnerabilities. */
3752
abstract class RequestForgerySink extends DataFlow::Node { }
3853

3954
private class UrlOpenSinkAsRequestForgerySink extends RequestForgerySink {
4055
UrlOpenSinkAsRequestForgerySink() { sinkNode(this, "open-url") }
4156
}
4257

58+
private class JdbcUrlSinkAsRequestForgerySink extends RequestForgerySink {
59+
JdbcUrlSinkAsRequestForgerySink() { sinkNode(this, "jdbc-url") }
60+
}
61+
4362
/** A sanitizer for request forgery vulnerabilities. */
4463
abstract class RequestForgerySanitizer extends DataFlow::Node { }
4564

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import javax.servlet.ServletException;
2+
import javax.servlet.http.HttpServlet;
3+
import javax.servlet.http.HttpServletRequest;
4+
import javax.servlet.http.HttpServletResponse;
5+
import java.sql.DriverManager;
6+
import java.sql.Driver;
7+
import java.sql.SQLException;
8+
import java.io.IOException;
9+
import com.zaxxer.hikari.HikariConfig;
10+
import com.zaxxer.hikari.HikariDataSource;
11+
import java.util.*;
12+
import org.springframework.jdbc.datasource.*;
13+
import org.jdbi.v3.core.Jdbi;
14+
import org.springframework.boot.jdbc.DataSourceBuilder;
15+
16+
public class JdbcUrlSSRF extends HttpServlet {
17+
18+
protected void doGet(HttpServletRequest request, HttpServletResponse response)
19+
throws ServletException, IOException {
20+
21+
String jdbcUrl = request.getParameter("jdbcUrl");
22+
Driver driver = new org.postgresql.Driver();
23+
DataSourceBuilder dsBuilder = new DataSourceBuilder();
24+
25+
try {
26+
driver.connect(jdbcUrl, null); // $ SSRF
27+
28+
DriverManager.getConnection(jdbcUrl); // $ SSRF
29+
DriverManager.getConnection(jdbcUrl, "user", "password"); // $ SSRF
30+
DriverManager.getConnection(jdbcUrl, null); // $ SSRF
31+
32+
dsBuilder.url(jdbcUrl); // $ SSRF
33+
}
34+
catch(SQLException e) {}
35+
}
36+
37+
protected void doPost(HttpServletRequest request, HttpServletResponse response)
38+
throws ServletException, IOException {
39+
40+
String jdbcUrl = request.getParameter("jdbcUrl");
41+
HikariConfig config = new HikariConfig();
42+
43+
config.setJdbcUrl(jdbcUrl); // $ SSRF
44+
config.setUsername("database_username");
45+
config.setPassword("database_password");
46+
47+
HikariDataSource ds = new HikariDataSource();
48+
ds.setJdbcUrl(jdbcUrl); // $ SSRF
49+
50+
Properties props = new Properties();
51+
props.setProperty("driverClassName", "org.postgresql.Driver");
52+
props.setProperty("jdbcUrl", jdbcUrl);
53+
54+
HikariConfig config2 = new HikariConfig(props); // $ SSRF
55+
}
56+
57+
protected void doPut(HttpServletRequest request, HttpServletResponse response)
58+
throws ServletException, IOException {
59+
60+
String jdbcUrl = request.getParameter("jdbcUrl");
61+
62+
DriverManagerDataSource dataSource = new DriverManagerDataSource();
63+
64+
dataSource.setDriverClassName("org.postgresql.Driver");
65+
dataSource.setUrl(jdbcUrl); // $ SSRF
66+
67+
DriverManagerDataSource dataSource2 = new DriverManagerDataSource(jdbcUrl); // $ SSRF
68+
dataSource2.setDriverClassName("org.postgresql.Driver");
69+
70+
DriverManagerDataSource dataSource3 = new DriverManagerDataSource(jdbcUrl, "user", "pass"); // $ SSRF
71+
dataSource3.setDriverClassName("org.postgresql.Driver");
72+
73+
DriverManagerDataSource dataSource4 = new DriverManagerDataSource(jdbcUrl, null); // $ SSRF
74+
dataSource4.setDriverClassName("org.postgresql.Driver");
75+
}
76+
77+
protected void doDelete(HttpServletRequest request, HttpServletResponse response)
78+
throws ServletException, IOException {
79+
80+
String jdbcUrl = request.getParameter("jdbcUrl");
81+
82+
Jdbi.create(jdbcUrl); // $ SSRF
83+
Jdbi.create(jdbcUrl, null); // $ SSRF
84+
Jdbi.create(jdbcUrl, "user", "pass"); // $ SSRF
85+
86+
Jdbi.open(jdbcUrl); // $ SSRF
87+
Jdbi.open(jdbcUrl, null); // $ SSRF
88+
Jdbi.open(jdbcUrl, "user", "pass"); // $ SSRF
89+
}
90+
91+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
//semmle-extractor-options: --javac-args -source 11 -target 11 -cp ${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/javax-ws-rs-api-2.1.1:${testdir}/../../../stubs/javax-ws-rs-api-3.0.0:${testdir}/../../../stubs/apache-http-4.4.13/:${testdir}/../../../stubs/servlet-api-2.4/:${testdir}/../../../stubs/projectreactor-3.4.3/
1+
//semmle-extractor-options: --javac-args -source 11 -target 11 -cp ${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/javax-ws-rs-api-2.1.1:${testdir}/../../../stubs/javax-ws-rs-api-3.0.0:${testdir}/../../../stubs/apache-http-4.4.13/:${testdir}/../../../stubs/servlet-api-2.4/:${testdir}/../../../stubs/projectreactor-3.4.3/:${testdir}/../../../stubs/postgresql-42.3.3/:${testdir}/../../../stubs/HikariCP-3.4.5/:${testdir}/../../../stubs/spring-jdbc-5.3.8/:${testdir}/../../../stubs/jdbi3-core-3.27.2/
22

java/ql/test/stubs/HikariCP-3.4.5/com/zaxxer/hikari/HikariConfig.java

Lines changed: 79 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)