GitHubプライベートページのバグバウンティで35,000ドルを稼ぐ
(robertchen.cc)高校3年生の学生がCovidで時間ができたためバグバウンティハンティングを行い、GitHubのprivate pagesのバグバウンティで35,000ドルを受け取った話。
GitHubのprivate pagesのバグバウンティとして報告され、2つのCTFボーナスがあった。
-
10,000ドル: ユーザーインタラクションなしで
flag.private-org.github.ioからフラグを読む。private-org組織の外にあるアカウントからこのフラグを読めれば5,000ドルの追加ボーナスがある。 -
5,000ドル: ユーザーインタラクションを通じて
flag.private-org.github.ioからフラグを読む。
認証フロー
GitHub Pagesは別ドメイン github.io でホストされるため、github.com の認証Cookieはprivate pagesサーバーには送信されない。したがって、private pagesの認証は github.com との追加の統合なしにはユーザーの身元を判別できない。そこでGitHubはカスタム認証フローを作成した。
-
privateページにアクセスすると、サーバーは
__Host-gh_pages_tokenCookieの存在を確認する。 -
Cookieが存在しない、または正しくない場合、private pageサーバーは
https://github.com/loginへリダイレクトする。 -
このリダイレクトは
__Host-gh_pages_sessionCookieにnonceも設定する。- このCookieは __Host- Cookieプレフィックスを使用しているため、ホストドメイン以外からJavaScriptで設定されるのを防ぐ。
-
/loginは/pages/auth?nonce=&page_id=&path=にリダイレクトする。 -
ここでは
tokenパラメータからhttps://pages-auth.github.com/redirectに渡す一時的な認証Cookieを生成する。 -
/redirectはhttps://repo.org.github.io/__/authに転送する。 -
この最終エンドポイントは
repo.org.github.ioドメインで認証Cookieである__Host-gh_pages_tokenと__Host-gh_pages_idを設定する。 -
ここで先ほど設定した
__Host-gh_pages_sessionのnonceも確認する。
元のリクエストパスとページIDはそれぞれクエリパラメータ path、page_id に保存され、nonceも nonce パラメータに保存される。
悪用
CRLFリターン
-
最初の脆弱性は
https://repo.org.github.io/__/authのpage_idパラメータにおけるCRLF注入だった。 -
page_idのパースが空白文字を無視し、この値がSet-Cookieヘッダーにそのまま設定されることが分かった。 -
従来のCRLF注入でパースを壊すことはできるが、それ以外の影響はない。
-
Location:ヘッダーがSet-Cookieヘッダーの後に付くため、302リダイレクトであるにもかかわらずLocationヘッダーが無視され、本文がレンダリングされる。
攻撃
-
GitHub Enterpriseのコードを見て、private pageサーバーがopenresty nginxで実装されていることが分かった。
-
nullバイトを追加してXSSに成功した。このnull byteは本文の先頭に来る必要があるため、ヘッダー注入攻撃はできない。
-
これによりprivate pageドメイン上で任意のJavaScriptコードを実行できるようになった。
-
あとはnonceをバイパスする方法を見つけるだけだった。
nonceバイパス
-
観察の結果、同じ組織のsibling privateページ同士は互いにCookieを設定できることを発見した。
-
private-org.github.ioで設定されたCookieはprivate-page.private-org.github.ioに送られる。 -
__Host-プレフィックス保護を回避できればnonceを簡単にバイパスできる。 -
ただし、これをサポートするのはすべてのブラウザではなく、IEは
__Host-プレフィックスをサポートしていない。 -
しかし、より良い方法を探しているうちに面白いアイデアを思いついた。
-
Cookieが大文字小文字をどう扱うか確認したところ、
__HOSTと__Hostを別物として扱い、GitHub private pagesはCookieをパースする際に大文字を無視することが分かった。 -
JavaScriptでnonceを指定できるようになった。
-
5,000ドルのボーナスを受け取ることになった。
キャッシュ汚染
-
/__/auth?エンドポイントのレスポンスは、フィッシングされたpage_idの整数値でキャッシュされる。 -
これにより、XSSペイロードを使ってキャッシュ汚染に成功すれば、インタラクションしていないユーザーにも影響を与えられる。
-
攻撃者が
unprivileged.org.github.ioを攻撃して認証を汚染すると、XSSペイロードがキャッシュされる。 -
Cookieが親ドメイン
org.github.ioで共有されるため、攻撃者はprivileged.org.github.ioも攻撃できる。
公開されたprivateページ
-
15,000ドルのボーナスを得るには、組織に属さないユーザーにこの攻撃を実行させる必要があった。
-
publicリポジトリでprivate pagesを設定する誤設定により、この攻撃が可能だった。
- privateリポジトリでページを作成した後、リポジトリをpublicに変更することを指す。
-
この誤設定されたprivateページでは、すべてのユーザーが認証フローに入るようになり、組織外のユーザーに読み取り権限を与えてしまう。
まだコメントはありません。