Skip to content

Commit 3e3162c

Browse files
committed
add jenkins blog
1 parent 84805c1 commit 3e3162c

File tree

3 files changed

+208
-0
lines changed

3 files changed

+208
-0
lines changed
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
---
2+
title: "Excessive Expansion: Uncovering Critical Security Vulnerabilities in Jenkins"
3+
date: 2024-01-24
4+
tags:
5+
- "java"
6+
- "rce"
7+
- "jenkins"
8+
- "arbitrary file read"
9+
advisory: false
10+
origin: https://www.sonarsource.com/blog/excessive-expansion-uncovering-critical-security-vulnerabilities-in-jenkins/
11+
cves:
12+
- "CVE-2024-23897"
13+
- "CVE-2024-23898"
14+
---
15+
# Key Information
16+
* Sonar’s Vulnerability Research Team has discovered security vulnerabilities in Jenkins, the leading open-source Continuous Integration and Continuous Deployment (CI/CD) software.
17+
* The discovered Critical vulnerability tracked as CVE-2024-23897 allows unauthenticated attackers to read a limited amount of arbitrary files’ data, and "read-only" authorized attackers to an entire arbitrary file from Jenkins’ server.
18+
* Attackers could leverage this vulnerability, by reading Jenkins secrets, to escalate privileges to admin and eventually execute arbitrary code on the server.
19+
* The discovered High severity, cross-site WebSocket hijacking (CSWSH), vulnerability tracked as CVE-2024-23898, allows an attacker to execute arbitrary CLI commands by manipulating a victim to click on a link.
20+
* The vulnerabilities were fixed in Jenkins versions 2.442, and LTS 2.426.3.
21+
22+
Jenkins is the leading open-source automation server widely used for building, deploying, and automating software projects. Originally developed as Hudson, Jenkins has evolved into a powerful tool for continuous integration and continuous delivery (CI/CD). It enables developers to automate various aspects of the software development lifecycle, including building, testing, and deploying applications. With a market share of approximately [44% in 2023](https://cd.foundation/announcement/2023/08/29/jenkins-project-growth/), the popularity of Jenkins is evident. This means the potential impact of security vulnerabilities in Jenkins is large.
23+
24+
# Vulnerabilities Impact
25+
Unauthenticated attackers can read the first few lines of arbitrary files from the server, while read-only authorized attackers can read the entire file. This could ultimately lead to the execution of arbitrary code in some cases (CVE-2024-23897). If one of the following conditions is met, even unauthenticated users have at least read permission:
26+
* Legacy mode authorization is enabled.
27+
* Configuration “Allow anonymous read access” is checked in the “logged-in users can do anything” authorization mode.
28+
* The signup feature is enabled.
29+
30+
The second vulnerability (CVE-2024-23898) resides within the WebSocket CLI feature, which lacks an origin check, allowing Cross-Site WebSocket Hijacking (CSWSH). This vulnerability might be exploited by sending a malicious link to a victim. Certain modern web browsers implement a “[lax by default](https://caniuse.com/mdn-http_headers_set-cookie_samesite_lax_default)” policy, which serves as a potential safeguard against this vulnerability. Nonetheless, given that some widely used browsers like Safari and Firefox do not strictly enforce this policy, and considering the associated risks of potential [bypass](https://portswigger.net/web-security/csrf/bypassing-samesite-restrictions#bypassing-samesite-lax-restrictions-with-newly-issued-cookies) techniques or users using outdated browsers, the severity classification for this vulnerability is High.
31+
<iframe width="736" height="414" src="https://www.youtube.com/embed/ucs-XF5X3bE" title="Excessive Expansion: Uncovering Critical Security Vulnerabilities in Jenkins" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
32+
33+
# Technical Details
34+
In this section of the blog, we will explore our findings taking a deeper dive into the code, to understand the vulnerabilities and how an attacker could exploit them. During the Jenkins security team’s triaging of our report, they found further ways to exploit the first vulnerability (CVE-2024-23897) using an unauthenticated user. The following "Technical Details" covers the attack scenario of a read-only capable attacker.
35+
36+
# Background
37+
Jenkins provides multiple ways of authorization, the unsafe *“anyone can do anything”*, the *“legacy”* permissions, and *“logged-in users can do anything”*. The latter authorization method allows the option for anonymous read access and gives read permission to anyone, which is also the case in the *legacy* mode.
38+
39+
<img src="/img/blogs/jenkins/image_1.webp" style="width: 100%;"/>
40+
41+
On top of that, there is also the not recommended option to *“Allow users to sign up”*, which makes everyone at least read-only capable.
42+
43+
According to the [official documentation](https://www.jenkins.io/doc/book/security/access-control/permissions/#overall-read), read-only access allows users to:
44+
45+
* Access the basic Jenkins API and the API of any object they have access to.
46+
* Access the people directory listing user accounts and known committer identities of anyone involved in visible projects.
47+
* List and view all agents configured in Jenkins and access their summary pages.
48+
49+
On the other hand, [administrators](https://www.jenkins.io/doc/book/security/access-control/permissions/#administer) can pretty much do everything on a Jenkins instance. From an attacker's point of view, admins can run arbitrary code on a Jenkins server.
50+
51+
# Jenkins-CLI Feature Background
52+
53+
[Jenkins-CLI](https://www.jenkins.io/doc/book/managing/cli/) provides users with a built-in command line interface to execute custom commands that are implemented in the [hudson/cli](https://github.com/jenkinsci/jenkins/tree/master/core/src/main/java/hudson/cli) directory of the Jenkins Git repository.
54+
55+
Aside from the common ways of invoking a command, using `jenkins-cli.jar` (which utilizes web sockets) or SSH, we found out that there is an additional option by sending two POST requests to `http://jenkins/cli?remoting=false`.
56+
57+
When [Stapler](https://github.com/jenkinsci/stapler) (Jenkins' component that correlates a method to an endpoint) is [getting](https://github.com/jenkinsci/stapler/blob/ea4fc6ed8cd1b5eca6b4ce80b35654da9376e2bc/core/src/main/java/org/kohsuke/stapler/Stapler.java#L725) the relevant method of the *“/cli”* path, the endpoint will throw a [PlainCliEndpointResponse()](https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/cli/CLIAction.java#L195) exception, which will end up in this [generateResponse](https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/jenkins/util/FullDuplexHttpService.java#L166) function:
58+
59+
```java
60+
public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException {
61+
try {
62+
UUID uuid = UUID.fromString(req.getHeader("Session"));
63+
//...
64+
if (req.getHeader("Side").equals("download")) {
65+
FullDuplexHttpService service = createService(req, uuid);
66+
//...
67+
try {
68+
service.download(req, rsp);
69+
}
70+
//...
71+
} else {
72+
FullDuplexHttpService service = services.get(uuid);
73+
//...
74+
try {
75+
service.upload(req, rsp);
76+
}
77+
//...
78+
}
79+
}
80+
```
81+
82+
This function requires a downloader and uploader. The downloader returns the command’s response, and the uploader invokes a specified command from the body of the request. Jenkins connects them (downloader and uploader) using the UUID from the `​​Session` header.
83+
84+
# Data Leak Vulnerability (CVE-2024-23897)
85+
When invoking a CLI command with arguments, we have noticed that Jenkins uses [args4j’s](https://github.com/kohsuke/args4j) [parseArgument](https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/cli/CLICommand.java#L248), which [calls](https://github.com/kohsuke/args4j/blob/master/args4j/src/org/kohsuke/args4j/CmdLineParser.java#L479) [expandAtFiles](https://github.com/kohsuke/args4j/blob/master/args4j/src/org/kohsuke/args4j/CmdLineParser.java#L548):
86+
87+
```java
88+
private String[] expandAtFiles(String args[]) throws CmdLineException {
89+
List<String> result = new ArrayList<String>();
90+
for (String arg : args) {
91+
if (arg.startsWith("@")) {
92+
File file = new File(arg.substring(1));
93+
if (!file.exists())
94+
throw new CmdLineException(this,Messages.NO_SUCH_FILE,file.getPath());
95+
try {
96+
result.addAll(readAllLines(file));
97+
} catch (IOException ex) {
98+
throw new CmdLineException(this, "Failed to parse "+file,ex);
99+
}
100+
} else {
101+
result.add(arg);
102+
}
103+
}
104+
return result.toArray(new String[result.size()]);
105+
}
106+
```
107+
The function checks if the argument starts with the `@` character, and if so, it reads the file in the path after the `@` and expands a new argument for each line.
108+
109+
<img src="/img/blogs/jenkins/image_2.webp" style="width: 100%;"/>
110+
111+
This means that if an attacker can control an argument, they can expand it to an arbitrary number of ones from an arbitrary file on the Jenkins instance.
112+
113+
One way an attacker could leverage this is to find a command that takes an arbitrary number of arguments and displays these back to the user. Since the arguments are populated from the contents of the file, an attacker could leak the file contents this way. We found the command [connect-to-node](https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/cli/ConnectNodeCommand.java) to be a good candidate: it receives a [list of strings as an argument](https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/cli/ConnectNodeCommand.java#L46) and tries to connect to each one. If it fails, an error message is generated with the name of the failed connected node.
114+
115+
```java
116+
public class ConnectNodeCommand extends CLICommand {
117+
//...
118+
@Argument(metaVar = "NAME", usage = "Agent name, or empty string for built-in node; comma-separated list is supported", required = true, multiValued = true)
119+
private List<String> nodes;
120+
//...
121+
122+
@Override
123+
protected int run() throws Exception {
124+
//...
125+
for (String node_s : hs) {
126+
try {
127+
Computer computer = Computer.resolveForCLI(node_s);
128+
computer.cliConnect(force);
129+
} catch (Exception e) {
130+
//...
131+
final String errorMsg = node_s + ": " + e.getMessage();
132+
stderr.println(errorMsg);
133+
//...
134+
}
135+
}
136+
//...
137+
}
138+
}
139+
```
140+
This [connect-to-node](https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/cli/ConnectNodeCommand.java) command would usually require the CONNECT permission, which is verified in the [cliConnect](https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/model/Computer.java#L480) function. But since the exception is thrown before the permission check in the [resolveForCLI](https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/model/Computer.java#L1661) function, the command actually doesn’t require any authorizations apart from the initial [read-only verification](https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/cli/CLICommand.java#L246).
141+
142+
Achieving code execution from arbitrary file read is dependent on the context. Some potentially interesting files for attackers could be:
143+
* SSH keys
144+
* /etc/passwd, /etc/shadow
145+
* Project secrets and credentials (refer to Jenkins' [advisory](https://www.jenkins.io/security/advisory/2024-01-24/) for more information)
146+
* Source code, build artifacts
147+
* and more…
148+
149+
### Binary Files Reading Limitations
150+
When a file is read, the process's default character encoding is used, which is UTF-8 for most deployments. Because of this, any invalid UTF-8 sequence (statistically almost 50% of all bytes, assuming an equal distribution) would be replaced by the sequence `0xef 0xbf 0xbd` and cause data loss.
151+
Some other encodings (such as Windows-1252, commonly used by instances running on Windows) would make it more feasible to exfiltrate binary data.
152+
153+
# CSWSH Vulnerability (CVE-2024-23898)
154+
As mentioned earlier, one of the ways to invoke the [Jenkins-CLI](https://www.jenkins.io/doc/book/managing/cli/) commands is by web sockets (which is the implementation of `jenkins-cli.jar`).
155+
156+
It is known that browsers don’t enforce SOP and CORS policies on WebSockets: “Cross-origin restrictions imposed by SOP and CORS policies do not apply to WebSockets because those restrictions are placed on HTTP responses while WebSockets work over WS(WebSocket) or WSS(WebSocketSecure) protocols.” ([source](https://dev.to/pssingh21/websockets-bypassing-sop-cors-5ajm)).
157+
158+
159+
160+
Since there is no Jenkins-crumb (CSRF token) nor Origin header check in the web sockets requests, any website can use [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) to invoke Jenkins-CLI commands with the victim's identity, in a similar fashion to CSRF vulnerabilities.
161+
162+
# Patch
163+
The Jenkins security team patched CVE-2024-23897 by adding a secure configuration, which disables the “[expandAtFiles](https://github.com/kohsuke/args4j/blob/master/args4j/src/org/kohsuke/args4j/CmdLineParser.java#L478)” feature.
164+
165+
```diff
166+
+ public static boolean ALLOW_AT_SYNTAX = SystemProperties.getBoolean(CLICommand.class.getName() + ".allowAtSyntax");
167+
//...
168+
- return new CmdLineParser(this);
169+
+ ParserProperties properties = ParserProperties.defaults().withAtSyntax(ALLOW_AT_SYNTAX);
170+
+ return new CmdLineParser(this, properties);
171+
```
172+
And CVE-2024-23898 was patched by adding an origin verification to the WebSocket endpoint (The `ALLOW` parameter serves as a toggle, granting administrators the ability to override the updated default behavior. Giving the option to consistently permit or deny access to the WS CLI, irrespective of the Origin):
173+
```diff
174+
public HttpResponse doWs(StaplerRequest req) {
175+
if (!WebSockets.isSupported()) {
176+
return HttpResponses.notFound();
177+
}
178+
+ if (ALLOW == null) {
179+
+ final String actualOrigin = req.getHeader("Origin");
180+
+ final String expectedOrigin = StringUtils.removeEnd(StringUtils.removeEnd(+Jenkins.get().getRootUrlFromRequest(), "/"), req.getContextPath());
181+
+
182+
+ if (actualOrigin == null || !actualOrigin.equals(expectedOrigin)) {
183+
+ LOGGER.log(Level.FINE, () -> "Rejecting origin: " + actualOrigin + "; expected was from request: " + +expectedOrigin);
184+
+ return HttpResponses.forbidden();
185+
+ }
186+
+ } else if (!ALLOW) {
187+
+ return HttpResponses.forbidden();
188+
+ }
189+
Authentication authentication = Jenkins.getAuthentication2();
190+
191+
```
192+
193+
# Timeline
194+
| Date | Action |
195+
| -------- | ------- |
196+
| 2023/11/13 | We reported all issues to the Jenkins Security team |
197+
| 2023/11/13 | Maintainers acknowledged the report |
198+
| 2023/11/24 | Maintainers confirmed the issues |
199+
| 2023/12/12 | We helped the vendor verify the fix |
200+
| 2024/01/10 | Maintainers updated us on other attack scenarios and the classification of Critical and High for our findings |
201+
| 2024/01/24 | Maintainers assigned CVEs, and released [advisory](https://www.jenkins.io/security/advisory/2024-01-24/) and patch versions 2.442, and LTS 2.426.3. |
202+
203+
# Summary
204+
In this blog, we uncovered two vulnerabilities on Jenkins, the first one leverages the “[expandAtFiles](https://github.com/kohsuke/args4j/blob/master/args4j/src/org/kohsuke/args4j/CmdLineParser.java#L478)” functionality to read arbitrary files and eventually execute arbitrary code on the server. The second finding has the potential to execute arbitrary commands as the victim, by manipulating them to visit a malicious link.
205+
206+
At Sonar, we emphasize the importance of Clean Code principles. Doing so creates software characterized by clarity, maintainability, and comprehensibility. These attributes not only help the identification and resolution of vulnerabilities throughout the development process but also lower the likelihood of introducing security weaknesses that malicious actors might exploit.
207+
208+
Lastly, we would like to give huge kudos to the Jenkins team, who quickly and professionally assessed our findings, maintained great communication throughout the disclosure process, and provided a comprehensive fix. Thank you!

source/img/blogs/jenkins/image_1.webp

3.9 KB
Binary file not shown.

source/img/blogs/jenkins/image_2.webp

29 KB
Binary file not shown.

0 commit comments

Comments
 (0)