
LogonUI.exe に DLL を注入して NgcSerializeConvenienceCredential に CPU ハードウェアブレークポイントを設定することで、入力時の Windows Hello PIN をキャプチャするツール。さらに WHFB/NGC/TPM 列挙機能も備えています。
Windows Hello が VBS/VSM(VTL1 内の分離された NgcIso コンテナ)+ TPM によって保護されているホストでは、PIN はオフラインでは復元不可能であり、オンライン総当たり攻撃はセキュアカーネルのトラストレットによってブロックされます。唯一信頼できる経路は入力時キャプチャです。
本プロジェクトは、LogonUI.exe 内で入力された PIN をパッケージ化する関数に CPU ハードウェアブレークポイントを設置し、ベクタード例外ハンドラ内でトラップされたスレッドコンテキストから引数を読み取り、元の未改変の命令を実行させることで PIN をキャプチャします。これにより、PIN ログインは通常どおり成功します。
また、WHFB/NGC/TPM の完全な列挙と鍵素材の収集を行う Get-WHFBKeys.ps1 も同梱しています。
Intel fTPM 2.0 と VBS が稼働する Windows 11 26200(Home) でエンドツーエンドの検証済みです。
| 観測された保護 | 結果 |
|---|---|
VBS / VSM が稼働(Win32_DeviceGuard の VBS ステータス = 2、LsaIso.exe が存在) | 鍵階層はVTL1(セキュアカーネル)に存在し、VTL0 の SYSTEM からは到達不可能 |
NgcIso コンテナ(Container.json 内の NgcIsoHeader_… / NgcSvc にロードされたプロテクターブロブ NgcIsoCtnr.dll) | PIN シークレットはトラストレット内部でアンシールされ、通常のユーザーモードには存在しない |
TPM でシールされたマスター素材(Container.json 内の srk、impl 鍵タイプ) | ディスク上に平文鍵はなく、アンシールには TPM が必要 |
アンチハンマリング(softLockoutAttemptLimit = 5、Protectors.json 内の sealedLockoutAuth) | オンライン推測はロックアウトされ、カウンタは VTL1 によって強制され、ディスクからリセット不可能 |
ロック画面 / サインイン時、PIN は LogonUI.exe がホストする NGC 資格情報プロバイダーに入力されます(SYSTEM として実行され、PPL ではなく、オンデマンドで起動)。プロバイダーは入力された PIN を ngckeyenum.dll!NgcSerializeConvenienceCredential に渡し、その**arg1(RCX)**が以下を指します:
struct { // arg1
uint64_t type; // 1 (convenience-PIN credential)
void* buffer; // -> ASCII PIN, e.g. 0x32 0x30 0x30 0x32 = "2002" example PIN
uint32_t length; // digit count (e.g. 4)
};
// arg2 = full NGC key name "S-1-5-...-1001/<container-guid>//<key-guid>"
// arg3 = username, arg4 = computername, arg5 = SID, arg6 = "Negotiate"
NgcSerializeConvenienceCredential(または NgcPackAuthBuffer)に対する古典的なインライン/トランポリンフッキングは、シリアライズされたログオン資格情報を破壊する。症状は厄介かつサイレントである: サインイン画面がエラーもなくただ再読み込みされ、正しい PIN が拒否され、拒否が繰り返されるとPIN がソフトロックされる(パスワードサインインを強制される)。NgcSvc を強制終了するのも同様にコンテナを破壊する。
必要なのは、対象関数のコードの 1 バイトも制御フローも変えることなく引数を読み取るキャプチャプリミティブである。
ngccap.dll(src/ngccap.c を参照)は LogonUI.exe にインジェクトされ、次のことを行う:
CPU ハードウェア実行ブレークポイントを設定する。 ngckeyenum!NgcSerializeConvenienceCredential を解決し、プロセスのすべてのスレッドに対して DR0 = target、DR7 = L0 | exec | len-1 を設定する(CreateToolhelp32Snapshot → OpenThread → SuspendThread → CONTEXT_DEBUG_REGISTERS を指定した GetThreadContext / SetThreadContext → ResumeThread を経由)。
ベクタード例外ハンドラでトラップを処理する。 関数に進入すると、CPU は命令を実行する前に EXCEPTION_SINGLE_STEP を発生させる。VEH は ContextRecord->Rip == target を確認し、Rcx → {buffer, length} を読み取り、ASCII PIN をコピーアウトしてから:
EFlags の**再開フラグ(RF)**を設定し、EXCEPTION_CONTINUE_EXECUTION を返す。元の命令は変更されずに実行される。 コードはパッチされておらず、トランポリンや追加のスタックフレームも導入されていないため、NgcSerializeConvenienceCredential は本来とまったく同じように実行される。資格情報は正しく構築され、LSA がそれを受け入れ、PIN ログインは正常に成功する。
自己クリーンアップする。 最初のキャプチャ後、DLL はすべてのスレッドで DR0/DR7 をクリアし、VEH を削除して、プロセスを元の状態に保つ。
WHFB-PIN-Capture/
├─ README.md
├─ Get-WHFBKeys.ps1 # WHFB/NGC/TPM enumerator + key-material harvester
└─ src/
├─ ngccap.c # HW-breakpoint capture DLL
├─ build.bat # cl.exe build (MSVC 2022 Build Tools)
└─ logonui_watch.ps1 # injects ngccap.dll into LogonUI on sight
SeDebugPrivilege を有効化します)。LogonUI は
SYSTEM として実行されますが PPL ではないため、昇格した管理者トークンで開いてインジェクトできます。:: 1) build the capture DLL
cd src
build.bat
:: 2) start the injector (elevated admin PowerShell)
powershell -ExecutionPolicy Bypass -File .\logonui_watch.ps1
3) Lock the workstation (Win+L) and sign back in with your PIN, it works normally.
4) Read the captured PIN:
type C:\Windows\Temp\pin_capture.log
-> >>> PIN CAPTURED = "1234"
パスは設定可能です。ngccap.c の先頭にある LOGPATH と logonui_watch.ps1 の $plog は一致している必要があります(デフォルトは C:\Windows\Temp\pin_capture.log)。
Get-WHFBKeys.ps1 は、TPM の状態、デバイス参加 / NGC プロビジョニング、WHFB ポリシー、ディスク上の NGC コンテナストア(コンテナ → プロテクター → キー、SYSTEM へ自己昇格)、NCrypt キー(Passport KSP / Platform Crypto Provider / Software KSP、公開キーのエクスポートを含む)、および生体認証の登録状況を報告し、JSON レポートを出力します。秘密キー(TPM に封印され、エクスポート不可)を抽出することは決してありません。公開キー、メタデータ、およびディスク上のプロテクターブロブをダンプします。
powershell -ExecutionPolicy Bypass -File .\Get-WHFBKeys.ps1 -DumpBlobs -OutDir .\out
オフラインおよびブルートフォースの経路は閉じられていることが示され、PINは所有者が正しいPINで通常どおりサインインしている間にハードウェアブレークポイントDLLを用いて取得された。
教育および許可されたセキュリティ研究目的でのみ使用すること。保証は一切ない。