域渗透之kerberos协议
前言
写完发现写的乱七八糟的,额,也懒得改了,就这样吧
Windows本地认证
也就是我们登录windows时进行的认证。
之前也写过了,就是拿我们输入的密码hash后(NTML hash)和C:\Windows\System32\config\sam
数据库中存储的hash值进行对比。
(远古版本中使用的是LM hash)。再贴一下NTML hash的生成代码
个人系统在Windows vista后,服务器系统在windows 2003以后,认证方式均为NTLM Hash。
import hashlib
password = "admin".encode('utf-16le')
hash = hashlib.new('md4', password).hexdigest()
print(hash)
大致流程
winlogon.exe 进程接收用户输入并交给 lsass.exe进程,lsass.exe对密码进行NTML hash,并读取本地数据库hash进行对比,如果相同,登录成功,返回 User SID 和 Group SID
网络认证 NTLM认证(挑战/质询)
- 认证协议协商
- 过程
- 客户端向服务器发送用户信息(用户名)请求
- 服务器接受请求,生成16位的随机数,称之为
Challenge
, 并使用登录用户名对应的NTML Hash 加密Challenge
,生成challenge1, 然后将challenge
发送给客户端 - 客户端接收到
chanllege
后,使用也NTML Hash加密challenge
,生成response
发送给服务端 - 服务端接收到客户端的
Response
后,用challenge1与服务端对比,若相等则认证通过。
NTMLv2
- NTML v1的challenge有8位,v2有16位
- NTMLv1 加密算法是DES, NTMLv2 的加密算法是 HMAC-MD5
哈希传递(pth)
利用用户的 NTLM hash 和用户名,实现NTML认证。
工具 CrackMapExec smbexec msf
smbexec
crackmapexec
至于命令执行的原理,还没弄明白,可能是开了其他什么服务
域环境认证 Kerberos认证
依稀记得上学期信息安全导论还考过Kerberos协议,不过也没怎么听,这次好好学一遍.
Kerberos协议中个角色
Client 客户端 ;
Server 服务端;
KDC(Key Distribution Center) 密钥颁发中心
KDC默认安装在DC(域控)上,KDC又由两个部分组成,AS(Authentication Server)认证服务器 和 TGS(Ticket Granting Ticket)票据授予服务器
AS 主要为 客户端生成 TGT (与黄金票据有关)
TGS 主要为客户端生成 某个服务的 Ticket (与白银票据有关)
AD(即 Account Dtabasae),存储客户端的白名单,只有在白名单中,才能申请到 TGT
经典老图
kerberos概述
kerberos认证过程中包含6个消息
KRB_AS_REQ,KRB_AS_REP (与 AS交互)
KRB_TGS_REQ,KRB_TGS_REP (与TGS交互)
KRB_AP_REQ和KRB_AP_REP (与Server交互)
此外,微软在标准kerberos的基础上,还实现了 PAC
和 S4U
PAC
PAC的全称是Privilege Attribute Certificate(特权属性证书), 其中所包含的是各种授权信息, 例如用户所属的用户组, 用户所具有的权限等。
当用户与KDC之间完成了认证过程之后, 用户需要访问服务器所提供的某项服务时, 服务器为了判断用户是否具有合法的权限必须通过将用户的用户名传递给KDC, KDC通过得到的用户名查询用户的用户组信息, 用户权限等, 进而返回给服务器, 服务器再将此信息与用户所索取的资源的ACL进行比较, 最后决定是否给用户提供相应的服务。
PAC开启与关闭
开启 PAC
reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters" /v ValidateKdcPacSignature /t REG_DWORD /d 1 /f
关闭 PAC
reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters" /v ValidateKdcPacSignature /t REG_DWORD /d 0 /f
- 注意:如果服务端并非一个服务程序,而是一个普通应用程序,它将不受以上注册表的影响,而总是进行PAC认证。
- 注意:如果服务端并非一个程序,而是一个驱动,其认证过程在系统内核内完成,它将不受以上注册表的影响,而永不进行PAC认证。
- 注意:使用以上注册表项,需要Windows Server 2003 SP2或更新的操作系统。
- 注意:在运行Windows Server 2008或更新操作系统的服务器上,该注册表项的值缺省为0。
用户层面关闭或开启PAC
勾上就不用PAC了
委派
见下文
大致认证流程
再放一张图,大佬画的很清楚

客户端得到 白银票据后,即可通过这个票据去访问对应的服务。
认证流程
- AS 认证通过后,会返回 用client端对应的 ntml hash加密的session key(随机生成) 以及 用KDC hash(krbtgt用户的ntlm hash)加密的之前生成的 session key 和 客户端信息。
具体来说:
client先发送 (KRB_AS_REQ),Client会向 KDC发送 用 client ntml hash 加密的 时间戳 、 Client info 以及 TGS的相关信息。
之后,KDC返回(KRB_AS_REP) 包括,被Client hash加密的session key,用于后续和TGS的通信 、用于向TGS申请票据的凭证TGT(仍然用krbtgt用户的ntlm hash加密),其中包括 session key, 客户端名称,以及TGT的有效时间.(PAC还包含用户SID和用户所在的组)
此时,客户端可以解密 Client hash加密的 session key,但由于不知道 kebtgt用户的hash,还不能得到文明TGT
- 客户端向 TGS 发送 加密的TGT、用解密得到的session key加密的 客户端信息和时间戳, 以及另外一些 客户端信息和服务端信息(KRB_TGS_REQ)。认证通过后,KDC会返回 用 session key 加密的 server sessions key 以及用Server Hash 加密的 票据(Ticket)(KRB_TGS_REP)
具体来说:
KDC收到请求后,用 KDC hash 解密 TGT,拿到其中的session key,再通过session key解密 客户端发送的加密信息,进行校验(对时间戳和和客户端信息进行校验)。
KDC 使用 Server Hash就是根据客户端提供的server info查询AD得到的。而Ticket包括 Server Sessiosn key,客户端名称以及票据的有效时间(和TGT的内容类似)
KDC认证通过后,客户端就会拿到 server sessions key,用于后续和 Server的通信。
也就是说,客户端可以拿到 server session key,以及server hash加密的 Ticket(不知道 **server **的hash,不能得到明文Ticket)
- 客户端向Server发送请求(KPB_AP_REQ),包括 加密的Ticket 以及 Server session key 加密的客户端信息和时间戳。校验通过后,就可以拿 Server session key通信了。
服务端口
Kerberos使用TCP/UDP 88端口进行认证,使用TCP/UDP 464端口进行密码重设。
令牌
之前也写过一点,这里再补充一点
- 每个进程创建时都会根据登录会话权限由LSA(Local Security Authority)分配一个Token,如果CreateProcess时自己指定了Token,LSA就会用这个Token,否则就使用父进程的Token。
- 当用户注销后,系统会将主令牌切换为模拟令牌,不会将令牌清除,只有重启后才会清除。
窃取模拟令牌的工具
incoginto (msf内置) CS的steal_token Powershell脚本
这个春秋云境上线John时提到过,不过我用的是进程注入。
想要看清楚包的结构说实话还是抓包来的块。https://github.com/daikerSec/windows_protocol这里有大佬写好的测试工具,我的域控主机在虚拟机里安着,本机只要配值其为首先DNS,配好工具中的连接配置,就能正常发包,并在wireshark中监听到。
AS_REQ && AS_REP
user enum
TGT 请求是通过 AS-REQ 消息发出的。当请求无效的用户名时,服务器将在 AS-REP 消息中使用 Kerberos 错误代码 KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN 进行响应。当使用有效的用户名时,要么会获得 TGT,要么会引发类似 KRB5KDC_ERR_PREAUTH_REQUIRED 的错误(即在这种情况下,表明用户需要执行预身份验证).
用户存在,密码正确
用户名存在,密码错误
用户不存在
可通过回显不同来判断用户是否存在。
Password Spraying
其实就是知道了密码,爆破用户名的情况
crackmapexec smb 192.168.122.231 -u name.txt -p 'admin@123' --continue-on-success
ASREP Roast
如果禁用了预身份认证,那么不需要发送用Client端的hash加密时间戳,也能得到AS_REP
,其中包含有 用户hash,加密的session key
拿到hash之后,我们就可以hashcat来爆破,不过hashcat支持的加密类型有23
des_cbc_crc = 1,
des_cbc_md4 = 2,
des_cbc_md5 = 3,
des3_cbc_md5 = 5,
des3_cbc_sha1 = 7,
dsaWithSHA1_CmsOID = 9,
md5WithRSAEncryption_CmsOID = 10,
sha1WithRSAEncryption_CmsOID = 11,
rc2CBC_EnvOID = 12,
rsaEncryption_EnvOID = 13,
rsaES_OAEP_ENV_OID = 14,
des_ede3_cbc_Env_OID = 15,
des3_cbc_sha1_kd = 16,
aes128_cts_hmac_sha1 = 17,
aes256_cts_hmac_sha1 = 18,
rc4_hmac = 23,
rc4_hmac_exp = 24,
subkey_keymaterial = 65
格式为 18200格式为 $krb5asrep$23$username@domain:hash[:32]$hash[32:]
hashcat -m 18200 -a 0 hash.txt password.txt
放在kali下跑了一下(Windows下一直跑不出不来),成功得到了密码(字典够用的话)
工具
impacket包中的 GetNPUsers.py 可得到有未开启 预认证的用户 并获取 加密的session key
单用户
python3 ./tools/impacket/examples/GetNPUsers.py cyyyy.lab/user1 -no-pass -dc-ip 192.168.122.231 [-o hash.txt]
但是讲道理,这东西不是TGT,是 用户hash加密的session key,脚本这样写容易误导。
多用户
python3 ./tools/impacket/examples/GetNPUsers.py -no-pass -dc-ip 192.168.122.231 -usersfile name.txt cyyy.lab\ -outputfile hash.txt
ASREQ Roast
原理类似,如果我们能监听到Client发送的AES_REQ
,并包含用户hash加密的时间戳,我们可以通过爆破来得到明文密码。
在前面可以看到,hashcat的预认证爆破是支持 17、18、23加密类型的
hashcat 爆破
hashcat -m 19900 hash.txt password.txt
当然前提都是字典足够大的情况下。
还有23类型的加密方式,但是看不太懂为什么还有salt字段,这个字段的值是什么还真不太清楚,
看到有个工具能直接导出来,但是用了下,有抓不到这个包.....以后有机会再看吧。
PTK(Pass the key)
预身份验证要求请求用户提供从其密码派生的密钥(DES、RC4、AES128 或 AES256)。知道密钥的攻击者不需要知道实际密码即可获取票证。这就是所谓的 pass the key
- 当使用RC4加密时, RC4 密钥实际上是用户的 NTLM hash。使用 NTLM 哈希来获取 Kerberos 票证称为 over pass the key
- 禁用 RC4 时,也可以传递其他 Kerberos 密钥(DES、AES-128、AES-256)。这种技术称为传递密钥。事实上,overpass the hash 和 pass the key 之间只是使用的名称和密钥不同,技术是相同的。
黄金票据
- 需要和DC通信
- 需要krbtgt用户的hash(域控上自动会生成这个用户)
拿到krbtgt的hash,就可以伪造TGT了。然后就可以不断的伪造其他服务器 Ticket了
伪造
msf golden_ticket_create -d 域名 -k krbtgt ntlm hash -s SID -u 用户名 -t save_file_path
msf kerberos_ticket_use save_file_path
mimikatz "kerberos::golden /domain:域名 /sid:域SID rc4:krbtgt NTLM hash /user:用户名 /ptt" exit
防御
krbtgt 密码
域管 不要登陆其他服务器 避免盗取token
钻石票据
首次提出钻石票据的应该是这个老哥 https://www.semperis.com/blog/a-diamond-ticket-in-the-ruff/
- 需要 krbtgt的aes256密钥
- 钻石票据是请求TGT,解密,修改,重新加密;黄金票据是直接进行伪造。
相关脚本已经集成到了Rubeus
中
作者还提到了Diamond PAC
攻击,其利用条件为:
-
请求一个没有PAC的TGT
-
确保要访问的服务的服务帐户未在其 UserAccountControl (UAC) 属性中设置 NA 位,(设置NA位指示当密钥分发中心(KDC)为此帐户颁发服务票证时,不得包含特权属性证书,即PAC)
-
伪造PAC,并用kerbtgt hash 签名
-
通过将 PAC 包含在 TGS-REQ 的 enc_authorization_data 部分中,将 PAC 注入到生成的服务票据中
只要授权数据包含在 TGS-REQ 的 enc_authorization_data 部分中,它就会被复制到生成的服务票证的加密部分的authorization_data 部分中。(说实话没太看懂)
当目标服务帐户没有设置 NA 位时(即有PAC),Diamond PAC 利用此行为将 PAC 注入到生成的服务票据中。
测试
.....
Rubeus.exe diamond /krbkey:29d6161a1c7fca9ebd06289dcea4f5597b88cfa25e2255f78e379560d55cf094 /user:user1 /password:userone@123 /enctype:aes /domain:cyyyy.lab /dc:DC.cyyyy.lab /ticketuser:Administrator /ticketuserid:500 /groups:512 /nowrap
TGS_REQ && TGS_REP
pass the ticket
有多种方法可以绕过或伪造Kerberos 票证。然后,可以使用票证对使用 Kerberos 的系统进行身份验证,而无需知道任何密码。这就是所谓的pass the ticket
(好像并没有说什么东西)
mimikatz注入票据
kerberos::ptt $ticket_kirbi_file
kerberos::ptt $ticket_ccache_file
Kerberoasting
类似ASREP Roast
。TGS_REP会返回服务端hash加密了的Server Ticket
,我们截取这个信息用hashcat离线爆破。
这里先学习一下SPN
SPN
SPN即 服务主体名称(Service Principal Name),感觉主要作用就是关联 服务和域内账户(机器账户和域用户账户)
SPN 分为两种:一种注册在 AD 上机器帐户(Computers)下,即内置账号(计算机名$),另一种注册在域用户帐户(Users)下
-
当一个服务的权限为 Local System 或 Network Service,则 SPN 注册在机器帐户(Computers)下。
-
当一个服务的权限为一个域用户,则SPN注册在域用户帐户(Users)下。
SPN命名格式
serviceclass "/" hostname [":"port] ["/" servicename]
注册
setspn -U -A spn_name username
测试
我们请求域控的文件共享服务
$krb5tgs$23$*user1$cyyyy.lab$cifs/DC.cyyyy.lab*$hash[:32]$hash[32:]
理论上是这样的,但在大多数情况下,服务帐户是机器帐户,其密码非常复杂、长且随机。但是,如果具有人为密码的服务帐户设置了 SPN,则攻击者可以请求该服务的 ST 并尝试离线破解它。
白银票据
- 不需要和 KDC进行交互
- 得到 server 端的 NTLM hash
- 不需要PAC
了解流程后就大概清楚了,因为有了 server端的NTLM hash就可以伪造 Ticket了。至于**Server Sessiosn key **我们随便写一个就行。
伪造
使用 mimikatz导出计算机名$
的hash(计算机的hash)
mimikatz "privilege::debug" "sekurlsa::logonpasswords" "exit" > hash.txt
伪造票据
mimikatz "kerberos::golden /domain:域名 /sid:域SID /target:目标服务器主机名 /service:服务类型 rc4:NTLM hash /user:用户名 /ptt" exit
防御
尽量保证服务器凭据不被窃取
开启PAC ,那么服务器就会将票据发送给Kerberos服务去验证是否有效。
委派
委派是一种域内应用模式,是指将域内用户账户的权限委派给服务账号,服务账号因此能以用户的身份在域内展开活动(请求新的服务等)。
委派的分类:
- 非约束委派(Unconstrained Delegation, UD),用户账户将自身的TGT转发给服务账户使用
- 约束委派(Constrained Delegation, CD),通过S4U2Self和S4U2Proxy两个扩展协议限制服务账户只能访问指定服务资源
- 基于资源的约束委派(Resource Based Constrained Delegation, RBCD),委派的管理移交给服务资源进行控制,其余和约束性委派基本相同
服务账号:注册了SPN
机器账号 : 内置的已经注册过了
域用户账号: 普通域用户
域用户注册了SPN后就变成了服务账号
非约束委派
机器账号的非约束性委派和服务账号的非约束性委派
机器
本来只有一个 WORKSTATION_TRUST_ACCOUNT
,设置非限制委派后还会多一个TRUSTED_FOR_DELEGATION
约束委派攻击
由微软的S4U2Self和S4U2Proxy两个拓展协议实现。对于约束性委派,服务账号只能获取该用户的ST服务票据,从而模拟该用户访问特定的服务,配置了约束性委派的账户的msDs-AllowedToDelegateTo
属性会指定对那个SPN进行委派。约束性委派的设置需要SeEnableDelegation
特权,该特权通常仅授予 域管理员
约束性委派有两种
- 一种仅支持Kerberos协议
- 一种是可使用任何身份验证协议,能进行地址转换。
指定允许委派到的服务
- 用户向Service1发出请求,用户通过身份验证,但Serivce1没有用户的授权数据,这是由于用户的身份验证是通过kerberos以外的其他方式验证的。
- Service1已经通过KDC进行了身份验证,并获取了TGT认购权证。它通过S4Uself协议代表用户向KDC请求一张访问自身Service1服务的 可转发的ST服务票据
- KDC返回后,该可转发的ST服务票据可能包含用户的授权数据。
- Service1可以使用ST1服务票据中的授权数据来满足用户的请求,然后响应用户
- 用户向Service1发出请求,请求访问Service2上的资源。
- Service1利用S4UProxy协议以用户的名义向KDC请求访问Serivce2的ST2服务票据,(该请求中带上了可转发的ST1服务票据)
- 如果请求中有PAC特权属性证书,则KDC通过检查PAC结构的签名数据来检验PAC。如果PAC有效或不存在,KDC返回service2的可转发ST2服务票据,并且存储在ST2服务票据中的cname和crealm字段中的客户端表示是用户,而不是Serivce1
- Service1以用户的身份使用可转发的ST2服务票据向Service2发起请求。
- Service2响应Service1
- Service1响应用户
看完之后再看这两个协议的名称 S4U2Self和S4U2Proxy。一个是Self,自己帮用户申请证书,一个是Proxy,代理用户访问其他服务。
需要注意的几个点就是
- 在S4USelf阶段,如果UserAccountControl位设置了
USERNOTDELEGATED
,那么不能被委派。 - 在S4UProxy阶段 ,Service1会将S4USelf阶段得到ST放到
AddtionTicket
模块中。 - 如果AddtionTicket里面的票据是可转发的,只要KDC Options里面置forwarable位,那么返回的票据必须置为可转发的
- 如果AddtionTicket中的服务票据未设置为可转发的,则KDC必须返回状态为STATUSNOMATCH的KRB-ERR-BADOPTION选项。除了一种情况之外,就是配置了服务1到服务2 的基于资源的约束委派,且PA-PAC-OPTION设置了Resource-Based Constrained Delegation标志位(这一例外的前提是S4U2SELF阶段模拟的用户没被设置为对委派敏感,对委派敏感的判断在S4U2SELF阶段,而不是S4U2PROXY阶段)。
基于资源的约束委派
基于资源的约束委派 允许 资源配置受信任的帐户委派给他们。基于资源的约束委派将委派的控制权交给拥有被访问资源的管理员。
个人感觉是,谁拥有资源谁就能写个白名单,指定谁来委派。
以前是域控,在server1配置server1到server2的委派(msDS-AllowedToDelegateTo),现在是 server2自己就能指定server1到server2的委派(msDS-AllowedToActOnBehalfOfOtherIdentity)
需要注意
- serivce 1 代表用户申请一个获得针对serivce1自身的kerberos服务票据(这一步就是S4U2SELF,这一步就区别传统的约束委派,返回的TGS可转发的一个条件是服务1配置了传统的约束委派,kdc会检查服务1 的TrustedToAuthForDelegation位和msDS-AllowedToDelegateTo 这个字段,由于基于资源的约束委派,是在服务2配置,服务2的msDS-AllowedToActOnBehalfOfOtherIdentity属性配置了服务1 的sid,服务1并没有配置TrustedToAuthForDelegation位和msDS-AllowedToDelegateTo 字段。因此这一步返回的TGS票据是不可转发的
- 将上一阶段得到的票据,放到 AddtionTicket部分,然后向 服务2申请 可转发的TGS
参考
https://learn.microsoft.com/zh-cn/archive/blogs/apgceps/packerberos-2
https://github.com/daikerSec/windows_protocol
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理