Contents
NTLM認証概要
- Client は、 Negotiate 認証トークンをサーバーに送信する
- Negotiate 認証トークンを受信した Server は、Challenge 値を生成し、Challenge 認証トークンとして Client に返送する
- Client は、ユーザの NTHash 値(SSPI security token)を用いて Authenticate 認証トークンを生成してサーバに送信する
- Server は、Challenge 値と Authenticate 認証トークンを用いて、ドメインコントローラに検証を依頼する
- ドメインコントローラでの検証されれば Access Token が作成される

NTLM認証の流れ
前提
- ユーザが Client にログオン
- 入力したパスワードから NTHash = MD4(UTF-16LE(password))が計算される
- NTHash は、Client の lsass.exe の msv1_0.dll のメモリにキャッシュされる
Client が Server へ接続する
- Client の Application(explorer.exeから\server\shareを開く) が接続要求を行う
- Windows Kernel の SMB redirector SMB接続の確立を試みる
- SMB Client が SSPI を呼ぶ
Negotiate メッセージの生成と送信する
- SMB Client が InitializeSecurityContext を初回呼び出しする
- Client LSA がNegotiate メッセージを作成する
- NegotiateFlags: Clientがサポートする機能フラグ
- InitializeSecurityContextがSMB ClientにNegotiate メッセージを渡す
- SMB Client Negotiate をリクエストに乗せてサーバへ送信する
サーバが Challenge を返送する
- SMB Service が Negotiate を受信
- SMB Service が AcceptSecurityContext を呼び出し、Negotiate メッセージを渡す
- Server LSA が Challenge メッセージ(NegotiateFlagsが含まれる)を作成
- ServerChallenge: 8バイトのランダムnonce
- NegotiateFlags: Client と Server で共通する機能のフラグ
- TargetInfo: 対象の詳細(ComputerName, DomainName など)
- Server LSA は、ServerChallenge を自身のメモリに保持する
- Challenge メッセージが AcceptSecurityContext から出力され、SMB Service から Client へ返る
Client が Response を計算してAuthenticate を送信する
- Client SMB が Challenge 受信
- SMB が InitializeSecurityContext を呼び出し、Challenge メッセージを渡す
- Client LSA の msv1_0.dll が NTLMv2 response を計算
- Blob: ClientChallenge や Timestamp、TargetInfo など
- NTProofStr: NTProofStr = HMAC_MD5(NTLMv2_Hash, ServerChallenge ‖ Blob)
- Client SLA が Authenticate メッセージ(NTLMv2 Responseなどを含む)を作成
- SMB Client から サーバへ送信される
サーバによる検証(ドメインアカウントの場合)
- Server SMB が Authenticate メッセージを受信し AcceptSercurityContext に渡す
- Server LSA が Authenticate メッセージ内の情報を見て、ドメインアカウントか、ローカルアカウントかを判定
- ドメインアカウントであれば、Server LSA が Netlogon Service(netlogon.dll) を呼び出す
- Netlogon が NetrLogonSamLogonEx RPC を DC に対して行う
- NTLMv2 response の内容等含む
- DC の Netlogon Service が RPCを受信し、DC LSA 認証要求を行う
- DC LSA が AD Database(NTDS.dit) からユーザの NTHash を取り出す
- DC 側で、ClientがAuthenticateメッセージを計算したように計算を同様に行う
- 一致していることを確認し、DC はNTDS.ditからユーザのPAC情報(SID, 特権等)や UserSessionKey 等の Server への返り値を作る
- DC が Netlogon RPC を通じて返り値を Server に送る
- ローカルアカウントの場合は、DC -> Server LSA, NTDS.dit -> ServerのSAM で行う
Access Token の生成
- Server LSA がこれまでの情報をもとに、LsaLogonUser 内部処理で Token の中身の組み立てを行う
- Server LSA が NtCreateToken をカーネルに発行する
- Server LSA は QuerySecurityContextToken から認証ユーザの AccessToken を取得
認証完了後の利用
- Client が SMB を要求する
- SMB Server が ImpoersonateSecurityContext を呼んで AccessToken のコンテキストを得ます
- SMB Server が CreateFile() 等の Win32 API を呼ぶ
- Windows Kernel が現在のスレッドの Token を参照し、SIDセットと DACL を比較し権限を確認する
- その結果を SMB Server 経由で Client に返却する
Pass the Hash
Client がログインじに生成したNTHash を持っていることから、Response を計算してAuthenticate を作成する際に当該のClientである必要がない そのため、Serverのサービスの利用が可能になる。
攻撃の流れ
- 事前にどこかから NTHash を盗む (Mimikatz, SAM dump, DPAPI 経由, etc.)
- ターゲットサーバに対して 本物のNEGOTIATE を送信
- サーバから CHALLENGE (ServerChallenge含む) が返ってくる
- 盗んだNTHash + 受領したServerChallenge で response 生成
- 計算したresponseを含む AUTHENTICATE を送信
- サーバ側にとって正しい response を受け取るので、正規認証として処理
- Access Token 発行