はじめに:Get-GPPPassword.py とは? 🤔
Impacket は、ネットワークプロトコルを扱うための Python クラスのコレクションであり、特に Windows 環境のセキュリティ評価やペネトレーションテストで広く利用されています。その Impacket に含まれる数多くの便利なツールの一つが Get-GPPPassword.py
です。
このスクリプトは、Active Directory (AD) 環境において、かつてグループポリシー基本設定 (Group Policy Preferences, GPP) を用いて配布されていたパスワード情報を探索し、復号するために使用されます。具体的には、ドメインコントローラーの SYSVOL 共有内にある特定の XML ファイル (Groups.xml
, ScheduledTasks.xml
など) を検索し、その中に含まれる暗号化されたパスワード (cpassword
属性) を見つけ出し、既知の復号キーを用いて平文のパスワードを明らかにします。
なぜこのようなツールが存在するのでしょうか?それは、過去に Microsoft が GPP でパスワードを安全でない方法で保存・配布してしまっていたという歴史的経緯があるからです。このブログでは、Get-GPPPassword.py
の使い方、背景にある脆弱性、そしてその対策について詳しく解説していきます。
背景:グループポリシー基本設定 (GPP) のパスワード脆弱性 (MS14-025) 📜
Get-GPPPassword.py
が対象とするのは、MS14-025 として知られる脆弱性です。これは、Windows のグループポリシー基本設定 (GPP) 機能に関連する深刻なセキュリティ問題でした。
GPP は、ドメイン管理者が管理下のコンピュータやユーザーに対して、ドライブのマッピング、ローカルユーザーやグループの作成・管理、スケジュールされたタスクの設定、サービスの構成などを一元的に行うための便利な機能です。しかし、これらの設定項目の一部 (特にローカルユーザーの作成・変更やマップされたドライブの認証情報など) では、パスワードを GPP 内に保存する必要がありました。
問題は、このパスワードが AES-256 で暗号化されていたものの、その暗号化に使用される秘密鍵 (復号キー) が、なんと Microsoft によって公開ドキュメント (MSDN) に記載されてしまっていたことです。これは2012年頃に発覚しました。
GPP の設定情報は、ドメインコントローラー上の SYSVOL という共有フォルダに XML ファイルとして保存されます。SYSVOL フォルダは、デフォルトでドメインに参加している全ての認証済みユーザー (Authenticated Users) が読み取り可能です。
この二つの事実、つまり「SYSVOL 内の GPP 設定ファイル (XML) はドメインユーザーなら誰でも読める」ことと、「GPP 内に保存されたパスワード (cpassword) は公開されたキーで復号できる」ことが組み合わさった結果、悪意のある攻撃者や内部の不正ユーザーが、ドメイン内の認証情報 (特にローカル管理者パスワードなど) を容易に入手し、権限昇格やラテラルムーブメント (横展開) に悪用できるという深刻な脆弱性が存在することになりました。
具体的には、以下のような XML ファイル内の cpassword
属性が対象となります。
Groups.xml
(ローカルユーザー/グループ設定)ScheduledTasks.xml
(スケジュールタスク設定)Services.xml
(サービス設定)DataSources.xml
(データソース設定)Drives.xml
(ドライブマップ設定)Printers.xml
(プリンタ設定)
Microsoft は 2014年5月13日に、この脆弱性に対処するためのセキュリティ更新プログラム MS14-025 (KB2962486) をリリースしました。このパッチは、GPP でパスワードを新規に設定・変更する機能を削除し、既存のポリシーについてはパスワード関連の操作を「削除」のみに制限するものです。しかし、重要な点として、このパッチは SYSVOL に既に保存されている GPP の XML ファイル自体を削除するわけではありません。そのため、パッチ適用後も古い XML ファイルが残っている場合、依然としてパスワード情報が漏洩するリスクが残ります。
Impacket と Get-GPPPassword.py の概要 🛠️
Impacket は、ネットワークプロトコルの低レベルな操作を可能にする Python ライブラリ群です。SMB、MSRPC、Kerberos、LDAP、NTLM など、Windows ネットワーク環境で重要な多くのプロトコルをサポートしており、セキュリティ専門家やペンテスターにとって必須のツールキットとなっています。現在は Fortra (旧 Core Security / SecureAuth) によってメンテナンスされています。
Get-GPPPassword.py
は、Impacket の examples
ディレクトリに含まれるスクリプトの一つです。このスクリプトの主な機能は以下の通りです。
- 指定されたターゲット (通常はドメインコントローラー) の SYSVOL 共有に接続する。
- SYSVOL 共有内を再帰的に探索し、GPP パスワードが含まれる可能性のある XML ファイル (
Groups.xml
など) を見つける。 - XML ファイル内に
cpassword
属性があれば、その値を抽出する。 - 公開されている AES 復号キーを使用して
cpassword
の値を復号し、平文のパスワードを表示する。
他の類似ツールと異なり、Get-GPPPassword.py
は SYSVOL 共有をローカルにマウントする必要がありません。代わりに SMB プロトコルのストリーム機能を利用して共有内を探索し、ファイルの内容を直接処理するため、より効率的で柔軟な操作が可能です。また、ローカルに保存された GPP の XML ファイルをオフラインで解析する機能も持っています。
Get-GPPPassword.py の使い方 💻
インストール
Get-GPPPassword.py
を使用するには、まず Impacket をインストールする必要があります。pip を使って簡単にインストールできます。
pip install impacket
# またはリポジトリから直接インストール
git clone https://github.com/fortra/impacket.git
cd impacket/
python setup.py install
インストール後、Get-GPPPassword.py
スクリプトは通常 Impacket のインストールディレクトリ内の examples
フォルダに配置されます。
基本的なコマンド構文
Get-GPPPassword.py
の基本的な構文は以下の通りです。
python Get-GPPPassword.py [オプション] [[ドメイン/]ユーザー名[:パスワード]@]ターゲット名またはアドレス
主要なオプション
以下に、よく使われるオプションをいくつか紹介します。
オプション | 説明 |
---|---|
target (位置引数) |
接続先のターゲットを指定します。ドメインコントローラーの FQDN、NetBIOS 名、または IP アドレスを指定します。認証情報 (ドメイン/ユーザー名:パスワード ) もここで指定できます。ローカルファイルを解析する場合は LOCAL を指定します。 |
-h, --help |
ヘルプメッセージを表示して終了します。 |
-no-pass |
パスワードなし (NULL セッション) または空のパスワードで認証を試みます。パスワードプロンプトを表示しません。Kerberos 認証 (-k ) と併用する場合に特に便利です。 |
-hashes LMHASH:NTHASH |
Pass-the-Hash (PtH) 攻撃を行うために、LM ハッシュと NT ハッシュを指定します。LM ハッシュは省略可能です。例: aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0 |
-k |
Kerberos 認証を使用します。環境変数 KRB5CCNAME で指定された Ccache ファイルから認証情報を取得しようとします (Pass-the-Cache)。Ccache が見つからない、または無効な場合は、位置引数で指定されたパスワード (平文 Kerberos 認証) または -hashes で指定された NT ハッシュ (Overpass-the-Hash) を使用します。 |
-aesKey HEXKEY |
Kerberos 認証で Pass-the-Key を行うために、AES キー (128 ビットまたは 256 ビット) を 16 進数で指定します。 |
-dc-ip IPアドレス |
ドメインコントローラーの IP アドレスを明示的に指定します。ターゲット名の名前解決がうまくいかない場合に役立ちます。 |
-target-ip IPアドレス |
ターゲットマシンの IP アドレスを明示的に指定します。-dc-ip と似ていますが、接続先が DC でない場合にも使用できます。 |
-port ポート番号 |
接続先の TCP ポートを指定します。デフォルトは SMB の 445 番ポートです。 |
-share 共有名 |
GPP パスワードを検索する SMB 共有名を指定します。デフォルトは SYSVOL です。 |
-base-dir ディレクトリ |
指定された共有内で検索を開始するベースディレクトリを指定します。デフォルトは / です。デフォルトの共有名 (SYSVOL) と組み合わせると、\\ターゲット\SYSVOL\ から検索を開始します。 |
-xmlfile ファイルパス |
ローカルに保存された GPP の XML ファイルを指定して解析します。このオプションを使用する場合、ターゲット引数には LOCAL を指定します。 |
-ts |
出力にタイムスタンプを追加します。 |
-debug |
デバッグモードを有効にし、詳細な情報を表示します。 |
実行例
以下にいくつかの実行例を示します。
-
NULL セッションでの試行 (認証情報なし):
python Get-GPPPassword.py -no-pass DC01.corp.local
ドメインコントローラー
DC01.corp.local
に対して、認証情報なしで接続し、GPP パスワードを検索します。匿名アクセスが許可されていれば成功する可能性があります。 -
ユーザー名とパスワードで認証:
python Get-GPPPassword.py CORP\\User1:Password123@DC01.corp.local
CORP
ドメインのUser1
ユーザー (パスワード:Password123
) としてDC01.corp.local
に接続し、検索します。ドメインユーザーであれば通常 SYSVOL への読み取りアクセス権があります。 -
Pass-the-Hash (NT ハッシュのみ):
python Get-GPPPassword.py -hashes :31d6cfe0d16ae931b73c59d7e0c089c0 CORP\\Administrator@DC01.corp.local
Administrator
ユーザーの NT ハッシュを使用してDC01.corp.local
に接続します。 -
Kerberos 認証 (Pass-the-Cache):
まず、
KRB5CCNAME
環境変数を設定します。export KRB5CCNAME=/tmp/user.ccache
次に、
-k
オプションと-no-pass
オプションを付けて実行します。python Get-GPPPassword.py -k -no-pass CORP\\User1@DC01.corp.local
/tmp/user.ccache
ファイル内の Kerberos チケットを使って認証します。 -
ローカルファイルの解析:
python Get-GPPPassword.py -xmlfile /path/to/Groups.xml LOCAL
ローカルにある
Groups.xml
ファイルを解析して、cpassword
を復号します。
想定される出力
スクリプトが GPP パスワードを含む XML ファイルを発見し、復号に成功すると、以下のような形式で出力が表示されます。
[+] Found credentials in file: \\DC01.corp.local\SYSVOL\corp.local\Policies\{GUID}\Machine\Preferences\Groups\Groups.xml
User: LocalAdminUser
Password: SuperSecretPassword123!
Changed: 2013-11-15 10:30:00
ユーザー名、復号されたパスワード、そしてパスワードが最後に変更された日時 (XML ファイルから取得) が表示されます。複数のパスワードが見つかれば、それぞれが表示されます。見つからなかった場合は、何も出力されないか、探索した旨のメッセージのみが表示されます。
脆弱性の悪用シナリオ (ペネトレーションテストの観点から) 😈
ペネトレーションテストやレッドチーム演習において、Get-GPPPassword.py
は初期アクセス後の権限昇格や横展開 (ラテラルムーブメント) のフェーズで非常に有効なツールとなり得ます。
シナリオ例:
-
初期アクセス: フィッシング攻撃や脆弱な Web アプリケーションなどを通じて、ドメインに参加している一般ユーザー権限の PC (例:
WKSTN01
) を侵害したとします。 -
情報収集: 侵害した PC から、現在のユーザーのドメイン情報 (ドメイン名、ドメインコントローラー名など) を特定します。
PS C:\> nltest /dsgetdc:corp.local DC: \\DC01.corp.local Address: \\192.168.1.10 Dom Guid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx Dom Name: corp.local ...
-
GPP パスワード探索: 攻撃者は自身のマシンから、入手したドメインユーザーの認証情報 (または NULL セッション) を使用して、ドメインコントローラー
DC01.corp.local
に対してGet-GPPPassword.py
を実行します。python Get-GPPPassword.py corp.local\\compromised.user:LeakedPassword@DC01.corp.local
-
パスワード発見: 運が良ければ (つまり、管理者が GPP でパスワードを配布しており、かつ古い XML ファイルが残っていれば)、以下のような結果が得られます。
[+] Found credentials in file: \\DC01.corp.local\SYSVOL\corp.local\Policies\{Some-GUID}\Machine\Preferences\Groups\Groups.xml User: CORP\StandardLocalAdmin Password: P@$$w0rdGPP! Changed: 2014-01-20 15:00:00
この例では、
StandardLocalAdmin
というローカル管理者アカウントのパスワードが発見されました。GPP で設定されたパスワードは、多くの場合、ドメイン内の複数のマシンで共通のローカル管理者パスワードとして使用されています。 -
横展開 (ラテラルムーブメント): 攻撃者は、発見した
StandardLocalAdmin
の認証情報を使用して、ドメイン内の他のマシン (例:SRV01
,WKSTN02
など) への管理者アクセスを試みます。psexec.py
やsmbexec.py
といった Impacket の他のツールがここで役立ちます。python psexec.py CORP\\StandardLocalAdmin:'P@$$w0rdGPP!'@SRV01.corp.local # または python smbexec.py CORP\\StandardLocalAdmin:'P@$$w0rdGPP!'@WKSTN02.corp.local
これにより、攻撃者はネットワーク内の他のシステムへと侵入範囲を拡大し、さらなる情報窃取や最終目的の達成を目指します。
このように、Get-GPPPassword.py
は、比較的簡単な手順でドメイン内の認証情報を取得し、攻撃の足掛かりを広げる強力な手段となり得るのです。
対策と緩和策🛡️
MS14-025 脆弱性への対策は、単にパッチを適用するだけでは不十分な場合があります。以下の対策を講じることが重要です。
-
セキュリティ更新プログラム MS14-025 (KB2962486) の適用:
まず最も基本的な対策として、影響を受ける全ての Windows システム (特にドメインコントローラーと管理用マシン) に MS14-025 パッチを適用します。これにより、GPP を使用して新たにパスワードを設定・変更する機能が無効になります。
-
SYSVOL 内の GPP XML ファイルのクリーンアップ:
パッチを適用しても、過去に作成された
cpassword
を含む XML ファイルは SYSVOL 内に残り続けます。これらのファイルを特定し、削除または修正する必要があります。- 手動での検索と削除: SYSVOL 共有 (例:
\\ドメイン名\SYSVOL\ドメイン名\Policies\
) を開き、Groups.xml
,ScheduledTasks.xml
,Services.xml
,DataSources.xml
,Drives.xml
,Printers.xml
などのファイルを探します。これらのファイルを開き、cpassword=
という文字列が含まれていないか確認します。もし含まれていれば、その GPO (ポリシー) の設定を見直し、パスワードを使用しない方法に変更するか、ポリシー自体が不要であれば削除し、対応する XML ファイルも SYSVOL から削除します。 - スクリプトの利用: Microsoft は、このクリーンアップ作業を支援するためのスクリプトを提供している場合があります。また、コミュニティで開発された検索・クリーンアップスクリプトも存在します。例えば、PowerShell を使って SYSVOL 内を検索し、
cpassword
を含むファイルをリストアップすることができます。Get-ChildItem \\your.domain.com\SYSVOL\your.domain.com\Policies -Recurse -Include Groups.xml,ScheduledTasks.xml,Services.xml,DataSources.xml,Drives.xml,Printers.xml | Select-String -Pattern 'cpassword=' | Select-Object -Unique Path
- 手動での検索と削除: SYSVOL 共有 (例:
-
パスワード管理方法の見直し:
GPP を使ってローカル管理者パスワードなどを配布する運用は、MS14-025 によって安全でないことが明らかになりました。代替となる、より安全なパスワード管理方法を導入する必要があります。
- LAPS (Local Administrator Password Solution): Microsoft が提供する無料のソリューションで、各コンピューターのローカル管理者パスワードをランダムかつ一意に設定し、Active Directory 内に安全に保管します。必要な権限を持つ管理者のみがパスワードを取得できます。現在推奨されるベストプラクティスです。
- 特権アクセス管理 (PAM) ソリューション: より高度な管理と監査が必要な場合は、商用の PAM ソリューションの導入を検討します。
- GPO によるローカル Administrators グループへの追加: ローカル管理者権限が必要なユーザーやグループを、GPO の「制限されたグループ (Restricted Groups)」や「ローカルユーザーとグループ (Local Users and Groups)」設定 (パスワード設定機能を除く) を使って、各コンピューターのローカル Administrators グループに追加します。この方法ではパスワード自体は配布されません。
-
SYSVOL のアクセス権限の確認:
デフォルトでは Authenticated Users に読み取り権限がありますが、組織のポリシーによっては、より厳格なアクセス制御を検討することも有効かもしれません。ただし、グループポリシーの正常な動作に必要な権限を奪わないよう注意が必要です。
-
定期的な監査と監視:
SYSVOL 内に意図せず GPP パスワードが作成・保存されていないか、定期的に監査します。また、ドメインコントローラーへの不審なアクセスや、
Get-GPPPassword.py
のようなツールの使用試行を検知するためのセキュリティ監視を強化することも重要です。
これらの対策を組み合わせることで、MS14-025 脆弱性によるリスクを効果的に低減することができます。特に、古い GPP XML ファイルのクリーンアップと LAPS の導入は強く推奨されます。👍
まとめ ✨
impacket-Get-GPPPassword
(Get-GPPPassword.py) は、過去にグループポリシー基本設定 (GPP) を通じて配布されたパスワード情報を探索・復号するための Impacket スクリプトです。このツールが悪用可能な背景には、GPP がパスワードを AES で暗号化していたものの、その復号キーが公開されていたという MS14-025 脆弱性が存在します。
攻撃者はこのツールを使うことで、ドメインコントローラーの SYSVOL 共有から容易に認証情報を窃取し、権限昇格や横展開に利用する可能性があります。
このリスクに対処するためには、MS14-025 パッチの適用はもちろんのこと、SYSVOL 内に残存する古い GPP XML ファイルを確実にクリーンアップし、LAPS のような安全なローカル管理者パスワード管理ソリューションへ移行することが不可欠です。
Get-GPPPassword.py
はペネトレーションテストにおいては有用なツールですが、その存在はシステム管理者にとって、過去の設定が依然としてセキュリティリスクとなり得ることを示す警鐘でもあります。適切な対策を講じ、安全な Active Directory 環境を維持しましょう。🔒
コメント