Security context
High· 7.5GHSA-p3rc-946h-8cf5 CVE-2022-34175CWE-693CWE-863Published Jun 24, 2022

Unauthorized view fragment access in Jenkins

Research this vulnerability

Research is free — Hunters explains how the bug works, the root-cause code pattern, how the fix addresses it, and how to test whether a target is affected, in chat. Investigate & write exploit is a paid run — the engine reads the advisory and fix commits, then builds and validates a working proof-of-concept exploit with reproduction steps.

Affected versions

2.335 → fixed in 2.356

Details

Jenkins uses the Stapler web framework to render its UI views. These views are frequently composed of several view fragments, enabling plugins to extend existing views with more content. Before [SECURITY-534](https://www.jenkins.io/security/advisory/2019-07-17/#SECURITY-534) was fixed in Jenkins 2.186 and LTS 2.176.2, attackers could in some cases directly access a view fragment containing sensitive information, bypassing any permission checks in the corresponding view. In Jenkins 2.335 through 2.355 (both inclusive), the protection added for SECURITY-534 is disabled for some views. As a result, attackers could in very limited cases directly access a view fragment containing sensitive information, bypassing any permission checks in the corresponding view. As of publication, the Jenkins security team is unaware of any vulnerable view fragment across the Jenkins plugin ecosystem. Jenkins 2.356 restores the protection for affected views. No Jenkins LTS release is affected by this issue, as it was not present in Jenkins 2.332.x and fixed in the 2.346.x line before 2.346.1.

The fix

[SECURITY-2777]

Daniel Beck· Jun 9, 2022, 03:46 AM+69137bd66a43a
core/src/main/java/hudson/Functions.java+0 1
@@ -2360,7 +2360,6 @@ public static String tryGetIconPath(String iconGuess, JellyContext context) {
}
StaplerRequest currentRequest = Stapler.getCurrentRequest();
- currentRequest.getWebApp().getDispatchValidator().allowDispatch(currentRequest, Stapler.getCurrentResponse());
String rootURL = currentRequest.getContextPath();
Icon iconMetadata = tryGetIcon(iconGuess);
String iconSource = null;
test/src/test/java/jenkins/security/Security2777Test.java+50 0
@@ -0,0 +1,50 @@
+package jenkins.security;
+
+import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
+import hudson.model.UnprotectedRootAction;
+import java.io.IOException;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.jvnet.hudson.test.JenkinsRule;
+import org.jvnet.hudson.test.TestExtension;
+
+public class Security2777Test {
+ public static final String ACTION_URL = "security2777";
+
+ @Rule
+ public JenkinsRule j = new JenkinsRule();
+
+ @Test
+ public void testView() throws IOException {
+ final JenkinsRule.WebClient wc = j.createWebClient();
+
+ // no exception on action index page
+ wc.getPage(wc.getContextPath() + ACTION_URL);
+
+ final FailingHttpStatusCodeException ex2 = Assert.assertThrows("no icon, no response", FailingHttpStatusCodeException.class, () -> wc.getPage(wc.getContextPath() + ACTION_URL + "/fragmentWithoutIcon"));
+ Assert.assertEquals("it's 404", 404, ex2.getStatusCode());
+
+ final FailingHttpStatusCodeException ex3 = Assert.assertThrows("icon, still no response", FailingHttpStatusCodeException.class, () -> wc.getPage(wc.getContextPath() + ACTION_URL + "/fragmentWithIcon"));
+ Assert.assertEquals("it's 404", 404, ex3.getStatusCode());
+ }
+
+ @TestExtension
+ public static class ViewHolder implements UnprotectedRootAction {
+
+ @Override
+ public String getIconFileName() {
+ return null;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return null;
+ }
+
+ @Override
+ public String getUrlName() {
+ return ACTION_URL;
+ }
+ }
+}
test/src/test/resources/jenkins/security/Security2777Test/ViewHolder/fragmentWithIcon.jelly+6 0
@@ -0,0 +1,6 @@
+<?jelly escape-by-default='true'?>
+<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout">
+ <j:new var="h" className="hudson.Functions" />
+ <l:icon src="lol" />
+ <h2>Help!</h2>
+</j:jelly>
test/src/test/resources/jenkins/security/Security2777Test/ViewHolder/fragmentWithoutIcon.jelly+5 0
@@ -0,0 +1,5 @@
+<?jelly escape-by-default='true'?>
+<j:jelly xmlns:j="jelly:core">
+ <h2>Help!</h2>
+ <p>I'm just HTML!</p>
+</j:jelly>
test/src/test/resources/jenkins/security/Security2777Test/ViewHolder/index.jelly+8 0
@@ -0,0 +1,8 @@
+<?jelly escape-by-default='true'?>
+<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout">
+ <l:layout>
+ <l:main-panel>
+ <h1>Hello</h1>
+ </l:main-panel>
+ </l:layout>
+</j:jelly>

References