セキュリティブログ

OOBE:レジストリ内の平文パスワードの話 (CVE-2023-21726)2023/04/26

OOBE:レジストリ内の平文パスワードの話 (CVE-2023-21726)2023/04/26

更新日:2024.03.21

概要

CVE-2023-21726 [1]はCredUI (CredPackAuthenticationBufferW [2])のWindows API 関数に発見された脆弱性であり、平文パスワードの漏洩につながる可能性があります。本記事では、考えられる攻撃の一例のみを紹介しますが、理論上は、CredPackAuthenticationBufferW (CRED_PACK_PROTECTED_CREDENTIALS, ...)のWindows API関数を使用するすべてのアプリケーションが端末のどこかにプレーンテキストのパスワードを保存している可能性があります。 CredUI以外にも、OOBE(Out-of-Box Experience [3])が奇妙な動作をしており、新しく作成したユーザーアカウントの認証情報をレジストリ(非特権Medium integrityユーザーがアクセスできる)にキャッシュしようとしていることが確認されています。この動作がこの問題を発見するきっかけとなりました。

パスワードの発見について

アプリケーションの設定データがディスク上に保存されることは一般的です。通常、このデータは暗号化された形で保存されます。方式は、DPAPIのCryptProtectDataデータブロブから独自の暗号化アルゴリズムまでさまざまです。

しかし、時々、セキュリティモデルの設計不足や開発者のミスで、重要なデータが平文の状態で保存されてしまうことがあります。

そのため、ディスク(Rawモード)やメモリをスキャンして、平文のパスワードを含む重要な文字列を探すことが魅力的になることがあります。ほとんどの場合、ランダムなデータの中で誤検知が発生しますが、たまに思いがけないものも見つかることがあります。

そこで、我々が行ったのは、新しいWindowsをインストールし、OOBEを通じて最初のパスワード保護されたアカウントを作成しました。次に、仮想マシンのディスクドライブをスキャンしたところ、「C:¥Windows¥System32¥Config¥DEFAULT」ファイルに作成したパスワードが平文で保存されていることを確認しました。

このファイルに対応するレジストリキーは(特権を持たないユーザーもアクセス可能)以下になります。

HKEY_USERS\.DEFAULT\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE\Broker\LocalSystemAuthBuffer

技術的詳細(CredUI側)

主要な問題は、credui.dllライブラリファイルにありました。ユーザーがCRED_PACK_PROTECTED_CREDENTIALSフラグを指定しているにもかかわらず、パスワードの暗号化が全く行われていないことが判明しました。

以下の簡単なプログラムによって、この問題を実証することができます。

#include <windows.h>
#include <stdio.h>
#include <wincred.h>

#pragma comment(lib,"Credui.lib")

void HexTable(pCred, dwCredSize) {
    unsigned char *ptr = (unsigned char *)pCred;
    for (size_t i = 0; i < dwCredSize; i++) {
        if (i % 16 == 0) {
            printf("%08zx: ", i);
        }
        printf("%02x ", ptr[i]);

        if (i % 16 == 15) {
            printf(" |");
            for (size_t j = i - 15; j <= i; j++) {
                printf("%c", isprint(ptr[j]) ? ptr[j] : '.');
            }
            printf("|\n");
        }
    }
}

int main() {
    DWORD dwCredSize = 0x1000;
    PBYTE pCred = (PBYTE)malloc(0x1000);
    if (CredPackAuthenticationBufferW(CRED_PACK_PROTECTED_CREDENTIALS, (LPWSTR) L"UserName", (LPWSTR)L"P@ssw0rd123", pCred, &dwCredSize)) {
        HexTable(pCred, dwCredSize);
    } else {
        printf("CredUnPackAuthenticationBufferW LastError=%08x", GetLastError());
    }
}

以下に、修正前と修正後で実行した結果を示します。

図1. パッチ未適用のシステムにおいてプログラムの実行出力
図2. パッチ適用のシステムにおいてプログラムの実行出力

結果の通り、修正後にパスワードは暗号化されているようです。これは、CryptProtectMemoryCRYPTPROTECTMEMORY_SAME_LOGON)[4]とほぼ同様なWindows API関数の内部呼び出しによって行われており、暗号化時に使用されたLUIDと等しくないLUIDを持つユーザーがパスワードを復号することができないようになっています。また、上記に加えて、再起動後に同様なLUIDを持つアカウントでも復号できなくなってしまいます。これは、暗号化に使用されるグローバルキー値が、コンピュータ起動時にcng.sysライブラリファイルによってランダムに生成されるためです。

現在のコールフロー

CredPackAuthenticationBufferW (cryptui.dll)
| - CredPackKerbBufferFromStrings (cryptui.dll)
| -- CredProtectEx (sechost.dll)
| --- CredpEncryptAndMarshalBinaryBlobEx (sechost.dll)
| ---- CredpEncodeSecretEx (sechost.dll)
| ------ SystemFunction040 / RtlEncryptMemory(RTL_ENCRYPT_OPTION_SAME_LOGON) (cryptbase.dll)
| ------- NtDeviceIoControlFile ("\\??\KSecDD", 0x39001E, ...)
| ....
| -------- CngEncryptMemoryEx (cng.sys)

CngEncryptMemoryExのWindows API関数の動作については、[5](flare-on, KeePass writeup)で詳しく説明されています。

以下では、cryptui.dllライブラリファイルの修正箇所を確認できます。

図3. 修正前のCredUIのCredPackKerbBufferFromStrings関数

修正後:

図4. 修正後のCredUIのCredPackKerbBufferFromStrings関数

その結果、最初に設定されるローカル管理者のパスワードは、端末の初回起動時にOOBEによって作成され、安全な形で管理されると考えられます。

技術的詳細(OOBE側)

以下は、我々がUserOOBE(Windows 11)と呼ぶ例です。

図5. OOBE(Out-of-box experience)

この画面はWindowsインストール直後に表示され、ユーザーがアカウントを設定したり、他の設定を適用したりすることができます。

これはexplorer.exeプロセス内でホストされ、UserOOBE.dllライブラリによって実装されています。すべてがdefaultuser0アカウントによって行われます。

注:本調査ではユーザーがローカルアカウントを作成する方式のみを検証しています。Microsoft 365アカウントによって作成する方式は対象外でした。

ユーザーがパスワードとセキュリティ質問を入力すると、RuntimeBroker.exeプロセス内に読み込まれているCloudExperienceHostBroker.dllCloudExperienceHostBroker::Account::LocalAccountManager::CreateLocalAccountWithRecoveryKindAsync関数にWinRTコールが送信されます。

この関数は、管理者アカウントを作成すると同時に、明確な理由は不明ですが(関連するインターフェイスの1つがIOOBEOneDriveOptinと呼ばれるため、OneDriveアカウント登録と何らかの関係があると推測)、平文パスワードをキャッシュしようとします。

図6. CloudExperienceHostBroker::Account::LocalAccountManager::s_PackAndCacheAuthBufferAsLocalSystem 関数

キャッシング処理は、msoobeplugins.dllに実装されたin-proc COMサーバーを呼び出すことで行われます。

図7. PackAuthBuffer関数

PackAuthBufferは、実質的にCRED_PACK_PROTECTED_CREDENTIALSを使用してCredPackAuthentificationBufferW関数を呼び出すだけの関数です。MSRCに報告時点では、この関数は提供された認証情報を暗号化せず、シリアライズされた形式に変換するだけでした。

図8. CredPackAuthentificationBufferW関数の出力結果

最後に、CacheAuthBuffer関数を呼び出すことで、結果がレジストリに保存されます。

HKEY_USERS\.DEFAULT\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE\Broker\LocalSystemAuthBuffer
図9. CacheAuthBuffer関数
図10. msoobeplugins.dll内に実装されているクラス関数により、レジストリパスの判明

ただし、上記で保存されたパスワードの使用箇所を特定できませんでした。

公式の緩和策

すでに一般的なCredUIの修正を示しましたが、これにより今後の認証情報の漏洩が防止されます。しかし、すでにレジストリキーに平文パスワードが保存されているステーションはどうなるのかという疑問が残ります。

ここで新しいOOBE-Maintenance.exeバイナリが役立ちます。

2023年1月のセキュリティアップデートパッケージでは、「\Microsoft\Windows\Registry\OOBE-Maintenance」というスケジュールされたタスクが作成され、このバイナリが1回実行されるようです。

バイナリのロジックはシンプルで、以下のようです。

1. LocalSystemAuthBuffer内のキャッシュされたパスワードをすべて削除
2. DeviceMigitationStatus 1 を保存
3. スケジュールされたタスクOOBE-Maintenanceを削除

図11. OOBE-Maintenance.exeの内部ロジック
図12. OOBE-Maintenance.exe実行前のレジストリ内容
図13. OOBE-Maintenance.exe実行後のレジストリ内容

パッチが適用されたインストールでは、パスワードはまだそのレジストリキーに保存されていますが、今度は暗号化されており、初めからDeviceMigitationStatus=1の値が付随しています。これにより、暗号化されたパスワードが安全に保管され、漏洩のリスクが軽減されます。

図14. パッチを当てたばかりのインストール後のレジストリ(パスワードは暗号化されています)

確認方法

- HEX文字列で値を取得する場合:

[string]::join(' ',((Get-Item -Path
Registry::HKEY_USERS\.Default\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE\Broker).GetValue('LocalSystemAuthBuffer')| ForEach{'{0:x2}' -f $_}))

- ASCII文字列で値を取得する場合:

[System.Text.Encoding]::ASCII.GetString((Get-Item -Path Registry::HKEY_USERS\.Default\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE\Broker).GetValue('LocalSystemAuthBuffer'))

タイムライン

• 2021/12/24 - MSRCに報告

• 2022/3/23 - MSRCから修正が2023年1月11日以降に予定されているとの連絡

• 2023/1/23 - CVE-2023-21726とともに修正のリリース

備考リンク

[1] https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-21726
[2] https://learn.microsoft.com/en-us/windows/win32/api/wincred/nf-wincred-credpackauthenticationbufferw
[3] https://learn.microsoft.com/en-us/windows-hardware/customize/desktop/customize-oobe-in-windows-11
[4] https://learn.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptprotectmemory
[5] https://github.com/eleemosynator/writeups/blob/master/flare-on-6/12%20-%20help/readme.md#7-the-shortening-of-the-way

セキュリティ診断のことなら
お気軽にご相談ください
セキュリティ診断で発見された脆弱性と、具体的な内容・再現方法・リスク・対策方法を報告したレポートのサンプルをご覧いただけます。

関連記事

経験豊富なエンジニアが
セキュリティの不安を解消します

Webサービスやアプリにおけるセキュリティ上の問題点を解消し、
収益の最大化を実現する相談役としてぜひお気軽にご連絡ください。

疑問点やお見積もり依頼はこちらから

お見積もり・お問い合わせ

セキュリティ診断サービスについてのご紹介

資料ダウンロード