このドキュメントは、NTLM認証プロトコルと関連するセキュリティサポートプロバイダの機能について、実装を行なう際のリファレンスとなるように、上級者向けの詳細を記載したものである。 このドキュメントが NTLM に関する網羅的な記載となっていくことを期待しているが、現在のところ筆者の知識不足と記載不足による抜け洩れがあり、いくつかの誤りも含んだものとなっている。 とはいえ、このドキュメントはさらなる調査を進める上での確実な第一歩を提供していると信じている。 ここに記載している情報は、http://jcifs.samba.org で提供しているオープンソースの jCIFS ライブラリにおける NTLM 認証の実装に用いられている。 このドキュメントは筆者による調査と、Samba における実装の解析に基づいて執筆されている。
ドメイン環境における認証プロトコルとしての NTLM は、多くの場合 Kerberos に取って代わられているが、Kerberos は信頼できる第三者(trusted-third-party)の存在を前提としているため、これが存在しないメンバサーバ(ドメインに参加していないサーバ)やローカルアカウント、信頼されていないドメイン上のリソースの認証に用いることができない。こうした環境においては、NTLM が主要な認証機構としての座を保ち続けている(その地位はもうしばらく続きそうである)。
詳細についての記載を行なう前に、さまざまなプロトコルで用いられているいくつかの用語について定義しておく必要がある。
Type 3 メッセージにおけるレスポンスは、サーバに対してクライアントのユーザは自身のアカウントのパスワードを把握しているという情報を提示するという点で非常に重要である。
認証のプロセスは両者の間で共有コンテキストを確立することから始まる。これには引続き行なわれる署名と暗号化処理に用いられる共有セッション鍵が含まれる。
(可能な限り)混乱を避けるため、このドキュメントでは、以下のような使いわけを行なう。
「long値」とは、リトルエンディアンで32ビットの符合なし整数を意味する。10進数の「1234」をlong値だとすると、16進数表記では「0xd2040000」となる。
「セキュリティバッファ」とはバイナリデータへのバッファを示すために用いられる構造体を意味する。これには、以下のメンバが含まれる。
例えば、「0xd204d204e1100000」というセキュリティバッファは、以下のように解析できる。
バッファ長: 0xd204 (1234 バイト)
割り当てられている領域: 0xd204 (1234 バイト)
オフセット: 0xe1100000 (4321 バイト目)
メッセージの最初のバイトからみると、4321バイトスキップした地点が、データバッファの先頭位置になる。ここから 1234 バイト(バッファ長)のデータを読みとれる。バッファに割り当てられた領域も 1234 バイトであるため、そこがバッファの終端になる。
ようやく NTLM 認証メッセージの実形式について記載するところまでたどり着いた。
ついで、メッセージの Type (1、2、3)を示すlong値が続く。Type 1 メッセージの場合、これは16進数で「0x01000000」となる。
さらににメッセージ固有の情報が続く。これは通常セキュリティバッファとメッセージフラグから構成されている。
| フラグ | 名称 | 詳細 |
|---|---|---|
| 0x00000001 | Negotiate Unicode | セキュリティバッファのデータで、Unicode 文字列がサポートされていることを示す。 |
| 0x00000002 | Negotiate OEM | セキュリティバッファのデータで、OEM 文字列がサポートされていることを示す。 |
| 0x00000004 | Request Target | サーバの authentication realm が Type 2 メッセージに含まれることを要求する。 |
| 0x00000008 | 不明 | このフラグの用途は確認されていない。 |
| 0x00000010 | Negotiate Sign | クライアントとサーバ間の認証に際して、デジタル署名(メッセージの整合性確保)の実施を指定する。 |
| 0x00000020 | Negotiate Seal | クライアントとサーバ間の認証に際して、暗号化(メッセージの機密性確保)の実施を指定する。 |
| 0x00000040 | Negotiate Datagram Style | datagram authentication の使用を指定する。 |
| 0x00000080 | Negotiate Lan Manager Key | 認証に際して、Lan Manager Session Key の使用を指定する。 |
| 0x00000100 | Negotiate Netware | このフラグの用途は確認されていない。 |
| 0x00000200 | Negotiate NTLM | NTLM 認証の使用を指定する。 |
| 0x00000400 | 不明 | このフラグの用途は確認されていない。 |
| 0x00000800 | Negotiate Anonymous | クライアントが送出する Type 3 メッセージで指定され、匿名コンテキストによる認証が確立されたことを示す。これはレスポンスフィールドの内容にも影響する(詳細は「匿名レスポンス(Anonymous Response)」セクションで記載している)。 |
| 0x00001000 | Negotiate Domain Supplied | クライアントが送出する Type 1 メッセージで指定され、クライアントのワークステーションが所属するドメイン名の情報がメッセージに含まれていることを示す。 これは、サーバがクライアントをローカル認証すべきかどうかを決定する際に用いられる。 |
| 0x00002000 | Negotiate Workstation Supplied | クライアントが送出する Type 1 メッセージで指定され、クライアントのワークステーション名がメッセージに含まれていることを示す。 これは、サーバがクライアントをローカル認証すべきかどうかを決定する際に用いられる。 |
| 0x00004000 | Negotiate Local Call | サーバによって送出され、クライアントとサーバが同一マシンであることを示す。 これにより、クライアントはチャレンジに対するレスポンスを計算する代わりに、確立済のローカルな資格情報を使用することが可能となる。 |
| 0x00008000 | Negotiate Always Sign | クライアントとサーバ間の認証に際して、「dummy」署名による署名を示す。 |
| 0x00010000 | Target Type Domain | サーバが送出する Type 2 メッセージで指定され、対象の authentication realm がドメインであることを示す。 |
| 0x00020000 | Target Type Server | サーバが送出する Type 2 メッセージで指定され、対象の authentication realm がサーバ(server)であることを示す。 |
| 0x00040000 | Target Type Share | サーバが送出する Type 2 メッセージで指定され、対象の authentication realm が共有(share)であることを示す。おそらく、これは共有レベルのセキュリティを意味していると思われるが、用途は明確になっていない。 |
| 0x00080000 | Negotiate NTLM2 Key | NTLM2 署名と暗号化のスキームを認証の際の保護に用いることを示す。 これは、セッションセキュリティのスキームに対するものであり、NTLMv2 認証の使用とは関係しない点に注意。 ただし、このフラグは、レスポンスの計算に影響を与える(詳細は「NTLM2 セッションレスポンス(NTLM2 Session Response)」セクションで記載している)。 |
| 0x00100000 | Request Init Response | このフラグの用途は確認されていない。 |
| 0x00200000 | Request Accept Response | このフラグの用途は確認されていない。 |
| 0x00400000 | Request Non-NT Session Key | このフラグの用途は確認されていない。 |
| 0x00800000 | Negotiate Target Info | サーバが送出する Type 2 メッセージで指定され、メッセージに Target Information ブロックが含まれることを示す。Target Information ブロックは NTLMv2 レスポンスの計算に用いられる。 |
| 0x01000000 | 不明 | このフラグの用途は確認されていない。 |
| 0x02000000 | 不明 | このフラグの用途は確認されていない。 |
| 0x04000000 | 不明 | このフラグの用途は確認されていない。 |
| 0x08000000 | 不明 | このフラグの用途は確認されていない。 |
| 0x10000000 | 不明 | このフラグの用途は確認されていない。 |
| 0x20000000 | Negotiate 128 | 128 ビットの暗号化がサポートされていることを示す。 |
| 0x40000000 | Negotiate Key Exchange | クライアントが Type 3 メッセージの「Session Key」フィールドで暗号化されたマスター鍵を提供することを示す。 |
| 0x80000000 | Negotiate 56 | 56 ビットの暗号化がサポートされていることを示す。 |
上記を合計した値は「0x00008205」となる。これは実際には「0x05820000」という形態で格納される(リトルエンディアンのバイト順で表現されるため)。
| Description | Content | |
|---|---|---|
| 0 | NTLMSSP 署名 | NULL 終端された ASCII 文字列「NTLMSSP」 (0x4e544c4d53535000) |
| 8 | NTLM メッセージのタイプ | long (0x01000000) |
| 12 | フラグ | long |
| (16) | Supplied Domain (オプション) | セキュリティバッファ |
| (24) | Supplied Workstation (オプション) | セキュリティバッファ |
| (32) | データブロックの先頭 (必要な場合) | |
通常、Type 1 メッセージには、以下のフラグが設定されている。
| Negotiate Unicode (0x00000001) | Unicode 文字列のサポートを示すため、このフラグを設定する。 |
| Negotiate OEM (0x00000002) | OEM 文字列のサポートを示すため、このフラグを設定する。 |
| Request Target (0x00000004) | サーバに対して、Type 2 の応答の際に authentication target の送信を要求する。 |
| Negotiate NTLM (0x00000200) | NTLM 認証がサポートされていることを示す。 |
| Negotiate Domain Supplied (0x00001000) | 設定されている場合、クライアントはワークステーションが所属するドメイン名をメッセージの一部として送信する。 |
| Negotiate Workstation Supplied (0x00002000) | クライアントが自身のワークステーション名をメッセージの一部として送信することを示す。 |
| Negotiate Always Sign (0x00008000) | 認証後のクライアントとサーバ間の通信が「dummy」署名で行なわれることを示す。 |
| Negotiate NTLM2 Key (0x00080000) | クライアントが NTLM2 の認証と暗号化スキームをサポートすることが示される。 ネゴシエーションに成功した場合は、レスポンスの計算にも影響する。 |
| Negotiate 128 (0x20000000) | クライアントが強力な(128ビット)暗号をサポートしていることを示す。 |
| Negotiate 56 (0x80000000) | クライアントが中程度の(128ビット)暗号をサポートしていることを示す。 |
supplied domain はクライアントが所属するドメイン名を格納するセキュリティバッファである。これは、クライアントが Unicode をサポートしていても、常に OEM 形式で格納される。
supplied workstation はクライアントのワークステーション名を格納するセキュリティバッファである。これも、常に OEM 形式で格納される。
4e544c4d535350000100000002020000
このメッセージには、NTLMSSP 署名 、NTLM メッセージのタイプと最低限のフラグ(Negotiate NTLM と Negotiate OEM)だけが含まれている。
16進数で表記した、以下の Type 1 メッセージを解析してみよう。
4e544c4d535350000100000007320000060006002b0000000b000b0020000000
574f524b53544154494f4e444f4d41494e
| 0 | 0x4e544c4d53535000 | NTLMSSP 署名 |
| 8 | 0x01000000 | Type 1 を示す識別子 |
| 12 | 0x07320000 |
Flags:
Negotiate Unicode (0x00000001)
|
| 16 | 0x060006002b000000 |
Supplied Domain セキュリティバッファ:
長さ: 6 bytes (0x0600)
|
| 24 | 0x0b000b0020000000 |
Supplied Workstation セキュリティバッファ:
長さ: 11 bytes (0x0b00)
|
| 32 | 0x574f524b53544154494f4e | Supplied Workstation のデータ ("WORKSTATION") |
| 43 | 0x444f4d41494e | Supplied Domain のデータ ("DOMAIN") |
クライアントが Type 1 を作成して、それをサーバに送信すると、サーバは我々が行なったのと同様にメッセージを解析して、応答を作成する。この応答が次のトピックである Type 2 メッセージである。
| Description | Content | |
|---|---|---|
| 0 | NTLMSSP 署名 | NULL 終端された ASCII 文字列「NTLMSSP」 (0x4e544c4d53535000) |
| 8 | NTLM メッセージのタイプ | long (0x02000000) |
| 12 | Target Name | セキュリティバッファ |
| 20 | Flags | long |
| 24 | Challenge | 8 バイト |
| (32) | Context (オプション) | 8 bytes (2つのlong値) |
| (40) | Target Information (オプション) | セキュリティバッファ |
| 32 (48) | データブロックの先頭 | |
Type 2 メッセージでは、通常以下のフラグが設定される。
| Negotiate Unicode (0x00000001) | Unicode 文字列の使用を示すため、このフラグを設定する。 クライアントが (Type 1 メッセージで) Unicode のサポートを示した場合のみ、このフラグの設定を行なうこと このフラグか Negotiate OEM フラグのいずれかを設定する必要があるが、両方を設定してはならない。 |
| Negotiate OEM (0x00000002) | OEM 文字列の使用を示すため、このフラグを設定する。 クライアントが (Type 1 メッセージで) OEM 文字列のサポートを示した場合のみ、このフラグの設定を行なうこと このフラグか Negotiate Unicode フラグのいずれかを設定する必要があるが、両方を設定してはならない。 |
| Request Target (0x00000004) | このフラグは Type 2 メッセージで設定されることも多い。Type 1 メッセージでのフラグの目的は明示されているが、Type 2 メッセージでの目的は不明なままである。 |
| Negotiate NTLM (0x00000200) | NTLM 認証がサポートされていることを示す。 |
| Negotiate Local Call (0x00004000) | サーバとクライアントが同一マシン上に存在していることをクライアントに提示する。 サーバは、メッセージの一部としてローカルセキュリティコンテキストハンドルを提供する。 |
| Negotiate Always Sign (0x00008000) | 認証後のクライアントとサーバ間の通信が「dummy」署名で行なわれることを示す。 |
| Target Type Domain (0x00010000) | このフラグにより、メッセージ内で送信される authentication target がドメインを表すことを示す。 |
| Target Type Server (0x00020000) | このフラグにより、メッセージ内で送信される authentication target がサーバを表すことを示す。 |
| Target Type Share (0x00040000) | このフラグにより、メッセージ内で送信される authentication target がネットワーク共有を示すことが明示される。このフラグの用途は確認されていない。 |
| Negotiate NTLM2 Key (0x00080000) | サーバが NTLM2 の認証と暗号化スキームをサポートすることが示される。 ネゴシエーションに成功した場合は、クライアントからのレスポンスの計算にも影響する。 |
| Negotiate Target Info (0x00800000) | このフラグにより、Target Information ブロックがメッセージ内で送信されることを示す。 |
| Negotiate 128 (0x20000000) | クライアントが強力な(128ビット)暗号をサポートしていることを示す。 |
| Negotiate 56 (0x80000000) | クライアントが中程度の(128ビット)暗号をサポートしていることを示す。 |
challenge は 8 バイトのランダムなデータである。クライアントはこの情報を用いてレスポンスを生成する。
context フィールドは、通常 Negotiate Local Call が設定された場合に存在する。 これには SSPI コンテキストハンドルが格納される。これにより、クライアントはチャレンジに対する応答を行なう代わりに「short-circuit」な認証を行なうことが可能となる。 context は 2 つの long値からなる。詳細は「ローカル認証」セクションで後述する。
target の情報は、Target Information ブロック中のセキュリティバッファに含まれる。これはNTLMv2 レスポンス (後述する)の計算に用いられる。このブロックは、サブブロックの配列から構成されている。各サブブロックは、以下の内容で構成されている。
| フィールド | Content | Description | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Type | short |
このサブブロックのデータのタイプを指定する。
|
||||||||
| 長さ | short | このサブブロックの content フィールドの長さ(バイト単位) | ||||||||
| Content | Unicode 文字列 | type フィールドで指定された情報。メッセージフラグで OEM が指定されていても、常に Unicode 文字列として送信される。 |
配列は、終端(terminator)サブブロックで終了する。このサブブロックは type が「0」で、長さ 0 である。type が「5」のサブブロックも存在する。これにはサーバが所属するドメインの「親」DNS ドメイン名が含まれているようである。その他にも未知のサブブロック type が存在するかも知れない。
context および target information は省略される場合がある。その場合、データブロックはオフセット 32 (チャレンジの直後)から開始される。最小の Type 2 メッセージは以下のようなバイト列となる。
4e544c4d53535000020000000000000000000000020200000123456789abcdef
このメッセージには、NTLMSSP 署名 、NTLM メッセージタイプ、空の target namae 、最低限のフラグ(Negotiate NTLM と Negotiate OEM)、チャレンジが含まれている。
16進数で表記した、以下の Type 2 メッセージを解析してみよう。
4e544c4d53535000020000000c000c003000000001028100
0123456789abcdef0000000000000000620062003c000000
44004f004d00410049004e0002000c0044004f004d004100
49004e0001000c0053004500520056004500520004001400
64006f006d00610069006e002e0063006f006d0003002200
7300650072007600650072002e0064006f006d0061006900
6e002e0063006f006d0000000000
| 0 | 0x4e544c4d53535000 | NTLMSSP 署名 | ||||||||||
| 8 | 0x02000000 | Type 2 を示す識別子 | ||||||||||
| 12 | 0x0c000c0030000000 |
Target Name セキュリティバッファ:
長さ: 12 バイト (0x0c00)
|
||||||||||
| 20 | 0x01028100 |
フラグ:
Negotiate Unicode (0x00000001)
|
||||||||||
| 24 | 0x0123456789abcdef | challenge | ||||||||||
| 32 | 0x0000000000000000 | context | ||||||||||
| 40 | 0x620062003c000000 |
Target Information セキュリティバッファ:
長さ: 98 バイト (0x6200)
|
||||||||||
| 48 |
0x44004f004d004100 49004e00 |
Target Name のデータ (「DOMAIN」) | ||||||||||
| 60 |
0x02000c0044004f00 4d00410049004e00 01000c0053004500 5200560045005200 0400140064006f00 6d00610069006e00 2e0063006f006d00 0300220073006500 7200760065007200 2e0064006f006d00 610069006e002e00 63006f006d000000 0000 |
Target Information のデータ:
|
target name は(Negotiate Unicode フラグで指定されたように)、Unicode 形式である点に注意。
サーバが Type 2 メッセージを作成して、クライアントに送信する。サーバのチャレンジに対するレスポンスは、クライアントが送出する Type 3 メッセージで提示される。
| Description | Content | |
|---|---|---|
| 0 | NTLMSSP 署名 | NULL 終端された ASCII 文字列「NTLMSSP」 (0x4e544c4d53535000) |
| 8 | NTLM メッセージのタイプ | long (0x03000000) |
| 12 | LM/LMv2 レスポンス | セキュリティバッファ |
| 20 | NTLM/NTLMv2 レスポンス | セキュリティバッファ |
| 28 | ドメイン名 | セキュリティバッファ |
| 36 | ユーザ名 | セキュリティバッファ |
| 44 | ワークステーション名 | セキュリティバッファ |
| (52) | セッション鍵(オプション) | セキュリティバッファ |
| (60) | フラグ (オプション) | long |
| 52 (64) | データブロックの先頭 | |
Type 3 メッセージにおいて、フラグはオプションであることに注意。以前のクライアントでは、メッセージ中にセッション鍵もフラグも含まれない。この場合、データブロックはワークステーション名セキュリティバッファ直後のオフセット 52 から開始される。Type 3 メッセージにフラグが存在していた場合でも、コネクションの認証に関しての情報は格納しないこと、認証にもセッションセキュリティの確立にも何ら影響を及ぼさないことが試験的に(experimentally)決定されている。クライアントがフラグを送信する場合は、通常 Type 2 メッセージで確立された設定のコピーである。このフラグを、サーバがネゴシエーションされた設定をキャッシュしてしまわないように、確立されたオプションの「リマインダ」として用いることが可能である。Type 3 のフラグについては、データグラム形式の認証 と関連している。
LM/LMv2 および NTLM/NTLMv2 レスポンスは Type 2 メッセージのチャレンジに対する応答としてユーザのパスワードから生成されたレスポンスを含むセキュリティバッファである。これらのレスポンスを生成する処理については、次のセクションで概説する。
ドメイン名は、認証されるアカウントが所属する authentication realm を含むセキュリティバッファである。これはネゴシエートされたエンコーディングにより、Unicode もしくは OEM 文字列として格納される。
ユーザ名は、認証されるアカウント名を含むセキュリティバッファである。これもネゴシエートされたエンコーディングにより、Unicode もしくは OEM 文字列として格納される。
ワークステーション名はクライアントのワークステーション名を含むセキュリティバッファである。これもネゴシエートされたエンコーディングにより、Unicode もしくは OEM 文字列として格納される。
セッション鍵の値は、鍵交換の際にセッションセキュリティ機構によって用いられる。詳細については、Session Security セクションにて記載している。
Type 2 メッセージで「Negotiate Local Call」が確立されていた場合、Type 3 メッセージのセキュリティバッファは通常すべて空(0 バイト長)である。クライアントは、レスポンスを計算する代わりに Type 2 メッセージで送付された SSPI context を「採用」する。
クライアントは Type 2 メッセージのチャレンジに対して、以下のいずれか 1 つ以上のレスポンスを生成して、Type 3 メッセージで送信する。レスポンスには以下の 6 種類がある。
LM レスポンスの計算方法を以下に記載する (Java における実装については 付録 D を参照のこと)。
この処理を詳細に記載したのが以下の例である。ユーザが「SecREt01」というパスワードを用いており、Type 2 チャレンジ「0x0123456789abcdef」に応答しようとしていると仮定する。
01010011 01000101 01000011 01010010 01000101 01010100 00110000
これを DES 鍵として使えるようにパリティビットの値を考慮せず、単にビットを挿入すると、以下になる。
01010010 10100010 01010000 01101010 00100100 00101010 01010000 01100000
(パリティビットは赤文字で表示している. これを 16 進数表記すると「0x52a2506a242a5060」となる。このバイト列に対して奇数パリティの条件を満たすように各オクテット毎にビットを設定したものが以下となる。
01010010 10100010 01010001 01101011 00100101 00101010 01010001 01100001
これが 1 つめの DES 鍵となる (16 進数で「0x52a2516b252a5161」)。引続き、同様の処理を 2 つ目のバイト列である「0x31000000000000」に対して行なう。このバイト列をビット列で表すと以下となる。
00110001 00000000 00000000 00000000 00000000 00000000 00000000
これを DES 鍵として使えるようにパリティビットの値を考慮せず、単にビットを挿入すると、以下となる
00110000 10000000 00000000 00000000 00000000 00000000 00000000 00000000
(16進数では「0x3080000000000000」) 。奇数パリティの設定を行なったものは以下となる。
00110001 10000000 00000001 00000001 00000001 00000001 00000001 00000001
これが 2 つ目の DES 鍵であり、16 進数では「0x3180010101010101」となる。DES の実装によっては(実際のところ多くの実装では)パリティを使用しないため、この処理は省略できる。この場合 DES 鍵として使えるように単にビットを挿入したバイト列が DES 鍵として用いられる。多くの場合、パリティビットは暗号化処理に影響を及ぼさない。
11111111 00110111 01010000 10111100 11000010 10110010 00100100
からは、パリティ処理を行なった DES 鍵として以下が生成される
11111110 10011011 11010101 00010110 11001101 00010101 11001000 01001001
(16 進数では「0xfe9bd516cd15c849」)。2 つ目のバイト列である
00010010 11000010 00100110 01011011 00100011 01110011 01001110
からは、以下のバイト列が生成される
00010011 01100001 10001001 11001011 10110011 00011010 11001101 10011101
(16 進数では「0x136189cbb31acd9d」)。最後に、3 つ目のバイト列である
00001101 10101100 00000000 00000000 00000000 00000000 00000000
からは、以下のバイト列が生成される。
00001101 11010110 00000001 00000001 00000001 00000001 00000001 00000001
これが 3 つ目の DES 鍵(「0x0dd6010101010101」)となる。
0xc337cd5cbd44fc9782a667af6d427c6de67c20c2d3e77c56
このアルゴリズムには平文を推測可能である脆弱性がいくつか存在する。脆弱性についての詳細は、Hertel のドキュメントで記載されているが、最も顕著な問題は、以下のようなものである。
NTLM レスポンスは以下のようにして計算される (Java における実装例については 付録 D を参照のこと)。
LM レスポンスと異なるのはハッシュ値の生成部分だけであり、レスポンスの計算部分は同一である。 詳細に記載するために、先ほど用いたサンプル(ユーザのパスワードが「SecREt01」で、Type 2 チャレンジ「0x0123456789abcdef」に応答しようとしている)にこの処理を適用してみよう。
11001101 00000110 11001010 01111100 01111110 00010000 11001001
からは、パリティ処理を行なった DES 鍵として
11001101 10000011 10110011 01001111 11000111 11110001 01000011 10010010
(16 進数で「0xcd83b34fc7f14392」)が生成される。2 つ目の値
10011011 00011101 00110011 10110111 01001000 01011010 00101110
からは、
10011011 10001111 01001100 01110110 01110101 01000011 01101000 01011101
(16進数で「0x9b8f4c767543685d」)が生成され、3 つ目の値
11011000 00001000 00000000 00000000 00000000 00000000 00000000
からは、
11011001 00000100 00000001 00000001 00000001 00000001 00000001 00000001
(16 進数で「0xd904010101010101」)が生成される。
0x25a98c1c31e81847466b29b2df4680f39958fb8c213a9cc6
NTLMv2 レスポンスは以下のようにして計算される (Java による実装例については 付録 D を参照のこと)。
| Description | Content | |
|---|---|---|
| 0 | Blob 署名 | 0x01010000 |
| 4 | 予約 | long (0x00000000) |
| 8 | Timestamp | 1601 年 1 月 1 日から 10 マイクロ秒単位の時刻を格納した 64 ビットの符合付き、リトルエンディアンの数値 |
| 16 | Client Nonce | 8 bytes |
| 24 | 不明 | 4 bytes |
| 28 | Target Information | Target Information ブロック (Type 2 メッセージより) |
| (可変) | 不明 | 4 bytes |
以下に例を示す。NTLMv2 レスポンスを計算するためにはいくつか必要な情報があるが、今回はここまでの例にならい、以下の値を用いる。
| ドメイン名: | DOMAIN |
| ユーザ名: | user |
| パスワードd: | SecREt01 |
| チャレンジ: | 0x0123456789abcdef |
| Target Information: |
0x02000c0044004f00 4d00410049004e00 01000c0053004500 5200560045005200 0400140064006f00 6d00610069006e00 2e0063006f006d00 0300220073006500 7200760065007200 2e0064006f006d00 610069006e002e00 63006f006d000000 0000 |
次に、ランダムな 8 バイトの「client nonce」を生成する必要がある。ここでは乱雑性に低いが「0xffffff0011223344」という値を用いることとする。blob の残りの部分は簡単に生成できる。これらを結合しよう。
| 0x01010000 | (Blob 署名) |
| 0x00000000 | (予約値) |
| 0x0090d336b734c301 | (タイムスタンプ) |
| 0xffffff0011223344 | (ランダムなクライアントの値) |
| 0x00000000 | (不明だが、0 にしておけば動作する) |
0x02000c0044004f00 4d00410049004e00 01000c0053004500 5200560045005200 0400140064006f00 6d00610069006e00 2e0063006f006d00 0300220073006500 7200760065007200 2e0064006f006d00 610069006e002e00 63006f006d000000 0000 |
(target information ブロック) |
| 0x00000000 | (不明だが、0 にしておけば動作する) |
0x0123456789abcdef0101000000000000 0090d336b734c301ffffff0011223344 0000000002000c0044004f004d004100 49004e0001000c005300450052005600 450052000400140064006f006d006100 69006e002e0063006f006d0003002200 7300650072007600650072002e006400 6f006d00610069006e002e0063006f00 6d000000000000000000
これに ステップ 2 で生成した NTLMv2 ハッシュを鍵として用いて、HMAC-MD5 を適用することで、16 ビットの値「0xcbabbca713eb795d04c97abc01ee4983」が生成される。
0xcbabbca713eb795d04c97abc01ee4983 01010000000000000090d336b734c301 ffffff00112233440000000002000c00 44004f004d00410049004e0001000c00 53004500520056004500520004001400 64006f006d00610069006e002e006300 6f006d00030022007300650072007600 650072002e0064006f006d0061006900 6e002e0063006f006d00000000000000 0000
LMv2 レスポンスは、古いサーバとのパススルー認証の互換性を維持するために用いられる。クライアントの通信先のサーバが実際の認証を行なわず、ドメインコントローラにレスポンスを提示する形態はごく一般的なことである。古いサーバでは、LM レスポンスのみを提示していた。これは 24 バイトのバイト列であった。LMv2 レスポンスはこれらのサーバを適切に動作させるために設計された。実際のところ、LMv2 レスポンスは「最低限の」NTLMv2 レスポンスである。以下に計算例を示す (Java による実装例については 付録 D を参照のこと)。
この処理を、前述した値を用いた例で簡単に説明しよう。
| ドメイン名: | DOMAIN |
| ユーザ名: | user |
| パスワード: | SecREt01 |
| チャレンジ: | 0x0123456789abcdef |
0x0123456789abcdefffffff0011223344
ステップ 2 で生成した NTLMv2 ハッシュを鍵として、上記のバイト列に HMAC-MD5 を適用する。これにより「0xd6e6152ea25d03b7c6ba6629c2d6aaf0」という 16 バイトのバイト列が生成される。
0xd6e6152ea25d03b7c6ba6629c2d6aaf0ffffff0011223344
NTLM2 セッションレスポンスは、以下で記載するように、LM レスポンスと NTLM レスポンスの両方を置き換える (Java による実装については 付録 D を参照のこと)。
先ほど用いたサンプル(ユーザのパスワードが「SecREt01」で、Type 2 チャレンジ「0x0123456789abcdef」に応答しようとしている)にこの処理を適用してみよう。
0xffffff001122334400000000000000000000000000000000
この値は Type 3 メッセージの LM レスポンスフィールドに格納される。
0x10d550832d12b2ccb79d5ad1f4eed3df82aca4c3681dd455
であり、Type 3 メッセージの NTLM レスポンスフィールドに格納される。
匿名の Type 3 メッセージは、クライアントにより「Negotiate Anonymous」フラグが設定され、NTLM レスポンスフィールドが空(0 バイト長)、LM レスポンスフィールドには 1 バイトの NULL 文字(「0x00」)が格納される。
Type 3 レスポンスについての知識を深めるため、以下の Type 3 メッセージを解析してみよう。
4e544c4d5353500003000000180018006a00000018001800
820000000c000c0040000000080008004c00000016001600
54000000000000009a0000000102000044004f004d004100
49004e00750073006500720057004f0052004b0053005400
4100540049004f004e00c337cd5cbd44fc9782a667af6d42
7c6de67c20c2d3e77c5625a98c1c31e81847466b29b2df46
80f39958fb8c213a9cc6
This message is decomposed as:
| 0 | 0x4e544c4d53535000 | NTLMSSP 署名 |
| 8 | 0x03000000 | Type 3 識別子 |
| 12 | 0x180018006a000000 |
LM Response セキュリティバッファ:
長さ: 24 バイト (0x1800)
|
| 20 | 0x1800180082000000 |
NTLM Response セキュリティバッファ:
長さ: 24 bytes (0x1800)
|
| 28 | 0x0c000c0040000000 |
ドメイン名セキュリティバッファ:
長さ: 12 バイト (0x0c00)
|
| 36 | 0x080008004c000000 |
ユーザ名セキュリティバッファ:
長さ: 8 バイト (0x0800)
|
| 44 | 0x1600160054000000 |
ワークステーション名セキュリティバッファ:
長さ: 22 バイト (0x1600)
|
| 52 | 0x000000009a000000 |
セッション鍵セキュリティバッファ:
長さ: 0 バイト (0x0000)
|
| 60 | 0x01020000 |
フラグ:
Negotiate Unicode (0x00000001)
|
| 64 |
0x44004f004d004100 49004e00 |
ドメイン名データ (「DOMAIN」) |
| 76 | 0x7500730065007200 | ユーザ名データ (「user」) |
| 84 |
0x57004f0052004b00 5300540041005400 49004f004e00 |
ワークステーション名データ (「WORKSTATION」) |
| 106 |
0xc337cd5cbd44fc97 82a667af6d427c6d e67c20c2d3e77c56 |
LM Response データ |
| 130 |
0x25a98c1c31e81847 466b29b2df4680f3 9958fb8c213a9cc6 |
NTLM Response データ |
Type 3 メッセージを受信したサーバは LM レスポンスと NTLM レスポンスを計算し、これらをクライアントから提供された値と比較する。これらが合致した場合、ユーザの認証は成功する。
NTLM Version 2 を有効にする方法の詳細については、 マイクロソフト社の技術情報 239869を参照のこと。一言でいうと、以下のレジストリ registry value:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\LSA\LMCompatibilityLevel
(Windows 9x ベースのシステムでは LMCompatibility) の修正が必要となる。 これは REG_DWORD のエントリであり、以下の値のいずれかを設定できる。
| レベル | クライアントとして送信するレスポンス | サーバとして受信するレスポンス |
|---|---|---|
| 0 |
LM
NTLM |
LM
NTLM LMv2 NTLMv2 |
| 1 |
LM
NTLM |
LM
NTLM LMv2 NTLMv2 |
| 2 | NTLM |
LM
NTLM LMv2 NTLMv2 |
| 3 |
LMv2
NTLMv2 |
LM
NTLM LMv2 NTLMv2 |
| 4 |
LMv2
NTLMv2 |
NTLM
LMv2 NTLMv2 |
| 5 |
LMv2
NTLMv2 |
LMv2
NTLMv2 |
NTLM2 セッションセキュリティは、すべてのレベルでサポートされており、利用可能な場合はネゴシエートされる(ほとんどのドキュメントでは、NTLM2 セッションセキュリティはレベル 1 以上でのみ有効であるという記載があるが、確認した限りレベル 0 でも同様に有効であった。 Windows 95 および Windows 98 プラットフォームがデフォルトでサポートするのは LM レスポンスのみであるが、ディレクトリサービスクライアントをインストールすることで、NTLMv2 もサポートされるようになる (加えて LMCompatibility の設定が有効となるが、設定可能な値は 0 と 3 のみとなる)。
レベル 2 の場合、クライアントは NTLM レスポンスを 2 重に (LM レスポンスと NTLM レスポンスフィールドの両方で) 送信する。レベル 3 以上では、LM と NTLM レスポンスが LMv2 と NTLMv2 レスポンスに置き換えられる。
NTLM2 セッションセキュリティがネゴシエートされた場合 (「Negotiate NTLM Key」フラグで確認可能)、レベル 0、1、2 では、脆弱な LM および NTLM レスポンスが NTLM2 セッションレスポンスに置き換えられる。 これにより、サーバ上での辞書攻撃に対する NTLMv1 の耐性が向上する。与えられたチャレンジに対するクライアントのレスポンスは、ランダムな client nonce を計算に加えることで、乱雑性の高いものとなる。
NTLM2 セッションレスポンスは、これをサポートしない古いドメインコントローラが存在していた場合でも、クライアントとサーバが新しいスキームをサポートしていればネゴシエートすることが可能となる点に注目したい。 典型的な例として、認証に関連するサーバはユーザのパスワードハッシュを持っておらず、ドメインコントローラのみが保持している場合を想定しよう。マシンが NT ドメインに参加した場合、暗号化された相互認証されたドメインコントローラとの間のチャネル(通常「NetLogon pipe」と呼んでいる) が確立される。クライアントが「本来の」NTLMv1 ハンドシェイクを用いてサーバに対して認証を行なう場合、背後では以下の処理が実行される。
NTLM2 セッションレスポンスが用いられる場合、クライアントとサーバの両方が新しいプロトコルを用いることが可能な状態である必要があるが、ドメインコントローラは古いままでよい。 上記を可能とするため、前述したハンドシェイクが以下のように若干修正される。
重要なのは、ドメインコントローラが古いままで NTLMv2 をサポートしていない(もしくはネットワーク管理者が LMCompatibilityLevel レジストリを設定して NTLMv2 を用いるようにしていない) ネットワークにおいても、クライアントとサーバが NTLM2 セッションレスポンスを用いることが可能な点である。
LMCompatibilityLevel の設定に関連する設定として、NtlmMinClientSec と NtlmMinServerSec という設定がある。これらは NTLMSSP が確立する NTLM コンテキストで最低限必要とされる設定を指定するものである。 いずれも REG_WORD のエントリとなっており、以下の NTLM フラグを組み合わせて指定可能なビットフィールドとなっている。
ここで NTLM の認証機構全体の中での位置付けについて解説する。
Windows は SSPI - Security Support Provider interface という名称のセキュリティフレームワークを提供している。これは GSS-API (Generic Security Service Application Program Interface、RFC 2743) のマイクロソフト版であり、非常に高レベルで下位の認証機構に依存しない認証、整合性、秘匿性確保のフレームワークを提供する。 SSPI は幾つかの下位プロバイダをサポートしている。その一つが NTLMSSP (NTLM Security Support Provider) であり、ここまで解説してきた NTLM 認証機構を提供するものである。 SSPI は柔軟な opaque やプロバイダ固有の認証トークンを扱うための柔軟な API を提供する。NTLM Type 1、Type 2、Type 3 メッセージもこうしたトークンの一つであり、NTLMSSP によって定義され、処理されている。 SSPI によって提供される API の概要については、NTLM の詳細とはほとんど関係がない。 アプリケーションの開発者はNTLM が用いられているかことを意識する必要はなく、アプリケーションレベルでは無修正もしくは微小な修正で、(Kerberos など)別の認証機構に変更することが可能である。
ここでは SSPI のフレームワークについて深くは言及しないが、SSPI 認証のハンドシェイクが、どのように NTLM に対して適用されているかを俯瞰したい。
ここまでの解説では、ローカル認証の手順について、さまざまな視点から言及してきた。SSPI の基本を理解することで、この詳細に確認できる。
ローカル認証は NTLM メッセージ中の情報に基づいて、クライアントとサーバ間で一連の決定が適切に行なわれた場合にネゴシエートされる。これは以下のように動作する。
データグラム形式の認証は、NTLM をコネクションレスの伝送路でネゴシエートする際に用いられる。メッセージの大半の部分は同一であるが、顕著な差異も幾つか存在する。
これにより認証が成功となる。「人工の」 Type 1 メッセージがサーバ側をデータグラム形式の認証モードに変更させたことが、Type 3 メッセージのフラグから確認できる。現実的な用途があるわけではないが、NTLM メッセージを意図的に操作することで、興味深い想定外の挙動が確認できる。
署名と暗号化には、さまざまな鍵のスキームが適用できる。ここでは鍵の形式とセッションセキュリティの中核について概説することから始めたい。
これはセッションセキュリティを実現する上で、基本となる鍵である。これには以下のようにさまざまな形式が存在する。
LM レスポンスのみが提供される場合(Windows 9x クライアント)に使用される。LM User Session Key は以下のようにして生成される。
この形式は、クライアントが NTLM レスポンスを送出する際に用いられる。鍵の計算方式は、非常に単純である。
LMv2 レスポンスが送出される(しかし、NTLMv2 レスポンスは送出されない) 場合に用いられる。この鍵の生成方法は若干複雑であるが、複雑過ぎるというレベルではない。
NTLMv2 レスポンスが送出される際に用いられる。この鍵の計算方法は、LMv2 User Session Key と非常に類似している。
NTLM2 セッションレキュリティを用いて NTLMv2 認証が行なわれる際に用いられる。この鍵は、NTLM2 セッションレスポンスの情報から以下のようにして生成される。
Null User Session Key は匿名認証が実施される際に用いられる。これは単純に 16 バイトの NULL バイト (「0x00000000000000000000000000000000」) となる。
Lan Manager Session Key は LM レスポンスを元にしたものである(LM ハッシュそのものではない)ことに注意。これは、サーバのチャレンジによって値が変化することを意味する。 これはパスワードハッシュのみに依存する LM User Session Key に対する優位点となる。 LM/NTLM User Session Keys は、ユーザがパスワードを変更しない限り、変わることがないが、Lan Manager Session Key は認証を実施する毎に変化する。 このため、Lan Manager Session Key は LM User Session Key より強力である。 とはいえ、これは NTLM User Session Key (認証毎に変化はしないものの 128 ビットの鍵空間を持つ。LM User Session Key と Lan Manager Session Key は両方とも有効な鍵空間が 64 ビットである) よりは脆弱であると考えて良いだろう。