Mass hunting IDORs using jq and Swagger files

Mass hunting IDORs using jq and Swagger files

In a recent API assessment, I discovered an IDOR; The URL: https://vulnerable-api.com/api/v1/cards?UserId=23 used the userID parameter to fetch the card data, as classic as it gets. Since I had the Swagger files, It was time to identify all IDOR instances; This was not a site-wide IDOR; They had securely implemented authentication and authorization for a few endpoints earlier. Time to crack up those automation muscles!

Follow along

For those interested in following along, I have uploaded a sample swagger test data that you can use to follow along at: https://github.com/akenofu/Mass-hunting-IDORs-using-jq-and-Swagger-files

Why not grep?

Grep is useful for parsing simple raw-text files. However, swagger files contain JSON with recursive nesting; It becomes complex to account for all the nesting and edge cases. Don't believe me? Trying doing this in grep yourself. I tried; I struggled with this for an hour and did not reach any satisfactory result

My non-functional grep regex before giving up on regex was: "(/api)(.|\n)+"get": ?(.|\n)+parameters?(.|\n)+id"

A Brief on jq

jq is a command-line JSON parsing tool. Once you get through the initial learning curve, it simplifies querying JSON files for the information you need.

Ippsec has a good video on using jq to parse bloodhound data:

Anatomy of an IDOR

The IDOR from earlier had a clear pattern; Looking at the IDOR endpoint in the swagger documentation, it was easy to break down the pre-conditions for IDORs in that application

Moving forward, I hypothesized that the IDORs meet the following pre-conditions:

  1. The endpoint expects aGET  request: GET requests are typically sent to fetch information.
  2. The endpoint expects a parameter that follows the naming convention: somethingID; I assumed the Id was used to fetch the data; This did not have to be the case But, it would not make sense to accept parameters and not use them.
Word of caution; Some APIs follow the previous pattern. However, they validate that the user has admin privileges — from the authorization token. In that case, it is not a security issue; It is a feature that enables admins to query any user's information using the API. That being said, the IDOR I identified earlier followed the previous formula. Hence, for automation purposes, I am ignoring this possibility.

Crafting the jq filter

Building the filter was an iterative process. To construct the filter incrementally, I followed the steps below:

  1. Identify the keys using the keys filter

The built-in function keys, when given an object, returns its keys in an array.

2. The paths key contains the endpoints and their operations. Filter everything else by specifying the .paths filter to show only the value inside the paths key

  1. Convert the values to a key-value pair; This will come in handy later when recursing into the value key. Use the to_entries filter to convert the objects' values to key-value pairs.
This function convert between an object and an array of key-value pairs. If to_entries is passed an object, then for each k: v entry in the input, the output array includes {"key": k, "value": v}.

4. The to_entries filter returns an array of key-value pairs; This array needs to be escaped to further filter the data.

5. The .[] filter recurses into the array; Escapes the context of the array

If you use the .[index] syntax, but omit the index entirely, it will return all of the elements of an array. Running .[] with the input [1,2,3] will produce the numbers as three separate results, rather than as a single array.

6. To select only endpoints that have a get key, use the select(.value.get)

The function select(foo) produces its input unchanged if foo returns true for that input, and produces no output otherwise.

7. Select keys where one of the parameter names contains the string "Id"; Use the select(.value.get) | select( .value.get.parameters[].name | test (".*Id") ) filter to accomplish that.

outputs an object for each match it finds. true or false for whether or not the regex matches the input.

8. Select the endpoint path by selecting the uppermost key for objects that satisfy the filter. Append a .key filter to the previous filter.

9. Use the sort CLI tool to remove duplicate items.

Voila! Time to go validate those endpoints for IDORs manually.

Checkout my jq cheat sheet:

jq - HackAllTheThings
The HackTricks we have at home. Except some notes are really extensive and include non-conventional ways of doing things