Back to posts

Access Control and Authorization Testing Workflow for GET HTTP requests

Learn how to create an IDOR testing workflow that will test GET requests with three different types of users and create a report with matching status codes and content length.

REST APIIDORAuthorization
Nenad Zaric

Nenad Zaric

Co Founder & CEO

February 08, 2023
6 mins read

Protecting sensitive information stored on web applications has become increasingly important in today's digital age. However, many organizations are still vulnerable to security threats such as Insecure Direct Object Reference (IDOR) vulnerabilities. IDOR occurs when a web application exposes direct access to sensitive resources using predictable and easily guessable parameters, such as IDs, in URLs.

For instance, a URL like https://example.com/user/123 may reveal information about user 123. If a malicious actor can guess or increment the user ID in the URL, they can access confidential information. This vulnerability can lead to data breaches and financial losses for organizations.

Companies must implement strict security measures and regularly conduct security assessments to prevent these attacks. This blog post will delve into the dangers of IDOR vulnerabilities and show how organizations can identify and remediate these types of security flaws in their web applications. By implementing the proper automation, companies can protect their sensitive information and avoid devastating consequences.

Raw API Paths

To test API for the IDORs, we need to have a list of API endpoints. This can be done by various techniques, which we will cover in the next blog posts.

To execute endpoints immediately, we can copy them from Burp, spidered/crawl data, or through the OSINT tools.

This is the example data:

https://api.example.com/v1/adaccounts/55b7e66e-5219-5431-b909-5f6ca7c266db/campaigns
https://api.example.com/v1/adaccounts/55b7e66e-5219-5431-b909-5f6ca7c266db/campaigns?limit=1000
https://api.example.com/v1/adaccounts/55b7e66e-5219-5431-b909-5f6ca7c266db/campaigns?limit=20
https://api.example.com/v1/adaccounts/55b7e66e-5219-5431-b909-5f6ca7c266db/experiments
https://api.example.com/v1/adaccounts/55b7e66e-5219-5431-b909-5f6ca7c266db/mobile_apps
https://api.example.com/v1/adaccounts/55b7e66e-5219-5431-b909-5f6ca7c266db/premium_content_bundles

We will drag & drop an eof-raw-data node where we can paste the URLs directly.

API Endpoints

This node will output all of the URLs to the out/output.txt or the file output on the node.

Extracting API host, paths and query parameters

Having the URLs, we can separate the data by types to be used as different file inputs later. We can use an awesome unfurl for this.

We will extract three types of data:

  • Host (https://api.example.com)
  • Only paths (/v1/adaccounts/)
  • Paths with query strings (/v1/adaccounts/?id=something)

Getting the URL

By using the %s://%d for custom parameter in unfurl we can extract the https://api.example.com to be used as hosts input

Unfurl API hosts

Getting the raw paths

We can enable the paths boolean flag to enable only path extraction through unfurl. By getting the only paths here we will have wider testing as we will also check the paths (/v1/adaccounts) without any query parameters.

Unfurl API Paths

Getting the paths with query strings

Using the %p%?%q, we can extract the path + query strings to be used as wordlists input.

Unfurl API Queries

Formatting

By dragging the cat-all-in script node, we can merge the only paths and the paths with query strings together, and by using the cut -c2- first character / will be deleted to format the wordlist.

Unfurl API Queries

Configuring ffuf for multiple user requests

As we want to send the exact requests as different users, one of the ways when testing for IDOR vulnerabilities could be to request as a legitimate user, we call him User 1, with an attacker user, we can call it User 2 and anonymous user.

Triple FFUF authorization testing

With this setup, three parallel instances of ffuf will be executed, but with different headers. When executing the workflow, headers should be replaced with the headers off appropriate users.

Formatting ffuf output

For now, we will only test for a status-code or content-length change.

This script will parse `ffuf's output to print out the URL, status and content-length.

cat in/*/* | jq -r '.results | .[]| "\(.url) \(.status) \(.length) \(.redirectlocation) "' | sort -n | uniq | tee out/output.txt

Final result of a single ffuf output:

Triple FFUF authorization testing

output.txt

https://api.example.com/v1 404 0  
https://api.example.com/v1/adaccounts/55b7e66e-5219-5431-b909-5f6ca7c266db/campaigns 401 12  
https://api.example.com/v1/adaccounts/55b7e66e-5219-5431-b909-5f6ca7c266db/campaigns?limit=1000 401 12  
https://api.example.com/v1/adaccounts/55b7e66e-5219-5431-b909-5f6ca7c266db/campaigns?limit=20 401 12  
https://api.example.com/v1/adaccounts/55b7e66e-5219-5431-b909-5f6ca7c266db/experiments 401 12  
https://api.example.com/v1/adaccounts/55b7e66e-5219-5431-b909-5f6ca7c266db/mobile_apps 401 12  

Compare responses

As we want to be quick, ChatGPT could help us here a lot. We provide it with the sample data and ask for a python script with an exact output.

We also asked it to compare the responses and to put WARNINGs where User1 and User2 have the same status-code and content-length.

Triple FFUF authorization testing

In the script nodes, we can firstly copy the files, install the python, paste the script and execute it at the end.

# User 1

cat in/jq-1/output.txt > user1.txt

# User 2

cat in/jq-2/output.txt > user2.txt

# Anonymous
cat in/jq-3/output.txt > anonymous.txt

apt-get update && apt-get upgrade > /dev/null 
apt-get install -y python3 python3-pip > /dev/null

cat << "EOF" > compare.py
import csv

# Create a dictionary to store the URLs and their status codes and content length for each user
urls = {}

# Open each file and extract the URLs and their status codes and content length
files = ['user1.txt', 'user2.txt', 'anonymous.txt']
for file in files:
    with open(file, 'r') as f:
        for line in f:
            parts = line.strip().split('')
            url = parts[0]
            status_code = parts[1]
            content_length = parts[2]

            if url not in urls:
                # If the URL is not in the dictionary, add it with its status code and content length
                urls[url] = {'user1': ('', ''), 'user2': ('', ''), 'anonymous': ('', '')}

            # Update the status code and content length for the current user
            if 'user1' in file:
                urls[url]['user1'] = (status_code, content_length)
            elif 'user2' in file:
                urls[url]['user2'] = (status_code, content_length)
            elif 'anonymous' in file:
                urls[url]['anonymous'] = (status_code, content_length)

# Write the results to a CSV file
with open('out/output.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerow(['URL', 'User1', 'User2', 'Anonymous', 'Warning'])
    for url, values in urls.items():
        user1_status_code, user1_content_length = values['user1']
        user2_status_code, user2_content_length = values['user2']
        anonymous_status_code, anonymous_content_length = values['anonymous']

        if user1_status_code == user2_status_code and user1_content_length == user2_content_length:
            writer.writerow([url, f'{user1_status_code} {user1_content_length}', f'{user2_status_code} {user2_content_length}', f'{anonymous_status_code} {anonymous_content_length}', 'WARNING: User1 <> User2'])
        elif user1_status_code == anonymous_status_code and user1_content_length == anonymous_content_length:
            writer.writerow([url, f'{user1_status_code} {user1_content_length}', f'{user2_status_code} {user2_content_length}', f'{anonymous_status_code} {anonymous_content_length}', 'WARNING: User1 <> Anonymous'])
        elif user2_status_code == anonymous_status_code and user2_content_length == anonymous_content_length:
            writer.writerow([url, f'{user1_status_code} {user1_content_length}', f'{user2_status_code} {user2_content_length}', f'{anonymous_status_code} {anonymous_content_length}', 'WARNING: User2 <> Anonymous'])
        else:
            writer.writerow([url, f'{user1_status_code} {user1_content_length}', f'{user2_status_code} {user2_content_length}', f'{anonymous_status_code} {anonymous_content_length}', ''])
EOF

python3 compare.py

API Compare Responses

Results

Nice! When the script is finished, it will output a CSV file that can be downloaded and used.

API Compare Responses

Conclusion & Improvements

This workflow was made with as always, flexibility in mind. It should improve the pentester's productivity when testing to very specific and dangerous vulnerability affecting API endpoints.

The workflow could also be scheduled and configured to alert on differences. This way you can import the endpoints in your CI/CD and execute an IDOR testing workflow in a few clicks with trickest-cli and Trickest GitHub Action.

Some of the improvements of this workflow:

  • Figure out the ways to get API endpoints from different sources and techiques
  • Create a separate logic on handling different types of requests (GET,POST,PUT,OPTIONS,PATCH)
  • Implement the 403 bypasses on the API endpoints

If you enjoyed this blog post and like the idea of automating your work, sign up to explore the platform and solutions.

Get a PERSONALIZED DEMO

See Trickest
in Action

Gain visibility, elite security, and complete coverage with Trickest Platform and Solutions.