Jenkins exposes other users' timezone and view names to users with Overall/Read permission
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
0 → fixed in 2.555.32.556 → fixed in 2.568
Details
Missing permission checks in Jenkins 2.567 and earlier, LTS 2.555.2 and earlier allow attackers with Overall/Read permission to determine other users' configured timezone and to enumerate view names of other users' "My Views".
The fix
[SECURITY-3713]
core/src/main/java/hudson/model/MyViewsProperty.java+2 −0
@@ -287,6 +287,8 @@ public ListBoxModel doFillPrimaryViewNameItems(@AncestorInPath User user) throwsListBoxModel items = new ListBoxModel();user = user == null ? User.current() : user;if (user != null) {+user.checkPermission(Jenkins.ADMINISTER);+MyViewsProperty property = user.getProperty(MyViewsProperty.class);if (property == null) {property = new MyViewsProperty();
core/src/main/java/hudson/model/TimeZoneProperty.java+4 −0
@@ -13,6 +13,7 @@import java.util.TimeZone;import java.util.logging.Level;import java.util.logging.Logger;+import jenkins.model.Jenkins;import org.jenkinsci.Symbol;import org.kohsuke.accmod.Restricted;import org.kohsuke.accmod.restrictions.NoExternalUse;@@ -88,6 +89,9 @@ public boolean isEnabled() {}public ListBoxModel doFillTimeZoneNameItems(@AncestorInPath User user) {+if (user != null) {+user.checkPermission(Jenkins.ADMINISTER);+}String userTimezone = user != null ? forUser(user) : forCurrentUser();ListBoxModel items = new ListBoxModel();items.add(Messages.TimeZoneProperty_DisplayDefaultTimeZone(), "");
test/src/test/java/hudson/model/Security3713Test.java+202 −0
@@ -0,0 +1,202 @@+package hudson.model;++import static org.hamcrest.MatcherAssert.assertThat;+import static org.hamcrest.Matchers.containsString;+import static org.hamcrest.Matchers.is;++import java.net.URL;+import jenkins.model.Jenkins;+import org.htmlunit.HttpMethod;+import org.htmlunit.Page;+import org.htmlunit.WebRequest;+import org.junit.jupiter.api.BeforeEach;+import org.junit.jupiter.api.Test;+import org.jvnet.hudson.test.Issue;+import org.jvnet.hudson.test.JenkinsRule;+import org.jvnet.hudson.test.MockAuthorizationStrategy;+import org.jvnet.hudson.test.junit.jupiter.WithJenkins;++@WithJenkins+class Security3713Test {++private static final String TIME_ZONE = "Antarctica/Troll";++private JenkinsRule j;++@BeforeEach+void setUp(JenkinsRule rule) {+j = rule;+}++@Test+@Issue("SECURITY-3713")+void regularUserCannotAccessOtherUserViewNames() throws Exception {+j.jenkins.setCrumbIssuer(null);+j.jenkins.setSecurityRealm(j.createDummySecurityRealm());+User alice = User.getOrCreateByIdOrFullName("alice");++MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();+authStrategy.grant(Jenkins.READ).everywhere().to("alice", "bob");+j.jenkins.setAuthorizationStrategy(authStrategy);++MyViewsProperty aliceProperty = new MyViewsProperty(null);+MyView customView = new MyView("Alice's View", aliceProperty);+alice.addProperty(aliceProperty);+aliceProperty.setUser(alice);+aliceProperty.addView(customView);+alice.save();++try (JenkinsRule.WebClient wc = j.createWebClient().withThrowExceptionOnFailingStatusCode(false)) {+wc.login("bob");++URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + MyViewsProperty.class.getName() + "/fillPrimaryViewNameItems");+WebRequest request = new WebRequest(url, HttpMethod.POST);+Page page = wc.getPage(request);++assertThat(page.getWebResponse().getStatusCode(), is(403));+}+}++@Test+@Issue("SECURITY-3713")+void adminCanAccessOtherUserViewNames() throws Exception {+j.jenkins.setCrumbIssuer(null);+j.jenkins.setSecurityRealm(j.createDummySecurityRealm());+User alice = User.getOrCreateByIdOrFullName("alice");++MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();+authStrategy.grant(Jenkins.READ).everywhere().to("alice", "bob");+authStrategy.grant(Jenkins.ADMINISTER).everywhere().to("bob");+j.jenkins.setAuthorizationStrategy(authStrategy);++MyViewsProperty aliceProperty = new MyViewsProperty(null);+MyView customView = new MyView("Alice's View", aliceProperty);+alice.addProperty(aliceProperty);+aliceProperty.setUser(alice);+aliceProperty.addView(customView);+alice.save();++try (JenkinsRule.WebClient wc = j.createWebClient()) {+wc.login("bob");++URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + MyViewsProperty.class.getName() + "/fillPrimaryViewNameItems");+WebRequest request = new WebRequest(url, HttpMethod.POST);+Page page = wc.getPage(request);+String body = page.getWebResponse().getContentAsString();++assertThat(page.getWebResponse().getStatusCode(), is(200));+assertThat(body, containsString("Alice's View"));+}+}++@Test+@Issue("SECURITY-3713")+void sameUserCanAccessOwnViewNames() throws Exception {+j.jenkins.setCrumbIssuer(null);+j.jenkins.setSecurityRealm(j.createDummySecurityRealm());+User alice = User.getOrCreateByIdOrFullName("alice");++MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();+authStrategy.grant(Jenkins.READ).everywhere().to("alice");+j.jenkins.setAuthorizationStrategy(authStrategy);++MyViewsProperty aliceProperty = new MyViewsProperty(null);+MyView customView = new MyView("Alice's View", aliceProperty);+alice.addProperty(aliceProperty);+aliceProperty.setUser(alice);+aliceProperty.addView(customView);+alice.save();++try (JenkinsRule.WebClient wc = j.createWebClient()) {+wc.login("alice");++URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + MyViewsProperty.class.getName() + "/fillPrimaryViewNameItems");+WebRequest request = new WebRequest(url, HttpMethod.POST);+Page page = wc.getPage(request);+String body = page.getWebResponse().getContentAsString();++assertThat(page.getWebResponse().getStatusCode(), is(200));+assertThat(body, containsString("Alice's View"));+}+}++@Test+@Issue("SECURITY-3713")+void regularUserCannotAccessOtherUserTimeZone() throws Exception {+j.jenkins.setCrumbIssuer(null);+j.jenkins.setSecurityRealm(j.createDummySecurityRealm());+User alice = User.getOrCreateByIdOrFullName("alice");++MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();+authStrategy.grant(Jenkins.READ).everywhere().to("alice", "bob");+j.jenkins.setAuthorizationStrategy(authStrategy);++alice.addProperty(new TimeZoneProperty(TIME_ZONE));+alice.save();++try (JenkinsRule.WebClient wc = j.createWebClient().withThrowExceptionOnFailingStatusCode(false)) {+wc.login("bob");++URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + TimeZoneProperty.class.getName() + "/fillTimeZoneNameItems");+WebRequest request = new WebRequest(url, HttpMethod.POST);+Page page = wc.getPage(request);++assertThat(page.getWebResponse().getStatusCode(), is(403));+}+}++@Test+@Issue("SECURITY-3713")+void adminCanAccessOtherUserTimeZone() throws Exception {+j.jenkins.setCrumbIssuer(null);+j.jenkins.setSecurityRealm(j.createDummySecurityRealm());+User alice = User.getOrCreateByIdOrFullName("alice");++MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();+authStrategy.grant(Jenkins.READ).everywhere().to("alice", "bob");+authStrategy.grant(Jenkins.ADMINISTER).everywhere().to("bob");+j.jenkins.setAuthorizationStrategy(authStrategy);++alice.addProperty(new TimeZoneProperty(TIME_ZONE));+alice.save();++try (JenkinsRule.WebClient wc = j.createWebClient()) {+wc.login("bob");++URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + TimeZoneProperty.class.getName() + "/fillTimeZoneNameItems");+WebRequest request = new WebRequest(url, HttpMethod.POST);+Page page = wc.getPage(request);+String body = page.getWebResponse().getContentAsString();++assertThat(page.getWebResponse().getStatusCode(), is(200));+assertThat(body, containsString("\"name\":\"" + TIME_ZONE + "\",\"selected\":true"));+}+}++@Test+@Issue("SECURITY-3713")+void sameUserCanAccessOwnTimeZone() throws Exception {+j.jenkins.setCrumbIssuer(null);+j.jenkins.setSecurityRealm(j.createDummySecurityRealm());+User alice = User.getOrCreateByIdOrFullName("alice");++MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();+authStrategy.grant(Jenkins.READ).everywhere().to("alice");+j.jenkins.setAuthorizationStrategy(authStrategy);++alice.addProperty(new TimeZoneProperty(TIME_ZONE));+alice.save();++try (JenkinsRule.WebClient wc = j.createWebClient()) {+wc.login("alice");++URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + TimeZoneProperty.class.getName() + "/fillTimeZoneNameItems");+WebRequest request = new WebRequest(url, HttpMethod.POST);+Page page = wc.getPage(request);+String body = page.getWebResponse().getContentAsString();++assertThat(page.getWebResponse().getStatusCode(), is(200));+assertThat(body, containsString("\"name\":\"" + TIME_ZONE + "\",\"selected\":true"));+}+}+}
[SECURITY-3713]
core/src/main/java/hudson/model/MyViewsProperty.java+2 −0
@@ -287,6 +287,8 @@ public ListBoxModel doFillPrimaryViewNameItems(@AncestorInPath User user) throwsListBoxModel items = new ListBoxModel();user = user == null ? User.current() : user;if (user != null) {+user.checkPermission(Jenkins.ADMINISTER);+MyViewsProperty property = user.getProperty(MyViewsProperty.class);if (property == null) {property = new MyViewsProperty();
core/src/main/java/hudson/model/TimeZoneProperty.java+4 −0
@@ -13,6 +13,7 @@import java.util.TimeZone;import java.util.logging.Level;import java.util.logging.Logger;+import jenkins.model.Jenkins;import org.jenkinsci.Symbol;import org.kohsuke.accmod.Restricted;import org.kohsuke.accmod.restrictions.NoExternalUse;@@ -88,6 +89,9 @@ public boolean isEnabled() {}public ListBoxModel doFillTimeZoneNameItems(@AncestorInPath User user) {+if (user != null) {+user.checkPermission(Jenkins.ADMINISTER);+}String userTimezone = user != null ? forUser(user) : forCurrentUser();ListBoxModel items = new ListBoxModel();items.add(Messages.TimeZoneProperty_DisplayDefaultTimeZone(), "");
test/src/test/java/hudson/model/Security3713Test.java+202 −0
@@ -0,0 +1,202 @@+package hudson.model;++import static org.hamcrest.MatcherAssert.assertThat;+import static org.hamcrest.Matchers.containsString;+import static org.hamcrest.Matchers.is;++import java.net.URL;+import jenkins.model.Jenkins;+import org.htmlunit.HttpMethod;+import org.htmlunit.Page;+import org.htmlunit.WebRequest;+import org.junit.jupiter.api.BeforeEach;+import org.junit.jupiter.api.Test;+import org.jvnet.hudson.test.Issue;+import org.jvnet.hudson.test.JenkinsRule;+import org.jvnet.hudson.test.MockAuthorizationStrategy;+import org.jvnet.hudson.test.junit.jupiter.WithJenkins;++@WithJenkins+class Security3713Test {++private static final String TIME_ZONE = "Antarctica/Troll";++private JenkinsRule j;++@BeforeEach+void setUp(JenkinsRule rule) {+j = rule;+}++@Test+@Issue("SECURITY-3713")+void regularUserCannotAccessOtherUserViewNames() throws Exception {+j.jenkins.setCrumbIssuer(null);+j.jenkins.setSecurityRealm(j.createDummySecurityRealm());+User alice = User.getOrCreateByIdOrFullName("alice");++MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();+authStrategy.grant(Jenkins.READ).everywhere().to("alice", "bob");+j.jenkins.setAuthorizationStrategy(authStrategy);++MyViewsProperty aliceProperty = new MyViewsProperty(null);+MyView customView = new MyView("Alice's View", aliceProperty);+alice.addProperty(aliceProperty);+aliceProperty.setUser(alice);+aliceProperty.addView(customView);+alice.save();++try (JenkinsRule.WebClient wc = j.createWebClient().withThrowExceptionOnFailingStatusCode(false)) {+wc.login("bob");++URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + MyViewsProperty.class.getName() + "/fillPrimaryViewNameItems");+WebRequest request = new WebRequest(url, HttpMethod.POST);+Page page = wc.getPage(request);++assertThat(page.getWebResponse().getStatusCode(), is(403));+}+}++@Test+@Issue("SECURITY-3713")+void adminCanAccessOtherUserViewNames() throws Exception {+j.jenkins.setCrumbIssuer(null);+j.jenkins.setSecurityRealm(j.createDummySecurityRealm());+User alice = User.getOrCreateByIdOrFullName("alice");++MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();+authStrategy.grant(Jenkins.READ).everywhere().to("alice", "bob");+authStrategy.grant(Jenkins.ADMINISTER).everywhere().to("bob");+j.jenkins.setAuthorizationStrategy(authStrategy);++MyViewsProperty aliceProperty = new MyViewsProperty(null);+MyView customView = new MyView("Alice's View", aliceProperty);+alice.addProperty(aliceProperty);+aliceProperty.setUser(alice);+aliceProperty.addView(customView);+alice.save();++try (JenkinsRule.WebClient wc = j.createWebClient()) {+wc.login("bob");++URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + MyViewsProperty.class.getName() + "/fillPrimaryViewNameItems");+WebRequest request = new WebRequest(url, HttpMethod.POST);+Page page = wc.getPage(request);+String body = page.getWebResponse().getContentAsString();++assertThat(page.getWebResponse().getStatusCode(), is(200));+assertThat(body, containsString("Alice's View"));+}+}++@Test+@Issue("SECURITY-3713")+void sameUserCanAccessOwnViewNames() throws Exception {+j.jenkins.setCrumbIssuer(null);+j.jenkins.setSecurityRealm(j.createDummySecurityRealm());+User alice = User.getOrCreateByIdOrFullName("alice");++MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();+authStrategy.grant(Jenkins.READ).everywhere().to("alice");+j.jenkins.setAuthorizationStrategy(authStrategy);++MyViewsProperty aliceProperty = new MyViewsProperty(null);+MyView customView = new MyView("Alice's View", aliceProperty);+alice.addProperty(aliceProperty);+aliceProperty.setUser(alice);+aliceProperty.addView(customView);+alice.save();++try (JenkinsRule.WebClient wc = j.createWebClient()) {+wc.login("alice");++URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + MyViewsProperty.class.getName() + "/fillPrimaryViewNameItems");+WebRequest request = new WebRequest(url, HttpMethod.POST);+Page page = wc.getPage(request);+String body = page.getWebResponse().getContentAsString();++assertThat(page.getWebResponse().getStatusCode(), is(200));+assertThat(body, containsString("Alice's View"));+}+}++@Test+@Issue("SECURITY-3713")+void regularUserCannotAccessOtherUserTimeZone() throws Exception {+j.jenkins.setCrumbIssuer(null);+j.jenkins.setSecurityRealm(j.createDummySecurityRealm());+User alice = User.getOrCreateByIdOrFullName("alice");++MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();+authStrategy.grant(Jenkins.READ).everywhere().to("alice", "bob");+j.jenkins.setAuthorizationStrategy(authStrategy);++alice.addProperty(new TimeZoneProperty(TIME_ZONE));+alice.save();++try (JenkinsRule.WebClient wc = j.createWebClient().withThrowExceptionOnFailingStatusCode(false)) {+wc.login("bob");++URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + TimeZoneProperty.class.getName() + "/fillTimeZoneNameItems");+WebRequest request = new WebRequest(url, HttpMethod.POST);+Page page = wc.getPage(request);++assertThat(page.getWebResponse().getStatusCode(), is(403));+}+}++@Test+@Issue("SECURITY-3713")+void adminCanAccessOtherUserTimeZone() throws Exception {+j.jenkins.setCrumbIssuer(null);+j.jenkins.setSecurityRealm(j.createDummySecurityRealm());+User alice = User.getOrCreateByIdOrFullName("alice");++MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();+authStrategy.grant(Jenkins.READ).everywhere().to("alice", "bob");+authStrategy.grant(Jenkins.ADMINISTER).everywhere().to("bob");+j.jenkins.setAuthorizationStrategy(authStrategy);++alice.addProperty(new TimeZoneProperty(TIME_ZONE));+alice.save();++try (JenkinsRule.WebClient wc = j.createWebClient()) {+wc.login("bob");++URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + TimeZoneProperty.class.getName() + "/fillTimeZoneNameItems");+WebRequest request = new WebRequest(url, HttpMethod.POST);+Page page = wc.getPage(request);+String body = page.getWebResponse().getContentAsString();++assertThat(page.getWebResponse().getStatusCode(), is(200));+assertThat(body, containsString("\"name\":\"" + TIME_ZONE + "\",\"selected\":true"));+}+}++@Test+@Issue("SECURITY-3713")+void sameUserCanAccessOwnTimeZone() throws Exception {+j.jenkins.setCrumbIssuer(null);+j.jenkins.setSecurityRealm(j.createDummySecurityRealm());+User alice = User.getOrCreateByIdOrFullName("alice");++MockAuthorizationStrategy authStrategy = new MockAuthorizationStrategy();+authStrategy.grant(Jenkins.READ).everywhere().to("alice");+j.jenkins.setAuthorizationStrategy(authStrategy);++alice.addProperty(new TimeZoneProperty(TIME_ZONE));+alice.save();++try (JenkinsRule.WebClient wc = j.createWebClient()) {+wc.login("alice");++URL url = new URL(j.getURL(), "user/alice/descriptorByName/" + TimeZoneProperty.class.getName() + "/fillTimeZoneNameItems");+WebRequest request = new WebRequest(url, HttpMethod.POST);+Page page = wc.getPage(request);+String body = page.getWebResponse().getContentAsString();++assertThat(page.getWebResponse().getStatusCode(), is(200));+assertThat(body, containsString("\"name\":\"" + TIME_ZONE + "\",\"selected\":true"));+}+}+}
References
- ADVISORYhttps://nvd.nist.gov/vuln/detail/CVE-2026-53439
- WEBhttps://github.com/jenkinsci/jenkins/commit/0586de425598497cfb4dcdafa5007e507a440a77
- WEBhttps://github.com/jenkinsci/jenkins/commit/98fe05f1753f664ffddd295a03492684b74e1950
- PACKAGEhttps://github.com/jenkinsci/jenkins
- WEBhttps://www.jenkins.io/security/advisory/2026-06-10/#SECURITY-3713