25 June 2021

Mass Assignment exploitation in the wild – Escalating privileges in style

SRT Community

By Gal Nagli

General

Since I was accepted to Synack’s Red Team at the beginning of March, I had to conduct a “mental shift”. While most of the lucrative bounty programs on the public platforms will be the wildcard scoped ones, it’s mostly the contrary while hunting on Synack.

Most hunters will receive 2-3 new application testing invites each week which starts with an 8 hour Initial Launch Period. During the Initial Launch period, it’s not about being the first to report the issue, it’s about the best report and finding as many vulnerable parameters as possible. Those invites mostly involve post-authentication webapp testing for big organizations.

Testing often exposes some unique vulnerabilities and attack vectors to explore.

During one of those engagements, I found a Mass Assignment vulnerability, which helped me escalate privileges from a normal user without any permissions to a fully permissive administrator account.

Mass Assignment

As the vulnerability of “Mass Assignment” is not often talked about and there are not many write ups / disclosed reports, I’ll briefly explain the core nature of it.

as per OWASP

Software frameworks sometimes allow developers to automatically bind HTTP request parameters into program code variables or objects to make using that framework easier on developers. This can sometimes cause harm.

Attackers can sometimes use this methodology to create new parameters that the developer never intended, which in turn creates or overwrites new variables or objects in program code that was not intended.

Adding to that, as per OWASP API top 10, Mass Assignment is ranked at 6th place, following its definition:

Binding client provided data (e.g., JSON) to data models, without proper properties filtering based on an allowlist, usually leads to Mass Assignment. Either guessing object’s properties, exploring other API endpoints, reading the documentation, or providing additional object properties in request payloads, allows attackers to modify object properties they are not supposed to.

To understand the attack vector better, let’s take a look at the following image:

credit

The image above demonstrates a scenario which was very similar to my own finding.

As we can see from the image, we are dealing with an API which accepts JSON objects from a client. Those are common on APIs when we want to update our account information, for example changing our phone number or email address.

We are being presented with a scheme which in most cases will be represented in a UI as text fields to fill before submitting the request by a button click, such as: address, email, first name and so on.

The user update panel often differs between normal user and administrator, as those often possess more functionality to assist the site owner. The displayed UI might look the same, but the JSON object submitted within the request might have additional sensitive parameters involved.

It’s pretty clear now how to exploit this vulnerability, as the sensitive fields are not presented to the regular user in his UI or upon his user update request.

There might be a misconfiguration in the Authorization model within the application which will accept the hidden JSON fields from the non-permissive user account and will process it as if was an administrator.

Taking it to the example above, imagine the user adds to his request the following key:value pair:

“role”:”admin”

As the server won’t enforce any authorization check, the user will become an administrator by issuing a single PUT request.

You might have wondered by now: “How can we as regular users without any permissions have guessed the hidden key:values pairs?”

There are 2 main vectors to find those, which can be divided into “Blackbox” and “Whitebox” approaches.

Blackbox Approach

As for “Blackbox” testing, we can use Burp’s Param Miner extension when we have an HTTP request which sends data within JSON format. We can use the the “Guess JSON parameters” option, which will bruteforce for common JSON params and see if it affects the server response. So by issuing the Param Miner probe, we can guess for those hidden sensitive fields.

Whitebox Approach

If we have administrator credentials, we should navigate through the supplied account, mostly into the profile update fields or new user invitations, mainly looking for those PUT or POST JSON-based requests and note down the JSON parameters supplied in those.

Later, we should approach the application the same way with a normal user account. By matching and comparing the JSON params available for a none-permissive account to an administrator we can determine and craft payloads based on the differences.

Exploitation

Now we can dive into the actual vulnerability I managed to find during the application assessment.

As for certain application testing for a client within Synack, we were presented with a post-auth application with several user roles. There were editors, viewers and administrators.

As a general approach with similar applications, I’d look for potential endpoints which could be vulnerable to Privilege Escalation, as those often require studying the application behaviour and will most likely have less competition going over the Initial Launch Period results later on.

We were presented with only an administrator account throughout the testing period.

As I was navigating throughout the application, I found an endpoint which gave us the ability to invite new users to the organization environment.

https://example.com/app/users/addNew

Therfore I invited myself for an additional account with a “Viewer” permissions only, so I could conduct the testing in an efficient manner.

Logging into the new user I have created, I went to my personal profile page located at

https://example.com/app/profile

We were presented with 3 fields only:

Name, Email and Language

Clicking on the “Save Changes” button presented us with the following HTTP request:

POST /web/api/user/v1/edit HTTP/1.1
Host: XXXXXXXXXXXXX
Connection: close
Content-Length: 184
sec-ch-ua: “Google Chrome”;v=”89″, “Chromium”;v=”89″, “;Not A Brand”;v=”99″
Accept: application/json, text/plain, */*
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36
Content-Type: application/json;charset=UTF-8
Origin: XXXXXXXXX
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: XXXXXXXX
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8,eu;q=0.7,he;q=0.6
Cookie: XXXXXXXX

{“uuid”:”user UUID”,”updates”:{“name”:”NAME”,”email”:”EMAIL”,”enabled”:true}}

a JSON object with the fields presented at the UI, so it looks fine up to this point.

Going to the Administrator account, I went to the same endpoint and issued the same request by clicking on the “Save Changes” button, only this time to see the following:

POST /web/api/user/v1/edit HTTP/1.1
Host: XXXXXXXXXXXXX
Connection: close
Content-Length: 184
sec-ch-ua: “Google Chrome”;v=”89″, “Chromium”;v=”89″, “;Not A Brand”;v=”99″
Accept: application/json, text/plain, */*
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36
Content-Type: application/json;charset=UTF-8
Origin: XXXXXXXXX
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: XXXXXXXX
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8,eu;q=0.7,he;q=0.6
Cookie: XXXXXXXX

{“uuid”:”user UUID”,”updates”:{“name”:”NAME”,”email”:”EMAIL”,”roles”:[“administrator”],”enabled”:true}}

So this one was interesting, I was presented with the same UI on both requests, but this time when I approached further and saved my new profile details as the Administrator there was an additional field presented, the “roles” one.

Although I didn’t really believe that it would work, all I did was navigate to the “Viewer” user account, again capturing the “Save Changes” request, only this time adding the key-value pair of:

“roles”:[“administrator”]

To the JSON object.

Upon issuing the POST request I could observe the 200 OK response, with a value of OK – indicating that the exploitation may have worked, as there was no indication of error.

And so we managed to escalate from normal user to administrator account with a simple POST request on the user profile edit area.

You may have noticed that it was easier to find the sensitive hidden parameter from the Whitebox approach, although the parameter name is pretty common and could have been found with Param Miner on Blackbox testing as well.

Wrapping up the report

Concluding the exploitation above, those were the steps taken as it was stated in the original report:

Navigate to
https://example.com/app/users/addNew
and add a new account with limited permissions

Log in with the new user account and navigate to
https://example.com/app/profile
Click on the “Save changes button” and capture the request

Add to the JSON scheme the following:

“updates”:{“name”:”hello”,”email”:”your email”,”roles”:[“administrator”],”enabled”:true}}

The User account has successfully escalated his privileges to Administrator.

Recommended fix

In order to block Mass Assignments vulnerabilities, there is a great cheatsheet here:

OWASP Cheatsheet

We should ensure that the roles parameter is bound to the Administrator account. We can’t allow it to be reached by normal users to tamper with. Or, simply do not allow the low privileged user accounts to add any other params than it’s name when they want to update their profile.

Conclusion

The Initial Launch Period for this specific application assessment had 40 reports submitted within the first 8 hours of the engagement.

I was the only one who reported this specific privilege escalation issue leveraging the Mass Assignment vulnerability.

It had a CVSS score of 8.1 and my payment from Synack received an additional criticality bonus.


Report Timeline

  • April 15th 14:00 GMT – Report Submitted
  • April 16th 00:20 GMT – Report accepted and Rewarded
  • April 16th 12:44 GMT – Critical bonus awarded

Thanks for sticking out!

Some Social Links: