デジタルペンテスト部の北原です。 本日は、Windows OS への侵入に成功した攻撃者が管理者権限を奪取した後に悪用する、Token Stealing という手法について解説します。
要点
技術的な詳細に興味がない読者向けに、先に要点を書きます。 要点は以下の3点です:
ログオンしている Windows OS の管理者権限の奪取に成功した場合、Token Stealing の手法により他のプロセスの実行アカウントの権限を借用できます。
Active Directory のドメインに参加している Windows OS の場合、他のドメイン端末で使用するアカウントで起動しているプロセスが存在すると、Token Stealing によりそのプロセスのアカウントの権限が借用できるため、攻撃者の侵入範囲の拡大につながります。ドメインに参加している端末の管理のために各端末にプロセスを実行する場合は、対象の端末でのみ管理者権限を持つアカウントでのプロセス実行が好ましいです。
Token Stealing 自体を可能な限り防ぐには、日頃からローカル管理者グループに所属していないアカウントで端末を運用し、セキュリティ更新プログラムの早急な適用による権限昇格対策をすると良いでしょう。
Token Stealing の検知と対策の詳細については、MITRE ATT&CK の ID T1134 にまとめられています。
アクセストークン
Token Stealing は、ログオン中の Windows OS で起動しているプロセスからアクセストークンを借用する手法です。
一般的には、管理者権限を獲得した状態で実行可能です。
Token Stealing を使用すると、例えば admin
という管理者ユーザアカウントで管理者権限が使える場合に、 winlogon.exe
のような NT AUTHORITY\SYSTEM
で稼働しているプロセスからアクセストークンを借用して、 NT AUTHORITY\SYSTEM
としての動作が可能となります。
アクセストークンは Windows OS での権限情報を管理するために使用されるものです。 Windows OS のプロセスの権限はアクセストークンにより管理されています。 アクセストークン含まれている代表的な情報は以下の通りです:
- 実行しているユーザアカウント
- 所属しているグループアカウント
- 整合性レベル
- 特権
- セッション情報
プロセスとスレッド
Token Stealing の原理を説明する前に、プロセスとスレッドについて簡単に解説します。 プロセスはスレッドの実行を管理するオブジェクトであり、プロセスでの処理はすべてスレッドにより実行されています。 簡易的な図にすると以下の通りです:
アクセストークンはプロセスとスレッドのそれぞれに割り当てられます。
スレッドのアクセストークンは、基本的にはスレッドを管理しているプロセスのアクセストークンが使用されますが、必要に応じて管理元プロセスとは異なるアクセストークンを割り当てることができます。
ただし、任意のアクセストークンをスレッドに割り当てたい場合は、 SeImpersonatePrivilege
という特権が必要です。
プロセスに割り当てられるトークンをPrimaryトークン、借用によりスレッドに割り当てられるトークンをImpersonationトークンと呼び、Windows APIでは以下に示す TOKEN_TYPE
列挙型で指定されます:
typedef enum _TOKEN_TYPE { TokenPrimary = 1, TokenImpersonation } TOKEN_TYPE;
メンバー名が示す通り、TokenPrimary
はPrimaryトークンを、TokenImpersonation
はImpersonationトークンを意味します。
また、ImpersonationトークンにはImpersonation Levelという情報を指定する必要があります。
Impersonation Levelは、Windows APIでは以下に示す SECURITY_IMPERSONATION_LEVEL
列挙型で指定されます:
typedef enum _SECURITY_IMPERSONATION_LEVEL { SecurityAnonymous, SecurityIdentification, SecurityImpersonation, SecurityDelegation } SECURITY_IMPERSONATION_LEVEL, *PSECURITY_IMPERSONATION_LEVEL;
SECURITY_IMPERSONATION_LEVEL
列挙型の4つのImpersonation Levelは、それぞれ以下の用途で使用します:
SecurityAnonymous
-- このImpersonation LevelはImpersonationトークンでは用いられません。PrimaryトークンもImpersonationトークンも、作成する際に同じWindows APIを使用するため、Primaryトークンを作成する際にImpersonation Levelを指定するパラメータとしてこのメンバーを使用します(PrimaryトークンにはImpersonation Levelが存在しないため)。SecurityIdentification
-- このImpersonation Levelが設定されたImpersonationトークンでは、アクセストークンに関連づけられているアカウント名と基本的な情報の確認のみが可能であり、アクセストークンに設定された権限は行使できません。SeImpersonatePrivilege
の使用ができないなど、十分な権限がない状態でスレッドにImpersonationトークンを割り当てた場合は、強制的にこのImpersonation Levelが設定された状態でImpersonationトークンが割り当てられます。SecurityImpersonation
-- このImpersonation Levelが設定されたImpersonationトークンでは、プロセスが稼働しているマシンの内部でのみアクセストークンに設定されている権限を行使できます。SecurityDelegation
-- このImpersonation Levelはサーバ/クライアント型のソフトウェアで使用されます。サーバのプロセスがクライアントのアクセストークンを借用してこのImpersonation Levelが指定されたImpersonationトークンを使用すると、サーバのプロセスが稼働しているマシンの内部だけではなく、クライアントのプロセスが稼働しているマシンでもアクセストークンに設定された権限を行使できます。
プロセスが使用可能な特権は、Sysinternals Suite の Process Explorer で確認できます。
以下の図は Process Explorer で winlogon.exe
の特権を表示しています:
cmd.exe
や powershell.exe
であれば、 whoami /priv
と実行すると以下のように特権情報が列挙できます:
C:\Windows\system32>whoami /priv PRIVILEGES INFORMATION ---------------------- Privilege Name Description State ========================================= ================================================================== ======== SeIncreaseQuotaPrivilege Adjust memory quotas for a process Disabled SeSecurityPrivilege Manage auditing and security log Disabled SeTakeOwnershipPrivilege Take ownership of files or other objects Disabled SeLoadDriverPrivilege Load and unload device drivers Disabled SeSystemProfilePrivilege Profile system performance Disabled SeSystemtimePrivilege Change the system time Disabled SeProfileSingleProcessPrivilege Profile single process Disabled SeIncreaseBasePriorityPrivilege Increase scheduling priority Disabled SeCreatePagefilePrivilege Create a pagefile Disabled SeBackupPrivilege Back up files and directories Disabled SeRestorePrivilege Restore files and directories Disabled SeShutdownPrivilege Shut down the system Disabled SeDebugPrivilege Debug programs Disabled SeSystemEnvironmentPrivilege Modify firmware environment values Disabled SeChangeNotifyPrivilege Bypass traverse checking Enabled SeRemoteShutdownPrivilege Force shutdown from a remote system Disabled SeUndockPrivilege Remove computer from docking station Disabled SeManageVolumePrivilege Perform volume maintenance tasks Disabled SeImpersonatePrivilege Impersonate a client after authentication Enabled SeCreateGlobalPrivilege Create global objects Enabled SeIncreaseWorkingSetPrivilege Increase a process working set Disabled SeTimeZonePrivilege Change the time zone Disabled SeCreateSymbolicLinkPrivilege Create symbolic links Disabled SeDelegateSessionUserImpersonatePrivilege Obtain an impersonation token for another user in the same session Disabled
Token Stealing の動作原理
Token Stealing の動作をまとめると、以下の手順になります:
借用したいアクセストークンで動作しているプロセスのアクセス権を取得
アクセス権を取得したプロセスのアクセストークンへのアクセス権を取得
アクセス権を取得したアクセストークンを複製
複製したアクセストークンを使用
それでは、Token Stealing がどのように実装されているかについて解説しましょう。
プロセスのアクセス権を取得
まずは、借用したいアクセストークンで動作しているプロセスのアクセス権を取得する必要があります。
アクセス権の取得に使用する代表的な Windows API は OpenProcess
APIです。
関数シグネチャを以下に示します:
HANDLE OpenProcess(
[in] DWORD dwDesiredAccess,
[in] BOOL bInheritHandle,
[in] DWORD dwProcessId
);
このWindows APIは、パラメータ dwProcessId
に指定したPIDを持つプロセスにアクセスするためのオブジェクト(ハンドル)を取得するためのものです。
OpenProcess
APIのパラメータの中で特に重要なパラメータは、アクセス権限を指定する dwDesiredAccess
です。
Token Stealing に必要な最低限のアクセス権限は、簡易的な情報の取得に必要な権限である PROCESS_QUERY_LIMITED_INFORMATION
です。
プロセスへのアクセス権の取得に成功すると、戻り値として、プロセスにアクセスするためのハンドルがゼロでは無い値で返されます。
アクセストークンのアクセス権を取得
続けて、アクセス権を取得したプロセスから、アクセストークンのアクセス権を取得します。
アクセストークンのアクセス権を取得するための基本的な Windows API は OpenProcessToken
APIです。
関数シグネチャを以下に示します:
BOOL OpenProcessToken(
[in] HANDLE ProcessHandle,
[in] DWORD DesiredAccess,
[out] PHANDLE TokenHandle
);
OpenProcessToken
APIでは、 OpenProcess
APIで取得したプロセスのハンドルと アクセス権 を設定する必要があります。
Token Stealingでは、アクセス権の取得に成功したアクセストークンを複製したいので、複製に必要な権限である TOKEN_DUPLICATE
を設定します。
アクセストークンへのアクセス権の取得に成功すると、戻り値として TRUE
が返されると共に、パラメータ TokenHandle
に設定したメモリ領域にアクセストークンへのハンドルが出力されます。
アクセストークンを複製
アクセス権を獲得したアクセストークンを複製するには、DuplicateTokenEx
APIに代表されるような Windows API を使用します。
関数シグネチャを以下に示します:
BOOL DuplicateTokenEx( [in] HANDLE hExistingToken, [in] DWORD dwDesiredAccess, [in, optional] LPSECURITY_ATTRIBUTES lpTokenAttributes, [in] SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, [in] TOKEN_TYPE TokenType, [out] PHANDLE phNewToken );
DuplicateTokenEx
APIには、アクセス権を取得したアクセストークンへのハンドルを指定すると共に、上述のToken TypeとImpersonation Levelを指定します。
新しく作成するプロセスに割り当てるアクセストークン(Primaryトークン)として複製したい場合は、TokenType
には TokenPrimary
を設定します。
PrimaryトークンにはImpersonation Levelが存在しないため、ImpersonationLevel
には SecurityAnonymous
を設定します。
スレッドに割り当てるアクセストークン(Impersonationトークン)として複製したい場合は、TokenType
には TokenImpersonation
を設定します。
借用するアクセストークンの権限を行使するには、ImpersonationLevel
は SecurityImpersonation
または SecurityDelegation
のいずれかを設定する必要があります。
アクセストークンへのアクセス権の取得に成功すると、戻り値として TRUE
が返されると共に、パラメータ phNewToken
に設定したメモリ領域に複製したアクセストークンへのハンドルが出力されます。
複製したアクセストークンを使用
複製したアクセストークンを使用する際には、新しいプロセスを起動するか Token Stealing を実行しているスレッドに割り当てるかによって異なります。
新しいプロセスを作成する
複製したアクセストークンを適用した新しいプロセスを作成する場合は、CreateProcessAsUser
APIという Windows API が一般的に使用されています。
アクセストークンを適用した新しいプロセスの作成には SeAssignPrimaryTokenPrivilege
と SeIncreaseQuotaPrivilege
が特権として必要になります。
デフォルトの Windows OS の設定では、SeAssignPrimaryTokenPrivilege
は Administrators グループのアカウントに与えられていないため、Administrators グループのアカウントでは直接的にToken Stealingで新しいプロセスを生成できません。
SeAssignPrimaryTokenPrivilege
を持つプロセスから取得したアクセストークンを Token Stealing を実行しているスレッドに割り当てるか、何かしらの手法で SeAssignPrimaryTokenPrivilege
を持つ高権限アカウントが使用できる状態である必要があります。
補足
SeAssignPrimaryTokenPrivilege
が使えなくても、SeImpersonatePrivilege
が使える場合はCreateProcessAsUser
API の代わりにCreateProcessWithToken
API を使用すると、複製したトークンを割り当てた新しいプロセスが起動できます。しかし、CreateProcessWithToken
API を使用するには、対象のWindows OS で Secondary Logon というサービスが稼働している必要があります。
Token Stealing しているスレッドに割り当てる
複製したアクセストークンを実行中のスレッドに割り当てるWindows APIはいくつか存在しますが、代表的なものは ImpersonateLoggedOnUser
APIです。
関数シグネチャを以下に示します:
BOOL ImpersonateLoggedOnUser(
[in] HANDLE hToken
);
パラメータに複製したアクセストークンへのハンドルを設定するのみです。
特殊な場合を除き、スレッドにアクセストークンを適用するには SeImpersonatePrivilege
が特権として必要です。
しかし ImpersonateLoggedOnUser
APIは基本的には TRUE
を返しエラーコードを出力しないため、単に戻り値を確認しただけではアクセストークンの適用に成功したかどうかが確認できません。
アクセストークンの適用に成功したかどうかを確認する手法としては、スレッドに割り当てられたアクセストークンのImpersonation Levelを確認する手法があります。
十分な権限を持たない状態でスレッドにアクセストークンを設定しようとすると、前述の通りImpersonation Levelは Identification
に設定されます。
スレッドに割り当てられたアクセストークンの状態を確認する場合は、GetTokenInformation
APIという Windows API を使用するか、Google Project Zero の James Forshaw 氏が開発した TokenViewer を使用すると良いでしょう。
TokenViewer は事前にビルドされたバージョンが公式には配布されていないため、自身でビルドする必要があります。
以下の図は、独自に開発した検証コードを使用して、SeImpersonatePrivilege
が無効な状態で winlogon.exe
から Token Stealing を試行し、ImpersonateLoggedOnUser
API の実行後に TokenViewer でスレッドに割り当てられたアクセストークンを確認した様子を示しています。
ImpersonateLoggedOnUser
API の実行結果は TRUE
を返しておりエラーコードは 0
(エラーは無い)ですが、TokenViewer でImpersonation Levelが Identification
に設定されていることが確認できます。
スレッドには winlogon.exe
から複製したアクセストークンが割り当てられていますが、Impersonation Levelが Identification
に設定されているので、実際には winlogon.exe
から複製したアクセストークンの権限を行使できません。
SeImpersonatePrivilege
が有効な状態で、同様の手順を実行して TokenViewer を確認すると、以下の図のようにImpersonation Levelが Impersonation
に設定されていることが確認できます。
補足 Token Stealing を実行するスレッドのアクセストークンを変更する場合は
ImpersonateLoggedOnUser
API を使用しますが、同一プロセスの別のスレッドのアクセストークンを変更する場合はSetThreadToken
API を使用します。アクセストークンを変更したスレッドのアクセストークンを元の状態に戻したい場合は、
RevToSelf
API を使用します。
Token Stealing の実際
攻撃検知のガイドラインとして世界的に活用されている MITRE ATT&CK では、ID T1134のページ に、Token Stealing の検知や軽減策に関する情報がまとめられています。
代表的なツールとしては、C2 フレームワークである Empire などで使用されている Invoke-TokenManipulation
や TokenVator
などが存在します。
また、世界で最も著名であろう攻撃検証ツールであるMetasploitには、incognito
というプラグインとして実装されています。
現実的な悪用手法としてまず考えられるのは、管理者権限アカウントから NTAUTHORITY\SYSTEM
の権限を使用したい場合でしょう。
NTAUTHORITY\SYSTEM
の権限を使えれば、通常の管理者権限ではアクセスできない Windows OS 上に保存された機密情報(認証情報など)にアクセスできます。
注意する必要があるのは、Windows OS の端末が組織の Active Directory のドメインに参加している場合です。
組織によっては、管理の都合上、Active Directory のドメインを管理する Domain Admins
グループに相当する権限を持つグループで、ドメインに参加している端末を管理するための動作(バッチ処理などによる保守作業)をしている場合があります。
もし、 Domain Admins
グループに相当する権限を持つグループで、攻撃者が管理者権限の奪取に成功している端末を操作してしまった場合は、Token Stealing により操作元アカウントの権限が奪取され、攻撃者の侵入範囲が大幅に拡大してしまう危険性が考えられます。
以下の図は、試験的に作成した Active Directory のドメイン環境で、Token Stealing によるドメインコントローラへの侵入の様子を示しています。
クライアント端末 cl01.contoso.local
にドメイン管理者アカウント contoso\administrator
が、Sysinternals Suite の PsExec のような遠隔管理ツールを用いて cmd.exe
を起動して、何かしらの保守作業をしている状況を想定しています。
この場合、管理者権限の奪取に成功すれば、Token Stealing によりドメイン管理者アカウント contoso\administrator
からアクセストークンの借用し、contoso\administrator
の権限でドメインコントローラ dc01.contoso.local
の管理共有の閲覧や遠隔コード実行が可能となります。
Token Stealing による侵入範囲の拡大には、借用するアクセストークンにネットワーク認証情報が使用されているという前提条件が必要ですが、不測の事態に備えてドメインアカウントでの遠隔操作は避けたほうが安全です。 ドメインに参加している端末に対してネットワーク越しに保守作業をしたい場合は、保守対象端末でのみ管理者権限を持つドメインに所属していないアカウントで実施すると良いでしょう。
まとめ
Windows OS への侵入に成功した攻撃者が管理者権限の奪取に成功してしまった場合は、Token Stealing により OS で稼働しているプロセスのアカウントの権限を使用されてしまいます。 Active Directory のドメインに参加している端末である場合は、他の端末で使用しているドメインアカウントのプロセスが稼働していると、侵入範囲の拡大につながる可能性があります。 権限昇格への対策はもちろんですが、権限昇格されてしまった場合に備えて、Active Directory のドメインに参加している端末のネットワーク越しの保守には、保守対象の端末でのみ管理者権限が行使可能なローカルアカウントを使用すると、攻撃者の更なる侵入範囲の拡大への危険性を低減できます。