Flag Command — HackTheBox Challenge Writeup

HackTheBox Web Challenge: Flag Command Writeup

Halo semuanya! Sekarang gw akan membahas salah satu challenge web dari HackTheBox, yaitu Flag Command.

Initial Access

Saat kita coba buka website target maka akan muncul tampilan seperti ini:

Disini kita akan coba start, terlihat seperti terminal, namun tidak ada command yang bisa kita jalankan langsung. Pertama-tama, kita akan cek dulu bagian robots.txt apakah ada informasi yang berguna.

Sepertinya tidak ada apapun yang menarik disitu. Selanjutnya kita akan coba lakukan inspect element, dan disini kita dapatkan ada 3 JavaScript files, yang kemungkinan mengatur jalannya web:

  1. commands.js
  2. game.js
  3. main.js

Source Code Analysis

Setelah kita cek ketiga files tersebut, disini kita dapatkan code dari file main.js yang menarik:

import { START, INFO, INITIAL_OPTIONS, HELP } from "./commands.js";
import { playerLost, playerWon } from "./game.js";

let availableOptions;

let currentStep = 1;
// SELECT HTML ELEMENTS
// ---------------------------------------
export const beforeDiv = document.getElementById("before-div"),
    currentCommandLine = document.getElementById("current-command-line"),
    commandText = document.getElementById("commad-written-text"),
    userTextInput = document.getElementById("user-text-input");

const typingSound = new Audio();
typingSound.src = document.getElementById("typing-sound").src;
typingSound.loop = true;

// COMMANDER VARIABLES
// ---------------------------------------
let currentCommand = 0,
    commandHistory = [],
    typingSpeed = 10,
    typing = true,
    playAudio = true,
    fetchingResponse = false,
    gameStarted = false,
    gameEnded = false;

export const startCommander = async () => {
    await fetchOptions();
    userTextInput.value = "";
    commandText.innerHTML = userTextInput.value;

    await displayLinesInTerminal({ lines: INFO });

    userTextInput.focus();
};

// 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;
    }
}

The Discovery

Yang menarik perhatian kita adalah bagian ini:

if (availableOptions[currentStep].includes(currentCommand) || availableOptions['secret'].includes(currentCommand)) {

Pada dasarnya, pernyataan if ini memeriksa apakah perintah saat ini ada dalam rangkaian perintah yang diterima (HEAD NORTH, FOLLOW A MYSTERIOUS PATH, SET UP CAMP) atau apakah perintah ada dalam rangkaian rahasia perintah (availableOptions['secret']) yang juga memenuhi syarat untuk melanjutkan di dalam loop.

Kita perlu menemukan perintah apa saja yang termasuk dalam rangkaian rahasia ini, karena kemungkinan besar akan berisi informasi yang kita perlukan untuk menemukan flagnya.

Selanjutnya kita check lagi di file main.js dan menemukan ada function bernama fetchOptions():

const fetchOptions = () => {
    fetch('/api/options')
        .then((data) => data.json())
        .then((res) => {
            availableOptions = res.allPossibleCommands;
        })
        .catch(() => {
            availableOptions = undefined;
        })
}

Disini kita bisa lihat bahwa function ini melakukan request ke /api/options dan mendapatkan response berupa JSON yang kemudian disimpan dalam variabel availableOptions. Ini artinya kita bisa mendapatkan semua command termasuk secret commands dengan memeriksa respons dari endpoint ini.

Intercepting the Network Traffic

Jika kita lihat pada inspect network saat web load, kita akan melihat request ke /api/options:

Network Request

Dan jika kita buka isi dari response filenya, kita akan mendapatkan sesuatu yang menarik:

{
  "allPossibleCommands": {
    "1": ["HEAD NORTH", "GO EAST", "TRAVEL WEST", "TRAVEL SOUTH"],
    "2": ["CONTINUE NORTH", "HEAD EAST", "GO SOUTH", "FOLLOW A MYSTERIOUS PATH"],
    "3": ["LOOK AROUND", "SET UP CAMP", "KEEP MOVING", "CLIMB THE HILL"],
    "4": ["SLEEP", "LOOK FOR FOOD", "EXPLORE SURROUNDINGS", "LEAVE THE CAMPSITE"],
    "secret": ["INSPECT THE SURROUNDINGS", "EXAMINE THE TRAIL", "INVESTIGATE THE CAMPSITE", "VIEW THE FLAG"]
  }
}

Bingo! Disini kita dapatkan command secret yang seharusnya kita gunakan untuk dapatkan flagnya. Mari kita coba masukan langsung ke terminal.

Exploiting the Secret Command

Dari data yang ditemukan, kita memiliki 4 secret commands:

  1. INSPECT THE SURROUNDINGS
  2. EXAMINE THE TRAIL
  3. INVESTIGATE THE CAMPSITE
  4. VIEW THE FLAG

Selanjutnya, kita akan coba command “VIEW THE FLAG” langsung di terminal:

Dan voila! Kita berhasil mendapatkan flag HTB{f14g_c0mm4nd_……………}.

Conclusion

Challenge ini mengajarkan kita beberapa hal penting:

  1. Pentingnya memeriksa kode JavaScript pada website
  2. Bagaimana menemukan endpoints tersembunyi dengan mempelajari kode sumber
  3. Mengidentifikasi logika rahasia yang mungkin tersembunyi di dalam aplikasi

Banyak kali developer menyisipkan “backdoor” atau logika rahasia yang seharusnya tidak diakses oleh pengguna biasa. Dengan memeriksa kode sumber dan traffic jaringan, kita bisa menemukan jalur-jalur tersembunyi ini.

Semoga writeup ini bermanfaat. Sekian writeup dari saya, terima kasih sudah membaca! 🚩

Bonus Tips

  • Selalu periksa JavaScript files untuk menemukan informasi sensitif atau logika tersembunyi
  • Gunakan Network tab di Developer Tools untuk melihat semua request/response
  • Perhatikan endpoint API yang mungkin bisa diakses langsung
  • Kadang-kadang solusi yang paling sederhana adalah yang paling efektif

Leave a Reply

Your email address will not be published. Required fields are marked *