域渗透-kerberos协议分析
文章中对之前的文章提出了一些疑问,如果发现有错误,还望斧正
环境搭建
这里的域环境搭建就不再细说
主机 | ip | 用户 | 主机名 |
---|---|---|---|
域控 | 192.168.164.133 | administrator | ad.test.com |
域内主机 | 192.168.164.129 | test | user.test.com |
两台主机均为win2008r2
名词解释
(1)Client:访问服务的客户机
(2)Server:提供服务的服务器
(3)KDC(Key Distribution Center):密钥分发中心
(4)KDC中分成两个部分:Authentication Service和Ticket Granting Service
Authentication Service(AS):身份验证服务
Ticket Granting Service(TGS):票据授予服务
AS和TGS如下:
Authentication Service:AS的作用就是验证Client端的身份,验证通过之后,AS就会给TGT票据(Ticket Granting Ticket)给Client.
Ticket-granting cookie(TGC):存放用户身份认证凭证的cookie,在浏览器和CAS Server间通讯时使用,是CAS Server用来明确用户身份的凭证。TGT封装了TGC值以及此Cookie值对应的用户信息.
Ticket-granting ticket(TGT):TGT对象的ID就是TGC的值,在服务器端,通过TGC查询TGT.
Ticket Granting Service(TGS):TGS的作用是通过AS发送给Client的TGT换取访问Server端的ST(Server Ticket)给Client.
SEerver Ticket(ST):ST服务票据,由TGS服务发布.
(5)Active Directory(AD):活动目录
(6)Domain Controller(DC):域控制器
(7)Ticket-granting cookie(TGC):存放用户身份认证凭证的cookie,在浏览器和CAS Server间通讯时使用,是CAS Server用来明确用户身份的凭证。TGT封装了TGC值以及此Cookie值对应的用户信息.
(8)Ticket-granting ticket(TGT):TGT对象的ID就是TGC的值,在服务器端,通过TGC查询TGT.
认证过程
我们这里直接抓包来看,让域内机器user使用用户test来登录
AS-REQ
Pvno kerberos协议版本号:05(Hex)
5MSG-TYPE 类型 AS_REQ对应(krb-as-req)0a(Hex)
PA-DATA 预认证信息数据 一个列表,包含若干个认证消息用于认证,每个认证消息有type和value。
AS_REQ 阶段主要用到的有两个
1.ENC_TIMESTAMP
这个是预认证,就是用用户hash加密时间戳,作为value 发送给AS服务器。然后AS服务器那边有用户hash,使用用户hash进行解密,获得时间戳,如果能解密,且时间戳在一定的范围内,则证明认证通过。
2.PA_PAC_REQUEST
这个是启用PAC支持的扩展。PAC(Privilege Attribute Certificate)并不在原生的kerberos里面,是微软引进的扩展。PAC包含在AS_REQ的响应body(AS_REP)。这里的value对应的是include=true或者include=false(KDC根据include的值来判断返回的票据中是否携带PAC)。
REQ_BODY
1.cname
PrincipalName 类型。PrincipalName包含type和value。
KRB_NT_PRINCIPAL = 1 means just the name of the principal 如daizhibin
KRB_NT_SRV_INST = 2 service and other unique instance (krbtgt) 如krbtgt,cifs
KRB_NT_ENTERPRISE_PRINCIPAL = 10 如 user@domain.com
在AS_REQ里面cname 是请求的用户,这个用户名存在和不存在,返回的包有差异,可以用于枚举域内用户名。
2.sname
PrincipalName 类型,在AS_REQ里面sname是krbtgt,类型是KRB_NT_SRV_INST
3.realm 域名
4.from 发送时间
5.till 到期时间
原文中的到期时间说可以作为流量监测,但是我在本地抓包时发现,win2008r2默认的到期时间即为文章中说的值,所以无法作为流量监测判定依据
6.nonce
随机生成的一个数kekeo/mimikatz nonce是12381973,rubeus nonce是1818848256,这个也可以用来作为特征检测工具。
这里是原文说的,https://www.anquanke.com/post/id/190261
但是我在本地测试时候发现,mimikatz并不会直接触发kerberos认证,pth也是注入到内存,触发的时候nonce还是走的本地,并不会涉及到mimikatz的nonce,而rubeus的nonce已经修复https://github.com/GhostPack/Rubeus/issues/34
7.etype
加密类型
这个地方要注意的是如果在配置里面选择用hash(不是plaintext)的话,hash的加密类型,要跟etype一样。因为KDC是按照etype类型选择用户对应加密方式的hash,如果是选择明文(plaintext),那么client 会按照etype里面的加密方式将明文加密成hash
AS获取用户名之后,获取对应的ntlm
值,通过加密的方法加密数据信息,并且验证时间戳,之后生成随机字符串Session Key
,使用用户的ntlm值加密Session Key
,使用krbtgt
用户的ntlm
加密Session Key
和客户端信息,一起返回客户端
Send=user_NTML_Hash(Session Key)+krbtgt_NTML_Hash(Session Key+client_info1)[TGT]
抓包的真实情况
AS-REQ存在俩种包
- 不存在
pA-ENC-TIMESTAMP
字段的,和前面存在密码字段的
AS-REP
用户名和密码正确(第二个包)
用户名不正确(第一个包)
这里很明显就可以看到,用户名不正确返回的结果有很大差异,可以利用这个方法来爆破用户名??,
用户名正确(第一个包)
密码不正确(只有第一个包,无第二个包)
这里可以看到首先发送一个不带密码的AS-REQ,判断用户名是否正确,之后再发送一个带密码的AS-REQ,来判断是否通过认证
我们可以通过第一步判断用户名的包进行一个用户枚举,通过返回值的不同来进行爆破
客户端在收到AS-REP
之后使用ntlm
解密获得Session Key
,使用Session Key
加密客户端信息和时间戳,连同TGT
发送给TGS
Send=Session Key(time+client_info2)+krbtgt_NTML_Hash(Session Key+client_info1)[TGT]
TGS-REQ
TGS
接受到数据后,使用krbtgt
的ntlm
解密tgt
,获得Session Key
,使用Session Key
解密获得client_info2
和time
,对比tgt
当中的clinet_info1
和time
,通过验证之后,TGS
发送票据给客户端
Send=Server_Hash(Server Session Key+Client_info+time)[Trick]+Session key(Server Session Key)
这里有个问题,为什么要来回发送这个tgt,反正AS和TGS都在域控内,
1.因为服务器少于客户端,如果每个tgt都存在服务器内,需要进行一个映射表,产生开销
2.在AS和TGS内部传输可能存在一些问题
req-body
padding:0
kdc-options:用于与KDC约定一些选项设置
realm:域名
sname:这里是要请求的服务
till:到期时间
(参考的文章说到期时间为20370913024805Z为mimikatz的特征,)这里抓包验证一下,win2008默认也是这个特征,这是个伪命题
nonce:随机生成数
etype:加密类型
TGS-REP
客户端使用Session key
解密Server Session Key
,向服务端提交请求,并提供Ticket
和Server Session key
加密的客户端信息与时间戳 ,与服务器进行通信。通过认证后Ticket会一致在客户端内存中。
Send=Server_Hash(Server Session Key+Client_info+time)[Trick]+Server Session Key(Client_info2+time)
S4U2SELF
S4U2self 使得服务可以代表用户获得针对服务自身的kerberos服务票据。这使得服务可以获得用户的授权( 可转发 的用户TGS票据),然后将其用于后期的认证(主要是后期的s4u2proxy),这是为了在用户以不使用 Kerberos 的方式对服务进行身份验证的情况下使用。这里面很重要的一点是服务代表用户获得针对服务自身的kerberos票据这个过程,服务是不需要用户的凭据的,其中转发这部分主要是委派使用,
S4U2PROXY
s4u2proxy 使得服务1可以使用来自用户的授权( 在S4U2SELF阶段获得),然后用该TGS(放在AddtionTicket里面)向KDC请求访问服务2的TGS,并且代表用户访问服务2,而且只能访问服务2。
ST认证
这里直接使用先知的图
1.服务端验证客户端:防止非法用户操作
2.客户端验证服务端:防止误入恶意服务
PS:PAC并不是所有服务都开启的,这需要配置验证KDC PAC 签名。没有验证PAC,可能会导致白银票据攻击。因为开启PAC后,就算攻击者拥有用户hash,能制作ST票据后,无法通过PAC验证,还是无法访问服务。
PAC
前面说到有些服务会不使用pac,pac存在最严重的漏洞就是ms14-068
下面是pac的结构,这里参考daiker的文章https://daiker.gitbook.io/windows-protocol/kerberos/3#0x02-pac-jie-gou
PAC的结构如下图所示。
PAC整体的结构上是一个AuthorizationData的结构
AuthorizationData ::= SEQUENCE OF SEQUENCE {
ad-type [0] Int32,
ad-data [1] OCTET STRING
}
AuthorizationData结构的ad-type主要有以下几个
AD-IF-RELEVANT 1
AD-INTENDED-FOR-SERVER 2
AD-INTENDED-FOR-APPLICATION-CLASS 3
AD-KDC-ISSUED 4
AD-AND-OR 5
AD-MANDATORY-TICKET-EXTENSIONS 6
AD-IN-TICKET-EXTENSIONS 7
AD-MANDATORY-FOR-KDC 8
Reserved values 9-63
OSF-DCE 64
SESAME 65
AD-OSF-DCE-PKI-CERTID 66 (hemsath @us.ibm.com)
AD-WIN2K-PAC 128 (jbrezak @exchange.microsoft.com)
AD-ETYPE-NEGOTIATION 129 (lzhu @windows.microsoft.com)
如上图所示,整个PAC最外层的ad-type为AD-IF-RELEVANT
,ad-data还是一个AuthorizationData结构。
这个AuthorizationData的ad-type 为AD-WIN2K-PAC
,ad-data为一段连续的空间,
这段空间包含一个头部PACTYPE
以及若干个PAC_INFO_BUFFER
头部PACTYPE包括cBuffers
,版本
以及缓冲区
,PAC_INFO_BUFFER
为key-value型的
key 的类型如下表所示
类型 | 意义 |
---|---|
0x00000001 | 登录信息。PAC结构必须包含一个这种类型的缓冲区。其他登录信息缓冲区必须被忽略。 |
0x00000002 | 凭证信息。PAC结构不应包含多个此类缓冲区。第二或后续凭证信息缓冲区在接收时必须被忽略。 |
0x00000006 | 服务器校验和。PAC结构必须包含一个这种类型的缓冲区。其他登录服务器校验和缓冲区必须被忽略。 |
0x00000007 | KDC(特权服务器)校验和(第2.8节)。PAC结构必须包含一个这种类型的缓冲区。附加的KDC校验和缓冲区必须被忽略。 |
0x0000000A | 客户名称和票证信息。PAC结构必须包含一个这种类型的缓冲区。附加的客户和票据信息缓冲区必须被忽略。 |
0x0000000B | 受约束的委派信息。PAC结构必须包含一个S4U2proxy请求的此类缓冲区,否则不包含。附加的受约束的委托信息缓冲区必须被忽略。 |
0x0000000C | 用户主体名称(UPN)和域名系统(DNS)信息。PAC结构不应包含多个这种类型的缓冲区。接收时必须忽略第二个或后续的UPN和DNS信息缓冲区。 |
0x0000000D | 客户索取信息。PAC结构不应包含多个这种类型的缓冲区。附加的客户要求信息缓冲区必须被忽略。 |
0x0000000E | 设备信息。PAC结构不应包含多个这种类型的缓冲区。附加的设备信息缓冲区必须被忽略。 |
0x0000000F | 设备声明信息。PAC结构不应包含多个这种类型的缓冲区。附加的设备声明信息缓冲区必须被忽略。 |
下面详细介绍四个比较重要的
- 0x00000001 KERB_VALIDATION_INFO
这个结构是登录信息,也是整个PAC最重要的部分,整个PAC就靠它来验证用户身份了,是个结构体,如下
typedef struct _KERB_VALIDATION_INFO {
FILETIME LogonTime;
FILETIME LogoffTime;
FILETIME KickOffTime;
FILETIME PasswordLastSet;
FILETIME PasswordCanChange;
FILETIME PasswordMustChange;
RPC_UNICODE_STRING EffectiveName;
RPC_UNICODE_STRING FullName;
RPC_UNICODE_STRING LogonScript;
RPC_UNICODE_STRING ProfilePath;
RPC_UNICODE_STRING HomeDirectory;
RPC_UNICODE_STRING HomeDirectoryDrive;
USHORT LogonCount;
USHORT BadPasswordCount;
ULONG UserId; //用户的sid
ULONG PrimaryGroupId;
ULONG GroupCount;
[size_is(GroupCount)] PGROUP_MEMBERSHIP GroupIds;//用户所在的组,如果我们可以篡改的这个的话,添加一个500(域管组),那用户就是域管了。在ms14068 PAC签名被绕过,用户可以自己制作PAC的情况底下,pykek就是靠向这个地方写进域管组,成为使得改用户变成域管
ULONG UserFlags;
USER_SESSION_KEY UserSessionKey;
RPC_UNICODE_STRING LogonServer;
RPC_UNICODE_STRING LogonDomainName;
PISID LogonDomainId;
ULONG Reserved1[2];
ULONG UserAccountControl;
ULONG SubAuthStatus;
FILETIME LastSuccessfulILogon;
FILETIME LastFailedILogon;
ULONG FailedILogonCount;
ULONG Reserved3;
ULONG SidCount;
[size_is(SidCount)] PKERB_SID_AND_ATTRIBUTES ExtraSids;
PISID ResourceGroupDomainSid;
ULONG ResourceGroupCount;
[size_is(ResourceGroupCount)] PGROUP_MEMBERSHIP ResourceGroupIds;
} KERB_VALIDATION_INFO;
-
0x0000000A PAC_CLIENT_INFO
-
客户端Id(8个字节):
包含在Kerberos初始TGT的authtime
-
NameLength(2字节)
用于指定Name 字段的长度(以字节为单位)。
-
Name
包含客户帐户名的16位Unicode字符数组,格式为低端字节序。
-
0x00000006和0x00000007 0x00000006 对应的是服务检验和,0x00000007 对应的是KDC校验和。分别由server密码和KDC密码加密,是为了防止PAC内容被篡改。
存在签名的原因有两个。首先,存在带有服务器密钥的签名,以防止客户端生成自己的PAC并将其作为加密授权数据发送到KDC,以包含在票证中。其次,提供具有KDC密钥的签名,以防止不受信任的服务伪造带有无效PAC的票证。
两个都是PAC_SIGNATURE_DATA结构,他包括以下结构。 1. SignatureType(4个字节)
类型 | 含义 | 签名长度 |
---|---|---|
0xFFFFFF76 | KERB_CHECKSUM_HMAC_MD5 | 16 |
0x0000000F | HMAC_SHA1_96_AES128 | 12 |
0x00000010 | HMAC_SHA1_96_AES256 | 12 |
-
Signature
包含校验和。签名的长度由SignatureType字段的值确定 3. RODCIdentifier(2个字节):
当KDC为RODC时,包含密钥版本号的前16位。当KDC不是RODC时,此字段不存在。
ms14-068
补丁编号是KB3011780,域里面最严重的漏洞之一,它允许任意用户提升到域管权限。下面简要分析下该漏洞。
该漏洞最本质的地方在于Microsoft Windows Kerberos KDC无法正确检查Kerberos票证请求随附的特权属性证书(PAC)中的有效签名,这里面的签名就是上面提到的服务检验和以及KDC校验和。导致用户可以自己构造一张PAC。 签名原本的设计是要用到HMAC系列的checksum算法,也就是必须要有key的参与,我们没有krbtgt的hash以及服务的hash,就没有办法生成有效的签名,但是问题就出在,实现的时候允许所有的checksum算法都可以,包括MD5。那我们只需要把PAC 进行md5,就生成新的校验和。这也就意味着我们可以随意更改PAC的内容,完了之后再用md5 给他生成一个服务检验和以及KDC校验和。在MS14-068修补程序之后,Microsoft添加了一个附加的验证步骤,以确保校验和类型为KRB_CHECKSUM_HMAC_MD5。
域用户(513)
域管理员(512)
架构管理员(518)
企业管理员(519)
组策略创建者所有者(520)
域控解密pac获得sid,以及所在的组,所以我们需要修改用户所在的组
漏洞的实现方法是在as-req发送时选择include_pac为false,生成的tgt也就不包含pac,之后伪造一个pac,sid为用户sid,将管理组的sid加入groupid,之后将伪造的pac加密放在enc-authorization-data里面,发起TGS-REQ请求,密钥是在PA-DATA里面的AP-REQ的authenticator里面的subkey,KDC收到TGS-REQ请求后解析AP_REQ,提取authenticator,用session_key解密或者subkey,之后解密enc-authorization-data,获得pac,通过校验,
参考文章
https://xz.aliyun.com/t/8187
https://www.anquanke.com/post/id/190261
https://daiker.gitbook.io/windows-protocol/kerberos/