Flag Command - Hack The Box
Table of Contents
Challenge Description
Embark on the “Dimensional Escape Quest” where you wake up in a mysterious forest maze that’s not quite of this world. Navigate singing squirrels, mischievous nymphs, and grumpy wizards in a whimsical labyrinth that may lead to otherworldly surprises. Will you conquer the enchanted maze or find yourself lost in a different dimension of magical challenges? The journey unfolds in this mystical escape!
Web Application
Accessing the challenge in the browser reveals a terminal-like interface that simulates a text-based adventure game. The player interacts with the application by typing commands into the terminal and receiving narrative responses.
The frontend behavior immediately suggests the use of JavaScript to handle user input and communicate with a backend API, making it a prime target for dynamic analysis through browser developer tools.
JavaScript Analysis
By analyzing the JavaScript files loaded by the application, it was possible to identify how it interacts with the backend through various API calls. The file /static/terminal/js/main.js
contains several functions that provide insight into the logic and flow of the challenge.
1. Fetching Available Commands
The following function is responsible for making a request to the /api/options
endpoint. It retrieves a list of all possible commands and stores them in the availableOptions
variable:
// .. SNIP ..
const fetchOptions = () => {
fetch('/api/options')
.then((data) => data.json())
.then((res) => {
availableOptions = res.allPossibleCommands;
})
.catch(() => {
availableOptions = undefined;
})
}
// .. SNIP ..
2. Validating and Sending Commands
The CheckMessage
function is responsible for validating the user input based on the available commands and sending it to the /api/monitor
endpoint. It then handles the response accordingly, checking for game-ending conditions or updating the state of the terminal interface:
// .. SNIP ..
// HTTP REQUESTS
// ---------------------------------------
async function CheckMessage() {
fetchingResponse = true;
currentCommand = commandHistory[commandHistory.length - 1];
if (availableOptions[currentStep].includes(currentCommand) || availableOptions['secret'].includes(currentCommand)) {
await fetch('/api/monitor', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ 'command': currentCommand })
})
.then((res) => res.json())
.then(async (data) => {
console.log(data)
await displayLineInTerminal({ text: data.message });
if(data.message.includes('Game over')) {
playerLost();
fetchingResponse = false;
return;
}
if(data.message.includes('HTB{')) {
playerWon();
fetchingResponse = false;
return;
}
if (currentCommand == 'HEAD NORTH') {
currentStep = '2';
}
else if (currentCommand == 'FOLLOW A MYSTERIOUS PATH') {
currentStep = '3'
}
else if (currentCommand == 'SET UP CAMP') {
currentStep = '4'
}
let lineBreak = document.createElement("br");
beforeDiv.parentNode.insertBefore(lineBreak, beforeDiv);
displayLineInTerminal({ text: '<span class="command">You have 4 options!</span>' })
displayLinesInTerminal({ lines: availableOptions[currentStep] })
fetchingResponse = false;
});
}
else {
displayLineInTerminal({ text: "You do realise its not a park where you can just play around and move around pick from options how are hard it is for you????" });
fetchingResponse = false;
}
}
// .. SNIP ..
Based on the information gathered from the JavaScript analysis, a direct request was made to the /api/options
endpoint. The API returned a JSON object containing all available commands categorized by step:
HTTP/1.1 200 OK
Server: Werkzeug/3.0.1 Python/3.11.8
Date: Wed, 18 Jun 2025 03:50:53 GMT
Content-Type: application/json
Content-Length: 637
Connection: close
{
"allPossibleCommands": {
"1": [
"HEAD NORTH",
"HEAD WEST",
"HEAD EAST",
"HEAD SOUTH"
],
"2": [
"GO DEEPER INTO THE FOREST",
"FOLLOW A MYSTERIOUS PATH",
"CLIMB A TREE",
"TURN BACK"
],
"3": [
"EXPLORE A CAVE",
"CROSS A RICKETY BRIDGE",
"FOLLOW A GLOWING BUTTERFLY",
"SET UP CAMP"
],
"4": [
"ENTER A MAGICAL PORTAL",
"SWIM ACROSS A MYSTERIOUS LAKE",
"FOLLOW A SINGING SQUIRREL",
"BUILD A RAFT AND SAIL DOWNSTREAM"
],
"secret": [
"Blip-blop, in a pickle with a hiccup! Shmiggity-shmack"
]
}
}
Notably, the response includes a key named "secret"
containing a unique and unusual command:
"Blip-blop, in a pickle with a hiccup! Shmiggity-shmack"
This suggests the existence of hidden or non-standard input paths that may trigger special behavior in the game logic. Such commands are likely not shown in the terminal UI and might be used to unlock alternate routes or extract the flag.
Obtaining the Flag
By leveraging the hidden command discovered in the previous analysis, a POST request was made to the /api/monitor
endpoint. This resulted in a successful flag retrieval.
- Request Sent:
POST /api/monitor HTTP/1.1
Host: 94.237.61.99:57278
Content-Type: application/json
Content-Length: 68
{"command":"Blip-blop, in a pickle with a hiccup! Shmiggity-shmack"}
- Response Received:
HTTP/1.1 200 OK
Server: Werkzeug/3.0.1 Python/3.11.8
Date: Wed, 18 Jun 2025 04:10:07 GMT
Content-Type: application/json
Content-Length: 76
Connection: close
{"message": "HTB{redacted}"}
As shown above, the API returned the final flag: HTB{redacted}
This confirms that the obscure "secret"
command was indeed the key to solving the challenge, and highlights the importance of analyzing frontend logic and hidden API functionality.
References
-
MDN Web Docs – Fetch API
Understanding how JavaScript fetch() works is essential when analyzing frontend communication with APIs. -
Burp Suite Documentation – Inspecting JavaScript for Hidden Endpoints
Techniques to find hidden API routes or secrets by reading JavaScript files. -
OWASP Testing Guide – Client Side Testing
Best practices for testing and understanding client-side logic and hidden data.