Skip to content

Lab 3 - Authorization Attacks and Defense

Lab 2 dealt with Authentication ("AuthN"). We were able to impersonate other users by manipulating authentication tokens. In this lab, we will move on to the next step: Access Control or Authorization ("AuthZ"). An existing user will attempt to access data they are not supposed to be able to access.

Time allocation

25 minutes

BOLA: Broken Object Level Access - Attacking

Please launch your browser and visit the URL http://www.sansapi.com/. Just like before, log in if you are not already logged in.

Step 1: Load the User Info page (button at the top of the page). You may remember it from the last exercise.

Top part of user info page.

Step 2: Click on the BOLA link. BOLA is short for "Broken Object Level Access." A user has access to an object they are not supposed access, due to insufficient checking or control by the application. In this case, a user may access another user's information.

Step 3: Open the developer tools in your browser. Use Ctrl + Shift + I or F12 on Windows and Linux. On macOS, use Cmd + Opt + I or F12 to enable the Tools. In Chrome, select the "Application" tab and open the Local Storage for "https://www.sansapi.com". For Firefox, use the Storage tab. Please do not confuse it with the Local Storage used by Keycloak. Its origin is https://auth.sansapi.com. We need the origin without a port number.

Chrome:

Local Storage for www.sansapi.com

Firefox:

Step 4: Edit the value for the "user_id" to "10" and reload the page. You will now see information for a different user. If nothing shows up, that means that user_id does not exist, try another user_id.

Result for user 10

You should not have access to this user's API Key and credit card number.

Let's explore the inner workings of this vulnerability.

Firefox

In the Developers Tool, select the Network tab. Then refresh the BOLA page (https://www.sansapi.com/userinfobola.html). This will reload the userinfo for the user specified in the Local Storage value. Right click on the most recent entry (scroll to the bottom of the request list) that is a GET request to the user_id number you selected earlier. This brings up a panel to the left which allows us to edit the HTTP request before sending to the API/server. Alter the number at the end of the URL to view other users data. If you don't know what user_id are available for querying, querying for the entire list of user information is possible. In a new browser tab, change the URL to

https://api.sansapi.com/api/user

to acquire the entire list of users in the system. The user_id field can then be used to query other users sensitive information. Note that the most sensitive information apikey and creditcard can only be queried if you use the /user/<user_id> path. The /user path only yield more general information. Once ready, with the request, hit send at the bottom of the menu to send the request. See screenshot below.

Postman

In the Developers Tool, select the Network tab. Then refresh the BOLA page (https://www.sansapi.com/userinfobola.html). This will reload the userinfo for the user specified in the Local Storage value. Right click on the most recent entry (scroll to the bottom of the request list) that is a GET request to the user_id number you selected earlier. Switch over to Postman and paste the information to the URL bar You are now able to click the send button to submit the request to the API and the response will be at the bottom panel. Alter the number at the end of the URL to view other users data. If you don't know what user_id are available for querying, querying for the entire list of user information is possible. In a new browser tab, change the URL to

https://api.sansapi.com/api/user

then hit send to acquire the entire list of users in the system. The user_id field can then be used to query other users sensitive information. Note that the most sensitive information apikey and creditcard can only be queried if you use the /user/<user_id> path. The /user path only yield more general information.

In summary, the /userbola API endpoint is vulnerable because it does not verify if the user requesting sensitive information has the proper authorization to do so. Although authentication is correctly handled by Kong, which validates the Authorization HTTP header sent by Postman/browser in the request, the API itself fails to check whether the user is actually allowed to access the data.

Defending against BOLA

Step 1: To fix this vulnerability, we must only trust data digitally signed by the authentication server and then validate the requestor having access to the data requested. Return to the "green" User Info page at https://www.sansapi.com/userinfo.html.

Step 2: Like before, alter the user_id parameter in Local Storage. Reload the page. Not only is the user id ignored, but it is also reset.

Step 3: Use the network tab in the browser's developer tools. Find the request for the user data. You will likely only see the number "1". It should be the last request.

Network Requests for user API call

Step 4: Right click on the number "1" and select "Copy" -> "Copy URL".

Copy URL

The URL should be https://api.sansapi.com/api/user/1.

Step 5: Inspect the request, specifcally the Headers tab of the request and then also review the Response tab. The request observed triggers the retrieval of user information from the api.sansapi.com host, where it specifically requests details for a user with user_id 1. However, instead of returning information for user_id 1, the system returns the correct information for the user who made the request, suggesting that the system is disregarding the user_id parameter in the URL and instead returns data based on the authenticated user making the request. This indicates a potential security feature that restricts data access to the requesting user.

alt text

Step 6. While still on the same request also review the authorization header under the header tab. The bearer token is the key to our puzzle, this is our JWT token that we inspected in earlier part of the exercise. The token contains information about the user's identity in OIDC format.

The application will include the OIDC token in any requests it sends to the API. The Kong API gateway first receives the request. Kong will verify the token's signature and verify that the user is authenticated. If the user is authenticated, the request is passed to the API. The API will retrieve the username from the token and verify if the user is allowed to access the data.

We need to include the OIDC token to use the properly configured API. This token is used by the API gateway to authenticate the request. The API itself will extract the user name from the token and return only data the user is authorized to see. In this case, the API ignores the numeric user ID passed in the URL. The diagram below depicts the process of validating the authentication and authorization.

BFLA: Broken Function Level Access Control

So far, we only used the "GET" method to connect to the /user endpoint. Broken Function Level Access Control refers to APIs controlling which endpoint a user may access but not properly restricting which methods may be used. "GET" retrieves data. But "POST", "PUT", and "DELETE" will alter data. Try to modify the data using any of these three methods.

Warning

In this section, you may alter data. Please be careful. You may want to setup a second account to experiment with. You may also use accounts with user IDs less than 14. Please do not spoil the exercise for others.

For this exercise, you will first capture a request. Next, you will modify the method, provide a payload, and replay the request. The documentation here covers Firefox and Chrome/Postman. But you may use other tools like the command line utility curl.

Step 1: Open Firefox and open the Developer Tools in Firefox (reminder: F12).

Step 2: Select the "Network" tab. Optionally, you may want to click "XHR" to only display XHR and fetch requests. This will make it easier to find the correct requests.

Firefox Developer Tools, Network Tab

Step 3: Reload the "User Info" page.

Scroll down if you prefer Chrome and Postman

Firefox

Step 4: Right-click the last request in the "Network" tab (GET request). The "File" column should display the number "1". Select "Edit and Resend." Edit and Resend the user API request

Step 5: Adjust the request:

  • adjust the method from "GET" to "POST"
  • remove the number "1" from the URL
  • add a header: "Content-Type: application/json"
  • add a user object as the body (you can copy it from the response returned to the original request)

The user object has to be JSON formatted like:

{
    "user_id": 14,
    "firstname": "Johannes",
    "lastname": "Ullrich",
    "email": "jullrich@sans.edu",
    "address": null,
    "apikey": "q02hg9",
    "creditcard": "4516655060277755"
}
Step 6: Click "Send" to submit the request. If you just copied the data for an existing user, you will receive a "409 Conflict" error. The email address must be unique.

Step 7: Adjust the email address to be unique. For example, add a few numbers to the end of the user name. Send the request again. It should work now.

You may use the "BOLA" vulnerability to verify the user's record.

Postman

Step 4: Find the request for the user information in Chrome's developer tools "Network" tab. Right click and "Copy as cURL". Chrome copy as cURL

Step 5: Open Postman, and paste the cURL command you just copied. Paste the entire command, not just the URL. Paste request into Postman

Step 6: Remove "1" from the end of the URL.

Step 7: Change the method from "GET" to "POST". Postman alter URL and Method

Step 8: Click "Headers" and add a Content-Type: application/json header.

Step 9: Select "Body". In the content type dropdown select "raw".

Step 10: Copy and paste the user object received in the response as a body for this request. Do not copy the square brackets surrounding the response.

Step 11: Click "Send". The response should be a conflict. The e-mail address has to be unique. This error is expected. Postman: response with conflict

Step 12: Alter the e-mail address in the request body and try again.

In summary, Broken Function Level Authorization (BFLA) occurs when an application fails to properly authorize users before allowing them to access or perform specific functions. This vulnerability enables attackers to perform actions or access data that they are not authorized to do. In the context of APIs, a common example of a BFLA flaw is when an endpoint that should only allow read access (GET request) is inadvertently configured to allow updates (PUT request), granting unauthorized modification privileges to attackers.

Bonus: Authorization Bypass Challenge

Do you remember that the API should validate the OIDC token before extracting data from it? This challenge will illustrate why it is so essential to validate the signature.

Especially in cloud-native applications, users can access APIs directly without the API Gateway's help. The API must authenticate the user based on the information provided in the OIDC token. For us, the user provides the OIDC token to the API. The OIDC token includes the user's name and email address.

In this challenge, we will simulate direct access to the backend /user API endpoint and see how you can spoof the identity.

Step 1. Visit the web page of https://bypass.sansapi.com/user/. This "GET" request does not require authentication. It returns a list of users without apikey and credit card data.

User list in Firefox

Step 2. Please turn on "Developer Tools". Next, visit https://bypass.sansapi.com/user/1. You will see an error message: "Unauthorized, user token not recognized." The error message suggests that this endpoint requires an authentication token.

Error attempting to retrieve individual user

Our API gateway, Kong, will, by default, insert a header "x-userinfo". This header includes data parsed from the OIDC token. The API may use this header to authenticate the user.

The OIDC token sent via Kong does not include the JWT header or the signature that usually accompanies the OIDC token. There is no signature for validation. Let's see if this helps us with our attack.

Step 3. There are two options for these instructions, depending on your skill level. The goal of the exercise is to spoof the "x-userinfo" header.

Kong inserts an x-userinfo header into requests it passes to the API. This header is not digitally signed. It is supposed to be only used if it is certain that Kong created the header. However, an API expecting the header from other sources may be vulnerable.

Option 1: More Challenging

In this version, you will do most of the work yourself.

Step 1. You need to obtain an OIDC token. For example, you can copy it from the console after loading the home page. Or you can find the token in requests sent to the API from the index page (https://www.sansapi.com)

Keycloak OIDC in Firefox console.

You only need to copy the "middle part", not the header or signature. The token format is [header].[data].[signature]. You only need to copy the [data] part.

Step 2. Decode the BASE64 encoding. For example, use https://base64decode.org. Decoding the token will allow you to edit the data. You may use another BASE64 decoder if you prefer.

Base64 Decode the data section of the token

Step 3. Edit the decoded JSON data. Replace the original email address with the email address of another user. Pick one from the '/user' API endpoint.

modified decoded token

Use the https://base64encode.org to encode the token in BASE64 again. Copy the base64 encoded token.

Step 4. Right click on the request for "/user/1" in Firefox's developer tool "Network" tab, and select "Edit and Resend". Or copy the request as curl and paste it into Postman.

Step 5. Add the "x-userinfo" header, and submit the request. It should work and retrieve this user's data.

Option 2: Simpler Option

  • Option 2 - Below is a sample token. We have already removed the header and the signature. It is long; scroll to the right:

    eyJhenAiOiJhbmltYWxzaGVsdGVyIiwiYXV0aF90aW1lIjoxNzE1MDAwODc0LCJzZXNzaW9uX3N0YXRlIjoiYmJjZDRhN2EtNWMzNi00NmM4LWIyMzUtNjQ1NDcxMDkxMDZhIiwiZmFtaWx5X25hbWUiOiJ0ZXN0IiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHBzOi8vd3d3LnNhbnNhcGkuY29tIiwiaHR0cHM6Ly9zYW5zYXBpLmNvbSJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1hbmltYWxzaGVsdGVyIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiIsInVzZXIiXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJqdGkiOiJhMGZjMWRmMi05Zjc2LTQwNjItYWViNS0yOTliNTA2ZTg1YjMiLCJub25jZSI6Ijk0MDIwOTgzLWYzNTYtNDFlYi1hMWI1LTY0MmRlMDZmMDRlOSIsImlzcyI6Imh0dHBzOi8vd3d3LnNhbnNhcGkuY29tOjg0NDMvcmVhbG1zL0FuaW1hbHNoZWx0ZXIiLCJleHAiOjE3MTUwMDk1NzksImF1ZCI6ImFjY291bnQiLCJpYXQiOjE3MTUwMDIzNzksInVzZXJuYW1lIjoidGVzdEB0ZXN0LmNvbSIsInR5cCI6IkJlYXJlciIsImlkIjoiYThlMDRkMjgtYWFjYy00ZTZjLWFjNWQtZDRhMjY3NjVkMmQwIiwiZW1haWwiOiJ0ZXN0QHRlc3QuY29tIiwiZ2l2ZW5fbmFtZSI6InRlc3QiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ0ZXN0QHRlc3QuY29tIiwiYWNyIjoiMCIsIm5hbWUiOiJ0ZXN0IHRlc3QiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInN1YiI6ImE4ZTA0ZDI4LWFhY2MtNGU2Yy1hYzVkLWQ0YTI2NzY1ZDJkMCIsInNjb3BlIjoib3BlbmlkIGVtYWlsIHByb2ZpbGUiLCJzaWQiOiJiYmNkNGE3YS01YzM2LTQ2YzgtYjIzNS02NDU0NzEwOTEwNmEifQ==
    

Step 4. Use https://www.base64decode.org/ to decode the token. The decoded token can look like the following. Edit the username and email fields to the target you are spoofing. How do you find the targets? Earlier, you visited https://bypass.sansapi.com/user/. Use one of the email addresses you found.

{
  "azp": "animalshelter",
  "auth_time": 1725000874,
  "session_state": "bbcd4a7a-5c36-46c8-b235-64547109106a",
  "family_name": "test",
  "allowed-origins": [
    "https://www.sansapi.com",
    "https://sansapi.com"
  ],
  "realm_access": {
    "roles": [
      "default-roles-animalshelter",
      "offline_access",
      "uma_authorization",
      "user"
    ]
  },
  "resource_access": {
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "jti": "a0fc1df2-9f76-4062-aeb5-299b506e85b3",
  "nonce": "s94020983-f356-41eb-a1b5-642de06f04e9",
  "iss": "https://auth.sansapi.com/realms/Animalshelter",
  "exp": 1735009579,
  "aud": "account",
  "iat": 1725002379,
  "username": "test@example.com",
  "typ": "Bearer",
  "id": "a8e04d28-aacc-4e6c-ac5d-d4a26765d2d0",
  "email": "test@example.com",
  "given_name": "test",
  "preferred_username": "test@example.com",
  "acr": "0",
  "name": "test test",
  "email_verified": false,
  "sub": "a8e04d28-aacc-4e6c-ac5d-d4a26765d2d0",
  "scope": "openid email profile",
  "sid": "bbcd4a7a-5c36-46c8-b235-64547109106a"
}

Insert the target's email address in the username and the email field, replacing the original values. Use https://www.base64encode.org to base64 encode the token.

Step 5. The best part of the challenge here is to leverage the new OIDC token you created. You can do this through Firefox or Postman. Trigger the browser to https://bypass.sansapi.com/user/1 and use that as a template to create the new request. The new request will contain an extra HTTP header of the name x-userinfo and the value is the new base64 encoded token.

Hint

Here is the base64 encoded token to attack jane.smith@example.com

eyJhenAiOiJhbmltYWxzaGVsdGVyIiwiYXV0aF90aW1lIjoxNzI1MDAwODc0LCJzZXNzaW9uX3N0YXRlIjoiYmJjZDRhN2EtNWMzNi00NmM4LWIyMzUtNjQ1NDcxMDkxMDZhIiwiZmFtaWx5X25hbWUiOiJ0ZXN0IiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHBzOi8vd3d3LnNhbnNhcGkuY29tIiwiaHR0cHM6Ly9zYW5zYXBpLmNvbSJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1hbmltYWxzaGVsdGVyIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiIsInVzZXIiXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJqdGkiOiJhMGZjMWRmMi05Zjc2LTQwNjItYWViNS0yOTliNTA2ZTg1YjMiLCJub25jZSI6Ijk0MDIwOTgzLWYzNTYtNDFlYi1hMWI1LTY0MmRlMDZmMDRlOSIsImlzcyI6Imh0dHBzOi8vYXV0aC5zYW5zYXBpLmNvbS9yZWFsbXMvQW5pbWFsc2hlbHRlciIsImV4cCI6MTczNTAwOTU3OSwiYXVkIjoiYWNjb3VudCIsImlhdCI6MTcyNTAwMjM3OSwidXNlcm5hbWUiOiJqYW5lLnNtaXRoQGV4YW1wbGUuY29tIiwidHlwIjoiQmVhcmVyIiwiaWQiOiJhOGUwNGQyOC1hYWNjLTRlNmMtYWM1ZC1kNGEyNjc2NWQyZDAiLCJlbWFpbCI6ImphbmUuc21pdGhAZXhhbXBsZS5jb20iLCJnaXZlbl9uYW1lIjoidGVzdCIsInByZWZlcnJlZF91c2VybmFtZSI6ImphbmUuc21pdGhAZXhhbXBsZS5jb20iLCJhY3IiOiIwIiwibmFtZSI6InRlc3QgdGVzdCIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwic3ViIjoiYThlMDRkMjgtYWFjYy00ZTZjLWFjNWQtZDRhMjY3NjVkMmQwIiwic2NvcGUiOiJvcGVuaWQgZW1haWwgcHJvZmlsZSIsInNpZCI6ImJiY2Q0YTdhLTVjMzYtNDZjOC1iMjM1LTY0NTQ3MTA5MTA2YSJ9