由淺入深理解Kerberos協議
本文是很久之前做的筆記,今天有空又梳理了一下,分享出來。如果有錯誤或疏漏,歡迎留言指出。
Kerberos是一種基于票據的、集中式的網絡認證協議,適用于C/S模型,由MIT開發和實現(http://web.mit.edu/kerberos/dist/)。
這里所謂的認證,就是保證使用票據(Ticket)的用戶必須是票據中指定的用戶。
簡單回憶一下,密碼學涉及機密性、完整性、認證性(實體認證+消息認證)、可用性、不可否認性。kerberos認證主要為了實現認證性中的實體認證。當然,每次交互發送的消息都會包含checksum,并被對稱加密(也可以配置為非對稱加密),保證了完整性和機密性。
1. Kerberos認證
Kerberos提供了單點登錄(SSO,Single Sign-On)機制,對于每個會話只需向服務進行一次自我驗證,即可自動保護該會話過程中所有后續事務的安全。
整個認證過程涉及3方(所以以三頭犬命名~):客戶端、KDC、服務端(或者說應用服務器AP,比如web服務器)。
密鑰分發中心KDC (Key Distribution Center)是 Kerberos 的核心組件,默認安裝在域控里,由兩個服務組成:
AS(Authentication Server),負責用戶信息認證,給客戶端提供TGT(Ticket Granting Tickets,即黃金票據);
TGS(Ticket Granting Server),向客戶端提供ST(Service Ticket,即白銀票據)和Session Key(服務會話密鑰)。
還有一個概念叫principal,代表一個用戶唯一的身份,由三部分組成:
primary,主名稱;
instance(可選),實例,有則說明是服務主體;
realm,域。
以starr/admin@FOOBAR.COM這個kerberos主體為例,
starr-->primary
admin-->instance
FOOBAR.COM-->realm
完整的認證流程,有客戶端與AS, TGS, AP的3次交互,每次交互,響應包都會包含兩條信息,一條可以用本地密鑰解密,另一條需要轉發。
1.1 認證流程v1.0

AS_REQ請求內容如下:
AS_REQ =
principal_client // 我是誰
+ principal_server // AP id, 又叫SPN,service principal name,標識客戶端要訪問的服務
+ nonce // 隨機數,用來匹配響應
+ Authenticator = encrypt( // 可譯作[認證因子]
key=hashUser, // 用戶的NTLM hash,后續所提到作密鑰的hash都是指NTLM hash
TimeStamp // 加密的時間戳,防御重放攻擊,一般5min有效期
)
這里省略了客戶端主機名、加密類型、隨機數(匹配請求和響應)等信息。
密鑰所使用的NTLM Hash是Windows的一種基于MD4算法的散列加密算法,前身是LM Hash(LAN Manager Hash)。
AS收到請求后,會從活動目錄查詢此用戶信息,之后返回的AS_REP響應內容如下:
AS_REP =
T1 = encrypt( //客戶端收到后可以解密
key = hashUser, // 使用用戶hash作為加密密鑰
content = principal_server // AP id
+ SessionKeyAP //用戶與服務的會話密鑰,即Service Session Key
+ nonce // 隨機數
)
+T2 = encrypt(
key = hashAP, // 用AP的hash作為密鑰,轉發給AP后可以解密
content = principal_client // 客戶端ID
+ SessionKeyAP // 同一個會話密鑰也要轉發給AP
)
每個域控都有一個 krbtgt賬戶,創建域控時自動生成。
之所以KDC能用客戶端、服務端的密鑰hash加密數據包,顧名思義,是因為密鑰分發中心(KDC)有所有principal的密鑰hash。AS收到請求后也會用用戶的hash來解密認證因子獲取時間戳,確認請求沒有過期。
客戶端收到AS的響應后,從AS_REP.T1解密出自己與服務的會話密鑰,并轉發AS_REP.T2給服務, 作為認證:
AP_REQ =
T2 // 轉發給AP解密
+ Authenticator = // 新的認證因子
encrypt(
key = SessionKeyAP, // 客戶端從AS_REP.T1解密出來的會話密鑰
content = TimeStamp // AP用來判斷是否過期,默認有效期也是5min
+ T2_checksum // 校驗T2完整性
)
AP用自己的hash解密AP_REQ.T2后,至少要確認以下幾件事:
(1)用T2.SessionKeyAP會話密鑰解密AP_REQ.Authenticator;
(2)讀取T2.principal_client,可以理解成KDC說“此人已在我這里認證通過”,需要驗證發送方是否為此人;
(3)讀取Authenticator.T2_checksum,檢查T2是否被篡改;
(4)讀取Authenticator.TimeStamp,檢查是否過期。
到這里已經可以看出,AP不是通過與KDC交互來完成認證,而是通過解密客戶端的請求來進行認證。
密碼學的認證問題和現實生活中很多場景都非常相似,比如像質檢這樣的第三方權威機構,通過給貨物貼上有類似“檢驗通過”字樣的、高度防偽的標識,來發揮作用。
AP認證完成后,會發送enc(SessionKeyAP, TimeStamp)作為AP_REP通知客戶端認證完成。這一步是可選的,雙向認證時才會有。
那么v1.0現在存在的缺陷是什么呢?試想如果要訪問幾個服務,那么客戶端就要多次訪問AS來重復認證,頻繁傳輸用戶的密鑰會增加被竊聽的風險,既不合理也不安全。
如果使用長時間不變的值作密鑰,比如這里的用戶口令hash,那就可能在某一天被破解,是不安全的,使用臨時密鑰才是正解。
1.2 認證流程v2.0
v1.0 域控已經配備了AS,現在要加上TGS,構成完整的KDC。
可以理解成在景區買那種聯票,用身份證在AS完成身份證明,有效期內去TGS取票口出示聯票,就能取出景區門票(應該還算恰當吧~),這樣就省去了多次認證。
注意AS和TGS都在域控服務器上,所以流程仍然涉及三方(下面的圖沒有體現出來):

AS_REQ相比v1.0, 把principal_server換成了tgs,即principal_tgs,也就是用來告訴AS,我要去找TGS服務取票。
當然AS_REP響應也有了變化(請看注釋部分):
AS_REP =
T1 = encrypt(
key = hashUser,
content = principal_tgs // tgs id,告知去哪領票
+ SessionKeyTGS // 用戶與TGS的會話密鑰,即Login Session Key
+ nonce
+ TGTExpirationTime // TGT默認有效期8小時
)
+TGT = encrypt( // 帶著它去領票
key = hashKrbtgt , // 密鑰用krbtgt賬戶的hash,TGS可以解密
content = SessionKeyTGS // 同一個會話密鑰也要轉發給TGS
+ principal_client
+ TGTExpirationTime
+ enc(key = hashKrbtgt, PAC) // pac被krbtgt_hash密鑰加密了兩次,v3.0再提它
)
客戶端收到響應后,解密AS_REP.T1,并緩存Login Session Key和TGT(統稱為憑證,credentials)。
這里我將T2改名為TGT,也就是黃金票據。
然后客戶端向TGS發出請求。再次強調,每次交互,都會收到兩條信息,一條(T1)可以用本地密鑰解密,另一條(TGT)需要轉發。
TGS_REQ =
principal_client
+ principal_server // AP id,告知TGS我要訪問哪個服務
+ TGT // 轉發給TGS解密
+ nonce
+ Authenticator = // TGS解密TGT后再解密這個認證因子
encrypt(
key = SessionKeyTGS, // 客戶端從T1解密出來的會話密鑰
content = TimeStamp // 驗證TGT是否過期,防止重放
+ TGT_checksum // 驗證TGT是否被篡改
)
這里之所以TGT能被TGS解密,是因為TGS和AS在同一服務器上,數據庫中有Krbtgt用戶的hash,即TGT的加密密鑰。
TGS用hashKrbtgt解密TGT后,同樣要確認以下幾件事:
(1)用TGT.SessionKeyTGS解密Authenticator;
(2)讀取TGT.principal_client,驗證發送方是否為此人;
(3)讀取Authenticator.TGT_checksum,檢查TGT是否被篡改;
(4)讀取Authenticator.TimeStamp,檢查是否過期。
認證完成就到了TGS發放ST(Service/Silver Ticket,即白銀票據)一步了:
TGS_REP =
T3 = encrypt(
key = SessionKeyTGS, // 客戶端可以用login session key解密
content = principal_server
+ SessionKeyAP // 客戶端與AP的會話密鑰, 即Service Session Key
+ nonce
+ STExpirationTime
)
+ ST= encrypt( // 白銀票據
key = hashAP, // AP可以解密
content = SessionKeyAP
+ principal_client
+ enc(key = hashKrbtgt, PAC) // 源于TGT
+ STExpirationTime
)
客戶端再次收到兩條信息,一條(T3)用收到AS_REP時緩存的SessionKeyTGS解密,于是得到了與AP的會話密鑰,另一條(ST)轉發給AP:
AP_REQ =
ST // 轉發白銀票據, 由AP解密
+ Authenticator =
encrypt(
key = SessionKeyAP,
content = principal_client
+ TimeStamp // 驗證ST是否過期,防止重放
+ TGT_checksum // 驗證ST是否被篡改
)
AP用自己的hash解密ST后,取出SessionKeyAP,驗收發送方、checksum和時間戳(和TGS_REQ那一步一樣)。認證通過后返給客戶端AP_REP, 告知可以開始愉快地交流了。
1、2兩步發生在用戶首次登錄時,3、4發生在請求訪問新的服務時。
1.3 認證流程v3.0(完整)

KDC(AS+TGS)的角色常常由DC擔任, 現在上圖把AS和TGS合并為DC,簡單提一下AP和DC的交互。
到第5步,客戶端已經證明自己是自己了,v1.0部分也提到,AP不是通過與KDC交互來完成認證,但AP為了確認憑客戶端的身份,是否有權限訪問服務,就需要向KDC確認。
再看一下ST的內容,里面有被krbtgt hash加密過的PAC(Privilege Attribute Certificate),即特權訪問證書,包含了當前用戶的SID,用戶組,UNC路徑等信息。它會作為token保留在客戶端與AP的會話中,而AP就是把PAC信息發給KDC來確認的,而KDC會將確認結果通過RPC返回給AP。
有些服務并沒有驗證PAC這一步。
2. 抓包
2.1 環境
- Windows 2012 R2, 域內IP 192.168.1.1
- Windows 7 x86 pro, 域內IP 192.168.1.2
域名:foobar.domain
域環境的搭建此處就省略了。看下win7上的環境:
C:\Users\foo>ipconfig /all
Windows IP 配置
主機名 . . . . . . . . . . . . . : win7
主 DNS 后綴 . . . . . . . . . . . : foobar.domain
節點類型 . . . . . . . . . . . . : 混合
IP 路由已啟用 . . . . . . . . . . : 否
WINS 代理已啟用 . . . . . . . . . : 否
DNS 后綴搜索列表 . . . . . . . . : foobar.domain
localdomain
...
以太網適配器 本地連接:
...
IPv4 地址 . . . . . . . . . . . . : 192.168.1.2(首選)
子網掩碼 . . . . . . . . . . . . : 255.255.255.0
...
DNS 服務器 . . . . . . . . . . . : 192.168.1.1
...
C:\Users\foo>whoami
foobar\foo
Win2012:
C:\Windows\System32>ipconfig Windows IP 配置 以太網適配器 Ethernet0: ... IPv4 地址 . . . . . . . . . . . . : 192.168.1.1 子網掩碼 . . . . . . . . . . . . : 255.255.255.0 ...
在win2012上安裝好wireshark,監聽Ethernet0網卡。Win7登錄foo用戶(如已登錄,注銷即可),win2012那邊就會抓到包,停止抓包,過濾條件填kerberos即可。
PS:真實數據包的結構與上面的偽代碼有差異。
2.2 與AS的交互
先看AS請求:

再看下req-body:

然后看下響應,先看黃金票據:

其中,etype加密類型是根據請求包里支持的加密類型來選擇的。
在ticket下面的enc-part就是用用戶foo的hash加密的與TGS的會話密鑰、隨機數、有效期等信息。
而上面的padata,類型變成了pA-ETYPE-INFO2,在RFC4120里有詳細描述:
When the AS server is to include pre-authentication data in a KRB-ERROR or in an AS-REP, it MUST use PA-ETYPE-INFO2, not PA-ETYPE-INFO.
沒有細究,就不截圖了。
另外可以記一下,在傳輸層,目標端口是88,就不截圖了。
2.3 與TGS的交互
客戶端發出的tgs請求包:

客戶端收到的tgs的響應包:

2.4 與AP的交互
在wireshark列表里是看不見ap-req, ap-rep兩個包的,因為這個交互過程集成在與服務的第一次請求中。為了查看ap-req, 不需要改變wireshark的過濾器,只需要在列表中找一個不是KRB5協議的數據包,里面就會有ap-req和ap-rep。
比如,DCERPC協議包中的目錄訪問服務:

響應:

這些服務,在前面的tgs-req包的sname字段中都是可以找到的。
對LDAP協議不太了解,不知道為什么DCERPC和LDAP兩種包里都會有LDAP服務的請求和響應,如果有哪位 師傅知道,歡迎留言賜教。

其它的,還有SMB協議中的cifs服務請求與響應:

3. Kerberos一些攻擊方式
很多攻擊方式我本人其實還沒有實踐過,只是簡單梳理一下。
3.1 AS-REQ階段
三種手法:PTH, 用戶枚舉,密碼噴灑。
PTH
首先是HASH傳遞攻擊(Pass The Hash, PTH),捕獲用戶的密碼hash來做身份驗證,獲取TGT,橫向訪問其它主機。
場景:內網滲透中要橫向移動,但獲取不到明文口令。
域內用戶枚舉及密碼噴灑
和web爆破原理類似,根據響應包返回碼來判斷是否爆破成功:

登錄一個不存在的域用戶:

登錄密碼錯誤:

當然域策略可以設置密碼嘗試次數,超過則鎖定賬戶。
針對SPN,也可以枚舉一下,看看目標域有哪些服務。
3.2 AS-REP階段
黃金票據
針對黃金票據的攻擊發生在這一階段。
因為TGT黃金票據是由krbtgt用戶的hash加密的,所以如果我們獲取了這個hash,就可以用mimikatz偽造任意TGT。
總共需要以下信息:
- 要偽造的域用戶(名稱、hash),一般是域管理員;
- 域名;
- 域的SID;
- krbtgt用戶的hash。
黃金、白銀票據主要為了維持權限,尤其是黃金票據,是后門的萬能鑰匙。
注意不是為了提權或者橫移。因為如v3.0中AP與DC的交互部分所說,權限是目標賬戶本身、或者所在用戶組決定的。
此類攻擊方法被稱為票據傳遞攻擊(Pass the Ticket,PtT)。
AS-REP Roasting
在剛剛抓的AS-REQ數據包(https://bbs.pediy.com/thread-272145.htm)中,可以看到padata(Pre-Authentication Data)預認證數據,里面包含的數據也可以稱為authenticator,包括加密的時間戳,以及啟動PAC請求。
其中PAC并不是kerberos本身支持的,而是微軟的擴展,相關漏洞有2014年的MS14-068(CVE-2014-6324)域控提權漏洞(https://docs.microsoft.com/zh-cn/security-updates/Securitybulletins/2014/ms14-068)。
如果關閉某個用戶的預認證后,AS-REQ.padata就不會有加密的時間戳了,而且即使輸入密碼錯誤,也能收到as-rep:

于是,就可以用Rubeus和hashcat工具進行離線爆破了。
MS14-068提權漏洞
漏洞位于 kdcsvc.dll ,允許經過身份驗證的用戶在其獲得的 TGT 中插入任意的 PAC。
3.3 TGS-REP階段
Kerberoasting
域上的任何經過身份驗證的用戶,只要TGT沒問題,就能夠從TGS處根據SPN請求ST,然后進行暴力破解。
SPN格式:SERVICE/host.name。可以用Windows自帶的SetSPN.exe查詢SPN, 命令為setspn -Q */*。
之后,可以用mimikatz指定服務目標、導出ST,用tgsrepcrack破解,相關工具有很多。
白銀票據
TGS生成的ST雖然只能訪問特定的服務,但不依賴KDC,所以相比偽造黃金票據動靜更小。
ST用AP的hash加密,如果獲取了這個hash,就可以偽造ST,進而跳過KDC的認證,直接和AP通信。
總共需要以下信息:
- 域用戶名稱、hash;
- 域名;
- 域的SID;
- 目標AP的服務名、ntlm hash等。
其實這也屬于票據傳遞攻擊。
需要注意,偽造的ST是不帶kdc簽名的,所以如果目標要驗證簽名(也就是V3.0所說的AP與KDC的交互),那就GG了。
也可以嘗試碰撞一下AP的hash來破解ST,成功率較小(所以更別提破解TGT了)。
3.4 委派
英文delegation。
假設用戶A訪問服務B,請求服務的資源C,但是服務B沒有資源C,那么服務B就需要以用戶A的身份去訪問另外一臺機器去獲取資源C,再響應到用戶A。
如果服務B的服務賬戶開啟了非約束委派,那么當用戶A訪問服務B的時候,用戶A的TGT會被發送給服務B,服務B就能夠以用戶A的身份去訪問用戶A能夠訪問的任意服務。
setspn工具可以設置服務賬戶。
如果我們已經拿到管理員的TGT(PTT),使用mimikatz導入后就可以實現對非約束委派的攻擊。
Windows2003引入了約束委派,以及兩個Kerberos擴展協議S4u2self和S4u2Proxy:
S4U2self(service for user to self)使服務B可以代表用戶A向KDC請求一個可轉發的ST;
S4U2proxy(service for user to proxy)使服務B可以通過可轉發的ST代表用戶A訪問請求目標服務的ST,而不能訪問其它服務。
這個過程共申請了兩個ST。
Mimikatz的作者還提供了一個工具叫kekeo,它可以先發起一次AS-REQ請求tgt,再發起兩次TGS-REQ請求兩個ST,那么用mimikatz導入目標ST后就可以實現對約束委派的攻擊。
4. 參考文章
Kerberos Protocol Extensions | Microsoft Docs
(https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/2a32282e-dd48-4ad9-a542-609804b02cc9)
Kerberos協議探索系列
(https://www.anquanke.com/post/id/171552)
RFC 4120 - The Kerberos Network Authentication Service (V5) (ietf.org)
(https://datatracker.ietf.org/doc/html/rfc4120/)
Pre-authentication Data | Microsoft Docs
(https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/ae60c948-fda8-45c2-b1d1-a71b484dd1f7)
淺析 Kerberos 認證過程以及黃金票據和白銀票據 | K0rz3n's Blog
(https://bbs.pediy.com/thread-272145.htm)
一文搞定Kerberos - 知乎 (zhihu.com)
(https://zhuanlan.zhihu.com/p/266491528)
內網滲透測試:Kerberos 協議& Kerberos 認證原理 - FreeBuf網絡安全行業門戶
(https://www.freebuf.com/articles/network/273725.html)
Kerberos: An Authentication Service for Computer Networks (isi.edu)
(http://gost.isi.edu/publications/kerberos-neuman-tso.htm)
Windows協議學習-Kerberos協議(上)_白帽子技術/思路_i春秋社區-分享你的技術,為安全加點溫度. (ichunqiu.com)
(https://bbs.ichunqiu.com/thread-61557-1-1.html)
Kerberos從0到1 - 知乎 (zhihu.com)
(https://zhuanlan.zhihu.com/p/420834577)
Kerberos協議探索系列之委派篇 - FreeBuf網絡安全行業門戶
(https://www.freebuf.com/articles/system/198381.html)