Nseclog

ペネトレーションテスターがセキュリティ検証内容を発信するブログ

Pass-the-Challengeとは?Credential Guardを回避する新しい攻撃手法について調べてみた

2022年12月末にPass-the-Challengeと呼ばれるWindowsに対する新たな攻撃手法が公開されました。

research.ifcr.dk

これは、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と呼ばれるプロセスが実行されます。

Credential GuardのアーキテクチャMicrosoft Learnより引用2

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応答値の生成方法を示した図です。

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の仕様に基づく攻撃手法であり、記事執筆時点では根本的な対策が困難であると考えられます。

そのため、多層防御の観点から以下のような普遍的な対策が有効です。

  • そもそも端末に侵入されないための対策
  • 端末に侵入されてもそれ以上侵入範囲を拡大されないための対策
  • 侵入されてしまった場合に早期に検知するための監視・運用体制の強化

具体的な対策方法の例としては、以下の資料が参考になります。

www.jpcert.or.jp

なお、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

  • The Hyper-V host must have an IOMMU, and run at least Windows Server 2016 or Windows 10 version 1607.
  • The Hyper-V virtual machine must be Generation 2, have an enabled virtual TPM, and be running at least Windows Server 2016 or Windows 10.
    • TPM is not a requirement, but we recommend that you implement TPM.

まずは、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ハッシュを回復する方法が解説されているため、こちらも参考にします。

hashcat.net

まずは、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で使用したツールに関する仕様や実装など、技術的にさらに詳しく解説されています。 興味のある方はぜひそちらも読んでみてください。

参考文献