Open1

AlpacaHack Round 11 (Web) Writeup

sirumasiruma

AlpacaHack Round 11 (Web)1問だけ解いたのでたまにはWriteup書こうと思います。

Jackpot

フォームに入力した10種類の数字が出目になるスロットマシン風のWebアプリが与えられます。
Pythonのソースコードは以下の通り

from werkzeug.exceptions import BadRequest, HTTPException
import os, re, random, json

app = Flask(__name__)
FLAG = os.getenv("FLAG", "Alpaca{dummy}")


def validate(value: str | None) -> list[int]:
    if value is None:
        raise BadRequest("Missing parameter")
    if not re.fullmatch(r"\d+", value):
        raise BadRequest("Not decimal digits")
    if len(value) < 10:
        raise BadRequest("Too little candidates")

    candidates = list(value)[:10]
    if len(candidates) != len(set(candidates)):
        raise BadRequest("Not unique")

    return [int(x) for x in candidates]


@app.get("/")
def index():
    return render_template("index.html")


@app.get("/slot")
def slot():
    candidates = validate(request.args.get("candidates"))

    num = 15
    results = random.choices(candidates, k=num)

    is_jackpot = results == [7] * num  # 777777777777777

    return jsonify(
        {
            "code": 200,
            "results": results,
            "isJackpot": is_jackpot,
            "flag": FLAG if is_jackpot else None,
        }
    )


@app.errorhandler(HTTPException)
def handle_exception(e):
    response = e.get_response()
    response.data = json.dumps({"code": e.code, "description": e.description})
    response.content_type = "application/json"
    return response


if __name__ == "__main__":
    app.run(debug=False, host="0.0.0.0", port=3000)

上記のvalidateの処理を整理すると
・value(フォームに入力された値)が入力されていること
・数字(r"\d+")であること
・10文字以上であること
・前から10文字目までは同じ文字を使用していないこと
がvalidateに引っかからないための条件となります。

注目すべきは、

    if not re.fullmatch(r"\d+", value):
        raise BadRequest("Not decimal digits")

で、そのまま読むと数字のみ受け付けるという処理のため同じ数字が使えなそうに見えます。
しかし、re.fullmatch(r"\d+", value)の\dはUnicode モードがデフォルトでオンになっているため、0-9以外の数字も使えてしまいます。
そこで、Unicodeの文字のうち7として扱われる文字を並べて7だらけのスロットにすることで簡単にAll7を達成することができます。

・・・ちなみに私の好きな7は「᭐」です🎰