Leveraging Gradle Plugin wildcard versions for remote code execution

Exploit allowed any Gradle Plugin on the Gradle Plugin Portal to have its artifact coordinates hijacked by a malicious actor.

Responsible Disclosure: September 6th, 2018
Exploit Patched: October 18th, 2018

The exploit stems from the ability to publish artifacts with group names that your Gradle account shouldn’t be able to publish to.

TL;DR

The user only had to be using a wildcard dependency version and be using the Gradle Plugin Portal repository to be vulnerable.

The group id for this plugin is org.jlleitschuh.testing.security (the gradle.plugin at the beginning is added by Gradle).
Due to a bug in the Gradle Plugin Portal, a malicious actor could set their plugin’s group id to the same group id as any plugin already on the portal and publish malicious plugins under a group they shouldn’t own. The malicious actor could not overwrite existing versions of artifacts, but they could publish newer versions. When a user is using a wildcard version for their dependency the newest version of the plugin (which is now malicious) would be downloaded by Gradle the next time the user runs their build. The plugin user would then be pwned.

Discovery

I discovered this issue because the spotbugs team had not yet released a patch for their plugin to support Gradle 4.10 (Issue). I was in the process of updating my internal builds to use 4.10 and this was blocking that progress. Since I already had a Gradle Plugin Portal account for other plugins I decided to release a version of the spotbugs under different artifact coordinates.
I forked their repository and changed just enough of their build.gradle so that I could run the publishPlugins task successfully.
The result of publishing that can be found here (this artifact has since been moved by a member of Gradle team to prevent it colliding with a future release of spotbugs).

The example code snippet offered by the Gradle Plugin Portal for how to apply this newly published plugin looked like this:

The thing that caught my attention was how the real spotbugs plugin had the exact same coordinates as my own.

  • Real: gradle.plugin.com.github.spotbugs:spotbugs-gradle-plugin:1.6.2
  • Mine: gradle.plugin.com.github.spotbugs:spotbugs-gradle-plugin:1.6.4

I began reporting this issue to Eric Wendelin, the Gradle Plugin Portal lead, on the Gradle Community Slack Channel as soon as realized what I was seeing. However, the full implications hadn’t completely dawned on me at the time.

The Proof of Concept

By this time, it was already 1:00 am in the morning, but why would I stop now? I was on to something!

The following testing was done using two different Gradle Plugin Portal accounts that are both registered to me.

I created the following proof of concept benign plugin. Think of this plugin as any plugin published by any author on the internet. This plugin is adding some convenient security features to Gradle builds.

A user comes along and thinks this plugin will be useful, but they don’t want to have to keep updating the version every time there is a new update. So they decide to use a wildcard version like this.

The version that they will be pulling from the Gradle Plugin portal is version 0.4.0. If they were to run ./gradlew they would see the following in their console.

./gradlew
> Configure project :
A security plugin
...

Now, a malicious actor comes along and sees that you are using a wildcard version in your build or, they just want to try to hijack a few plugins and see what happens. Using this exploit they can add their own code to the plugin and publish it. This is what the malicious actor changes the code to.

The malicious actor publishes their version of the plugin to the Gradle Plugin Portal. Now all they have to do is wait.

The next time that our user comes along and runs ./gradlew they are greeted with this.

./gradlew
Download https://plugins.gradle.org/m2/gradle/plugin/org/jlleitschuh/testing/security/gradle-testing/0.4.1/gradle-testing-0.4.1.pom
> Configure project :
A security plugin. I'm malicious!
...

The user has just been pwned.

Gradle Team’s Response

The Gradle team responded very quickly to my report. They also informed me that they had been made aware of this vulnerability by the Google team, around a week before I found it. However, the Google report didn’t include the part where the user could be pwned just by using a wildcard version. The version of the exploit offered by Google required the user to change the plugin being applied to the following.

apply(plugin = "org.jlleitschuh.testing.security-plugin.tmp")

The Fix

The fix for this exploit was delivered to the Gradle Plugin Portal as a part of the Gradle Plugin Portal Approval Policy Update although this exploit was not mentioned.
When asked if an audit was run to ensure that this security vulnerability wasn’t being exploited in the wild, Eric Wendelin responded “Yes, but [we] found no evidence of malicious behavior”.

The Takeaway

Artifact servers like the Gradle Plugin Portal, Maven Central Repository, and JFrog Artifactory are all the perfect target for a malicious actor. If a malicious actor can hijack artifact coordinates they can execute arbitrary code on hundreds or even thousands of machines across the globe.

How to protect yourself

While these tips won’t protect you from this sort of attack, not doing these things can leave you or your users vulnerable to being hacked by malicious dependencies.

Areas for Improvement

These are just suggestions that might have helped mitigate these sorts of attacks in the future.

  • GPG signatures on artifacts and forcing the user to confirm when the newer release isn’t signed by the same authority.

Found a Security Vulnerability in Gradle?

Security vulnerabilities should be reported to security@gradle.com.