Crypto Audit of the Jenkins CI System

Graham Steel
June 25, 2018

Jenkins is a popular tool for managing continuous integration (CI), i.e. coordinating builds, tests and deployment of a software project in an automated way.In an enterprise context Jenkins has some security requirements, like ensuring that only users with the right permissions can access certain projects and carry out certain tasks, protecting sensitive data such as tokens for access to APIs, etc. As Jenkins is open source and uses cryptography to try to fulfil some of these security requirements, it seemed to us like a good candidate for a test with Cryptosense Analyzer. Running a test was as easy as invoking jenkins with java -javaagent agent.jar -jar jenkins.war on the server (agent.jar is the Cryptosense tracer agent) and then playing around with the Jenkins server a little to exercise the cryptographic functionality. For full coverage we could have used the integration test suite, but this is rather long running, so we're saving that for a future project.

Crypto Audit Results

Results from running the trace through our Analyzer included 6 findings rated High and 3 rated Medium.

List of 6 critical findings in Cryptosense Analyzer
High criticality findings on Jenkins
Set of Three Medium level findings in Cryptosense Analyzer
Medium criticality findings on Jenkins

To trace these back to the source code you just have to click on a rule to access the instances. Here are the instances for rule 35:

Three unauthenticated encryption rules
Instances for rule 35

You can click on an instance to see the call and stacktrace, what we call the Developer View in Analyzer. Below we discuss what we found. Note that we registered issues related to these results with Jenkins maintainers on 2018-02-20. Some issues have already been fixed, while some fixes are in progress. Those still open do not appear to be immediately exploitable, and the maintainers are aware of the content of this post.

Use of ECB Mode to Store Secrets

The first thing flagged up (Rule 8) is the use of AES in Electronic Code Book (ECB) mode. This means that encryption is deterministic (the same plaintext always gives the same ciphertext), and when used without some kind of MAC or signature, unauthenticated (flagged by Rule 38) which means various manipulations of the ciphertext are possible. There are many examples of attacks on this kind of weak encryption. We cross-referenced the stacktraces in the findings to the source code to see this mainly comes from the DefaultConfidentialStore class that encrypts keys and other secret data and stores them in ~/.jenkins/secrets. It's interesting that the string "ECB" is not seen in the Jenkins source code:

Cipher sym = Secret.getCipher("AES");

sym.init(Cipher.ENCRYPT_MODE, masterKey);

try (OutputStream fos = Files.newOutputStream(getFileFor(key).toPath());

CipherOutputStream cos = new CipherOutputStream(fos, sym)) {

cos.write(payload);

cos.write(MAGIC);

}

The Secret class actually calls the crypto library to get the cipher is here:

public static Cipher getCipher(String algorithm) throws GeneralSecurityException {

return PROVIDER != null ? Cipher.getInstance(algorithm, PROVIDER)

: Cipher.getInstance(algorithm);

}


The problem is that no mode at all is specified, only an algorithm, so the default provider mode is used (Rule 10), which in this case is ECB. Cryptosense Analyzer detects that the default mode is ECB by also detecting the provider that ends up being used to execute the call, and referring to our provider information database to check which mode is used.

Insecure use of RSA and SHA-1

Jenkins fetches a list of updates and verifies it using a 1024-bit RSA certificate included in the response (Rule 1, rating Low). The certificate must itself be valid with regards to a trust anchor controlled by Jenkins, hardcoded in the JAR. The signed payload is the SHA-1 digest (Rule 11) of the JSON response with the "signature" field removed. SHA-1 is now regarded as insecure since the first full public collision was published. The format of the message might make exploitation difficult but still it should be replaced with a hash currently believed secure like SHA-256. Generally speaking 1024 bit RSA keys are considered too weak now (they are disallowed by current NIST and ECRYPT recommendations), even if only a powerful nation state might possibly have the capacity to break them today (which is why by default the Analyzer rates it as Low criticality - this can be changed in the Crypto Profile). Meanwhile, the Jenkins team are already working on moving to SHA-512 for the update functionality.RSA PKCS#1v1.5 padding (Rule 9) is used in a hybrid encryption of usage statistics sent to a central server. This mode is well-known for its susceptibility to padding oracle attacks.  The usage statistics payload is encrypted using AES ECB mode, triggering further instances of rules 8 and 10 above.

Insecure Storage of Private TLS key

Jenkins stores a private TLS key with the alias "jenkins" in a JKS keystore (detected Rule 21). JKS is now deprecated as insecure. The password for this keystore is "password" (caught by Rule 39).

Reuse of IVs in CBC Mode and Use of MD5

Each Jenkins user has an API token. The token is stored in .jenkins/users/<user>/config.xml with other information but, unlike other attributes, the API token isn't stored in plaintext there.The stored value is actually a 16-byte seed encrypted with AES-CBC and serialized as Base64. When read and decrypted, the token actually provided to the user is the MD5 digest (detected by rule 12) of that seed. The Initialisation vector (IV) for the CBC encryption is first generated randomly, but then fixed for that token (detected by Rule 18). This is not as serious as using a fixed IV for all the encryptions carried out using the key, so could be considered a "false positive". However, the Jenkins maintainers are rewriting and updating the secure storage module anyway, so this is an opportunity to switch to best practices.

Conclusions

Cryptosense Analyzer allowed us to very quickly audit the cryptography in Jenkins and find a number of areas for immediate improvement. Readers might be surprised by much cryptography was being used in Jenkins, since it doesn't seem to have an immediate cryptographic function, but in fact this is rather typical from our experience of auditing modern applications.