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:
- The endpoint expects a
GET
request: GET requests are typically sent to fetch information. - 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:
- 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
- Convert the values to a key-value pair; This will come in handy later when recursing into the
value
key. Use theto_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 functionselect(foo)
produces its input unchanged iffoo
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
orfalse
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.