Zerologon (CVE-2020-1472)  「ドメインコントローラーにパッチ適用を」


# はじめに

9/11（現地時間) に オランダのセキュリティ企業 Securaより Netlogon Remote Protocol (MS-NRPC)　に関する脆弱性(Zerologon CVE-2020-1472) の情報が公開されました。

- [[Blog] Zerologon: instantly become domain admin by subverting Netlogon cryptography (CVE-2020-1472)](https://www.secura.com/blog/zero-logon)
(詳細は リンク先の [ホワイトペーパー](https://www.secura.com/pathtoimg.php?id=2055) 参照)

9/18 には米国土安全保障省(CISA) から行政機関に対しパッチ適用の[「緊急命令」](https://www.cisa.gov/blog/2020/09/18/windows-server-vulnerability-requires-immediate-attention)
が出てから、Twitter でもこの脆弱性についてのツイートが増えている気がします。

さらに 9/24には、マイクロソフトが本脆弱性を悪用した攻撃を確認したとのツイートがありました。
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Microsoft is actively tracking threat actor activity using exploits for the CVE-2020-1472 Netlogon EoP vulnerability, dubbed Zerologon. We have observed attacks where public exploits have been incorporated into attacker playbooks.</p>&mdash; Microsoft Security Intelligence (@MsftSecIntel) <a href="https://twitter.com/MsftSecIntel/status/1308941504707063808?ref_src=twsrc%5Etfw">September 24, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<br>

本記事では、**「ドメインコントローラーにパッチ適用しましょう」** ということ、攻撃を受けたかもしれない場合の確認方法、本脆弱性の根本原因となる実装上の問題について記載します。


# 脆弱性について

前提として この脆弱性を悪用するには 攻撃者は ドメインコントローラーと通信できるネットワーク上に足場を築く必要があります。「内部に悪い人はいない」のかもしれないですが、例えば ウィルスに感染するなどして 他の脆弱性により乗っ取られてるデバイスがある場合は、攻撃を受ける可能性があります。

脆弱性の内容や対応については、冒頭のホワイトペーパーの他、たくさん記事がでているのでそちらを参照してください。

- [[AD 管理者向け] CVE-2020-1472 Netlogon の対応ガイダンスの概要](https://msrc-blog.microsoft.com/2020/09/14/20200915_netlogon/)
- [Microsoft Windows Netlogon Remote Protocol (MS-NRPC) に権限昇格の脆弱性](https://jvn.jp/vu/JVNVU95778184/index.html)




# 対策

すでに 8月にマイクロソフトから Windows Server のセキュリティ更新プログラムが提供されています。システム管理者は、ドメインコントローラーに対して直ちに更新プログラムを適用することが求められています。

- [CVE-2020-1472 | Netlogon の特権の昇格の脆弱性](https://portal.msrc.microsoft.com/ja-JP/security-guidance/advisory/CVE-2020-1472)
- [CVE-2020-1472 に関連する Netlogon セキュア チャネル接続の変更を管理する方法](https://support.microsoft.com/ja-jp/help/4557222/how-to-manage-the-changes-in-netlogon-secure-channel-connections-assoc) (9/30追記)

# 攻撃されたかどうかの確認

**<font color="Red">POCが複数公開されていますが、決して商用環境で実行しないでください。ドメインコントローラーのコンピュータアカウントパスワードがリセットされると 最悪の場合 ドメイン環境が壊れます。</font>**

**検証環境( Windows Server 2016 / Active Directory) で確認した限り**ですが、POCを実行すると、ドメインコントローラーのイベントログに `4742`, `5805` が記録されました。

`イベントID:4742, 5805` は、他のコンピューターについても上がるイベントなので、**これ自体が攻撃を受けたことを示すものではありません**が、脆弱性の情報が公開された日付以降で、**ドメインコントローラー**についての当該ログを確認するのが指標になるかと思います。

**イベントID 4742**

POCのコードを実行した場合、ドメインコントローラーに対して以下のように、`NT AUTHORITY` で `ANONYMOUS LOGN` して、パスワードが変更された旨のログが記録されます。

```
ログの名前:         Security
ソース:           Microsoft-Windows-Security-Auditing
日付:            2020/09/23 14:06:04
イベント ID:       4742
タスクのカテゴリ:      コンピューター アカウント管理
レベル:           情報
キーワード:         成功の監査
ユーザー:          N/A
コンピューター:       <DCのコンピュータ名>.<ドメイン>
説明:
コンピューター アカウントが変更されました。

サブジェクト:
	セキュリティ ID:		ANONYMOUS LOGON
	アカウント名:		ANONYMOUS LOGON
	アカウント ドメイン:		NT AUTHORITY
```


**イベントID 5805**

同時刻にシステムログに以下のイベントが記録されました。

```
日付:            2020/09/23 14:06:04
イベント ID:       5805
タスクのカテゴリ:      なし
レベル:           エラー
キーワード:         クラシック
ユーザー:          N/A
コンピューター:      <DCのコンピュータ名>.<ドメイン> 
説明:
コンピューター <DCのコンピュータ名> からのセッション設定を認証できませんでした。 次のエラーが発生しました: 
アクセスが拒否されました。
```

脆弱性を利用した攻撃によりドメインコントローラーのコンピュータアカウントパスワードがリセットされても、しばらくは問題なく動いているように見えますが、ドメインコントローラーを再起動するとドメイン環境は正常に動作しなくなりました[^1]。

[^1]: Microsoft-Windows-DNS-Server-Service のエラー(イベントID: 4000）発生, メンバーサーバーのセキュアチャネル破損等）


# 脆弱性の根本原因

Secura の [ホワイトペーパー](https://www.secura.com/pathtoimg.php?id=2055)にあるように、本脆弱性の根本原因は、Netlogon 認証プロセスで使われている **暗号モードの実装上の問題**にあります。


**こちらの動画がとても参考になりますのでお勧めします。ホワイトペーパーを読んでもチンプンカンプンでしたが、この説明を見て分かりました。すごいです！**
[CVE-2020-1472 ZeroLogonの原因をわかりやすく解説！](https://www.youtube.com/watch?v=1h_wlMWwyB4&feature=youtu.be)


## 何が問題なのか

Netlogon リモートプロトコル の `ComputeNetlogonCredential` 関数 では、8ビットCFBモードの AES-128 暗号アルゴリズム (AES-CFB8) が使われていますが、**初期化ベクトル (Initialization Vector) を オールゼロで固定する実装[^2] になっていることが問題です。 初期ベクトルは毎回ランダムに作成する必要があります。**

[^2]: [[MS-NRPC]:Netlogon Remote Protocol  - 3.1.4.4.1 AES Credential -]((https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-NRPC/%5BMS-NRPC%5D.pdf))「If AES support is negotiated between the client and the server, the Netlogon credentials are computed using the AES-128 encryption algorithm in 8-bit CFB mode with a zero initialization vector.」


ホワイトペーパーにはこう書いています（訳はDeepL）

>**(ホワイトペーパーより引用)** In order to be able to encrypt the initial bytes of a message, an Initialisation Vector (IV) must be specified to bootstrap the encryption process. This IV value must be unique and randomly generated for each separate plaintext that is encrypted with the same key. 
The ComputeNetlogonCredential function, however, defines that this IV is fixed and should always consist of 16 zero bytes. 
This violates the requirements for using AES-CFB8 securely: its security properties only hold when IVs are random.

「メッセージの初期バイトを暗号化できるようにするには、暗号化プロセスを起動するために初期化ベクトル(IV)を指定しなければなりません。この IV 値は、同じキーで暗号化される個別の平文ごとに一意で、ランダムに生成されなければなりません。
しかし、ComputeNetlogonCredential 関数では、この IV は固定であり常に 16 個のゼロバイトで構成されるべきであると定義しています。
これは、AES-CFB8 を安全に使用するための要件に違反しています。」

## AES-CFB8 暗号化

ホワイトペーパーには、AES-CFB8 暗号アルゴリズムの解説があります。

__通常時（ランダムな初期ベクトルの場合）__

AES-CFB8では、16バイトの 初期化ベクトル(IV) をメッセージの前に付加し、AES を IV + メッセージの最初の16バイトに適用します。次に AES出力の最初のバイトとXORをとってメッセージの各バイトを暗号化します。

1. ランダムな初期ベクトル(IV) を メッセージ（平文ブロック 8バイト)の前に付加
1. AES を 初期ベクトル(IV) に適用し、先頭バイトのみを取得
1. AES出力 と メッセージ先頭バイトの XOR をとって 先頭バイトを暗号化
1. AES を 次の16バイト に適用 (右に 8ビットシフト）
1. メッセージ全体が暗号化されるまで 繰り返す


**[画像は Secura のホワイトペーパーより引用 ](https://www.secura.com/pathtoimg.php?id=2055)**
![aes-cfb8_normal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118309/bc3b443c-505e-cf61-0575-11c64d6b1f8a.png)


__初期ベクトルとメッセージをオールゼロにした場合__

初期ベクトルの暗号文の先頭バイトが ゼロ となった場合（図の1,2の部分) は、生成される暗号文は 全てゼロになってしまうということが示されています。暗号文の先頭バイトが ゼロ になる確率は 1/256 です。


**[画像は Secura のホワイトペーパーより引用 ](https://www.secura.com/pathtoimg.php?id=2055)**

![aes-cfb8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118309/5956bc11-f340-5301-4184-d78b9a8bfae0.png)


つまり、初期ベクトル を ゼロ で固定すると 256回 鍵を作成したうち 1回 は 全て ゼロ の暗号文が生成される可能性があります。この脆弱性を悪用すると 1/256の確実で 認証が成功してしまうことになります。

## 初期ベクトル(IV) をオールゼロすると 暗号文は 本当にゼロになってしまうのか

[CVE-2020-1472 ZeroLogonの原因をわかりやすく解説！](https://www.youtube.com/watch?v=1h_wlMWwyB4&feature=youtu.be) にあるコードを真似して　`cryptography` で確認しました。

- 暗号化アルゴリズムは `AES`なので ブロック長は 16バイト(128ビット）です。モードは `CFB8` です。
- 鍵 `key` は毎回ランダムに生成します。
- 暗号処理では 脆弱性の原因となった 初期ベクトル(`iv`) を オールゼロで固定します。
- また、メッセージ `message`（平文ブロック) 8バイトも 全てゼロにします。
 
```py
# This code is based on
# https://cryptography.io/en/latest/hazmat/primitives/symmetric-encryption/
# https://github.com/kurenaif/aes_cfb8

import os
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

def encrypt(key, plaintext):
    iv = b"\x00" * 16
    # when IVs are random
    ##iv = os.urandom(16)
    encryptor = Cipher(algorithms.AES(key), modes.CFB8(iv)).encryptor()
    ciphertext = encryptor.update(plaintext) + encryptor.finalize()
    return iv, ciphertext

def decrypt(key, iv, ciphertext):
    decryptor = Cipher(algorithms.AES(key), modes.CFB8(iv)).decryptor()
    return decryptor.update(ciphertext) + decryptor.finalize()

MAX_ATTEMPTS = 100000
cnt = 0
for i in range(MAX_ATTEMPTS):
    # Only 128, 192, and 256 bit keys are allowed for this AES mode
    key = os.urandom(32)
    message = b"\x00" * 8
    iv, ciphertext = encrypt(key, message)
    if ciphertext == message:
        cnt += 1
        #print(f"ciphertext: {ciphertext} decrypted:{decrypt(key, iv, ciphertext)}")

print("score: ", cnt)
print("probability: ", cnt/MAX_ATTEMPTS)
print("1/256: ", 1/256)    
```

実行結果：

```py
score:  395
probability:  0.00395
1/256:  0.00390625
```

100000 回 試行のうち 395回、 オールゼロ の 暗号文 が返ってきました。初期ベクトル(IV) を オールゼロで固定すると 1/256 (0.4 %) の 確率で 認証が通ってしまうであろうことが推測できます。

なお、初期ベクトル(IV) をランダム `iv = os.urandom(16)` にすると、暗号文は ゼロになりません。


__参考書籍__: 「[暗号技術入門 第3版](https://www.amazon.co.jp/dp/4797382228/ref=cm_sw_r_cp_apa_fab_Nz1BFbSN0JHKB)」

## Netlogon認証プロセスで考える

ホワイトペーパーには、Netlogon リモートプロトコルにおける クライアントとサーバーの認証プロセス[^3]において、この脆弱性がどのように悪用されるのか、その概要が書かれています。

[^3]: [[MS-NRPC]:Netlogon Remote Protocol  - 3.1.4.1  Session-Key Negotiation - ](https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-NRPC/%5BMS-NRPC%5D.pdf)を参考にしました。

1. 最初のフェーズ `NetrServerReqChallenge` では、クライアントとサーバーは チャレンジと呼ばれる nonce(一度しか使われないランダムな値) を交換します。これにより、クライアントとサーバの両方が、セッションキーを計算[^4]することができます。

[^4]: [[MS-NRPC]:Netlogon Remote Protocol  - 3.1.4.3  Session-Key Computation -](https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-NRPC/%5BMS-NRPC%5D.pdf)セッションキーを計算する際、`shared secret` （身元を証明するための情報；パスワード等）も使われます。

2. 次のフェーズ `NetrServerAuthenticate3` では、クライアントとサーバーは、計算されたセッションキーを使用して自身の チャレンジを入力として Netlogon クレデンシャルを計算[^5]し交換します。
最初のフェーズで チャレンジ が交換されているため、クライアントとサーバーは相手の Netlogon クレデンシャルを ローカルで再計算し、受信した Netlogon クレデンシャルと比較します。

[^5]: ここで `ComputeNetlogonCredential` 関数が使われています。

3. クライアント（攻撃者）は、本脆弱性を悪用して Netlogon クレデンシャル がオールゼロになるまで 認証プロセスを繰り返し 認証を突破します。認証が通ったあと、攻撃者は 長さゼロのパスワードを指定して コンピュータアカウントパスワードに空パスワード[^6]を設定(`NetrServerPasswordSet2`) します。

[^6]: コンピュータに空のパスワードを設定することは禁止されていない

**[画像は Secura のホワイトペーパーより引用 ](https://www.secura.com/pathtoimg.php?id=2055)**
![zerologon_attack.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118309/65250b3e-e5ba-8315-51bc-ed395aa4c611.png)


# 最後に 

ドメインコントローラーにパッチ適用を。