2022年12月末にPass-the-Challengeと呼ばれるWindowsに対する新たな攻撃手法が公開されました。
これは、NTLMハッシュやKerberosチケットなどの資格情報の保護を目的としたWindowsの機能であるWindows Defender Credential Guard(以下、Credential Guardとする)をバイパスし、暗号化されたNTLMハッシュを回復する攻撃手法です。 本記事では、このPass-the-Challengeについて調べた内容を解説します。
※注意
本記事の内容は犯罪行為を助長するものではありません。サイバー空間の安心・安全な環境を確保する目的にのみ利用し、絶対に悪用しないでください。
Credential Guardとは
Credential Guardとは、NTLMハッシュやKerberosチケットなどの資格情報の保護を目的としたWindowsの機能です。 デフォルトでは無効の機能ですが、Windows 11 Enterprise 22H2およびWindows 11 Education 22H2以降の、Credential Guardと互換性のある端末ではデフォルトで有効となります1。
Credential Guardが無効の場合、ドメインに紐づく資格情報はLSA(Local Security Authority)と呼ばれる、Windowsの認証を司るサブシステムによって管理されています。 そのため、資格情報はLSASS(LSAのプロセス)のメモリ上に直接保存されます。
このとき、端末のローカル管理者権限を行使可能であれば、LSASSのメモリからNTLMハッシュやKerberosチケットを取り出すことができ、Pass-the-HashやPass-the-Key、Pass-the-Ticketといった攻撃に悪用される恐れがあります。
それに対しCredential Guardは、仮想化ベースセキュリティ(Virtualization-based security)によって資格情報をLSAから分離します。
Credential Guardが有効の場合、下図のように仮想化ベースセキュリティによってホストのWindowsOSとは異なるVM内で、LSAIsoと呼ばれるプロセスが実行されます。
LSAIsoは資格情報の暗号化や復号といった資格情報の直接的な操作のみを担当し、LSASSは認証が必要な他のプログラムと通信を行うなどといった資格情報の使用を担当しています。
LSAIsoを呼び出すことができるのはLSASSのみであり、資格情報を直接操作できるのはLSAIsoのみであるため、他のプログラムは資格情報の閲覧や復号を行うことができず、資格情報が保護される仕組みとなっています。
実際に、Credential Guardが有効な端末でLSASSのメモリをダンプしてみると、以下のように暗号化されたNTLMハッシュが保存されており、悪用が困難であることを確認できます。
Pass-the-Challengeの概要
Pass-the-Challengeとは、Credential Guardによって保護、暗号化された資格情報からNTLMハッシュを回復するための攻撃手法であり、2022年12月27日にOliver Lyak(@ly4k_)氏によって公開されました。
すでに侵害されたサーバにおいて、これから新たにログオンするユーザを標的とした従来のCredential Guardのバイパス手法とは異なり、サーバが侵害される前にログオンしていたユーザのNTLMハッシュも回復できるという特徴があります。
なお、元の記事ではLSAIsoのNtlmIumCalculateNtResponseメソッドを悪用するPtCv1と、NtlmIumLm20GetNtlm3ChallengeResponseメソッドを悪用するPtCv2の2つの手法が公開されていますが、本記事ではPtCv1を中心に解説します。
PtCv1の仕組み
以下は、PtCv1の主な流れを示した図です。
まず、LSASSのメモリから後に必要となる情報(Context Handle、Proxy Info、Encrypted blob)をダンプします(①)。 次に、LSAIsoを悪用するためのライブラリであるSecurityPackage.dllをLSASSにロードし(②)、SecurityPackage.dllを介してLSAIsoのNtlmIumCalculateNtResponseメソッドを呼び出します(③)。
ここで登場するNtlmIumCalculateNtResponseメソッドとは、NetNTLMv1による認証を行う際にNetNTLMv1の応答値を計算するためのメソッドです。 最近のWindowsではNetNTLMv1による認証はデフォルトで無効化されているものの、LSAIsoには後方互換性のために応答値を計算するメソッドが残っており、それを悪用しています。 なお、NtlmIumCalculateNtResponseメソッドを呼び出す際に、最初にLSASSのメモリからダンプした情報と固定のチャレンジ値「1122334455667788」を用いています。
最後に、NtlmIumCalculateNtResponseメソッドによってNetNTLMv1応答値が計算され、SecurityPackage.dllに返ってきます(④)。
NetNTLMv1の仕組み
上述のPtCv1によって、NetNTLMv1応答値を取得可能であることがわかりました。 しかし、NetNTLMv1応答値はそのままでは悪用ができないため、NetNTLMv1応答値を元に平文パスワードかNTLMハッシュを特定する必要があります。
以下は、NetNTLMv1応答値の生成方法を示した図です。
John the RipperやHashcatなどの主要なパスワードクラッキングツールはNetNTLMv1に対応しているため、平文パスワードの強度次第ではNetNTLMv1応答値を元に直接平文パスワードを特定することができます。
しかし、上図のとおりNetNTLMv1応答値の生成には危殆化した暗号化方式であるDES暗号が使われており、チャレンジ値と暗号文さえわかれば現実的な時間で秘密鍵を特定することが可能です。 秘密鍵を特定することができれば、秘密鍵のもととなっているNTLMハッシュを回復することができるため、平文パスワードが十分な強度を持っていることによって特定できなかったとしても悪用することができてしまいます。
ここでは、チャレンジ値は「1122334455667788」であることがわかっており、PtCv1によって暗号文(NetNTLMv1応答値)もわかっているため、NTLMハッシュの回復が可能であることがわかります。
元の記事では、crack.shというオンラインサービスを利用してNetNTLMv1応答値からNTLMハッシュを回復しています。
crach.shはDES暗号のレインボーテーブルを保持しており、高速でNTLMハッシュを回復できるみたいです。
(実際にアップロードしても問題ないデータを送信してみましたが、1分ほどでNTLMハッシュを回復することができました。)
しかし、機密情報をオンラインサービスにほいほいアップロードするわけにはいかないため、後述の技術的な検証では自力でNTLMハッシュの回復を行っています。
Pass-the-Challengeの対策
Pass-the-Challengeは、現在のCredential Guardの仕様に基づく攻撃手法であり、記事執筆時点では根本的な対策が困難であると考えられます。
そのため、多層防御の観点から以下のような普遍的な対策が有効です。
- そもそも端末に侵入されないための対策
- 端末に侵入されてもそれ以上侵入範囲を拡大されないための対策
- 侵入されてしまった場合に早期に検知するための監視・運用体制の強化
具体的な対策方法の例としては、以下の資料が参考になります。
なお、Credential Guardは攻撃の障壁として十分有効な機能であるため、本記事によってCredential Guardは意味がないなどといった受け取り方をしないようにお願いします。
技術的な検証
ここからは、実際に検証環境上でPass-the-Challengeを行った内容を解説します。
Pass-the-Challengeの対象となるアカウントのパスワードにはzmh4A5HVXG3sCiwuESKh
を設定しています。
仮想マシンにおけるCredential Guardの有効化
MicrosoftによるCredential Guardの要件3を確認したところ、以下のような記述があったため、Hyper-V仮想マシン上にCredential Guardの検証環境を構築することにしました。
Requirements for running Windows Defender Credential Guard in Hyper-V virtual machines
まずは、Hyper-V上に仮想マシン(Windows 10 Enterprise 22H2)をインストールします。このとき、上記要件のとおり第2世代を選択します。
インストール後、TPMを有効化します。
次に、仮想マシンを適当なドメインに参加させます。これは、Credential Guardによる保護の対象がドメインに紐づく資格情報である4ためです。
最後に、ローカルグループポリシーから仮想化ベースセキュリティとCredential Guardを有効化します。
ローカルグループポリシーのパスはComputer Configuration -> Administrative Templates -> System -> Device Guard -> Turn On Virtualization Based Security
で、設定内容は以下のとおりです。
Mimikatzを用いて、実際にCredential Guardが有効になっているか確認してみます。
LSASSのメモリをダンプした結果、図のように暗号化されたNTLMハッシュが表示されれば、Credential Guardが有効化されています。
Pass-the-Challengeの実行
上記で解説したPtCv1を実際に試してみます。
Pypykatzのインストールと実行
Pass-the-Challengeに必要な情報(Context Handle、Proxy Info、Encrypted blob)をダンプするため、Oliver Lyak氏によって変更が加えられたPypykatzを仮想マシン上にインストールします。
C:\> python -V Python 3.11.1 C:\> python -m pip install minidump minikerberos aiowinreg msldap winacl C:\> git clone https://github.com/ly4k/Pypykatz.git C:\> cd pypykatz\ C:\> python setup.py install
管理者権限でコマンドプロンプトを開き、PypykatzによってLSASSのメモリをダンプします。
これにより、必要な情報(Context Handle、Proxy Info、Encrypted blob)を入手することができました。
SecurityPackage.dllのロード
PassTheChallengeのリリースページからPassTheChallenge.exeとSecurityPackage.dllをダウンロードし、SecurityPackage.dllをLSASSにロードします。
NetNTLMv1応答値の取得
以下のコマンドによって、NetNTLMv1応答値を取得します。
C:\> PassTheChallenge.exe nthash <Context Handle>:<Proxy Info> <Encrypted blob>
実際に、NetNTLMv1応答値が取得できていることを確認できます。
NTLMハッシュの回復
上述のとおり、取得したNetNTLMv1応答値をcrack.shにアップロードすることでNTLMハッシュを回復することができ、Pass-the-Hashなどに悪用できてしまいますが、ここでは自力でNTLMハッシュを回復してみます。
基本的なアプローチ方法としては、上図「NetNTLMv1応答値の生成方法」の手順を逆に行います。 また、Hashcatのフォーラムにて、NetNTLMv1応答値からNTLMハッシュを回復する方法が解説されているため、こちらも参考にします。
まずは、NetNTLMv1応答値(24Bytes)を3分割し、それぞれct1、ct2、ct3とします。
- ct1: C983F4DF622452F3
- ct2: 5F4B79678441E9E0
- ct3: 95BA5A87B298E15E
それぞれのDES暗号文のうち、ct3に関しては秘密鍵のもととなるNTLMハッシュ5の後ろ5Bytesが\x00であるため、実質的な鍵空間が2Bytesしかありません。 そのため、容易にその2Bytes(=NTLMハッシュの末尾2Bytes)を求めることが可能です。
hashcat-utilsに含まれるct3_to_ntlm.exeを使用します。
C:\> ct3_to_ntlm.exe <ct3> <Challenge>
NTLMハッシュの末尾2Bytesが「e566」であることがわかりました。
次に、Hashcatを用いてct1、ct2を解析し、それぞれの秘密鍵を求めます。
解析対象として以下のファイルを用意します。
フォーマットは<DES暗号文>:<平文(チャレンジ値)>
です。
このファイルをHashcatで解析します。
C:\> hashcat.exe -m 14000 -a 3 -1 charsets\DES_full.hcchr --hex-charset hashes.txt ?1?1?1?1?1?1?1?1
これにより、秘密鍵が特定できるはずですが、それなりに時間がかかります。
手元の環境6では、解析終了までの予想時間が14日ほどでした。
(それでも、攻撃という観点では十分現実的な時間です。)
今回は平文パスワードが既知であるため、秘密鍵を逆算し、時間の短縮を図ります。
(2023.02.07 追記)
実際にDES暗号の解析を行ったところ、丸14日間で秘密鍵を特定することができ、NTLMハッシュを回復できました。
ntlmv1-multiというツール群に含まれる、ntlm-to-des.pyを用いて秘密鍵を計算します。 なお、NTLMハッシュは以下のLinuxコマンドで求めることができます。
2つの秘密鍵を求めることができました。 ツールの出力によって次のコマンドが指示されていますが、一旦それを無視し、以下のファイルを作成します。
このファイルを辞書ファイルとし、Hashcatで解析します。
C:\> hashcat.exe -m 14000 -a 0 --hex-charset hashes.txt des.cand
解析結果は以下のとおりです。
正解の秘密鍵のみが含まれたファイルを辞書ファイルとして指定しているため、当たり前ではありますが一瞬で解析が終了します。 しかし、これにより上記の時間のかかる総当たりを実施した場合にも、同様の結果を得られることが推測されます。
最後に、2つの秘密鍵から残りのNTLMハッシュ14Bytes分を求めます。 これは適当なツールが見当たらなかったため、簡単なプログラムを自作しました。
これで、NTLMハッシュを回復することができました。 Hashcatによる解析の際に求めたNTLMハッシュとも一致していることを確認できます。
ここまでで、ようやく以下の目的が達成となります。
- Pass-the-ChallengeによるCredential GuardのバイパスとNetNTLMv1応答値の入手
- NetNTLMv1からNTLMハッシュの回復
なお、最後に使用したプログラムのソースコードはここにあります。
おわりに
本記事では、Credential Guardをバイパスする新しい攻撃手法「Pass-the-Challenge」について解説しました。
元の記事ではPass-the-Challengeで使用したツールに関する仕様や実装など、技術的にさらに詳しく解説されています。 興味のある方はぜひそちらも読んでみてください。
参考文献
- https://research.ifcr.dk/pass-the-challenge-defeating-windows-defender-credential-guard-31a892eee22
- https://learn.microsoft.com/en-us/windows/security/identity-protection/credential-guard/credential-guard
- https://binary-pulsar.hatenablog.jp/entry/2018/12/11/090000
- https://hashcat.net/forum/thread-9009.html
- https://learn.microsoft.com/en-us/windows/security/identity-protection/credential-guard/credential-guard-manage#default-enablement↩
- https://learn.microsoft.com/en-us/windows/security/identity-protection/credential-guard/credential-guard-how-it-works↩
- https://learn.microsoft.com/en-us/windows/security/identity-protection/credential-guard/credential-guard-requirements↩
- https://learn.microsoft.com/en-us/windows/security/identity-protection/credential-guard/credential-guard-protection-limits↩
- NTLMハッシュに\x00\x00\x00\x00\x00を連結し、3分割した際の3つ目の部分であり、図「NetNTLMv1応答値の生成方法」では「NTLMハッシュ3」に該当します。↩
- NVIDIA GeForce RTX 3090↩