Skip to content

Commit 423ff32

Browse files
authored
Merge pull request github#5384 from luchua-bc/java/insecure-spring-actuator-config
Java: CWE-016 Query to detect insecure configuration of Spring Boot Actuator
2 parents 6d4ddc0 + bb23866 commit 423ff32

File tree

10 files changed

+361
-0
lines changed

10 files changed

+361
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
2+
<qhelp>
3+
<overview>
4+
<p>Spring Boot is a popular framework that facilitates the development of stand-alone applications
5+
and micro services. Spring Boot Actuator helps to expose production-ready support features against
6+
Spring Boot applications.</p>
7+
8+
<p>Endpoints of Spring Boot Actuator allow to monitor and interact with a Spring Boot application.
9+
Exposing unprotected actuator endpoints through configuration files can lead to information disclosure
10+
or even remote code execution vulnerability.</p>
11+
12+
<p>Rather than programmatically permitting endpoint requests or enforcing access control, frequently
13+
developers simply leave management endpoints publicly accessible in the application configuration file
14+
<code>application.properties</code> without enforcing access control through Spring Security.</p>
15+
</overview>
16+
17+
<recommendation>
18+
<p>Declare the Spring Boot Starter Security module in XML configuration or programmatically enforce
19+
security checks on management endpoints using Spring Security. Otherwise accessing management endpoints
20+
on a different HTTP port other than the port that the web application is listening on also helps to
21+
improve the security.</p>
22+
</recommendation>
23+
24+
<example>
25+
<p>The following examples show both 'BAD' and 'GOOD' configurations. In the 'BAD' configuration,
26+
no security module is declared and sensitive management endpoints are exposed. In the 'GOOD' configuration,
27+
security is enforced and only endpoints requiring exposure are exposed.</p>
28+
<sample src="pom_good.xml" />
29+
<sample src="pom_bad.xml" />
30+
<sample src="application.properties" />
31+
</example>
32+
33+
<references>
34+
<li>
35+
Spring Boot documentation:
36+
<a href="https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html">Spring Boot Actuator: Production-ready Features</a>
37+
</li>
38+
<li>
39+
VERACODE Blog:
40+
<a href="https://www.veracode.com/blog/research/exploiting-spring-boot-actuators">Exploiting Spring Boot Actuators</a>
41+
</li>
42+
<li>
43+
HackerOne Report:
44+
<a href="https://hackerone.com/reports/862589">Spring Actuator endpoints publicly available, leading to account takeover</a>
45+
</li>
46+
</references>
47+
</qhelp>
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/**
2+
* @name Insecure Spring Boot Actuator Configuration
3+
* @description Exposed Spring Boot Actuator through configuration files without declarative or procedural
4+
* security enforcement leads to information leak or even remote code execution.
5+
* @kind problem
6+
* @id java/insecure-spring-actuator-config
7+
* @tags security
8+
* external/cwe-016
9+
*/
10+
11+
/*
12+
* Note this query requires properties files to be indexed before it can produce results.
13+
* If creating your own database with the CodeQL CLI, you should run
14+
* `codeql database index-files --language=properties ...`
15+
* If using lgtm.com, you should add `properties_files: true` to the index block of your
16+
* lgtm.yml file (see https://lgtm.com/help/lgtm/java-extraction)
17+
*/
18+
19+
import java
20+
import semmle.code.configfiles.ConfigFiles
21+
import semmle.code.xml.MavenPom
22+
23+
/** The parent node of the `org.springframework.boot` group. */
24+
class SpringBootParent extends Parent {
25+
SpringBootParent() { this.getGroup().getValue() = "org.springframework.boot" }
26+
}
27+
28+
/** Class of Spring Boot dependencies. */
29+
class SpringBootPom extends Pom {
30+
SpringBootPom() { this.getParentElement() instanceof SpringBootParent }
31+
32+
/** Holds if the Spring Boot Actuator module `spring-boot-starter-actuator` is used in the project. */
33+
predicate isSpringBootActuatorUsed() {
34+
this.getADependency().getArtifact().getValue() = "spring-boot-starter-actuator"
35+
}
36+
37+
/**
38+
* Holds if the Spring Boot Security module is used in the project, which brings in other security
39+
* related libraries.
40+
*/
41+
predicate isSpringBootSecurityUsed() {
42+
this.getADependency().getArtifact().getValue() = "spring-boot-starter-security"
43+
}
44+
}
45+
46+
/** The properties file `application.properties`. */
47+
class ApplicationProperties extends ConfigPair {
48+
ApplicationProperties() { this.getFile().getBaseName() = "application.properties" }
49+
}
50+
51+
/** The configuration property `management.security.enabled`. */
52+
class ManagementSecurityConfig extends ApplicationProperties {
53+
ManagementSecurityConfig() { this.getNameElement().getName() = "management.security.enabled" }
54+
55+
/** Gets the whitespace-trimmed value of this property. */
56+
string getValue() { result = this.getValueElement().getValue().trim() }
57+
58+
/** Holds if `management.security.enabled` is set to `false`. */
59+
predicate hasSecurityDisabled() { getValue() = "false" }
60+
61+
/** Holds if `management.security.enabled` is set to `true`. */
62+
predicate hasSecurityEnabled() { getValue() = "true" }
63+
}
64+
65+
/** The configuration property `management.endpoints.web.exposure.include`. */
66+
class ManagementEndPointInclude extends ApplicationProperties {
67+
ManagementEndPointInclude() {
68+
this.getNameElement().getName() = "management.endpoints.web.exposure.include"
69+
}
70+
71+
/** Gets the whitespace-trimmed value of this property. */
72+
string getValue() { result = this.getValueElement().getValue().trim() }
73+
}
74+
75+
/**
76+
* Holds if `ApplicationProperties` ap of a repository managed by `SpringBootPom` pom
77+
* has a vulnerable configuration of Spring Boot Actuator management endpoints.
78+
*/
79+
predicate hasConfidentialEndPointExposed(SpringBootPom pom, ApplicationProperties ap) {
80+
pom.isSpringBootActuatorUsed() and
81+
not pom.isSpringBootSecurityUsed() and
82+
ap.getFile()
83+
.getParentContainer()
84+
.getAbsolutePath()
85+
.matches(pom.getFile().getParentContainer().getAbsolutePath() + "%") and // in the same sub-directory
86+
exists(string springBootVersion | springBootVersion = pom.getParentElement().getVersionString() |
87+
springBootVersion.regexpMatch("1\\.[0-4].*") and // version 1.0, 1.1, ..., 1.4
88+
not exists(ManagementSecurityConfig me |
89+
me.hasSecurityEnabled() and me.getFile() = ap.getFile()
90+
)
91+
or
92+
springBootVersion.matches("1.5%") and // version 1.5
93+
exists(ManagementSecurityConfig me | me.hasSecurityDisabled() and me.getFile() = ap.getFile())
94+
or
95+
springBootVersion.matches("2.%") and //version 2.x
96+
exists(ManagementEndPointInclude mi |
97+
mi.getFile() = ap.getFile() and
98+
(
99+
mi.getValue() = "*" // all endpoints are enabled
100+
or
101+
mi.getValue()
102+
.matches([
103+
"%dump%", "%trace%", "%logfile%", "%shutdown%", "%startup%", "%mappings%", "%env%",
104+
"%beans%", "%sessions%"
105+
]) // confidential endpoints to check although all endpoints apart from '/health' and '/info' are considered sensitive by Spring
106+
)
107+
)
108+
)
109+
}
110+
111+
from SpringBootPom pom, ApplicationProperties ap, Dependency d
112+
where
113+
hasConfidentialEndPointExposed(pom, ap) and
114+
d = pom.getADependency() and
115+
d.getArtifact().getValue() = "spring-boot-starter-actuator"
116+
select d, "Insecure configuration of Spring Boot Actuator exposes sensitive endpoints."
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#management.endpoints.web.base-path=/admin
2+
3+
4+
#### BAD: All management endpoints are accessible ####
5+
# vulnerable configuration (spring boot 1.0 - 1.4): exposes actuators by default
6+
7+
# vulnerable configuration (spring boot 1.5+): requires value false to expose sensitive actuators
8+
management.security.enabled=false
9+
10+
# vulnerable configuration (spring boot 2+): exposes health and info only by default, here overridden to expose everything
11+
management.endpoints.web.exposure.include=*
12+
13+
14+
#### GOOD: All management endpoints have access control ####
15+
# safe configuration (spring boot 1.0 - 1.4): exposes actuators by default
16+
management.security.enabled=true
17+
18+
# safe configuration (spring boot 1.5+): requires value false to expose sensitive actuators
19+
management.security.enabled=true
20+
21+
# safe configuration (spring boot 2+): exposes health and info only by default, here overridden to expose one additional endpoint which we assume is intentional and safe.
22+
management.endpoints.web.exposure.include=beans,info,health
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>spring-boot-actuator-app</groupId>
8+
<artifactId>spring-boot-actuator-app</artifactId>
9+
<version>1.0-SNAPSHOT</version>
10+
11+
<properties>
12+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
13+
<maven.compiler.source>1.8</maven.compiler.source>
14+
<maven.compiler.target>1.8</maven.compiler.target>
15+
</properties>
16+
17+
<parent>
18+
<groupId>org.springframework.boot</groupId>
19+
<artifactId>spring-boot-starter-parent</artifactId>
20+
<version>2.3.8.RELEASE</version>
21+
<relativePath/>
22+
</parent>
23+
24+
<dependencies>
25+
<dependency>
26+
<groupId>org.springframework.boot</groupId>
27+
<artifactId>spring-boot-starter-web</artifactId>
28+
</dependency>
29+
<dependency>
30+
<groupId>org.springframework.boot</groupId>
31+
<artifactId>spring-boot-starter-actuator</artifactId>
32+
</dependency>
33+
<dependency>
34+
<groupId>org.springframework.boot</groupId>
35+
<artifactId>spring-boot-devtools</artifactId>
36+
</dependency>
37+
38+
<!-- BAD: No Spring Security enabled -->
39+
<!-- dependency>
40+
<groupId>org.springframework.boot</groupId>
41+
<artifactId>spring-boot-starter-security</artifactId>
42+
</dependency -->
43+
44+
<dependency>
45+
<groupId>org.springframework.boot</groupId>
46+
<artifactId>spring-boot-test</artifactId>
47+
</dependency>
48+
</dependencies>
49+
50+
</project>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>spring-boot-actuator-app</groupId>
8+
<artifactId>spring-boot-actuator-app</artifactId>
9+
<version>1.0-SNAPSHOT</version>
10+
11+
<properties>
12+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
13+
<maven.compiler.source>1.8</maven.compiler.source>
14+
<maven.compiler.target>1.8</maven.compiler.target>
15+
</properties>
16+
17+
<parent>
18+
<groupId>org.springframework.boot</groupId>
19+
<artifactId>spring-boot-starter-parent</artifactId>
20+
<version>2.3.8.RELEASE</version>
21+
<relativePath/>
22+
</parent>
23+
24+
<dependencies>
25+
<dependency>
26+
<groupId>org.springframework.boot</groupId>
27+
<artifactId>spring-boot-starter-web</artifactId>
28+
</dependency>
29+
<dependency>
30+
<groupId>org.springframework.boot</groupId>
31+
<artifactId>spring-boot-starter-actuator</artifactId>
32+
</dependency>
33+
<dependency>
34+
<groupId>org.springframework.boot</groupId>
35+
<artifactId>spring-boot-devtools</artifactId>
36+
</dependency>
37+
38+
<!-- GOOD: Enable Spring Security -->
39+
<dependency>
40+
<groupId>org.springframework.boot</groupId>
41+
<artifactId>spring-boot-starter-security</artifactId>
42+
</dependency>
43+
44+
<dependency>
45+
<groupId>org.springframework.boot</groupId>
46+
<artifactId>spring-boot-test</artifactId>
47+
</dependency>
48+
</dependencies>
49+
50+
</project>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| pom.xml:29:9:32:22 | dependency | Insecure configuration of Spring Boot Actuator exposes sensitive endpoints. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import org.springframework.stereotype.Controller;
2+
import org.springframework.web.bind.annotation.RequestParam;
3+
import org.springframework.web.bind.annotation.RequestMapping;
4+
5+
@Controller
6+
public class SensitiveInfo {
7+
@RequestMapping
8+
public void handleLogin(@RequestParam String username, @RequestParam String password) throws Exception {
9+
if (!username.equals("") && password.equals("")) {
10+
//Blank processing
11+
}
12+
}
13+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#management.endpoints.web.base-path=/admin
2+
3+
# vulnerable configuration (spring boot 1.0 - 1.4): exposes actuators by default
4+
5+
# vulnerable configuration (spring boot 1.5+): requires value false to expose sensitive actuators
6+
management.security.enabled=false
7+
8+
# vulnerable configuration (spring boot 2+): exposes health and info only by default, here overridden to expose everything
9+
management.endpoints.web.exposure.include=*
10+
management.endpoints.web.exposure.exclude=beans
11+
12+
management.endpoint.shutdown.enabled=true
13+
14+
management.endpoint.health.show-details=when_authorized
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>spring-boot-actuator-app</groupId>
8+
<artifactId>spring-boot-actuator-app</artifactId>
9+
<version>1.0-SNAPSHOT</version>
10+
11+
<properties>
12+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
13+
<maven.compiler.source>1.8</maven.compiler.source>
14+
<maven.compiler.target>1.8</maven.compiler.target>
15+
</properties>
16+
17+
<parent>
18+
<groupId>org.springframework.boot</groupId>
19+
<artifactId>spring-boot-starter-parent</artifactId>
20+
<version>2.3.8.RELEASE</version>
21+
<relativePath/>
22+
</parent>
23+
24+
<dependencies>
25+
<dependency>
26+
<groupId>org.springframework.boot</groupId>
27+
<artifactId>spring-boot-starter-web</artifactId>
28+
</dependency>
29+
<dependency>
30+
<groupId>org.springframework.boot</groupId>
31+
<artifactId>spring-boot-starter-actuator</artifactId>
32+
</dependency>
33+
<dependency>
34+
<groupId>org.springframework.boot</groupId>
35+
<artifactId>spring-boot-devtools</artifactId>
36+
</dependency>
37+
<!-- dependency>
38+
<groupId>org.springframework.boot</groupId>
39+
<artifactId>spring-boot-starter-security</artifactId>
40+
</dependency -->
41+
<dependency>
42+
<groupId>org.springframework.boot</groupId>
43+
<artifactId>spring-boot-test</artifactId>
44+
</dependency>
45+
</dependencies>
46+
47+
</project>

0 commit comments

Comments
 (0)