コンテンツにスキップ

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

USE_NTLMv2 = True # ntlm.py

コメントどおり、NTLMv2 はクライアント/サーバの flag 交換では決まりません。 両方のローカルポリシーが v2 を使うときに使われます。 NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY (0x00080000) は NTLMv1 の 拡張セッションセキュリティ (ESS / NTLM2) であり、NTLMv2 ではありません。

computeResponse(..., use_ntlmv2=USE_NTLMv2) が分岐点です。

署名は "NTLMSSP\x00"。

クラスType内容
NTLMAuthNegotiate1flag、OEM domain/host、任意の VERSION
NTLMAuthChallenge28 byte challenge、TargetInfoFields (AV_PAIRS)、任意 Version
NTLMAuthChallengeResponse3LM/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_PAIRS は <H type><H len><bytes> の列で、NTLMSSP_AV_EOL=0 で終わります。

ID名前意味
0x01HOSTNAMENetBIOS ホスト
0x02DOMAINNAMENetBIOS ドメイン
0x03DNS_HOSTNAMEDNS ホスト
0x04DNS_DOMAINNAMEDNS ドメイン
0x05DNS_TREENAMEフォレスト
0x06FLAGSMIC / 制約などの bit
0x07TIMEFILETIME
0x08RESTRICTIONS制限
0x09TARGET_NAMEクライアントが書き戻す SPN
0x0aCHANNEL_BINDINGSCBT (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 もここに載ります。

  • LMOWFv1: 大文字 OEM パスワードを DES(KGS!@#$%) で。非 latin-1 なら DEFAULT_LM_HASH = AAD3B435B51404EEAAD3B435B51404EE
  • NTOWFv1: 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)
responseKeyNT = HMAC_MD5(nthash, UPPER(user) || domain)
temp = 0x0101 || 0 || Time || clientChal || 0 || AvPairs
ntProofStr = HMAC_MD5(responseKeyNT, serverChal || temp)
NT response = ntProofStr || temp
LM response = HMAC_MD5(responseKeyNT, serverChal || clientChal) || clientChal
sessionBaseKey= HMAC_MD5(responseKeyNT, ntProofStr)

匿名は空の NT/LM 応答、session key は 16 ゼロです。

keyExchangeKey = KXKEY(...) # v2 では sessionBaseKey と同じ
if KEY_EXCH:
exportedSessionKey = 16 乱数
Type3.session_key = RC4(keyExchangeKey, exportedSessionKey)
else:
exportedSessionKey = keyExchangeKey

SMB / 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.py は pyasn1 ではなく手書き BER です。

シンボル役割
GSSAPI0x60 + OID 1.3.6.1.5.5.2 + payload
SPNEGO_NegTokenInitmechTypes + 任意 mechToken
SPNEGO_NegTokenRespNegState / SupportedMech / ResponseToken / 任意 mechListMIC
SPNEGOCipherNTLM SIGN/SEAL を SPNEGO 上で使う (DCERPC / LDAP)

OID:

名前OID
NTLMSSP1.3.6.1.4.1.311.2.2.10
MS KRB51.2.840.48018.1.2.2
KRB51.2.840.113554.1.2.2
KRB5 U2U1.2.840.113554.1.2.2.3
NEGOEX1.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 は それを解析しません。

[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 も未使用です。

ntlmrelayx が触るのはこの層です。

要素効果
MICType3 改変を検知。--remove-mic は CVE-2019-1040 向け
SIGN/SEAL flag--remove-sign-seal は署名・封印関連 flag を落とす
Channel binding / EPALDAPS / HTTPS / TDS の relay を阻害
SMB signingSMB relay を阻害
Target name (SPN) AVサーバ側のターゲット検証
Extended ProtectionCBT 必須時は channel_binding_value 無しでは失敗

新しい inbound server / outbound client を足すときは、Type1/2/3 の 所有権、MIC 再計算、signing 要求、CBT の 4 点を明示してください。

  • USE_NTLMv2 と ESS flag を混同しない
  • Type3 の MIC を「ライブラリが必ず付ける」と仮定しない
  • AV pair の順序と未知 type を落とさない
  • 関連テスト: tests/SMB_RPC/test_ntlm.py