VYPR
High severityNVD Advisory· Published Oct 29, 2025· Updated Nov 4, 2025

CVE-2025-64131

CVE-2025-64131

Description

Jenkins SAML Plugin 4.583.vc68232f7018a_ and earlier does not implement a replay cache, allowing attackers able to obtain information about the SAML authentication flow between a user's web browser and Jenkins to replay those requests, authenticating to Jenkins as that user.

AI Insight

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

Jenkins SAML Plugin prior to 4.583.585.v22ccc1139f55 lacks a replay cache, enabling attackers to replay SAML authentication requests and impersonate users.

Vulnerability

Description

The Jenkins SAML Plugin versions 4.583.vc68232f7018a_ and earlier fail to implement a replay cache for SAML authentication responses [1][3]. This means that once a valid SAML assertion is used to authenticate a user, the plugin does not track whether that assertion has already been consumed. An attacker who can obtain a copy of the SAML authentication flow (e.g., by sniffing network traffic or accessing browser history) can resubmit the same assertion to the Jenkins server [1][2].

Exploitation

Prerequisites

To exploit this vulnerability, an attacker must first gain access to the SAML authentication data exchanged between a legitimate user's browser and the Jenkins instance. This could be achieved through man-in-the-middle attacks, compromised network segments, or by extracting cached browser data [1][2]. No additional authentication is required beyond possessing a valid SAML assertion; the attacker simply replays the captured request to the Jenkins SAML endpoint.

Impact

Successful replay of a SAML assertion allows the attacker to authenticate to Jenkins as the original user, inheriting all of that user's permissions and roles [1][3]. This can lead to unauthorized access to Jenkins jobs, configurations, credentials, and other sensitive resources, potentially enabling further compromise of the CI/CD pipeline.

Mitigation

The vulnerability is fixed in SAML Plugin version 4.583.585.v22ccc1139f55, which introduces a replay cache that rejects previously used SAML assertions [1][4]. Users should upgrade to this version or later immediately. No workarounds are documented; the only remediation is to apply the plugin update [2].

AI Insight generated on May 19, 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.plugins:samlMaven
< 4.583.585.v22ccc1139f554.583.585.v22ccc1139f55

Affected products

2

Patches

1
6170b1013daf

[SECURITY-3613]

https://github.com/jenkinsci/saml-pluginAntonio MuñizOct 21, 2025via ghsa
5 files changed · +161 1
  • src/main/java/org/jenkinsci/plugins/saml/JenkinsReplayCacheProvider.java+16 0 added
    @@ -0,0 +1,16 @@
    +package org.jenkinsci.plugins.saml;
    +
    +import hudson.ExtensionPoint;
    +import org.pac4j.saml.replay.ReplayCacheProvider;
    +
    +/**
    + * {@link ReplayCacheProvider} being an interface suggests that there can be different providers, so this abstraction
    + * will allow to switch to another provider implementation if needed.
    + */
    +public interface JenkinsReplayCacheProvider extends ExtensionPoint {
    +
    +    /**
    +     * Returns the {@link ReplayCacheProvider} to be used by {@link JenkinsSAML2Client}
    +     */
    +    ReplayCacheProvider getProvider();
    +}
    
  • src/main/java/org/jenkinsci/plugins/saml/JenkinsSAML2Client.java+43 0 added
    @@ -0,0 +1,43 @@
    +package org.jenkinsci.plugins.saml;
    +
    +import hudson.ExtensionList;
    +import java.util.logging.Logger;
    +import org.pac4j.saml.client.SAML2Client;
    +import org.pac4j.saml.config.SAML2Configuration;
    +import org.pac4j.saml.replay.InMemoryReplayCacheProvider;
    +import org.pac4j.saml.replay.ReplayCacheProvider;
    +
    +/**
    + * This override of the base {@link SAML2Client} is needed to fix SECURITY-3613, by setting
    + * up a replay cache which is static to all client instances.
    + */
    +public class JenkinsSAML2Client extends SAML2Client {
    +
    +    private static final Logger LOGGER = Logger.getLogger(JenkinsSAML2Client.class.getName());
    +
    +    static final ReplayCacheProvider REPLAY_CACHE = getProvider();
    +
    +    public JenkinsSAML2Client(SAML2Configuration config) {
    +        super(config);
    +    }
    +
    +    @Override
    +    protected void initSAMLReplayCache(boolean forceReinit) {
    +        replayCache = REPLAY_CACHE;
    +    }
    +
    +    private static ReplayCacheProvider getProvider() {
    +        var providers = ExtensionList.lookup(JenkinsReplayCacheProvider.class);
    +        if (providers.isEmpty()) {
    +            // Default if no extension is registered
    +            return new InMemoryReplayCacheProvider();
    +        }
    +        if (providers.size() > 1) {
    +            LOGGER.fine(() -> String.format(
    +                    "There is more than one JenkinsReplayCacheProvider extension registered. "
    +                            + "Picking [%s] as it's first one in the extensions list.",
    +                    providers.get(0).getClass().getName()));
    +        }
    +        return providers.get(0).getProvider();
    +    }
    +}
    
  • src/main/java/org/jenkinsci/plugins/saml/OpenSAMLWrapper.java+1 1 modified
    @@ -168,7 +168,7 @@ protected SAML2Client createSAML2Client() {
             config.setServiceProviderMetadataResource(new SamlFileResource(SamlSecurityRealm.getSPMetadataFilePath()));
             // Apply all configured property executions to the configuration
             propertyExecutions.forEach(property -> property.customizeConfiguration(config));
    -        SAML2Client saml2Client = new SAML2Client(config);
    +        SAML2Client saml2Client = new JenkinsSAML2Client(config);
             saml2Client.setCallbackUrl(samlPluginConfig.getConsumerServiceUrl());
             saml2Client.setCallbackUrlResolver(new NoParameterCallbackUrlResolver());
             saml2Client.setStateGenerator(new RefererStateGenerator());
    
  • src/test/java/org/jenkinsci/plugins/saml/JenkinsReplayCacheProviderDefaultTest.java+43 0 added
    @@ -0,0 +1,43 @@
    +package org.jenkinsci.plugins.saml;
    +
    +import static org.hamcrest.MatcherAssert.assertThat;
    +import static org.hamcrest.Matchers.instanceOf;
    +
    +import org.junit.jupiter.api.Test;
    +import org.jvnet.hudson.test.JenkinsRule;
    +import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
    +import org.pac4j.saml.config.SAML2Configuration;
    +import org.pac4j.saml.replay.InMemoryReplayCacheProvider;
    +
    +/**
    + * Test to verify that the default InMemoryReplayCacheProvider is used
    + * when no JenkinsReplayCacheProvider extension is registered.
    + * This test validates the default behavior in the SECURITY-3613 fix.
    + */
    +@WithJenkins
    +class JenkinsReplayCacheProviderDefaultTest {
    +
    +    @Test
    +    void testDefaultReplayCacheProviderIsUsed(JenkinsRule jenkinsRule) {
    +        SAML2Configuration config = new SAML2Configuration();
    +        JenkinsSAML2Client client = new JenkinsSAML2Client(config);
    +        client.setCallbackUrl("http://localhost");
    +        setKeyStore(config);
    +        client.init();
    +        assertThat(
    +                "Default InMemoryReplayCacheProvider should be used when no extension is registered",
    +                JenkinsSAML2Client.REPLAY_CACHE,
    +                instanceOf(InMemoryReplayCacheProvider.class));
    +    }
    +
    +    static void setKeyStore(SAML2Configuration config) {
    +        BundleKeyStore keyStore = new BundleKeyStore();
    +        if (!keyStore.isValid()) {
    +            keyStore.init();
    +        }
    +        config.setKeystorePath(keyStore.getKeystorePath());
    +        config.setKeystorePassword(keyStore.getKsPassword());
    +        config.setPrivateKeyPassword(keyStore.getKsPkPassword());
    +        config.setKeyStoreAlias(keyStore.getKsPkAlias());
    +    }
    +}
    
  • src/test/java/org/jenkinsci/plugins/saml/JenkinsReplayCacheProviderTest.java+58 0 added
    @@ -0,0 +1,58 @@
    +package org.jenkinsci.plugins.saml;
    +
    +import static org.hamcrest.MatcherAssert.assertThat;
    +import static org.hamcrest.Matchers.instanceOf;
    +import static org.junit.jupiter.api.Assertions.assertTrue;
    +
    +import org.junit.jupiter.api.Test;
    +import org.jvnet.hudson.test.JenkinsRule;
    +import org.jvnet.hudson.test.TestExtension;
    +import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
    +import org.pac4j.saml.config.SAML2Configuration;
    +import org.pac4j.saml.replay.InMemoryReplayCacheProvider;
    +import org.pac4j.saml.replay.ReplayCacheProvider;
    +
    +/**
    + * Test for the JenkinsReplayCacheProvider extension point functionality.
    + * This test validates SECURITY-3613 fix by ensuring custom replay cache providers
    + * are properly loaded and used by JenkinsSAML2Client.
    + */
    +@WithJenkins
    +class JenkinsReplayCacheProviderTest {
    +
    +    @Test
    +    void testCustomReplayCacheProviderIsUsed(JenkinsRule jenkinsRule) {
    +        SAML2Configuration config = new SAML2Configuration();
    +        JenkinsSAML2Client client = new JenkinsSAML2Client(config);
    +        client.setCallbackUrl("http://localhost");
    +        BundleKeyStore keyStore = new BundleKeyStore();
    +        JenkinsReplayCacheProviderDefaultTest.setKeyStore(config);
    +        client.init();
    +        assertThat(
    +                "Custom replay cache provider should be used",
    +                JenkinsSAML2Client.REPLAY_CACHE,
    +                instanceOf(TestReplayCacheProvider.class));
    +        assertTrue(
    +                TestReplayCacheProvider.wasProviderCalled,
    +                "Test replay cache provider getProvider() method should have been called");
    +    }
    +
    +    @TestExtension
    +    public static class TestJenkinsReplayCacheProvider implements JenkinsReplayCacheProvider {
    +
    +        @Override
    +        public ReplayCacheProvider getProvider() {
    +            return new TestReplayCacheProvider();
    +        }
    +    }
    +
    +    public static class TestReplayCacheProvider extends InMemoryReplayCacheProvider {
    +
    +        static boolean wasProviderCalled = false;
    +
    +        public TestReplayCacheProvider() {
    +            super();
    +            wasProviderCalled = true;
    +        }
    +    }
    +}
    

Vulnerability mechanics

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

References

5

News mentions

1