En este post estaremos resolviendo el challenge web llamado «Flag Command» de Hack The Box, calificado como Very Easy. Se trata de un challenge ya retirado.
Accediendo a la web, como vemos, simplemente se nos presenta un mensaje y se nos indica que procedamos con el comando «START» para comenzar el juego.

Dado que es un reto web, analizamos con Burp Suite las peticiones y respuestas. Tal como vemos, existe una API a la que se hacen peticiones, así como diversos ficheros JavaScript.

A la vista de los ficheros, podemos analizar los códigos JavaScript, obteniendo lo siguiente:
- El fichero «commands.js» simplemente exporta variables relativas a los textos mostrados en la aplicación web
- El fichero «game.js» se encarga únicamente de mostrar resultados del juego en pantalla
- El fichero «main.js» es el que contiene toda la lógica de la aplicación
En base a lo anterior, parece claro que debemos encontrar el flujo que nos permita obtener el flag final. Podríamos hacerlo en modo «ensayo/error», pero siendo un challenge web, vamos a centrarnos en el código JavaScript, legible client-side. El siguiente fragmento de «main.js» es la clave:
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;
}
}
Lo realmente relevante es lo marcado en negrita. Sabiendo que la variable «currentStep» se inicializa a «1» fuera de este código, parece que la manera de avanzar en el juego es lanzar los siguientes comandos en orden estricto:
- HEAD NORTH
- FOLLOW A MYSTERIOUS PATH
- SET UP CAMP
Así las cosas, lanzamos dichos comandos en la aplicación web:


Sin embargo, no obtenemos el flag. Nos falta un último paso. Eligiendo cualquiera de las 4 opciones que vemos ahora, perderemos el juego. Hay algo que hemos pasado por alto. Volvemos al código y nos fijamos ahora en la siguiente línea en negrita:
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 })
})
(...)
La línea es la clave del reto. No solamente acepta comandos que estén en el array del paso en curso (los vistos anteriormente para los pasos 1, 2 y 3) sino que también acepta un comando de un array cuya clave es «secret». Volviendo a la API que habíamos comentado y haciendo un simple GET, obtenemos todos los posibles comandos para cada paso y, además, el comando secreto:

Reseteamos el juego de nuevo, lanzamos el comando secreto en cualquiera de los pasos y resolvemos con ello el challenge:

~km0xu95
Comments are closed.