VYPR
High severityNVD Advisory· Published Mar 8, 2023· Updated Feb 28, 2025

CVE-2023-27898

CVE-2023-27898

Description

Jenkins 2.270 through 2.393 (both inclusive), LTS 2.277.1 through 2.375.3 (both inclusive) does not escape the Jenkins version a plugin depends on when rendering the error message stating its incompatibility with the current version of Jenkins, resulting in a stored cross-site scripting (XSS) vulnerability exploitable by attackers able to provide plugins to the configured update sites and have this message shown by Jenkins instances.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Jenkins fails to escape plugin dependency version in error messages, enabling stored XSS via malicious plugins on update sites.

CVE-2023-27898 is a stored cross-site scripting (XSS) vulnerability in Jenkins core, versions 2.270 through 2.393 and LTS 2.277.1 through 2.375.3. The flaw exists because Jenkins does not escape the Jenkins version a plugin depends on when rendering an error message about incompatibility with the current Jenkins version [1][2].

To exploit this vulnerability, an attacker must be able to provide plugins to the configured update sites, such as by controlling a custom update site or by having the instance show outdated plugin metadata (e.g., after a downgrade). The vulnerable error message appears in the plugin manager when a plugin's required Jenkins version is higher than the current version. Notably, exploitation does not require the manipulated plugin to be installed [1].

Successful exploitation results in stored XSS, allowing the attacker to execute arbitrary JavaScript in the context of the Jenkins web interface. This can lead to credential theft, configuration changes, or further compromise of the Jenkins instance and its builds [1].

The vulnerability is fixed in Jenkins 2.394, LTS 2.375.4, and LTS 2.387.1 [1][3]. For many up-to-date instances, the vulnerable message is not shown because the official update site only returns compatible plugins. However, instances using custom update sites or older versions are at risk. Users should update Jenkins core immediately.

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.jenkins-ci.main:jenkins-coreMaven
>= 2.376, < 2.3942.394
org.jenkins-ci.main:jenkins-coreMaven
< 2.375.42.375.4

Affected products

9

Patches

1
59ac866d9946

[SECURITY-3037]

https://github.com/jenkinsci/jenkinsYaroslav AfenkinFeb 23, 2023via ghsa
3 files changed · +172 2
  • core/src/main/java/hudson/PluginManager.java+6 2 modified
    @@ -1469,7 +1469,11 @@ public HttpResponse doPluginsSearch(@QueryParameter String query, @QueryParamete
                         jsonObject.put("sourceId", plugin.sourceId);
                         jsonObject.put("title", plugin.title);
                         jsonObject.put("displayName", plugin.getDisplayName());
    -                    jsonObject.put("wiki", plugin.wiki);
    +                    if (plugin.wiki == null || !(plugin.wiki.startsWith("https://") || plugin.wiki.startsWith("http://"))) {
    +                        jsonObject.put("wiki", StringUtils.EMPTY);
    +                    } else {
    +                        jsonObject.put("wiki", plugin.wiki);
    +                    }
                         jsonObject.put("categories", plugin.getCategoriesStream()
                             .filter(PluginManager::isNonMetaLabel)
                             .map(UpdateCenter::getCategoryDisplayName)
    @@ -1486,7 +1490,7 @@ public HttpResponse doPluginsSearch(@QueryParameter String query, @QueryParamete
                         jsonObject.put("version", plugin.version);
                         jsonObject.put("popularity", plugin.popularity);
                         if (plugin.isForNewerHudson()) {
    -                        jsonObject.put("newerCoreRequired", Messages.PluginManager_coreWarning(plugin.requiredCore));
    +                        jsonObject.put("newerCoreRequired", Messages.PluginManager_coreWarning(Util.xmlEscape(plugin.requiredCore)));
                         }
                         if (plugin.hasWarnings()) {
                             JSONObject unresolvedSecurityWarnings = new JSONObject();
    
  • test/src/test/java/hudson/PluginManagerSecurity3037Test.java+101 0 added
    @@ -0,0 +1,101 @@
    +package hudson;
    +
    +import static org.junit.Assert.assertEquals;
    +import static org.junit.Assert.assertTrue;
    +
    +import com.gargoylesoftware.htmlunit.AlertHandler;
    +import com.gargoylesoftware.htmlunit.Page;
    +import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
    +import com.gargoylesoftware.htmlunit.html.HtmlElement;
    +import com.gargoylesoftware.htmlunit.html.HtmlElementUtil;
    +import com.gargoylesoftware.htmlunit.html.HtmlPage;
    +import hudson.model.DownloadService;
    +import hudson.model.RootAction;
    +import hudson.model.UpdateSite;
    +import java.io.IOException;
    +import java.util.ArrayList;
    +import java.util.Collections;
    +import java.util.List;
    +import java.util.stream.Collectors;
    +import javax.servlet.ServletException;
    +import jenkins.model.Jenkins;
    +import org.junit.Rule;
    +import org.junit.Test;
    +import org.jvnet.hudson.test.FlagRule;
    +import org.jvnet.hudson.test.JenkinsRule;
    +import org.jvnet.hudson.test.TestExtension;
    +import org.kohsuke.stapler.StaplerRequest;
    +import org.kohsuke.stapler.StaplerResponse;
    +
    +public class PluginManagerSecurity3037Test {
    +    @Rule
    +    public JenkinsRule r = new JenkinsRule();
    +
    +    @Rule
    +    public FlagRule<Boolean> signatureCheck = new FlagRule<>(() -> DownloadService.signatureCheck, x -> DownloadService.signatureCheck = x);
    +
    +    @Test
    +    public void noInjectionOnAvailablePluginsPage() throws Exception {
    +        DownloadService.signatureCheck = false;
    +        Jenkins.get().getUpdateCenter().getSites().clear();
    +        UpdateSite us = new UpdateSite("Security3037", Jenkins.get().getRootUrl() + "security3037UpdateCenter/update-center.json");
    +        Jenkins.get().getUpdateCenter().getSites().add(us);
    +
    +        try (JenkinsRule.WebClient wc = r.createWebClient()) {
    +            HtmlPage p = wc.goTo("pluginManager");
    +            List<HtmlElement> elements = p.getElementById("bottom-sticker")
    +                    .getElementsByTagName("a")
    +                    .stream()
    +                    .filter(link -> link.getAttribute("href").equals("checkUpdatesServer"))
    +                    .collect(Collectors.toList());
    +            assertEquals(1, elements.size());
    +            AlertHandlerImpl alertHandler = new AlertHandlerImpl();
    +            wc.setAlertHandler(alertHandler);
    +
    +            HtmlElementUtil.click(elements.get(0));
    +            HtmlPage available = wc.goTo("pluginManager/available");
    +            assertTrue(available.querySelector(".alert-danger")
    +                    .getTextContent().contains("This plugin is built for Jenkins 2.999"));
    +            wc.waitForBackgroundJavaScript(100);
    +
    +            HtmlAnchor anchor = available.querySelector(".jenkins-table__link");
    +            anchor.click(true, false, false);
    +            wc.waitForBackgroundJavaScript(100);
    +            assertTrue(alertHandler.messages.isEmpty());
    +        }
    +    }
    +
    +    static class AlertHandlerImpl implements AlertHandler {
    +        List<String> messages = Collections.synchronizedList(new ArrayList<>());
    +
    +        @Override
    +        public void handleAlert(final Page page, final String message) {
    +            messages.add(message);
    +        }
    +    }
    +
    +    @TestExtension("noInjectionOnAvailablePluginsPage")
    +    public static final class Security3037UpdateCenter implements RootAction {
    +
    +        @Override
    +        public String getIconFileName() {
    +            return "gear2.png";
    +        }
    +
    +        @Override
    +        public String getDisplayName() {
    +            return "security-3037-update-center";
    +        }
    +
    +        @Override
    +        public String getUrlName() {
    +            return "security3037UpdateCenter";
    +        }
    +
    +        public void doDynamic(StaplerRequest staplerRequest, StaplerResponse staplerResponse) throws ServletException, IOException {
    +            staplerResponse.setContentType("application/json");
    +            staplerResponse.setStatus(200);
    +            staplerResponse.serveFile(staplerRequest, PluginManagerSecurity3037Test.class.getResource("PluginManagerSecurity3037Test/update-center.json"));
    +        }
    +    }
    +}
    
  • test/src/test/resources/hudson/PluginManagerSecurity3037Test/update-center.json+65 0 added
    @@ -0,0 +1,65 @@
    +updateCenter.post(
    +{
    +  "connectionCheckUrl": "https://www.google.com/",
    +  "core": {
    +    "buildDate": "Jan 31, 2023",
    +    "name": "core",
    +    "sha1": "TO8l3+wqx2hWtLWMHjmIOj+ERsk=",
    +    "sha256": "BrXTCwJse5e6b/shXjv5edRJQRjxfJ/GbNrhgYcrZoU=",
    +    "size": 98362341,
    +    "url": "https://updates.jenkins.io/download/war/2.389/jenkins.war",
    +    "version": "2.389"
    +  },
    +  "generationTimestamp": "2023-02-03T11:04:30Z",
    +  "id": "Security3037",
    +  "deprecations": [],
    +  "plugins": {
    +    "workflow-aggregator": {
    +      "buildDate": "Jun 29, 2022",
    +      "defaultBranch": "master",
    +      "dependencies": [
    +        {
    +          "name": "pipeline-groovy-lib<img src=x onerror=alert(1)>",
    +          "optional": false,
    +          "version": "591.v3a_7f422b_d058<img src=x onerror=alert(1)>"
    +        }
    +      ],
    +      "developers": [
    +        {
    +          "developerId": "johndoe<img src=x onerror=alert(1)>",
    +          "name": "john doe<img src=x onerror=alert(1)>"
    +        }
    +      ],
    +      "excerpt": "A suite of plugins that lets you orchestrate automation, simple or complex. See <a href=\"https://www.jenkins.io/solutions/pipeline/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Pipeline as Code with Jenkins</a> for more details.",
    +      "gav": "org.jenkins-ci.plugins.workflow:workflow-aggregator:590.v6a_d052e5a_a_b_5<img src=x onerror=alert(1)>",
    +      "issueTrackers": [
    +        {
    +          "reportUrl": "https://www.jenkins.io/participate/report-issue/redirect/#21710<img src=x onerror=alert(1)>",
    +          "type": "jira<img src=x onerror=alert(1)>",
    +          "viewUrl": "https://issues.jenkins.io/issues/?jql=component=21710<img src=x onerror=alert(1)>"
    +        }
    +      ],
    +      "labels": [
    +        "cli<img src=x onerror=alert(1)>",
    +        "misc",
    +        "slaves",
    +        "trigger"
    +      ],
    +      "name": "workflow-aggregator<img src=x onerror=alert(1)>",
    +      "popularity": 256809,
    +      "previousTimestamp": "2022-05-24T12:27:03.00Z<img src=x onerror=alert(1)>",
    +      "previousVersion": "<img src=x onerror=alert(1)>581.v0c46fa_697ffd",
    +      "releaseTimestamp": "2022-06-29T21:02:16.00Z<img src=x onerror=alert(1)>",
    +      "requiredCore": "2.999<img src=x onerror=alert(1)>",
    +      "scm": "https://github.com/jenkinsci/workflow-aggregator-plugin<img src=x onerror=alert(1)>",
    +      "sha1": "6iNlKTnXvr4W+9Svwy1+ctyqgvo=<img src=x onerror=alert(1)>",
    +      "sha256": "P0SFgnqOBFZjxkdKORiQUbrhdlfW5YU7tOpe85bfGWE=<img src=x onerror=alert(1)>",
    +      "size": 8282,
    +      "title": "Pipeline<img src=x onerror=alert(1)>",
    +      "url": "https://updates.jenkins.io/download/plugins/workflow-aggregator/590.v6a_d052e5a_a_b_5/workflow-aggregator.hpi",
    +      "version": "590.v6a_d052e5a_a_b_5<img src=x onerror=alert(1)>",
    +      "wiki": "javascript:alert(1)"
    +    }
    +  },
    +  "updateCenterVersion": "1"
    +});
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

1