Platform: TryHackMe · Difficulty: Easy · Category: Client-Side Authentication Anti-Pattern
Introduction
No exploit. No payload. No shell. The entire "challenge" lives in a browser's View Source and it took longer to write this sentence than it took to solve the box. That sounds like a joke, but it's actually the most important lesson in this whole series: if your authentication logic runs in the browser, it's not authentication, it's a suggestion the attacker is free to ignore.
CyberHeroes has the audacity to call itself a challenge while leaving its front door not just unlocked, but labelled "FRONT DOOR" in neon letters. It's a single-page portfolio site with a login form that genuinely believes security is a server-side problem. Spoiler: they didn't check with the server. If this box were a bank, the vault combination would be written on the outside of the vault in permanent marker. It's that kind of day.
Recon
Same opening move as always — RustScan for speed, Nmap for detail.
rustscan -a 10.49.154.83 -r 1-65535 --ulimit 5000 -- -Pn -sC -sV
| Port | Service | Version |
|---|---|---|
| 22/tcp | SSH | OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 |
| 80/tcp | HTTP | Apache httpd 2.4.48 (Ubuntu) |
Two ports, nothing exotic. Apache 2.4.48 is not ancient like some of the others in this series — this one isn't about outdated services. The HTTP title comes back as CyberHeros : Index. Typo in the page title is always a great sign of quality software engineering.
Feroxbuster with PHP, TXT, and HTML extensions across the medium wordlist:
feroxbuster -u http://10.49.154.83/ \
-w /usr/share/wordlists/seclists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txt \
-x php,txt,html
# output (relevant hits only)
200 http://10.49.154.83/index.html
200 http://10.49.154.83/login.html # ^^^ this is where the fun is
200 http://10.49.154.83/changelog.txt
(truncated — the rest was asset files: images, vendor JS/CSS, nothing actionable)
login.html is the obvious next stop. changelog.txt is worth a read — it appeared to be a generic template changelog with no sensitive information — so the login page it is.
Foothold
Authentication is supposed to be validated on the server — a system the attacker cannot directly inspect or manipulate. When a developer moves that logic into client-side JavaScript, they've handed the attacker the padlock, the key, the combination, and a handwritten note explaining how the lock works. The browser must download and execute all JavaScript to render the page — which means the attacker can read every line of it before touching a single input field.
Navigating to login.html and hitting Ctrl+U (View Source) immediately reveals the authenticate() function:
function authenticate() {
a = document.getElementById('uname')
b = document.getElementById('pass')
const RevereString = str => [...str].reverse().join('');
if (a.value=="h3ck3rBoi" & b.value==RevereString("54321@terceSrepuS")) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("flag").innerHTML = this.responseText ;
document.getElementById("todel").innerHTML = "";
document.getElementById("rm").remove() ;
}
};
xhttp.open("GET", "RandomLo0o0o0o0o0o0o0o0o0o0gpath12345_Flag_"+a.value+"_"+b.value+".txt", true);
xhttp.send();
}
else {
alert("Incorrect Password, try again.. you got this hacker !")
}
}
Breaking this down:
-
Username is hardcoded in the comparison:
h3ck3rBoi -
Password is the string
54321@terceSrepuSpassed throughRevereString(), which reverses it character by character — yieldingSuperSecret@12345 - On success, the script constructs a GET request to a
.txtfile at an "obscure" path that's... right there in the source, assembled from the username and password values
The credentials, the reversal algorithm, and the flag's file path are all completely readable before submitting a single form field.
| Username | Password | Where Found |
|---|---|---|
h3ck3rBoi |
SuperSecret@12345 |
login.html JavaScript source |
Substituting the known credential values into the path template from the JS:
RandomLo0o0o0o0o0o0o0o0o0o0gpath12345_Flag_h3ck3rBoi_SuperSecret@12345.txt
This file can be fetched directly without ever touching the login form:
curl "http://10.49.154.83/RandomLo0o0o0o0o0o0o0o0o0o0gpath12345_Flag_h3ck3rBoi_SuperSecret@12345.txt"
Or simply by entering the credentials into the login form, which triggers the XHR fetch and injects the response into the page.
Shell / Access
No shell required for this challenge. Access to the flag was achieved entirely via browser interaction with the login page after reading the client-side source.
Flags
| Flag | Value | Location |
|---|---|---|
| Challenge Flag | flag{edb0be532c540b1a150c3a7e85d2466e} |
Fetched via XHR after login — or direct URL access |
Mitigations
| Vulnerability | Severity | Mitigation |
|---|---|---|
| Hardcoded credentials in client-side JavaScript | Critical | Move all authentication logic server-side; never compare credentials in the browser |
| Plaintext (reversed) password obfuscation | Critical | String reversal is not encryption — use proper server-side password hashing (bcrypt, Argon2) |
| Predictable flag file path assembled client-side | High | Flag delivery should be gated by a server-side session check, not a client-constructed URL |
Key Takeaway
Anything that runs in the browser is readable by the user — no amount of string reversal, obfuscation, or "obscure" path names changes that. Authentication logic belongs on the server, full stop.
Room: TryHackMe — CyberHeroes
Top comments (0)