Webブラウザの脆弱性を探していたところ、変わった切り口のXSS攻撃手法を思いつきました。本稿は、SSL/TLS サーバ証明書にXSSのペイロードを仕込んだ場合の挙動はどうなるのか、Webブラウザの警告ページで脆弱性は内在しているのかといった部分に触れます。
概要
本稿での証明書とは、https通信においてのSSL/TLSサーバ証明書を指します。本来サーバ証明書は非常にざっくり表現すると、対向のWebサーバの妥当性の検証をするためにも用いられますが、今回はあえて自己署名証明書やCommonNameの不一致などの不正な証明書を意図的に生成し、その証明書に悪意のあるコードを仕込んだ場合、Webブラウザはどのような挙動を示すのかを実験しました。
XSS証明書の作成方法
今回はWindows 10 Proで自己署名証明書を作成するコマンドを応用して、XSSを引き起こすコードを仕込んだサーバ証明書を作成し、IISのWeb Siteにバインドして確認します。証明書の作成自体はOpenSSLでも可能ですが、IIS 6.0 Resources KitのselfsslコマンドがWindows 10でエラー出るようになってしまっていたので、代替方法を探した結果、そのナレッジ共有も兼ねてWindows 10 Proでの作成方法を題材にします。Windows 10 Homeの場合は、おそらくIIS Expressでもやり方があると思われますが、これは未検証です。
0. 環境準備
IISの準備とPowerShellを使えるところがスタートですので、詳細手順は割愛しますが、「Windows 機能の有効化または無効化」からIISとPowerShellを有効にします。
1. 証明書を発行
管理者権限でPowerShellを起動し、プロンプトに下記のコマンドを流して実行します。
New-SelfSignedCertificate -Subject "CN=<script>alert(1)</script>,OU=<script>alert(1)</script>,DC=<script>alert(1)</script>" -DnsName "</p>:\""><script>alert(1)</script>\0" -CertStoreLocation "cert:\LocalMachine\My" -KeyAlgorithm RSA -KeyLength 2048
上記のコマンドを実行すると証明書が発行され、自動的に証明書がストアされます。
2. サーバ証明書をバインド
MMC(IISの管理)から「Default Web Site>バインド>https」を選択または、ない場合はhttps/443を追加します。次に、「編集>SSL証明書」のプルダウンメニューの中から「"<script>alert(1)</script>"」を選択しOKボタンを押下します。
3. Webブラウザで確認
「https://127.0.0.1/」などでアクセスし、上手くブラウザの警告ページが表示されれば成功です。Firefox Nightlyで確認すると面白いです。
各ブラウザの挙動
●Firefox Nightly 71.0a1
面白い切り口だと思ったのですが、view-source:で見ると、HTMLエンティティに置換されるため、XSSは刺さらず。
●Safari Technology Preview Release 91
証明書ビュアーの画面は特に何も起きず、これでは面白くないので、違うアプローチでやってみました。
ひたすら長い文字列を挿入した変な証明書を作ってみると、Safariの証明書ビュアーの画面が外に出てしまい、カーソルでOKボタンが押せなくなります。ただし、既定の選択がOKボタンなので、Enterキーで閉じることができるため、完全な操作不能に陥ることはありませんでした。
実際にこれは刺さる?
調べてみると実際の事例として、証明書を検証するサービスにて、証明書のOU=内にJavaScriptコードを挿入した結果、XSSの脆弱性がありました。
この他、いくつかのXSSの脆弱性の事例が見つかりました。
対策方法
いわゆる一般的なXSS攻撃の対策方法と同様に、HTMLとして出力する際、特別な意味を持つ記号「"><';)(」などは、HTMLエンティティに置換するなど、入力値をそのまま出力しないように改修することを推奨します。
安全でない証明書の存在
例示したコマンドでは、CommonNameなどに<script>alert(1)</script>を挿入しており、明らかな悪意のある証明書を作成することは可能でした。私の考えですが、X.509の構造的にSubjectなどに、ある程度の任意の文字列を埋め込むことは可能であるため、SSL/TLSサーバ証明書に限らず、広義の証明書においても無関係な話ではありません。端的に言うと、証明書であるから必ず安全である(安全な文字列しか入力されない)ということは落とし穴になるかもしれません。