Gradle Plugin Portal: Clickjacking & Cross-Site Request Forgery enabling Account Takeover
Two security vulnerabilities in the Gradle Plugin Portal would have allowed any website to change the username, email & password of any logged in plugin author.
Responsible Disclosure: October 22nd, 2018
Vulnerabilities Patched: October 31st, 2018
Disclosure Published: January 9th, 2019
Clickjacking, also known as a “UI redress attack”, is when an attacker uses multiple transparent or opaque layers to trick a user into clicking on a button or link on another page when they were intending to click on the the top level page. Thus, the attacker is “hijacking” clicks meant for their page and routing them to another page, most likely owned by another application, domain, or both.
Using a similar technique, keystrokes can also be hijacked. With a carefully crafted combination of stylesheets, iframes, and text boxes, a user can be led to believe they are typing in the password to their email or bank account, but are instead typing into an invisible frame controlled by the attacker.
- OWASP Clickjacking
I’ve recently acquired an interest in Software Security Capture the Flag (CTF) challenges. While perusing YouTube I stumbled across this Mr. Robot CTF Hacking Walkthrough where I learned about an interesting utility called Nikto. Nikto is a simple utility you can use to scan a web domain for known security vulnerabilities. I’d previously discovered a security vulnerability in the Gradle Plugin portal so I decided to try running it against the Gradle Plugin Portal.
The line that stood out to me was this:
The anti-clickjacking X-Frame-Options header is not present.
I learned about clickjacking vulnerabilities in college and even reported one I found while working as a web dev intern at my previous employer. For those who aren’t as familiar with the header
X-Frame-Options here’s a description.
X-Frame-OptionsHTTP response header can be used to indicate whether or not a browser should be allowed to render a page in a
<object>. Sites can use this to avoid clickjacking attacks, by ensuring that their content is not embedded into other sites.
- MDN web docs X-Frame-Options
Proof Of Concept (POC)
This was interesting. I came up with a quick POC that demonstrated a Clickjacking attack that could be leveraged against the Gradle Plugin Portal.
In order for this particular POC attack to work, a plugin author must be logged into their Plugin Portal account. However, a malicious and well-crafted site could have additionally used clickjacking to get a plugin author to unknowingly log in to their account if they were not already logged in.
Below is what this attack would look like on a “real” website attempting to take advantage of a plugin author who is already logged in.
Disclosure & Fix
This issue was responsibility disclosed promptly to the Gradle team. They have patched the issue using the deprecated but still secure header
This lead to my discovering the second and far worse security vulnerability that evening.
Cross-Site Request Forgery (CSRF)
Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions on a web application in which they’re currently authenticated. CSRF attacks specifically target state-changing requests, not theft of data, since the attacker has no way to see the response to the forged request. With a little help of social engineering (such as sending a link via email or chat), an attacker may trick the users of a web application into executing actions of the attacker’s choosing. If the victim is a normal user, a successful CSRF attack can force the user to perform state changing requests like transferring funds, changing their email address, and so forth. If the victim is an administrative account, CSRF can compromise the entire web application.
CSRF is an attack that tricks the victim into submitting a malicious request. It inherits the identity and privileges of the victim to perform an undesired function on the victim’s behalf. For most sites, browser requests automatically include any credentials associated with the site, such as the user’s session cookie, IP address, Windows domain credentials, and so forth. Therefore, if the user is currently authenticated to the site, the site will have no way to distinguish between the forged request sent by the victim and a legitimate request sent by the victim.
- OWASP Cross-Site Request Forgery
If you load up the Gradle Plugin Portal user management page you are greeted with a form that allows you to change your username, email, and password. Opening up the Chrome Dev tools you can see that this is an HTML form element. You will notice that this form doesn’t include any sort of authentication token that is sent as a part of the form's HTTP POST request. This means that the server is entirely relying upon the browser’s session cookies to provide authentication for this request. This is a pretty good sign that the page is vulnerable to CSRF.
Proof of Concept (POC)
To test for CSRF, I copied the entire form element out of the Gradle Plugin Portal source code and into my own HTML document. After cleaning off the formatting/style elements, I was left with an HTML document that just contained a form. Opening this HTML document on my computer with my browser, I set a different username and password to a new password and clicked the “Update Profile” button.
When I clicked on the “Update Profile” button I was greeted with my Gradle Plugin Portal testing account’s profile page. I logged out and tried the password that I’d just set. I was able to login with this new password. I had just changed the password of my Plugin Portal account from a site that shouldn’t have had permission to do so.
What’s actually happening here is that the browser understands the
action="https://plugins.gradle.org/user" method="POST" on the
form tag and when the
<input type="submit"/> button is pressed the browser sends the HTML form payload as a POST to
https://plugins.gradle.org/user. The catch here is that the browser sends that payload with the session cookies of the logged in user; this means that the server thinks this is an authenticated request and updates the account. This attack could be completely automated with no user interaction besides visiting a malicious site with one simple addition to the document.
Any website on the internet could have embedded this form on their website and any Gradle plugin author logged into their Gradle Plugin Portal account (even if they had long since navigated away from the plugin portal page) could have their account easily compromised. This is because the
GRADLE_PORTAL_SESSION_ID is set to never expire. However, if a plugin author was not logged in, the malicious site could have used clickjacking to get the author to unknowingly click on the “login” button.
Disclosure & Fix
I immediately reported this directly to the Gradle team explaining the significance, severity, and potential impact of this vulnerability. Additionally, I provided links to resources discussing how to fix this vulnerability correctly. If you now view the source code for the Gradle Plugin Portal you’ll see that there is now a new hidden form element that has been added.
Now when this form is sent to the Gradle Plugin Portal server, this token is also checked to ensure that it’s a valid token. This
csrfToken can’t be generated by the malicious site because it doesn’t have any way to access the cookie that it is derived from.
Both vulnerabilities have been successfully patched by the Gradle team quickly. However, this vulnerability disclosure provides the perfect moment to reflect upon the resources we as developers utilize in our daily lives.
Plugin Portal value to Attackers
The Gradle plugin portal is a very valuable target for hackers. If an attacker wanted to compromise hundreds of pieces of software around the world or potentially every single newly developed Android app, a good place to start would be compromising something like the Gradle Plugin Portal, Maven Central, or JFrog. If a malicious actor can get a malicious plugin onto the plugin portal under a plugin author who is trusted they could easily get their code executing on many developer machines and Continuous Integration (CI) servers around the world. A simplified version of this attack would be for a malicious actor to compromise a Gradle Plugin author’s account within 7 days of a new release of a plugin being published, delete the existing artifact and upload their own. The plugin author has no idea that their artifact has been compromised and the user’s build systems are happily downloading a compromised plugin. Now an attacker has their code executing in a critical place in the software development stack. Even worse, since this malicious code is executing close to where build artifacts are being produced the malicious plugin could inject malicious bytecode into the generated artifacts, compromising web apps and Android applications across the world.
Attacks on build servers have happened before as evidenced by the successful compromise of CCleaner’s build and release process. In the Cisco Talos blog post where the CCleaner hack was publicly disclosed the Cisco team wrote: “it is likely that an external attacker compromised a portion of their development or build environment and leveraged that access to insert malware into the CCleaner build that was released and hosted by the organization”. The fallout for these sorts of attacks can be quite widespread. In the case of CCleaner, they claim “to have over 2 billion downloads worldwide as of November 2016 and [they are] reportedly adding new users at a rate of 5 million a week.”
I’ve strongly urged the Gradle team to hire a professional penetration testing team as soon as possible to explore what other security vulnerabilities may exist in the Plugin Portal and Build Scan portal. Additionally, there are several steps that I recommend the team explore.
- Automatically send an email to the plugin author when their email address/username/password is changed. Additionally, sending a confirmation email confirmation when a new version of a plugin they own is uploaded would inform plugin authors if their authentication tokens have been compromised.
- Add support for two-factor authentication on plugin portal accounts and requiring a second factor of authentication when logging in and changing an account’s email or password.
- Require the current password of the plugin account to be entered before allowing a new password to be set.
- Consider offering a Bug Bounty program. There is a very profitable grey market for unfixed security vulnerabilities. There are companies (eg. Zerodium) and governments that are willing to purchase security vulnerabilities so that they can be leveraged for their own purposes. These vulnerabilities are generally never reported to the affected company and are instead resold to individuals who will exploit them. Bug bounty programs have proven to be effective motivators for security researchers to explore a companies product for security vulnerabilities. Additionally, it provides an alternative market for security vulnerabilities that are responsibly disclosed.
- Hiring a professional penetration testing team. I know I stated this above, but it’s worth repeating. Professional penetration testing teams find security vulnerabilities that automated tools will never find. The Gradle Plugin Portal is an incredibly valuable asset that the Gradle team maintains.
Found a Security Vulnerability in Gradle?
Security vulnerabilities should be reported to email@example.com.