NTLM / SPNEGO / NEGOEX
NTLM は、サーバーからのチャレンジに対してクライアントが応答を計算する
認証方式です。Impacket では SMB、LDAP、DCE/RPC、TDS、HTTP などが同じ
NTLM メッセージ実装を利用します。認証の流れや NT ハッシュとの違いは
NTLM 認証の解説を参照してください。
メッセージ本体は ntlm.py、カプセル化は
spnego.py、NEGOEX は
negoex.py にあります。
関連: DumpNTLMInfo、 ntlmrelayx、 rdp_check
NTLMv1 と NTLMv2 はネゴしない
Section titled “NTLMv1 と NTLMv2 はネゴしない”USE_NTLMv2 = True # ntlm.pyコメントどおり、NTLMv2 はクライアント/サーバの flag 交換では決まりません。
両方のローカルポリシーが v2 を使うときに使われます。
NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY (0x00080000) は NTLMv1 の
拡張セッションセキュリティ (ESS / NTLM2) であり、NTLMv2 ではありません。
computeResponse(..., use_ntlmv2=USE_NTLMv2) が分岐点です。
3 メッセージ
Section titled “3 メッセージ”署名は "NTLMSSP\x00"。
| クラス | Type | 内容 |
|---|---|---|
NTLMAuthNegotiate | 1 | flag、OEM domain/host、任意の VERSION |
NTLMAuthChallenge | 2 | 8 byte challenge、TargetInfoFields (AV_PAIRS)、任意 Version |
NTLMAuthChallengeResponse | 3 | LM/NT response、UTF-16 の domain/user/host、encrypted session key、Version、MIC 領域 |
入口関数:
getNTLMSSPType1(workstation, domain, signingRequired, use_ntlmv2, version)getNTLMSSPType3(type1, type2, user, password, domain, lmhash, nthash, use_ntlmv2, channel_binding_value, service='cifs')
Type1 が常に付ける flag: NTLM | EXTENDED_SESSIONSECURITY | UNICODE | REQUEST_TARGET | 128 | 56。
署名が要るときは KEY_EXCH | SIGN | ALWAYS_SIGN | SEAL。v2 なら TARGET_INFO。
workstation 名は Type3 用に保持し、Type1 の wire には載せません。
AV pair
Section titled “AV pair”AV_PAIRS は <H type><H len><bytes> の列で、NTLMSSP_AV_EOL=0 で終わります。
| ID | 名前 | 意味 |
|---|---|---|
| 0x01 | HOSTNAME | NetBIOS ホスト |
| 0x02 | DOMAINNAME | NetBIOS ドメイン |
| 0x03 | DNS_HOSTNAME | DNS ホスト |
| 0x04 | DNS_DOMAINNAME | DNS ドメイン |
| 0x05 | DNS_TREENAME | フォレスト |
| 0x06 | FLAGS | MIC / 制約などの bit |
| 0x07 | TIME | FILETIME |
| 0x08 | RESTRICTIONS | 制限 |
| 0x09 | TARGET_NAME | クライアントが書き戻す SPN |
| 0x0a | CHANNEL_BINDINGS | CBT (MD5 of channel binding struct) |
v2 クライアントは Type3 構築時に TargetInfo を書き換えることがあります
(computeResponseNTLMv2):
AV_TARGET_NAME = service/ + DNS_HOSTNAME(既定service='cifs')- 無ければ
AV_TIME channel_binding_valueがあればAV_CHANNEL_BINDINGS
DumpNTLMInfo が見るのは Type2 の AV pair です。relay 成否に効く MIC、 channel binding、target name もここに載ります。
NTLMv1
Section titled “NTLMv1”LMOWFv1: 大文字 OEM パスワードを DES(KGS!@#$%) で。非 latin-1 ならDEFAULT_LM_HASH = AAD3B435B51404EEAAD3B435B51404EENTOWFv1:MD4(UTF-16LE(password))= NT hash- 応答: challenge を 3 つの DES ブロックで暗号化
- ESS 時: NT = DES(nthash, MD5(serverChal||clientChal)[:8])、LM = clientChal || 0×16
sessionBaseKey=MD4(NT hash)
NTLMv2 ([MS-NLMP] 3.3.2)
Section titled “NTLMv2 ([MS-NLMP] 3.3.2)”responseKeyNT = HMAC_MD5(nthash, UPPER(user) || domain)temp = 0x0101 || 0 || Time || clientChal || 0 || AvPairsntProofStr = HMAC_MD5(responseKeyNT, serverChal || temp)NT response = ntProofStr || tempLM response = HMAC_MD5(responseKeyNT, serverChal || clientChal) || clientChalsessionBaseKey= HMAC_MD5(responseKeyNT, ntProofStr)匿名は空の NT/LM 応答、session key は 16 ゼロです。
セッション鍵と MIC
Section titled “セッション鍵と MIC”keyExchangeKey = KXKEY(...) # v2 では sessionBaseKey と同じif KEY_EXCH: exportedSessionKey = 16 乱数 Type3.session_key = RC4(keyExchangeKey, exportedSessionKey)else: exportedSessionKey = keyExchangeKeySMB / RPC / LDAP の署名・暗号はこの exportedSessionKey から派生します。
MIC:
MIC = HMAC_MD5(exportedSessionKey, Type1 || Type2 || Type3_with_MIC_zeroed)getNTLMSSPType3() は MIC を計算しません。 LDAP SASL bind、TDS、
ntlmrelayx など必要な呼び出し側が後から埋めます。
NTLMAuthChallengeResponse.checkMIC は VERSION flag を見ているだけで、
本物の MIC 検出ではありません。
GSS の SIGN/SEAL (SIGNKEY / SEALKEY / MAC / SEAL) は DCERPC と
LDAP signing が使います。SMB メッセージ署名とは別計算です。
SPNEGO
Section titled “SPNEGO”spnego.py は pyasn1 ではなく手書き BER です。
| シンボル | 役割 |
|---|---|
GSSAPI | 0x60 + OID 1.3.6.1.5.5.2 + payload |
SPNEGO_NegTokenInit | mechTypes + 任意 mechToken |
SPNEGO_NegTokenResp | NegState / SupportedMech / ResponseToken / 任意 mechListMIC |
SPNEGOCipher | NTLM SIGN/SEAL を SPNEGO 上で使う (DCERPC / LDAP) |
OID:
| 名前 | OID |
|---|---|
| NTLMSSP | 1.3.6.1.4.1.311.2.2.10 |
| MS KRB5 | 1.2.840.48018.1.2.2 |
| KRB5 | 1.2.840.113554.1.2.2 |
| KRB5 U2U | 1.2.840.113554.1.2.2.3 |
| NEGOEX | 1.3.6.1.4.1.311.2.2.30 |
SMB クライアントは mech を 1 つだけ 載せます (NTLM または MS KRB5)。 Kerberos token は生 AP-REQ ではなく
AID + OID(KRB5) + KRB5_AP_REQ + DER(AP_REQ)です。NegState: 0 completed、1 incomplete、2 reject、3 request-mic。
SPNEGO_NegTokenResp は mechListMIC を出せますが、fromString は
それを解析しません。
NEGOEX
Section titled “NEGOEX”[MS-NEGOEX] 相当。署名 NEGOEXTS。メッセージ種別は INITIATOR/ACCEPTOR_NEGO、
META_DATA、CHALLENGE、AP_REQUEST、VERIFY、ALERT。
NegoExContext が状態機械です。VERIFY の checksum は RFC 3961、
それまでの全メッセージ、key usage 23 (initiator) / 25 (acceptor)。
SMB / LDAP / RPC の login 経路には繋がっていません。 OID 定義と 単独モジュールに留まります。PKU2U GUID も未使用です。
Relay で効く防御
Section titled “Relay で効く防御”ntlmrelayx が触るのはこの層です。
| 要素 | 効果 |
|---|---|
| MIC | Type3 改変を検知。--remove-mic は CVE-2019-1040 向け |
| SIGN/SEAL flag | --remove-sign-seal は署名・封印関連 flag を落とす |
| Channel binding / EPA | LDAPS / HTTPS / TDS の relay を阻害 |
| SMB signing | SMB relay を阻害 |
| Target name (SPN) AV | サーバ側のターゲット検証 |
| Extended Protection | CBT 必須時は channel_binding_value 無しでは失敗 |
新しい inbound server / outbound client を足すときは、Type1/2/3 の 所有権、MIC 再計算、signing 要求、CBT の 4 点を明示してください。
変更時の確認
Section titled “変更時の確認”USE_NTLMv2と ESS flag を混同しない- Type3 の MIC を「ライブラリが必ず付ける」と仮定しない
- AV pair の順序と未知 type を落とさない
- 関連テスト:
tests/SMB_RPC/test_ntlm.py

