An Utsusemi of Keramas

Tales of security research, penetration testing, red teaming, and general hacking shenanigans.

3 April 2020

JFrog Artifactory Server-side Request Forgery Vulnerability

by Keramas

During a live engagement against a client, my recon uncovered a publicly facing instance of JFrog Artifactory running on an IIS server. With a good amount of previously released CVEs, this quickly became an attractive target. While none of the existing CVEs and exploits in the wild applied to this running instance, after reading the documentation on the API and how the application was structured, I was able to gain access as an admin due to misconfigurations and default credentials, which ultimately led to the discovery of a neat server-side request forgery (SSRF) vulnerability (CVE-2019-19937).

(Note: All screenshots included in this blog post are taken from a locally created Active Directory lab used for testing and validation–they are not from the actual engagement for obvious reasons.)

Initial Access

JFrog has an access-admin account which is not granted the same privileges as a full-fledged admin user. By default, this user is not able to access any of the admin functionality in the dashboard; however, it does have the power to make certain administrative API calls such as activating accounts and changing passwords for recovery purposes in case the real admin account is locked out.

To my luck, I discovered that the access-admin user’s password was still the default: password, but UI access was disabled for this user.

JFrog uses a special set of API endpoints for the “access” tools which the access-admin has privileges for. By default, this is set so that it can only be accessed via localhost. This is where CVE-2019-9733 came in for a prior version of JFrog, which allowed this to be bypassed. However, depending on how the application is configured, it is possible that external IPs are allowed to access as well. Luckily, there were no IP restrictions for this API. This allowed me to perform a series of API calls so that I could access the application as the root admin user.

I first made an API call to change the main admin user’s password; however, the response showed that while the password was changed successfully, this account was in a “locked” state and inactive.

With another API call I was able to enable this account and login with the newly created password. With full access to the admin dashboard and all the functionality, it was time to dig in and see if getting a shell on the box was possible.

Application Exploration

With the admin password changed, I could now access the UI as an administrator user, and once in, the admin-only “Import & Export” feature for repositories looked extremely interesting.

This feature made it possible to copy any data from the repositories onto the file system, and oppositely, it allowed for any file(s) from the file system to be copied into a repository. With JFrog running as a local administrator user in most cases, essentially this allows for an entire file system’s data to be read by placing it into a repository folder.

After importing a slew of configuration files and other data, nothing was really contributing to the advancement of the attack. With shells on the mind, I spent quite a bit of time trying to find a way to place a WAR file into the Tomcat webapps folder; however, any time a repository is copied onto the disk, it is transferred as a folder/directory which takes the form of {Repositories}/{Repository name}/{files within the repository}. Due to this, getting a reverse shell via a malicious WAR payload was not possible. Similarly, it was possible to drop a JSP file to disk, but due to web server settings as well as the JFrog settings, that file was not parsed (the aforementioned directory issue also applied here). A CVE exists for previous versions of JFrog where directory traversal allows JSP files to be dropped to a location where they can be parsed; however, this was not applicable here.

Attempts were also made to upload modified config files within the repositories and such, but I was running out of ideas and nothing was working.

Server-side Request Forgery Vulnerability

While investigating the import and export features and performing some probing with Burp, I decided to manually specify a UNC path for the import instead of what can be selected through the UI. To my surprise, the request hung a bit, and despite returning an error, I felt like it was actually parsing this to make the request.

For this I spun up an SMB server on a remote host to see if it could reach out to me. In the example below, .37 is a Kali box that I was using Impacket’s SMB server on.

I got a connection back, and while it didn’t immediately grab any files, it proved that it worked. As a bonus, if SMB isn’t configured correctly you can also grab NTLMv2 hash of the account running JFrog for cracking and/or NTLM relay attacks if signing is not enabled/enforced.

Since this worked, I decided to try other IPs on the internal subnet that the server was on. Based on log files and LDAP connection settings present within the application, I knew the domain controller’s IP address. Using this IP address, the response back was quick. I then incremented the IP to the next address and I noticed a huge delay in response time–this was a good sign that this could in fact reach out to internal IPs, and response times could be used to determine active hosts.

Using Burp Intruder, I set up a list of all the possible IP addresses in the subnet, and fired off the requests to monitor for quick response times, which would indicate live hosts.

Results:

Based on the above results, the shorter response times indicate live hosts, so the next step was to start hunting for shares on these hosts. Again, using Intruder in conjunction with a list of potential share names, it’s possible to monitor for shares that the user running JFrog can successfully access by looking for 200 responses. The below example is illustrating this using the Kali box at .37. An SMB share named “share” is present, which contains a simple file named “test.txt”.

(As a side note, a Burp “Cluster bomb” attack type can be used to simultaneously scan for shares from all discovered hosts.)

Naturally, SMB and Active Directory configurations play a big role here as to what will be accessible. If the account running JFrog has access to certain shares, it will be possible to import all files from the share. Likewise, if anonymous or guest SMB access is permitted on shares, those will be importable as well.

With a valid share that we can connect to, now we can import files from an internal share, which we can organize for exfiltration by either using the UI’s download feature, or we can use this same UNC functionality with the Export feature to send data to a remote SMB share. Depending on configurations, it could be possible to import any shares found that the account running JFrog has access to, including SYSVOL and such, which may contain GPP passwords or other sensitive data that can be used to subsequent attacks on the network.

Mitigation and Other Notes

Access-admin default password and Access API access

With more recent versions of JFrog Artifactory, there is no longer a default password of “password” for the access-admin user. Instead, a randomly generated value that is only accessible from the server’s file system is used. This can of course be changed to something weak by accident, but without changing it, it is much more secure than before.

Furthermore, by default now, the access API is set to accept connections from localhost only. This can be modified situationally if it needs to be accessed from external hosts, but the default setting is quite secure.

Server-side request forgery vulnerability

This vulnerability only exists in the Windows versions of JFrog Artifactory due to how UNC paths are processed by Windows and a lack of user input validation by the application.

JFrog remediated this issue in the 7.x series and also implemented it in version 6.18.0. User input for the target import and export is now sanitized and will reject any UNC paths. Additionally, they added an option to disable the Import/Export feature. Please note, that this is not a default option, so if you are deploying an instance of Artifactory, you will need to manually disable this functionality.

Conclusion

If you are using JFrog Artifactory 6.17 or earlier, it is strongly recommended to update to version 7 or the latest version for the 6.x series, 6.18.

Disclosure notes

tags: web application security - JFrog - SSRF - OWASP - CVE-2019-19937