RECRUIT RED TEAMのセキュリティトレーニング:OSSへの貢献とCVE IDの取得
平松 耕輔
このエントリは全9回を予定する18卒新人ブログリレーの第2回です!今回は、RECRUIT RED TEAMの新人セキュリティエンジニアが配属から現在までに行ったトレーニング内容についてご紹介します!
はじめに
はじめまして。リクルートテクノロジーズ新人の平松耕輔です!ブートキャンプや新人プロジェクト研修を経て、7月からセキュリティエンジニアリンググループ RECRUIT RED TEAM(以下RED TEAM)の一員になりました!
セキュリティエンジニアリンググループはその名前の通りセキュリティを専門としたグループです。私は大学時代にセキュリティの研究をしていたわけではなく、趣味でCTF(Capture The Flag)をやっていた程度でした。そんな自分も、RED TEAMでのトレーニングを通して実践的なセキュリティについて学び、配属から2ヶ月経った今では社内プロダクトの脆弱性検査など、実案件に入って仕事をすることができるようになっています。
配属されてからのトレーニングでは、
- 脆弱性情報調査・社内展開
- 脆弱性検査手法(ソースコード検査、ブラックボックス検査)の習得
- OSSへの貢献(脆弱性の発見・修正提案)
- リリース前のサービスの脆弱性検査
などの様々な活動を行います。例えば、「OSSへの貢献」では、OSSの脆弱性を発見し、開発者に内容を報告して修正方法を提案するという形で貢献を行いました。また、発見した脆弱性の一部についてはCVE(Common Vulnerabilities and Exposures)に申請し、CVE IDを2つ取得しています。
このエントリを通して、私がフォロー無しで実務に入れるようになるまでにどのようなトレーニングを行ったのか、RED TEAMでは何を大切にしているのか、皆さんに詳しい内容をお伝えできればと思っています!
RED TEAMについて
まずは、私が働いているRED TEAMというチームについてご紹介します。
RED TEAMは、社内のリスクを能動的に洗い出し、開発者と協業しながら技術的にセキュリティの課題を解決することが特徴のチームです。
リクルートの新規事業開発では仮説・検証を高速で回すようなスピード感のある開発が行われています。この開発スピードを止めず、合理的にセキュリティの品質を保つ支援をするのがチームの目的の1つになっています。
セキュリティと一口に言っても、リクルート内には、インシデントを監視するモニタリングがミッションのグループもあれば、発生したインシデントへの対応がミッションのグループもあります。その中でRED TEAMが属するセキュリティエンジニアリンググループは、平常時に社内のセキュリティ品質を高めることで、インシデントを未然に防ぐことをミッションとしたグループとなっています。
RED TEAMはセキュリティ品質を高めるために様々な取り組みを実施しています。脆弱性検査、脆弱性情報の調査、パッチマネジメントツールや自動検査プラットフォームの開発。その他にも社内の人材教育やセキュリティに関する相談など、業務の幅はとても広いです。
少数精鋭型の組織で、今までは新人の受け入れは行っていなかったのですが、最近は私のような新人の受け入れや、インターン生の受け入れなども行うようになっています。
RED TEAMについてもっと詳しく知りたい方は、ぜひグループマネジャー 西村の資料やITmediaさんの記事をご覧ください!
脆弱性発見のプロ集団ーーリクルート「レッドチーム」の仕事とは? (ITmedia)
RED TEAMでのトレーニングについて
RED TEAMに配属後の数ヶ月間は、手厚いフォローを受けながら仕事をしてセキュリティ業務のスキルを学ぶトレーニングを行います。
トレーニングのゴールは、実際のサービスに携わるセキュリティ業務に、フォロー無しで入れるようになることです。もう少し細かく言うと、以下の目標を達成することです。
- 脆弱性を見つけるスキルを身につける
- 脆弱性のリスクを正しく見積もれるようになる
- 脆弱性のリスクを正しく伝えられるようになる
- 業務に必要なドメイン知識を身につける
- 独自色を出せるようになる
ここからは、実際に私がトレーニングで行った内容についてご紹介します。
RED TEAMのトレーニングでは、実務に並行して仕事に必要なスキルを学べるようなメニューが予め用意されています。トレーニングをこなしてメンターにレビューしてもらい、フィードバックを元にして再びトレーニングに取り組むというのが基本的な進め方です。
「トレーニングメニュー」の具体的な内容は以下のようなものでした。
- 早期警戒レポート:毎日最新の脆弱性を1つ選び、調査してレポートを作成する
- 脆弱性検査手法の習得 その1:練習用の脆弱アプリを攻撃して脆弱性の原理を学ぶ
- 脆弱性検査手法の習得 その2:社内で過去に発見された脆弱性を再現する
- OSSへの貢献:OSSプロジェクトの脆弱性調査を行い貢献する
トレーニング中はこれらのメニューをその他の業務と並行してこなしていきます。ここから先は、各トレーニングメニューについての詳しい内容をご紹介します。
早期警戒レポート
最新の脆弱性を1つ選び、調査してレポートを作成する
早期警戒レポートは、脆弱性の原理を知ることと、セキュリティリスクを分析するスキルを養うために実施しているトレーニングです。このトレーニングでは、毎日VulnDBなどで公開された脆弱性を1つ選んで、その内容について調査し、レポートを作成します。
どのようなフォーマットでレポートを作成するかは個人の判断に任されています。私の場合は選んだ脆弱性について、
- 影響範囲
- 発生原理
- 再現方法
- 対策
という4つの観点について調査してレポートにまとめています。
多様な種類の脆弱性をレポートの対象に選ぶように心がけ、Webアプリケーションを中心に、暗号ライブラリやRDBなどの様々な種類の脆弱性について調査しました。
ここで、実際に私が作成したレポートを1つ紹介します。今年の6月、Firebase Realtime Databaseの脆弱性により数多くの企業で機密データが公開状態になっているというプレスリリースが発表されたときのものです。
この発表の内容について、具体的にどのような原因により脆弱性が発生し、どのようなリスクが存在するのかを明らかにするためにレポートを作成しました。下図のレポートは、実際に社内のConfluenceで公開したものです。
毎日の早期警戒レポートを通して、脆弱性のリスク分析や発生原因について少し肌感を身につけることができました。以下で紹介するトレーニングに取り組んでいる際も、基本的には毎日レポート作成は継続して行っています。
脆弱性検査手法の習得 その1
練習用の脆弱アプリを攻撃して脆弱性の原理を学ぶ・検査報告書を作成する
その次は、実践的な検査の訓練を行うために、あえて脆弱に作られた「やられアプリ」を使った模擬検査を行いました。
SQLインジェクション検査用のアプリケーション SQLInjectionTest-Docker や、Web+DB PRESS でも紹介されたSNSアプリケーション Bad SNS,Bad SNS Android を対象に検査を行いました。実務で使用しているものと同様の進捗管理シート(下図)を使いながら検査項目をリストアップし、検査を進めていきます。
検査終了後は、発見した各脆弱性について、概要・深刻度・脆弱性のある箇所・脆弱性のあるパラメータ・再現手法・想定される攻撃シナリオ・対策方法をまとめた報告書を作成しました。作成した報告書を周囲の人達にレビューしてもらい、レビュー内容を元に修正を重ねながらクオリティを高めます。
ここまでのトレーニングで、脆弱性を発見するスキルや対策を考えるスキルが身についてきていました。しかし、対象としていたのはやられアプリや社外のプロダクトで、実際に仕事で扱うような社内の脆弱性には触れることができていませんでした。
脆弱性検査手法の習得 その2
社内で過去に発見された脆弱性を再現する
そこで次に、実際に社内でどのような脆弱性が発見され、どのようにハンドリングしたのかを追体験するために、過去に社内システムで発見された脆弱性を再現して体験してみるというトレーニングを行いました。
再現を行った内容については残念ながらご紹介することはできません…。しかし、社内システム上でのリアルな脆弱性を再現し、原因を考察することで実検査のプロセスを知ることができました。学びはとても大きかったです。
ここまでのトレーニングで、脆弱性の原理、対策方法、脆弱性検査と報告の流れ、リアルな脆弱性検査のプロセスなどを知ることができました。しかし、脆弱性発見という点では、あえて作り込まれた脆弱性や過去に見つかった脆弱性が対象で、まだこの時点では未知の脆弱性を見つけることはできていませんでした。
OSSへの貢献
脆弱性の発見・修正提案
いよいよ実践のときです。今までのトレーニングで学んだスキルを活かし、未知の脆弱性の発見を目指します。GitHubなどでソースコードが公開されているOSSを対象に脆弱性検査を行い、脆弱性を発見し、原因を考察し、修正方法を提案してプロジェクトに貢献する。これを一人で達成できるならばトレーニングのゴールは近いはずです。
■ 脆弱性検査の方針
「OSSへの貢献」というトレーニングでは、検査対象のプロジェクトが予め指定されているわけではありません。ネット上にある無数のOSSの中から、とにかく脆弱性を発見&修正方法を提案して貢献できれば良いのです。とはいえ、長らく更新されていない放置されたプロジェクトや、他人が使用することを想定していないような個人的なプロジェクトで脆弱性を発見したとしても、プロジェクトへの貢献度は大きくないでしょう。
そこで、私は直近1ヶ月以内に更新されていることとライセンスが明記されていることを条件に、プロジェクトを絞って検査を行うことにしました。
また、検査対象とするプロジェクトはWebアプリケーション限定としました。Webアプリケーションの脆弱性として有名なもの(SQLインジェクション、XSSなど)は危険な書き方がされている場合に比較的わかりやすく、脆弱性を見つけられる可能性も高いと考えられるからです。
検査の基本戦略は、1つ1つのプロジェクトに掛ける時間を短くして、とにかく沢山のプロジェクトのソースコードを見ることにしました。具体的には下記のような手順で検査を進めました。
- GitHub APIを使ってソースコードを自動収集するスクリプトを書く
- 検査対象とするプロジェクトのソースコードを集める
- grepでSQLクエリを発行している箇所やHTMLに文字列をセットしている箇所など、脆弱性が見つかりやすいポイントをピックアップする
- ピックアップしたポイントの周辺を見て、怪しい処理が無いか探す
- 1つのプロジェクトを見始めて10分以内に気になるポイントが見つからなければ次のプロジェクトに進む
最初のうちは1つのプロジェクトに30分以上時間を掛けていたのですが、段々ペースが上がって途中からは10分で1プロジェクトのハイペースで検査を進められるようになりました。
プロジェクトA:OSS脆弱性検査 その1
脆弱性が見つかった1つ目のプロジェクトは、PHPで書かれたCMS(Contents Management System)でした。知名度が高いアプリケーションではないものの、ブログやECサイトとして運用しているユーザが存在する多機能なアプリケーションです。このプロジェクトを仮にプロジェクトAと呼称します。
私がプロジェクトAに発見した脆弱性は以下のようなものでした。
- 記事検索フォームのReflected XSS
- 記事投稿のStored XSS
- URL中の記事IDにSQLインジェクション
- PHPファイルアップロードによる任意コード実行
- 新規ユーザ登録制限の回避
■ 記事検索フォームのReflected XSS
このアプリケーションのTOPページには、記事検索用の検索フォームが配置されます。ユーザはこの検索フォームから好きな文字列で記事を探すことができます。下の図のように、この検索フォームに入力した文字列は検索結果ページにそのまま表示されるようになっています。
結果画面の文字列は<>
などの特殊文字がエスケープされずにそのままHTMLに埋め込まれたものとなっています。そのため、ここに<script>alert("XSS")</script>
などの文字列を埋め込むと…。
文字列はHTMLタグとして解釈されてしまいます。これを利用してscriptタグを埋め込むことで、任意のスクリプトを実行することが可能です。これはReflected XSSと呼ばれる脆弱性です。
自分のブラウザの中で任意のスクリプトを実行する分には害はありません。しかし、検索文字列はURLの一部となっているため、https://example.com/search/<script>任意スクリプト</script>
といったリンクを作成し、他のユーザにリンクを踏ませることで他人のブラウザで任意のスクリプトを実行することができてしまいます。
攻撃者はこれを悪用することで、罠を踏んだユーザに不正な記事を投稿させたり、不正にアカウントを作成させたりすることができる可能性があります。この脆弱性を足がかりに権限昇格が行われる可能性もあるため、リスクは低くない脆弱性です。
■ 記事タイトルのStored XSS
このアプリケーションではAdmin機能の中で記事の投稿やレイアウト変更をすることができます。記事投稿ページは下の図のような一般的なものです。
このページのタイトルフォームに <script>alert('XSS')</script>
などの文字列を含めると、そのままHTMLタグとして記事タイトルに埋め込まれてしまいます。これを利用してscriptタグを埋め込むことで、記事タイトルが表示されるページにアクセスしたユーザのブラウザで任意のスクリプトを実行することが可能です。これはStored XSSと呼ばれる脆弱性です。
しかし、Admin機能を使用できるのはアカウントを持っているユーザだけであるため、認証が必要であるという点で比較的脆弱性のリスクは低いと考えられます。
■ URL中の記事IDにSQLインジェクション
Admin機能には、当然ですが既存の記事を編集することができるページがあります。URLは https://example.com/edit/posts/記事ID
といった形式で、記事ID含んだものとなっています。このURLのうち、記事IDを指定している部分にSQLインジェクションの脆弱性がありました。
指定された記事IDは、サーバサイドで次のようにSQL文に埋め込まれます。
https://example.com/edit/posts/記事ID
↓ SQL文に埋め込み
SELECT * FROM posts WHERE id = 記事ID
この際、次のようなURLを指定すると、記事IDを埋め込んだ時に本来のSQL構文が破壊され、意図されないSQL文が実行されてしまうことになります。
https://example.com/edit/posts/1; SELECT SLEEP(10)
↓ SQL文に埋め込み
SELECT * FROM posts WHERE id = 1; SELECT SLEEP(10)
これはSELECT * FROM posts WHERE id = 1
とSELECT SLEEP(10)
の2つのSQLクエリであると解釈され、サーバサイドで両方のSQLクエリが実行されることになります。このSQL文であれば10秒間プロセスがスリープするだけで済みます。しかし、埋め込む文字列を変えればUPDATE文やDELETE文も実行できてしまうため、認証は必要であるものの攻撃によるリスクは高い脆弱性であると言えます。
■ PHPファイルアップロードによる任意コード実行
記事の投稿機能では、ユーザは画像や動画などの任意のファイルをアセットとしてアップロードすることができます。この際、アップロードされたファイルはユーザが認証なしでアクセス可能なパスに配置されることになります。
このアプリケーションはPHPで書かれており、Webサーバでは当然PHPが動いています。この際、通常の設定であれば、PHPファイルへアクセスがあった場合にはそのファイルがユーザによってアップロードされたものであってもPHPスクリプトとして解釈されて実行されることとなります。
<?php system('id') ?>
というid
コマンドを実行するPHPファイルをcmd_exec.php
という名前で作成し、記事の投稿画面からアセットとしてアップロードします。アップロードしたPHPファイルへはhttps://example.com/assets/cmd_exec.php
のようなURLでアクセスすることができます。
このURLへアクセスすると、サーバサイドでid
コマンドが実行された結果が表示されます。
uid=33(www-data) gid=33(www-data) groups=33(www-data)
認証は必要であるものの、アップロードするPHPファイルの内容を変えればどんなコードでも実行が可能であり、任意コード実行ができる危険な状態になってしまっています。攻撃によるリスクは高いと考えられます。
■ 新規ユーザ登録制限の回避
ここまで紹介してきた脆弱性の多くはAdmin機能へアクセスできるアカウントを持ったユーザでなければ攻撃できないものでした。しかし、このアプリケーションにはAdmin機能へのアクセス権を持ったユーザを誰でも作成できてしまうという脆弱性がありました。事実上、攻撃者は一切の権限がない状態から上記の全ての攻撃を成功させることができてしまいます。
このアプリケーションでは、新規ユーザを登録するためのRegisterページは設定で有効にしなければアクセスすることができない仕様になっています。しかし、Registerページが設定で無効になっていても、登録用のPOSTリクエストを直接サーバに送信することで、制限を回避して新規ユーザ登録ができてしまうようになっていました。
この脆弱性を足がかりとすることで、攻撃者は上記の任意コード実行を始めとした様々な攻撃を仕掛けることが可能となってしまいます。これはリスクが高い脆弱性です。
■ 開発者への報告
脆弱性を報告する際に、いきなりGitHubリポジトリへIssueを上げてしまうと、攻撃者は修正が行われる前に脆弱性の詳細を知ることができてしまいます。そうなれば、利用者は対策を取ることもできないまま攻撃に晒されてしまう可能性があります。
基本的に開発者への報告方法はそのプロジェクトで定められた貢献方法に従うことが推奨されます。しかし、脆弱性を報告する場合に限ってはいきなりオープンな場に公開することを避けるべきです(貢献方法で公開することが明示的に推奨されている場合を除く)。
今回検査を行ったプロジェクトAでは、セキュリティに関する問題の報告方法が明記されていなかったため、直接開発者へメールで報告を行うことにしました。メールには脆弱性の概要・再現方法・原因・対策方法を記載します。メールを出した翌日には、近い内に修正がされるということと、感謝を伝えるメールが返信されてきました。
後日、報告した脆弱性は修正され、更にプロジェクトAへの貢献者としてREADME.md
に私のGitHubIDを記載していただきました!
さて、プロジェクトAについては以上です。多機能でよく作り込まれたアプリケーションではあったものの、セキュリティ的には少し危うい部分もありました。私の報告でプロジェクトAの開発者の方が少しでもセキュリティへの関心を持っていただけたなら嬉しいです。
プロジェクトB:OSS脆弱性検査 その2
その後のOSS脆弱性検査では、さらに広く貢献できるように、より知名度があるものを対象にしました。検査対象の絞り込み条件に「Star数100以上であること」を加えて検査を続けます。
次に脆弱性を発見したプロジェクトは、プロジェクトAと同じくPHPで書かれたCMSで、Star数200以上のものです。このプロジェクトを仮にプロジェクトBと呼称します。
私が発見した脆弱性は以下のようなものでした。
- PHPファイルアップロードによる任意コード実行
- ブラックリスト管理機能のSQLインジェクション
■ PHPファイルアップロードによる任意コード実行
プロジェクトAで発見された脆弱性とよく似ています。認証が必要なAdmin機能に見つかったもので、記事に使用できるアセットをアップロードするためのメディアマネージャー機能に存在するものです。
プロジェクトAとの違いは、仕様上は明らかにPHPファイルをアップロードさせないようにしてあることです。プロジェクトBのメディアマネージャーからPHPファイルをアップロードしようとすると、正常動作では下の図のようにエラーメッセージが表示されてアップロードが失敗します。
しかし、この制限を回避してPHPファイルをアップロードすることができてしまう脆弱性が存在します。
ファイルタイプを制限する処理はクライアントサイドのJavaScriptで書かれているのですが、クライアントサイドで掛けられた制限は簡単に回避することができてしまいます。Chromeの開発者オプションなどを使ってJavaScriptを改変して制限を回避することが可能なほか、BurpSuiteやPostmanなどのソフトウェアを使用してPOSTリクエストを改ざんすることでも制限を回避することができます。
アップロードされたファイルは認証なしでユーザがアクセス可能なパスに配置されることになります。この脆弱性を悪用することで、攻撃者は任意のコードをサーバ上で実行することができてしまうため、攻撃によるリスクは高いと考えられます。
■ ブラックリスト管理機能のSQLインジェクション
プロジェクトBには特定のIPからの通信を遮断するためのブラックリスト機能があります。この機能も設定には認証が必要なAdmin機能です。この機能のソースコードには現在の機能には使用されていない、正常系の動作では到達することのないコードが残されていました。
このコードはかつて使われていた古い機能の名残で、長らくメンテナンスもされていないようでした。コードにはセキュリティ意識がそこまで高くなかったと考えられる時代のものも含まれています。
※ 実際のコードとは異なります。
実は、そのうちの一部のコードは、特定のPOSTパラメータを付与してリクエストを送信することで今でも処理に到達させることができるようになっていました。そうやって到達できるコードの中にSQLインジェクション脆弱性を持ったものが残されていたのです。
この脆弱性を悪用することで、攻撃者は任意のSQLクエリをサーバ上で実行させることができてしまいます。攻撃によるリスクは高いと考えられます。
■ 開発者への報告
プロジェクトBの場合はセキュリティに関する問題の報告方法が明記されていました。その方法は、セキュリティ関係の報告専用に用意されたメールアドレスへ詳細を送るというものです。
まずはその方法に従い、脆弱性の概要・再現方法・原因・対策方法を記載したメールを開発者へ送信しました。しかし、その後数日間待っても返信が無かったため、当該のメールアドレスへメールを送ったことを知らせるIssueを立てました。その後、Issueへ開発者の方からのコメントがあり、メールではなくGitterで詳細を送ってほしいとの旨が書かれていました。それに従ってGitterで脆弱性の詳細について報告を行いました。
その後、PHPファイルアップロードによる任意コード実行については「サーバサイドでもファイルタイプのチェックを行うようにする」という対策方法(私が提案した方法です!)で修正が行われました。ブラックリスト管理機能のSQLインジェクションについては、「使用されていないコードに到達する前にexitする」という、応急処置的な方法ではあるものの一旦の修正が行われました。
脆弱性が修正されたバージョンが正式リリースされた後、脆弱性が及ぼす影響の大きさを鑑みて、開発者の方にCVE(Common Vulnerabilities and Exposures)へ脆弱性の詳細を報告する許可を得ました。
■ CVE IDの申請
CVE IDを申請する際は、まずどの組織へ報告を行うべきなのかを知る必要があります。こちらのページには、製品のタイプ毎にどの組織へ報告するべきかという情報がまとめられています。
プロジェクトBのような、この一覧に無いOSSプロジェクトの場合は、DWF(Distributed Weakness Filing Project)が脆弱性情報を管理しています。DWFに脆弱性を報告するとリザーブされたCVE IDが発行され割り当てられます。CVE IDの申請は報告用の専用フォームから行うことができます。
DWFでは、対象のプロジェクト・製品名・影響するバージョン・脆弱性のタイプ・脆弱性の影響などの項目が報告対象となっています。ここで注意が必要なのがReference URLです。Reference URLには脆弱性が存在する証拠としてパブリックなソースを提示する必要があります。しかし、今回のケースのように報告のやり取りを非公開な場で行っていた場合、パブリックな証拠がないために申請が通らない可能性があります。その場合は、開発者に許可を取ってからPoCを公開するなどの方法で、自分でソースとなる情報を用意する必要があります。
私の場合はソースの不足により一時申請が保留となり、後から脆弱性の詳細をまとめたGistを作成して公開することで申請を通すことができました。
■ その後
現在、私が報告した脆弱性には、それぞれCVE-2018-16388
,CVE-2018-16389
のCVE IDが割り当てられて公開されています!
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-16388
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-16389
おわりに
入社前は実践的なセキュリティに触れる機会はあまり多く無かったのですが、トレーニングの終盤には、OSSプロジェクトへの貢献という形で「脆弱性を発見」「原因を考察」「修正を提案する」という流れを一人で実践することができました。
現在は、実案件の脆弱性検査にも参加するようになり、社内プロダクトに対しても脆弱性の発見や修正方法の提案を行うことができています。また、本格的に脆弱性早期警戒活動へ参加するようになり、毎週金曜日は脆弱性情報の調査を行い、社内への影響を分析して情報を関係者にSlackで共有しています。
この他にも、セキュリティエンジニアリングに関する様々な仕事に携わるようになりました。
この2ヶ月で学んだことや意識が変わったことは沢山ありましたが、特に大きく変わったのは脆弱性への接し方です。脆弱性を見つけて終わりではなく、脆弱性が発生する原理、脆弱性が及ぼす具体的な影響範囲、考えられる攻撃のシナリオ、安全にコードを修正する方法、などといった観点で脆弱性を考えていく。そうすることで「攻撃者目線」「守り手目線」「開発者目線」などの様々な目線でセキュリティを考えることができるようになったと思います。
今後は、より俯瞰的にセキュリティリスクを分析できる能力や、より開発者に近い位置で協業できる能力を身に着けて、合理的なセキュリティを実践できるエンジニアを目指していきたいと考えています!