Summary
CyberHeros is an easy-rated web challenge built on the iPortfolio Bootstrap template. The site advertises a "login page" challenge directly in its About section. Inspection of login.html reveals that authentication is performed entirely client-side in JavaScript, with the username hardcoded in plaintext and the password obfuscated by a trivial string-reversal function. Once the credentials are recovered from the page source, the same client script reveals the exact filename of a flag file hosted on the webserver, constructed dynamically from the submitted username and password. No exploitation of the server itself is required - the vulnerability is a client-side logic flaw combined with a predictable, credential-derived file path.
- Target:
<MACHINE_IP> - Category: Web
- Difficulty: Easy
- Key weakness: Client-side authentication with hardcoded/obfuscated credentials in JS, plus a predictable flag filename built from those credentials
1. Reconnaissance
Confirmed host is up and scanned open ports:
nmap -A -Pn <MACHINE_IP> -o nmap
Results:
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.4
80/tcp open http Apache httpd 2.4.48 (Ubuntu)
http-title identified the site as "CyberHeros : Index", running the iPortfolio Bootstrap template.
2. Initial Web Enumeration
Pulled the index page and reviewed static assets (aos.js, purecounter.js, validate.js) - all confirmed to be unmodified template vendor libraries, no custom logic there.
The index page's About section directly hints at the objective:
We find vulnerabilities in the website legally... find the vuln on our login page and login to join us.
Nav bar links to login.html.
3. Directory Enumeration
gobuster dir -u http://<MACHINE_IP> -w /usr/share/wordlists/dirb/common.txt -x php,txt,html,bak -t 50
Notable results:
assets (Status: 301)
changelog.txt (Status: 200) [Size: 2756]
index.html (Status: 200) [Size: 6568]
login.html (Status: 200) [Size: 5753]
No PHP backend, no admin panel, no backup files - confirming the target is static/client-side only.
4. Inspecting login.html
Fetched the login page source directly:
curl http://<MACHINE_IP>/login.html
The page ships an inline <script> block containing the entire authentication logic:
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;
}
};
xhttp.open("GET", "RandomLo0o0o0o0o0o0o0o0o0o0gpath12345_Flag_"+a.value+"_"+b.value+".txt", true);
xhttp.send();
}
}
Two things fall out of this immediately:
- The credential check happens entirely client-side, with the username hardcoded (
h3ck3rBoi) and the password only lightly obfuscated via string reversal. - On a successful check, the script requests a flag file whose name is dynamically built from the raw username and password values - meaning the filename itself is fully derivable from the source, with no need to actually trigger the JS in a browser.
5. Recovering the Credentials
Reversing the obfuscated password string by hand:
54321@terceSrepuS -> SuperSecret@12345
Recovered credentials:
username: h3ck3rBoi
password: SuperSecret@12345
6. Retrieving the Flag
Reconstructed the flag filename directly from the JS template string and requested it with curl, bypassing the browser/JS entirely:
curl "http://<MACHINE_IP>/RandomLo0o0o0o0o0o0o0o0o0o0gpath12345_Flag_h3ck3rBoi_SuperSecret@12345.txt"
Response:
Congrats Hacker, you made it !!
Go ahead and nail other challenges as well :D
flag{REDACTED}
Key Vulnerabilities
| # | Vulnerability | Location | Impact |
|---|---|---|---|
| 1 | Client-side authentication logic |
login.html inline <script>
|
Credentials and pass/fail logic fully readable in page source, no server-side verification |
| 2 | Weak obfuscation (string reversal) of password | Inline JS RevereString()
|
Trivial to reverse by hand, provides no real protection |
| 3 | Predictable, credential-derived sensitive file path | Inline JS xhttp.open(...) URL construction |
Flag/secret file directly retrievable via curl once credentials are known, without ever executing the JS or submitting the form |
Attack Chain
Recon (nmap: 22, 80 open)
|
v
Enumerate index.html -> hints at login.html
|
v
gobuster -> confirms login.html, no backend endpoints
|
v
curl login.html -> read inline <script> source
|
v
Extract hardcoded username + reverse obfuscated password
|
v
Derive flag filename from JS template string (no browser needed)
|
v
curl flag file directly -> flag captured
Mitigations
- Never perform authentication decisions client-side; all credential validation must happen on a trusted server, with the client only submitting credentials over an authenticated session.
- Do not rely on obfuscation (encoding, reversal, simple ciphers) as a substitute for real access control - anything shipped to the client is fully readable by the client.
- Do not derive sensitive resource paths (flags, secrets, admin files) from data that is already exposed to the client, such as hardcoded credentials in JS - this makes the resource trivially reachable without ever satisfying the intended check.
- Serve sensitive files only behind server-side, session-validated access checks, and avoid predictable or pattern-based filenames for anything not meant to be publicly enumerable.
Top comments (0)