
CVE-2017-9822の詳細な分析と概念実証エクスプロイト。DotNetNuke CMSのXXE/安全でない逆シリアル化の脆弱性で、Cookie操作によるリモートコード実行につながります。
DotNetNuke(通常は DNN と略される)は、Microsoft の ASP.NET テクノロジに基づく CMS(コンテンツ管理システム)プラットフォーム および Webアプリケーションフレームワーク です。
影響を受ける製品: DotNetNuke(DNN Platform) – 一般的な .NET CMS/ポータル。
公開日: 2017年7月。
深刻度: Critical(CVSS 約9.8)。
脆弱性の種類: XML External Entity (XXE) / 安全でない逆シリアル化 → リモートコード実行 (RCE)。
影響: バージョン 9.1.1 より前では Cookie を介してリモートでコードを実行される可能性があります。
ここでは、Windows 10 を使用してプログラムのセットアップとデバッグを行っています。インストールしたバージョンは 9.1.0 です。インストール方法は こちら を参照してください。完了時の結果は次のとおりです:


私が読んだレポートによると、この脆弱性は DotNetNuke の Cookie 処理部分に存在します。
DNN は、DNNPersonalization Cookie に対して安全でない逆シリアル化(unsafe deserialization)を使用しています。

.dll や .exe などのコンパイル済みファイルから ソースコードを表示、分析、編集 できます。こちら からインストールできます。デバッグには 2 つのバージョンをダウンロードする必要があります。
DotNetNuke.dll を開き、Edit Assembly Attributes (C#) を選択します。
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default |
DebuggableAttribute.DebuggingModes.DisableOptimizations |
DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints |
DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]

その後、保存します。
Attach to Process を選択します。
w3wp.exe を選択します。
w3wp.exe を選択する理由は次のとおりです:
w3wp.exe は IIS Worker Process です。
IIS の Application Pool が実行するプロセスです。
Web サイトに HTTP リクエストが送信されると、IIS はそのリクエストを処理するために w3wp.exe を作成または再利用します(ASP.NET コードの実行、モジュール、ミドルウェア、データベース接続などの処理)。
各 Application Pool は、構成(Web Garden、リサイクル)に応じて 1 つまたは複数の w3wp.exe プロセスを持つことができます。
次に、Debug -> Window -> Modules を選択します。

完了すると Modules が表示されるので、任意の項目を右クリックして Open All Modules を選択します。

最後に、DNN に関連するすべての Assembly が表示されます。

DotNetNuke.dll の PersonalizationController#LoadProfile(int, int) 内に入ります。
この関数は、DNN ポータルでユーザーの個人設定(プロファイル)データを読み込むために使用されます。
ログイン済みユーザーの場合 → データベース + キャッシュ からプロファイルを取得します。
匿名ユーザー(未ログイン)の場合 → Cookie DNNPersonalization からプロファイルを取得します。
ここでは、DNNPersonalization に焦点を当てるべきです。
userId が無効な場合(匿名ユーザー)。
リクエスト内に DNNPersonalization Cookie があるかどうかを確認します。
あれば → この Cookie から XML 値を取得します。
Web サイトに 404 リクエストを送信し、任意の DNNPersonalization を使用します。dnSpy で DotNetNuke.dll の PersonalizationController#LoadProfile(int, int) にブレークポイントを設定すると、デバッグできます。


PortalSettings クラスの分析に焦点を当てましょう。
注目すべき点は、現在のリクエストが IsAuthenticated であるかどうかを if 条件で確認していることです。
そして、送信したリクエストは 404 であるため、unauthenticated になります。
さらに Call Stack 内で、Handle404OrException に注目します。

context.User が null かどうかを確認し、null の場合は context.User に現在のスレッドユーザーを割り当てます。

Handle404OrException 内では、IsAuthenticated 変数が true になり、ユーザーは IIS サーバーのユーザーであるため、リクエストは認証済みユーザーとして処理されることがわかります。
問題の原因は次のコードにあります。
else if (transfer)
{
if (context.User == null)
{
context.User = Thread.CurrentPrincipal;
}
response.TrySkipIisCustomErrors = true;
IHttpHandler handler = new CDefault();
context.Handler = handler;
server.Transfer("~/" + text, true);
}
context.User が存在しない場合 → Thread.CurrentPrincipal(つまり現在のスレッドの ID)を割り当てます。
これにより、後続の処理でリクエストにユーザー/ロール情報が含まれるようになります。
=> DNNPersonalization 変数を使用して Cookie に任意の内容を渡すと、通常のユーザーとして実行されます。
引き続き DotNetNuke.dll の PersonalizationController#LoadProfile(int, int) 内で確認します。
変数 text が Cookie の値を受け取り、その後 Globals.DeserializeHashTableXml() への入力として渡されることがわかります。

Globals.DeserializeHashTableXml() の中に入ります。
DeserializeHashTableXml 関数の役割:
XML 文字列(Source)を受け取ります。
その XML 文字列を解析して、Hashtable オブジェクトに変換します。
解析中に、"profile" パラメータを指定して XML ルートノードを指定し、XmlUtils.DeSerializeHashtable 関数を呼び出します。
XmlUtils.DeSerializeHashtable の中に入ると、その処理方法がわかります。

DeSerializeHashtable 関数は XML 文字列を受け取り、それを Hashtable に変換します。各 <item> ノードに対して、関数は次の処理を行います:
key をキーとして取得します。
type を取得し、Type.GetType(type) を呼び出してデータ型を特定します。
XmlSerializer.Deserialize を使用して、XML の内容を実際のオブジェクトに変換します。
Hashtable に追加します。
👉 問題:type と XML の内容は(Cookie DNNPersonalization から)完全にユーザーが制御できます。
XmlUtils#DeSerializeHashtable が脆弱性の位置であることを踏まえ、オブジェクトをシリアル化/逆シリアル化する同様のプログラムを作成します:
using System.Xml;
using System.Diagnostics;
using System.Xml.Serialization;