Static Keys, Shattered Security Dreams: A CVE-2024–5764 Story


Red teaming is all about seizing opportunities, and in a recent assessment, we did just that. We came across a Sonatype Nexus Repository 3 instance — perfect timing, as a fresh vulnerability (CVE-2024–4956) was burning a hole in our pocket thanks to a timely tip-off from Phithon (@phithon_xg) on X.



This unauthenticated path traversal flaw had the potential to expose critical files on the server, and we couldn’t resist taking a closer look. After some creative maneuvering, we successfully accessed the system’s file structure and began pulling back some intriguing finds. Among the files we retrieved were OrientDB .pcl files, containing a treasure trove of encrypted secrets and password hashes.

As we unraveled the data, we uncovered another issue now known as CVE-2024–5764. Security researchers at Maveris were the first to discover the issue and report it to Sonatype. The encryption protecting these secrets relied on a static key — hidden in plain sight within the public source code. With this key in hand, we managed to decrypt sensitive information, exposing flaws that could allow attackers to pivot to other systems by obtaining credentials.

In this post, we’ll walk you through how we exploited both vulnerabilities, from initial access to the ultimate discovery of CVE-2024–5764. Buckle up as we dive into the details and reveal how these critical missteps can expose environments to serious risk.

Unauthenticated Path Traversal Exploitation

Once we spotted the Nexus Repository Manager instance in the environment, we knew it was game on. With CVE-2024–4956 in our arsenal, we quickly spun up a vulnerable Docker instance to test exploitation.



We launched our attack against the test server, expecting quick success. But, as is often the case, the path wasn’t as straightforward as we hoped:



It was time to get creative. Realizing the manual approach wasn’t cutting it, we switched tactics and began creating a Python script to automate the exploitation process. By programmatically walking the directory paths, we finally struck gold — successfully exfiltrating /etc/passwd and confirming the vulnerability:



OrientDB

With this initial win under our belt, we decided to dive deeper. This led to taking a look at the database Repository Manager 3 was using. At the time we discovered the vulnerability, it used OrientDB by default, but now OrientDB has been deprecated according to the information here.



Not to worry as all versions from 3.0.0 up to 3.71.0 use OrientDB and CVE-2024–5746 impacts versions 3.0.0 through 3.72.0, so this is good for our blog piece. Understanding that Repository Manager 3 uses OrientDB (up to 3.71.0) is a key piece of information as it allows us to dissect the structure of the database for potential abuse cases. The key pieces of information were:

In OrientDB, a cluster is a fundamental storage unit that groups related records (such as documents or vertices) together within a database.



Each OrientDB database is made up of multiple clusters, each of which stores data associated with specific classes (tables, in SQL terms). Clusters allow OrientDB to manage data at scale, distributing it across storage segments for efficient retrieval and manipulation.

OrientDB uses Plocal storage, which stands for “Paginated Local” storage, a binary format optimized for high-performance disk I/O. In this format, OrientDB stores each cluster’s data in its own .pcl file, making it the primary container for the records associated with that cluster. These .pcl files contain raw, serialized records stored in binary format, representing anything from documents to edges and vertices in the graph.

Basically, we knew that files ending with the .pcl extension were going to contain the cluster records data for the database in actual files on disk and we could access data in the database while being unauthenticated via the directory traversal vulnerability. Bingo!



With this information in hand, it is possible to issue a simple find command to find all of the .pcl files within the server directory:



Looking at the filenames, some of them piqued our interest. Notably, some files under nexus3/db/config including email.pcl, http_client.pcl, and ldap_<INTEGER>.pcl. We suspected these would contain configuration details and possibly credentials pertaining to Email Server, HTTP, and LDAP in the web console. We decided to fill out some dummy settings for these configurations on our test server.





After filling out the dummy data, we then decided to take a look at the files mentioned previously by concatenating their contents. Seen below is displaying the contents of email.pcl.



We noticed right away what appeared to be encrypted/hashed strings surrounded by curly braces inside of the email.pcl file after configuring SMTP credentials under the Email Server tab of the web console. We noticed a similar pattern for the http_client.pcl and ldap_<INTEGER>.pcl files.



Following this discovery, we decided to take a look at the public source code for the community edition of Sonatype Nexus Repository Manager 3, which is hosted on GitHub here in an attempt to identify the encryption/hashing logic for these strings.

Source Code Analysis

We quickly identified the code base was using MyBatis as an ORM for OrientDB and we were able to hone in on some code that appeared to be handling the encryption of passwords in the database.



The class responsible for this encryption was named PasswordHelper. A recursive grep command throughout the source directory can reveal all files using the class and can be seen below. We will be taking a closer look at some of the files in the output, so make note of the file names as they will correlate to classes we observe.



Taking a look at the PasswordHelper class itself and the encrypt method, there are some interesting things of note. The class has three variables that are members of the class including ENC, mavenCipher, and phraseService. In the call to the encrypt method of the MavenCipher object, the password to be encrypted is passed as an argument along with the return value from phraseService.getPhrase(ENC).



The method signature for encrypt and the MavenCipher class implementation can be seen below. The takeaway here is that the string passed as passPhrase will be the password for encryption.



If your senses are tingling like mine are at this moment, you are interested in the logic of getPhrase and the use of a static string value being passed to it. On the surface, it looks like the makings for a static encryption password responsible for encrypting other passwords and sensitive data in a database. Sounds like no encryption to me!



Let’s verify our assumptions though. Ultimately, the getPhrase logic can be seen below in the AbstractPhraseService class.



The logic of getPhrase is simple: if hasMasterPhrase is true, the value of getMasterPhrase is returned, otherwise defaultPhrase is returned. As you might imagine, in this context this means the static string (ENC) value is used for all password/secret encryption unless a custom password is specified by an administrator.

Remember that mark method I outlined earlier? That is also of serious interest here. The reason being the developers are tracking encrypted passwords/secrets using “legacy” encryption (a.k.a. using a static encryption password) so that they can decrypt them using the static password later on. This is interesting because it is an indicator that there is no functionality to re-encrypt all data in the database with a custom password created by an administrator. If there was, this marking strategy wouldn’t be necessary. This theory was verified in collaboration with one of our clients, then later verified by Sonatype.

We can also verify the sensitive attributes filters used for encryptSensitiveFields in the MyBatis code along with the SensitiveJsonFactory class.



This would indicate all passwords/secrets stored in JSON data processed by MyBatis are using this encryption logic based on a static hardcoded key. This logic would encapsulate all passwords/secrets except for the HTTP proxy credentials and the SMTP configuration credentials. An example of this is shown below with OrientEmailConfigurationEntityAdapter class.



The SMTP password is encrypted using this logic as shown above. The same can be said for the HTTP proxy configuration details. Seen below is the serialization logic for the HTTP proxy configuration.



As you can see, depending on the authentication type used, the logic will either encrypt the password for a basic/NTLM authentication using PasswordHelper, or a bearer token. With all of our discoveries so far and our suspicions after source code review, it’s time to put everything to the test and see if the encrypted strings we found earlier are using that static encryption key. The encryption wrappers ultimately lead to the encrypt method of the PasswordCipher class and can be seen below.



There are multiple ways to go about testing the static encryption key. If you want the fastest testing, they include their own encryption tests for use with Maven and you could run the PasswordHelper tests with some modifications. If you want something more dynamic and easier to use, you could create a Python script capable of decrypting the Java encryption logic. We obviously chose the latter and our script can be seen below. It is also hosted on GitHub and available for public download/use here.



Remember our dummy LDAP credentials from earlier and how we revealed them from a .pcl file? I couldn’t think of a better test for our script!




As you can see, the decryption was successful using the static encryption key we identified in the source code! Mission success! Did someone say lateral movement into Active Directory via decrypted LDAP credentials? How about sending a phish through a trusted SMTP server at an organization with decrypted SMTP credentials to evade email filtering? I like the sound of that!



I know we are having so much fun, but that’s not the only fun we can have here using the directory traversal vulnerability.



There are also Apache Shiro hashes stored within these .pcl files that are capable of being cracked with a custom hashcat module developed during this research. Read all about this attack vector here.

Remediation Evaluation

Sonatype has addressed the vulnerability and communicated that the issue has been resolved. Their prompt action is commendable, but I wanted to take a closer look to evaluate the effectiveness of the implemented solutions. Sonatype introduced the ability to perform re-encryption of secrets in the database and released a post outlining the steps for re-encryption here. The fix involves setting custom encryption passwords per secret in a JSON file on disk versus the global static, hardcoded key. The problem is the approach still relies on static encryption keys.



Below are the steps for re-encryption according to Sonatype:



You then can inform Nexus about the path to the file by specifying it inside of the nexus.properties file or an environment variable.



It is a good initiative from Sonatype to create this re-encryption functionality and allow administrators to set custom encryption passwords per secret in the database. If administrators decide to use the functionality and data were to get leaked from a system, attackers will not be able to decrypt it using the default static key without file system access. In other words, it offers some protection but is not bulletproof by any means.

If an attacker were to gain file system access, because they do not need to be authenticated to Nexus to view this file or the environment variable (/proc/<pid>/environ), they are easily able to view the path and display the contents of the file. Since the file contains all of the encryption passwords in plaintext, an attacker can easily use the decryption script shared earlier to decrypt credentials in the database.



During discussions with Sonatype, I explained the use of a global static encryption key that is hardcoded is unnecessary and risky in and of itself. I bring this up because Sonatype is still shipping the application with this default static, hardcoded key until an administrator goes through the re-encryption process. Not all administrators may choose to do the re-encryption for whatever reason and there will still be traces of this key throughout Nexus instances. I suggested a randomly generated key that gets created upon initial installation per instance would be a much better approach, if you even wanted to go the route of static encryption keys saved to disk. We very well might see decrypted secrets for years to come due to this static key and installations still coming shipped with it by default.

Conclusion

In conclusion, CVE-2024–5764 allows anyone with file system access to decrypt passwords/secrets stored within the Nexus Repository Manager 3 OrientDB database. An administrator can set a custom password at any point, but anything encrypted with the default static passphrase will remain encrypted with the default indefinitely until the instance is patched and re-encryption takes place. This attack vector allows attackers to decrypt highly sensitive information such as passwords/secrets, allowing access to other systems and can serve as a lateral movement primitive.

Scroll to Top