Skip to main content

Command Palette

Search for a command to run...

TryHackMe - CyberHeroes Writeup

Updated
4 min readView as Markdown
Y
I write detailed writeups on HackTheBox, PicoCTF and other CTF challenges. Passionate about web exploitation, Active Directory attacks and ethical hacking

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:

  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

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.