HackTheBox – Flag Command walkthrough (Challenge Web/Very Easy)

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.