關於 Relay Attack

說到 relay attack 我相信大家一定都不陌生,透過 EFS 或 printer 等 RPC 可以將高權限的 NTLM 認證打到 attacker 的機器上面,並透過 ntlmrelayx 轉發進而拿到 smb / ldap / http 等服務的 session。

不過這種打法在 kerberos 基本上不會成功,主要是因為 kerberos 的驗證中綁定了特定的 service, kerberos 的 ST 是用目標 service 的 key 加密的。如果 UNC path 指定 attacker 的 hostname,KDC 找不到 attacker 的 SPN(因為 attacker 不在 domain 裡),會 fallback 到 NTLM,根本拿不到 kerberos ticket;如果指定 target 的 hostname,ticket 是正確的但 TCP 也連到 target,attacker 攔不到。兩個條件(正確的票 + 送到 attacker)沒辦法同時滿足。

如何做到 Kerberos Relay

早在 2021 年 James Forshaw 便針對 kerberos relay 的可行性進行相關研究
理論上 kerberos relay 是可行的,不過當時除了中間人攻擊之外找不到合適的漏洞達成。

要完成 kerberos relay,我們需要透過某種方法指定正確的 SPN ,同時將這個服務的 AP_REQ 送到自己手上。

DHCPv6 DNS Spoofing

2022 年 Dirk-jan 發表了一篇 blog 說明如何透過 DHCPv6 達成 kerberos relay。

首先,在大部分的企業環境內根本沒人會用 DHCPv6,所以可以自己架一台 DHCPv6 回應所有 DHCPv6 solicit,並且把自己設定成 IPv6 DNS Resolver,為什麼要用 IPv6 ?因為 IPv6 的 precedence 會比 v4 還高,因此機器會優先詢問 IPv6 的 DNS Server。

victim 的 DNS resolver 被劫持後,被動等待 victim 自行發起 DNS dynamic update(Windows 機器定期會做),此時:

  1. victim 向假的 DNS server 發起 DNS dynamic update
  2. Attacker 的 DNS server 回應 SOA,把 primary nameserver 指向 target(例如 ADCS)的 hostname
  3. victim 準備對 target hostname 做 authenticated DNS update,向 KDC 請求 target SPN 的 ST
  4. victim 向 DNS 查詢 target 的 ip
  5. 假的 DNS Resolver 回應 attacker 的 ip
  6. victimAP-REQ 發送到 attacker 手上
  7. attacker 拿 AP-REQ relay 到 target 的其他 service(例如 ADCS HTTP endpoint),完成 kerberos relay

DNS 和 kerberos 的前後解析不一致

Synacktiv 在 2024 年的時候提出了另一個手法

首先,他們透過 dns 註冊了一組奇怪的 A Record:

fileserver1UWhRCAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAfileserversBAAAA

接著要求 victim 強制認證 fileserver1UWhRCAA..A ,此時:

  1. victim 向 DNS 查詢 fileserver1UWhRCAA..A ,得到 Attacker IP
  2. victim 嘗試 parse 出 SPN,偵測到為 marshalled data,將 marshalled data strip 掉
  3. 得到 SPN 為 cifs/fileserver
  4. KDC 請求 cifs/fileserver 的 ST
  5. 向 Attacker 發送 AP-REQ

SPN 的解析問題

為什麼 fileserver1UWhRCA..A 會被解析成 cifs/fileserver

我們可以來看 SMB client 的正常連線流程,當 SMB client 要連線到 target 時:

  1. SMB client 呼叫 SecMakeSPNEx2("cifs", "target")SecMakeSPNEx2 會:
    1. 把 service class 和 hostname 組成 SPN: cifs/target
    2. 嘗試 CredUnmarshalTargetInfo("cifs/target") 回傳 null
    3. 呼叫 CredMarshalTargetInfo 把目標資訊 base64 encode
  2. SPN 和 base64-encoded marshal 接起來丟給 SSPI

聽起來很合理,但如果 target 已經是 marshalled data 會發生什麼事?

  1. SMB client 呼叫 SecMakeSPNEx2("cifs", "targetMarshalledData")SecMakeSPNEx2 會:
    1. 把 service class 和 hostname 組成 SPN: cifs/targetMarshalledData
    2. 嘗試 CredUnmarshalTargetInfo("cifs/targetMarshalledData") 回傳 unmarshalled data
    3. 用 unmarshalled data 重新 CredMarshalTargetInfo 把目標資訊 base64 encode
  2. SPN 和 base64-encoded marshal 接起來丟給 SSPI

此時 fileserver1UWhRCA..A 被成功解析出 cifs/fileserver 並申請正確的 ST ,但 DNS 卻解析出 fileserver1UWhRCA..A 的 ip,並將 AP-REQ 發送給 attacker,造成前後解析不一致的漏洞。

SecMakeSPNEx2

很明顯就是這個函式在搞,而 SecMakeSPNEx2 本身就是拿來解析 SPN 的,所以理論上可以控制輸入就是會有上面的這些問題

CVE-2025-33073

前面有提到 SMB client 在解析 target name 的時候可以透過 marshalled data 造成前後解析不一致,除了拿來做 kerberos relay 之外,Synacktiv 發現可以利用這個問題繞過 authentication reflection 的檢查

什麼是 Authentication Reflection

前面提到若透過 UNC path 觸發驗證便能拿到該使用者的 NTLM 認證(challenge-response),由於這些有問題的 RPC 通常都是以系統服務運行的,因此我們可以拿到該機器 machine account 的 NTLM 認證,那如果把它 relay 給他自己會發生什麼事?假設我們 relay 給自己的 SMB Service ,就會得到一個 SYSTEM 權限的 SMB session,但這也太蠢了吧?

微軟也是這樣想的,早在 2008 年的 MS08-068 他們就把這個問題給修掉了,修法很簡單就是在 SMB server 端檢查 challenge 是不是自己發的,如果是就拒絕。

但是遇到 SecMakeSPNEx2 一切都不一樣了,我們一樣將 fileserver1UWhRCA..A 指向 attacker IP,同時針對 fileserver1 發起 authentication coercion,此時:

  1. Coerce fileserver1 的 SYSTEM service 連到 fileserver1UWhRCA..A
  2. DNS 解析 fileserver1UWhRCA..A → attacker IP → TCP 連到 attacker
  3. SecMakeSPNEx2CredUnmarshalTargetInfo strip marshalled data → hostname = fileserver1
  4. SspIsTargetLocalhost("fileserver1") → 是自己 → 走 NTLM local authentication
  5. NTLM 認證透過已建好的 TCP 連線送到 attacker
  6. Attacker 用 ntlmrelayx relay 回 fileserver1:445
  7. fileserver1 的 SMB server 收到認證 → 來自外部 IP(attacker) → MS08-068 的 challenge 檢查不觸發(因為 challenge 不是同一個 SMB instance 發的)→ 認證通過
  8. 拿到 SYSTEM 身份的 SMB session

修補

mrxsmb.sysSmbCeCreateSrvCall 中,新增了一段檢查:

if ((unsigned int)CredUnmarshalTargetInfo(
    TargetName->Buffer, 
    TargetName->Length, 0, 0) != STATUS_INVALID_PARAMETER) 
{
    return STATUS_INVALID_PARAMETER;
}

CredUnmarshalTargetInfo 在 target name 不包含 marshalled data 或格式錯誤時會回傳失敗(STATUS_INVALID_PARAMETER)。所以這段程式碼反過來用 — 如果 CredUnmarshalTargetInfo 成功了(代表 target name 裡有合法的 marshalled data),就拒絕建立 SMB 連線。

在這個修補之後今年又被繞過了
CVE-2026-24294 透過 SMB port multiplexing 新功能 connection reuse 時忘記檢查是否為相同 port 繞過
CVE-2026-26128 透過 Unicode normalization 不一致繞過

不過這兩個繞過方式並非透過 SPN 前後解析不一致達成,因此就不在這篇討論,有興趣的可以去看 synacktiv 的兩篇文章:

References