Microsoftは2019年2月、Webベースのコラボレーションソフトウェア「SharePoint」で遠隔からのコード実行(Remote Code Execution、RCE)が可能になる2つの脆弱性に対処する修正プログラムを公開しました。2つの脆弱性の深刻度は両方とも緊急(Critical)に分類されました。攻撃者は特別に細工したリクエストを送信することでこの脆弱性を突き、SharePointのアプリケーションプールと企業アカウントのSharePointサーバにおいて任意のコードを実行することが可能です。
問題の脆弱性は、Markus Wulftange氏によってトレンドマイクロが運営する脆弱性発見・研究コミュニティ「Zero Day Initiative(ZDI)」に報告されました。Wulftange氏は脆弱性「CVE-2019-0604」の詳細についてMicrosoftに情報提供しています。
この脆弱性を利用する実際の攻撃が、セキュリティ機関「SANS Internet Storm Center」によって2019年5月20日に報告されています。
■「XmlSerializer」の仕組み
新しい脆弱性を見つける1つの方法はボトムアップのアプローチです。このアプローチは、関心を引くデータ出力先(シンク)を探し、制御フローおよびデータフローを逆方向に追跡することで当該シンクに到達できるかどうかを解析する手法です。
見込みのあるシンクの1つに「XmlSerializer」クラスを使用したデシリアライゼーションがあります。「XmlSerializer」は、期待される型と一緒に使用する必要があるため一般的には安全なシリアライザだと考えられています。期待される型のオブジェクトグラフに含まれない任意の型をストリーム内で指定することはできないからです。しかし、リサーチペーパー「Friday the 13th – JSON Attacks by Alvaro Muñoz & Oleksandr Mirosh」に示されているように、期待される型も同時に操作することができれば脆弱性を突くことが可能になります。
SharePoint 2016のアセンブリを解析するために、「.NET」アプリケーションのデコンパイルとデバッグが可能な「dnSpy」は非常に優れたツールです。dnSpyの対象として、SharePoint 2016を実行するWebサーバ「IIS(Internet Information Services)」のワーカープロセス「w3wp.exe」を指定すると、アセンブリを読み込み、コンストラクタ「XmlSerializer(Type)」を解析することができます。ここからは単調な作業が必要になります。「XmlSerializer(Type)」の呼び出しを一つひとつ確認し、期待される型の変更および操作の可否をチェックしなければなりません。なぜなら、「new XmlSerializer(typeof(DummyType)))」のように、型はハードコードされていないためです。
コンストラクタ「XmlSerializer(Type)」を呼び出すメソッドの1つに「Microsoft.SharePoint.dll」の「Microsoft.SharePoint.BusinessData.Infrastructure.EntityInstanceIdEncoder.DecodeEntityInstanceId(string)」メソッドがあります。同じ型と機能は「Microsoft.SharePoint.Portal.dll」の名前空間「Microsoft.Office.Server.ApplicationRegistry.Infrastructure」にも存在します。これについては後述しますが、まずは「Microsoft.SharePoint.dll」を見ていきます。
図1:「Microsoft.SharePoint.BusinessData.Infrastructure.EntityInstanceIdEncoder.DecodeEntityInstanceId(string)」メソッド
ここで、期待する型を指定する変数「typeName」とデシリアライズされるデータの両方は、変数「text」から取得します。この「text」は引数「encodedId」に由来します。
問題のメソッドを実際に呼び出すことが可能で、メソッドに渡すパラメータを操作できるのであれば、脆弱性を突く上で問題は無いように思えます。
■データフローを発生源(ソース)まで追跡
次の手順は、外部からこの呼び出しを開始できるかどうか、そして引数の値を提供できるかどうかを確認することです。
図2:「Microsoft.SharePoint.BusinessData.Infrastructure.EntityInstanceIdEncoder.DecodeEntityInstanceId(string)」メソッドに対する呼び出し
「ASP.NET」に親しんでいる人であれば、「Page_Load(object, EventArgs)」や「OnLoad(EventArgs)」のようなメソッドに見覚えがあるかもしれません。これらのメソッドはASP.NETにおけるページのライフサイクルの中で呼び出されます。ページの型は、「.aspx」ファイルを表す基底クラス「System.Web.UI.Page」で定義されます。実際、以下の3つのページの型には、それぞれに対応する「.aspx」ファイルがあります。
- Microsoft.SharePoint.ApplicationPages.ActionRedirectPage:/_layouts/15/ActionRedirect.aspx
- Microsoft.SharePoint.ApplicationPages.DownloadExternalData:/_layouts/15/downloadexternaldata.aspx
- Microsoft.SharePoint.Portal.WebControls.ProfileRedirect:/_layouts/15/TenantProfileAdmin/profileredirect.aspx
これら3つの場合、パラメータの値はHTTPリクエストのクエリ文字列に由来します。十六進数のエンコーディングは、文字列の長さを4倍にし、それによってHTTPリクエストの制限文字数を超える場合があるため、問題となり得ます。
さらなる解析の結果、図2の最後のメソッド「ItemPicker.ValidateEntity(PickerEntity)」が適していることが分かりました。
図3:「ItemPicker.ValidateEntity(PickerEntity)」メソッド
ここで渡されている「PickerEntity」の「Key」プロパティは「EntityInstanceIdEncoder.DecodeEntityInstanceId(string)」メソッドの呼び出しに使用されます。このメソッドは、「EntityEditor.Entities」プロパティに保持された各エントリを列挙して検証する「EntityEditor.Validate()」メソッドによって呼び出されます。
図4:「EntityEditor.Validate()」メソッド
「EntityEditor.Validate()」メソッドは、「System.Web.UI.IPostBackDataHandler.LoadPostData(string, NameValueCollection)」を実装した「EntityEditor.LoadPostData(string, NameValueCollection)」メソッドによって呼び出されます。
図5:「EntityEditor.LoadPostData(string, NameValueCollection)」メソッド
これにより、WebControlsクラスの「ItemPicker」に対するポストバックリクエストの際にメソッドが自動的に呼び出されます。メソッドの呼び出しグラフは以下の通りです。
図6:メソッド呼び出しグラフ
型の階層は以下の通りです。
図7:型の階層
■データフローの検証
以上のように、「ItemPicker」のポストバックリクエストから「EntityInstanceIdEncoder.DecodeEntityInstanceId(string)」に到達する経路がありますが、「PickerEntity」の「Key」プロパティを操作可能かどうかはまだ分かりません。
「EntityEditor.Entities」プロパティは、インスタンス化の際と「EntityEditor.Validate()」メソッド内の2か所でのみで値が代入されるprivateフィールド「m_listOrder」を使用しています。後者の場合、図4の597行目のように、「m_listOrder」フィールドには「m_listOrderTemp」フィールドの値が代入されます。「m_listOrderTemp」フィールドもまた、インスタンス化の際と「EntityEditor.ParseSpanData(string)」メソッド内の2か所でのみ値が代入されます。「EntityEditor.ParseSpanData(string)」メソッドは「EntityEditor.LoadPostData(string, NameValueCollection)」メソッドによって呼び出されます。この時、図5の707行目のように、引数として「HiddenSpanData」プロパティの値(「HtmlInputHidden」クラス)が与えられます。この値はユーザによって操作することが可能です。
後は、「EntityEditor.ParseSpanData(string)」メソッドが与えられたデータをどのように処理するか、そして「PickerEntity」の「Key」プロパティに到達するかどうかを検証すれば終わりです。「EntityEditor.ParseSpanData(string)」メソッドは、ここで紹介するには非常に長いため解説は省略します。データに<SPAN>および<DIV>タグがネストされた特別な構造が含まれていてパースによって除外される場合以外は、「PickerEntity」の「Key」プロパティに、続いて「m_listOrderTemp」リストに代入されます。
以上により、「ItemPicker」のポストバックリクエストから「EntityInstanceIdEncoder.DecodeEntityInstanceId(string)」メソッドに到達する経路と、入力データをユーザが操作可能であることが分かりました。次は、「ItemPicker」のインスタンスについて見ていきましょう。
■エントリポイントの発見
実際は、「ItemPicker」が「.aspx」ページから直接使用されることはありません。しかし、基底クラスである「EntityEditorWithPicker」を調べてみると、「ItemPicker」を使用する「/_layouts/15/Picker.aspx」が見つかります。
この「.aspx」ページは、使用する型がアセンブリ修飾名の形で、URLの「PickerDialogType」パラメータを介して与えられることを期待します。ここで、以下の2つのうちいずれかの「ItemPickerDialog」型が使用されます。
- 「Microsoft.SharePoint.dll」の「Microsoft.SharePoint.WebControls.ItemPickerDialog」
- 「Microsoft.SharePoint.Portal.dll」の「Microsoft.SharePoint.Portal.WebControls.ItemPickerDialog」
図8:「Microsoft.SharePoint.WebControls.ItemPickerDialog」を含む「Picker.aspx」ページ
図8下部のテキストフィールドが、「ItemPicker」および「ctl00$PlaceHolderDialogBodySection$ctl05$hiddenSpanData」という名前の「HtmlInputHidden」クラスに対応しています。このテキストフィールドが、データの出力先(シンク)「EntityInstanceIdEncoder.DecodeEntityInstanceId(string)」の発生源(ソース)となっています。
■概念実証(Proof of Concept、PoC)
問題のフォームが、「__」から始まる文字列(例:「__dummy」)を値として持つ「ctl00$PlaceHolderDialogBodySection$ctl05$hiddenSpanData」と共にサブミットされた際、「EntityInstanceIdEncoder.DecodeEntityInstanceId(string)」に設置したブレークポイントが示す状態は図9の通りです。
図9:「EntityInstanceIdEncoder.DecodeEntityInstanceId(string)」に設置したブレークポイントが示す状態
(「theencodedId」の値は「__dummy」)
この時点のコールスタックは図10のようになります。
図10:コールスタック
もう一方の「ItemPickerDialog」が使用された場合も、図11のように、一番上の2つのエントリが異なるだけです。
図11:もう一方の「ItemPickerDialog」を使用した際のコールスタック
以上で「ctl00$PlaceHolderDialogBodySection$ctl05$hiddenSpanData」のデータが「EntityInstanceIdEncoder.DecodeEntityInstanceId(string)」に渡されることが実証されました。後は、エンティティのインスタンスIDをエンコードし、適切な「XmlSerializer」のペイロードを見つけることで攻撃が成立します。
■Microsoftによる修正プログラムの公開
修正プログラムが2019年2月に公開された後、Markus氏は修正が不完全であることを通知しました。元の修正プログラムは「Microsoft.SharePoint.dll」の「Microsoft.SharePoint.BusinessData.Infrastructure.EntityInstanceIdEncoder」にのみ対処し、「Microsoft.SharePoint.Portal.dll」の「Microsoft.Office.Server.ApplicationRegistry.Infrastructure.EntityInstanceIdEncoder」には対応していませんでした。つまり、「Picker.aspx」ページで「Microsoft.SharePoint.Portal.dll」から「EntityInstanceIdEncoder」を使用することで、修正プログラムのインストール後にも依然として脆弱性を突くことが可能でした。Microsoftは2019年3月12日に公開した修正プログラムでこの問題に対応しました
■トレンドマイクロの対策
ネットワーク挙動監視ソリューション「Deep Discovery™ Inspector」は、以下の DDIルールによって問題の脆弱性を利用する脅威を検知します。
- CVE-2019-0604 SharePoint Remote Code Execution Exploit – HTTP (Request)
参考記事:
- 「CVE-2019-0604: DETAILS OF A MICROSOFT SHAREPOINT RCE VULNERABILITY」
by Zero Day Initiative (ZDI)
翻訳: 澤山 高士(Core Technology Marketing, TrendLabs)