Kerberos

image-20220211114025754

前言

Kerberos 是一种由 MIT(麻省理工大学)提出的一种网络身份验证协议。它旨在通过使 用密钥加密技术为客户端/服务器应用程序提供强身份验证。

因为Kerberos协议 一般运用在域环境中 所以先结束一下域环境

域环境

​ 由于 Kerberos 主要是用在域环境下的身份认证协议,所以在说之前先说下域环境的一 些概念。首先域的产生是为了解决企业内部的资源管理问题,比如一个公司就可以在网络中 建立一个域环境,更方便内部的资源管理。在一个域中有域控、域管理员、普通用户、主机 等等各种资源。

​ 在下图中 YUNYING.LAB 为其他两个域的根域,NEWS.YUNYING.LAB 和 DEV.YUNYING.LAB 均为 YUNYING.LAB 的子域,这三个域组成了一个域树。子域的概念可以理解为一个集团在不 同业务上分公司,他们有业务重合的点并且都属于 YUNYING.LAB 这个根域,但又独立运作。 同样 TEST.COM 也是一个单独的域树,两个域树 YUNYING.LAB 和 TEST.COM 组合起来被称为 一个域林。

image-20220211195403199

​ YUNYING.LAB 的域控是 DC.YUNYING.LAB, 子域 NEWS.YUNYING.LAB 和 DEV.YUNYING.LAB 的域控分别为 NEWSDC.NEWS.YUNYING.LAB 和 DEVDC.DEV.YUNYING.LAB。

​ 上面说的都是 FQDN(Fully Qualified Domain Name)名称,也就是全限定域名,是同时包含 主机名和域名的名称。

​ 例:DC.YUNYING.LAB 中 DC 为主机名,域名为 YUNYING.LAB,那他的 FQDN 名称就是 DC.YUNYING.LAB。

image-20220211195519811

如何搭建域环境以及如何建立子域可参考网上的一些说明,这里放两个链接作为参考。

https://jingyan.baidu.com/article/19192ad8e1593ae53e5707be.html

http://blog.51cto.com/vbers/2058306

这里是参考的云影Kerberos系列

kerberos 协议概述

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

  • 访问服务的Client

  • 提供服务的Server

  • KDC(Key Distribution Center)密钥分发中心

  • Client代表用户,用户有自己的密码,Server上运行的服务也有自己的密码,KDC是受信任的三方认证中心,它拥有用户和服务的密码信息。

  • KDC服务默认会安装在域控中,而Client和Server为域内的用户或者是服务,如HTTP服务,SQL服务。在Kerberos中Client是否有权限访问Server端的服务由KDC发放的票据来决定。Client想要访问Server的服务(xxx service),前提是通过KDC认证,再由KDC发放的票据决定Client是否有权限访问Server的服务。

  • KDC 服务框架中包含一个 KRBTGT 账户,它是在创建域时系统自动创建的一个账号,你可以暂时理解为他就是一个无法登陆的账号,在发放票据时会使用到它的密码 HASH 值。

image-20220212004526739

名词基本概念:

​ KDC: Key Distribution Center,密钥分发中心,负责管理票据、认证票据、分票据,但是KDC不是一个独立的服务,它由AS和TGS组成。

​ AS: Authentication Service,验证服务,为client生成TGT的服务

​ TGS: Ticket Granting Service,票据授权服务,验证TGT,发放ST。

​ TGT: Ticket Granting Ticket,票据授权票据,由KDC的AS发放,客户端获取到该票据后,以后申请其他应用的服务票据(ST)时,就不需要向KDC的AS提交身份认证信息(credential),TGT具有一定的有效期。由 KBRTGT HASH 加密的 sessionkey-as 和 Timestamp 等信息。TGT=KBRTGT HASH()

​ ST(Service Ticket):服务票据,由KDC的TGS发放,是客户端应用程序访问Server某个服务的凭证,Server端验证通过则完成Client与Server端信任关系的建立。

​ Session key:用来加密client和TGS之间传输的数据。

​ Server session key:用来加密client和server之间传输的数据、认证

​ Ticket:票据,是网络中各对象之间互相访问的凭证

​ AD: Account Database,存储所有client的白名单,只有存在于白名单的client才能顺利申请到TGT。

​ DC: Domain Controller,域控

​ KRBTGT: 每个域控制器都有一个krbtgt账户,是KDC的服务账户,用来创建TGS加密的密钥。

如果把Kerberos中的票据类比为一张火车票,那么Client端就是乘客,Server端就是火车,而KDC就是就是车站的认证系统。如果Client端的票据是合法的(由你本人身份购买并由你本人持有)同时有访问Server端服务的权限(车票对应车次正确)那么你才能上车。当然和火车票不一样的是Kerberos中有存在两张票,而火车票从头到尾只有一张。
由上图中可以看到KDC又分为两个部分:

Authentication Server: AS的作用就是验证Client端的身份(确定你是身份上的本人),验证通过就会给一张TGT(Ticket Granting Ticket)票给Client。
Ticket Granting Server: TGS的作用是通过AS发送给Client的票(TGT)换取访问Server端的票(上车的票ST)。ST(Service Ticket)也有资料称为TGS Ticket,为了和TGS区分,在这里就用ST来说明。

image-20220212005207409

Kerberos认证流程

整体流程

​ 当域内某个用户(client)去请求同域内的某个服务的时候(server),这个请求首先会被送到KDC进行身份认判断是否是可信赖的,在AD(account database)中查询该client name对应的master key,并对pre-authentication data数据进行解密,如果可以提取出一个合法的时间戳,那就说明该用户是合法的,验证通过(有些这里是说成黑白名单的说法,在AD中对比黑白名单 看看用户是否是可信赖的),返回AS,然后AS给用户返回一个可重复使用的TGT,后续Client使用这个TGT向TGS请求ST即可。然后用户再拿着TGT票据去请求TGSTGS验证成功后会返回一个用对应服务账号的密码hash加密过的票据ST,然后用户就可以拿着ST去访问server上对应的服务了。但是这个Ticket只能用来访问这个Server,如果要访问其他Server需要向KDC重新申请。

分步

image-20220212012430878

当 Client 想要访问 Server 上的某个服务时,需要先向 AS 证明自己的身份,然后通过 AS 发放的 TGT 向 Server 发起认证请求,这个过程分为三块:

  • The Authentication Service Exchange(认证服务器)–>

    Client 与 AS 的交互AS_REQ•AS_REP

  • Ticket-Granting Service (TGS) Exchange(票据授予服务器)–>

    Client 与 TGS 的交互 TGS_REQ•TGS_REP

  • The Client/Server Authentication Exchange(pc和要访问的服务)–>

    Client 与 Server 的交互 AP_REQ•AP_REP

第一步:Client 与 AS

image-20220212013650350

AS_REQ(请求):

image-20220212013147551

  • Pre-authentication data:就是用client对应的master key加密了一个timestamp。
  • Client info:client用户信息
  • Server info:这里并不是Client真正要访问的Server的名称,实际上是KDC的Ticket Granting Service的Server Name。

第一步 Client 先向 KDC 的 AS 发送 Authenticator1,内容为通过 Client 密码 Hash 加密的 时间戳、Client ID、网络地址、加密类型等内容。KDC端收到该请求后,Authentication service用client info部分信息,在AD(account database)中查询该client name对应的master key,并对pre-authentication data数据进行解密,如果可以提取出一个合法的时间戳,那就说明该用户是合法的,验证通过,并回复KRB_AS_REP给client。

AS_REP(返回):

image-20220212014028121

  • AS 收到用户认证请求后,AS 根据请求中的 用户名 AA 信息,从数据库中查找用户名是否存在。如果 用户名 AA 存在,则从 KDC 中可以获取 用户 AA 的密码,使用单向函数为该密码生成一个 Client 密钥(即NTLM Hash)。AS 生成随机字符串 Client/TGS Session Key,使用 Client 密钥(用户 AA 的密码 NTLM Hash)对 Client/TGS Session Key 加密得到 sessionkey_as; //Sessionkey_as = 用户密码NTLM HASH(Client/TGS Session Keys)

  • 再使用 TGS 密钥(krbtgt 用户的 NTLM Hash)对 Client/TGS Session Key 、 Client Info 和 Timestamp 加密,得到 TGT(TGT票据)。

  • //TGT = KRBTGT用户NTLM Hash(Client/TGS Session_key,Client info,Timestamp)

  • 将 sessionkey_as 和 TGT 一起返回给 Client。

  • Client 收到 AS 的响应消息后,利用自身的 Client 密钥(AA 的 NTLM Hash)对sessionkey_as 解密,这样就获取到 Client/TGS Session Key。

总体来说KRB_AS_REP分为两部分:

  • 用client master key对session key进行加密后的值。Session key是KDC随机生成的UUID,用于client和TGS服务之间的数据加密、认证。
  • 用KDC master key值对Client/TGS Session Key进行加密。这部分client解不了。由此处也可以看出,TGT包括三个部分,分别是session key、client name、end time。(当响应信息里面有KDC Hash,即可伪造黄金票据) //Master Key = NTLM Hash

第二步:Client与TGS交互

image-20220212020233719

当client端接收到AS_REP时,client使用client master key对KRB_AS_REP的第一部分信息进行解密,得到session key,并再次拼装出TGS_REQ请求体,向KDC的TGS发出请求

请求结构如下,TGS_REQ请求体包括:client/TGS Session key(client info+时间戳)、TGT、client info、server info;

其中,server info就是该client真正要访问的server,步骤一AS_REQ中的不一样

TGS-REQ(请求):

client像TGS发送client解密出的Session key然后和client info+时间戳加密的新的Session key(client info+时间戳) 以及 TGT,当TGS服务收到到client请求体KRB_TGS_REQ时,因为TGS端并没有session key,只能先利用KDC的master key去解TGT部分内容,得到session key,再去解Session key(client info+时间戳)部分,从而验证该用户是否是AS颁发给该client的。验证通过后,给client回复KRB_TGS_REP给client

TGS-REP(返回):

TGS 收到请求后,检查 KDC 数据库中是否存在所请求的服务(Service ID)。如果存在,TGS 使用 KDC密钥(krbtgt 的 NTLM Hash)解密 TGT,得到 Client/TGS Session Key、timestamp、Client info;同时使用从 TGT 中解密得到的 Client/TGS Session Key去解密 Authenticator2,得到 Client info 和 timestamp。比对 Authenticator2 和TGT 的解密内容以验证通过。

•TGS 比对 Authenticator2 包含的 Client ID 和 TGT 中的 Client ID•比较时间戳(误差范围在2分钟)

•通过生命周期字段检查 TGT 是否过期

•检查 Authenticator2 已经不再 TGS 的缓存中

•若原始请求中的网络地址不为 NULL,比较 TGT 中的 IP 和请求的 IP验证成功后,随机生成 Client 所请求服务的会话密钥 Client/Server Session Key;

使用 Server 密钥(即服务器计算机的NTLM Hash)对 Client/Server Session Key、Client Info(包含 Client ID)、TimeStamp 加密得到 Client-To-Server Ticket(也称为 ST 票据);

使用 Client/TGS Session Key 对 Client/Server Session Key 加密得到sessionkey_tgs

//ST=Server NTLM Hash(Client/Server Session Key,Client Info,TimeStamp)

//Sessionkey_tgs = Client/TGS Session Key(Client/Server Session Key)

最终将 Client-To-Server Ticket、sessionkey_tgs 返回给 Client。

第三步:Client与SS交互

image-20220212021542029

Client 向 SS(Service Server)发送服务请求

AP-REQ(请求):

Client 收到 Client-To-Server Ticket、sessionkey_tgs 之后,使用Client/TGS Session Key 对 sessionkey_tgs 解密得到 Client/Server Session Key,然后使用 Client/Server Session Key 对 Client Info 和 timestamp 加密得到Authenticator3;将 Authenticator3 和 Client-To-Server Ticket 发送给所请求服务的服务器(Service Server)。

AP-REP(返回):

Service Server 收到客户端的服务访问请求之后,利用 Server 密钥(Server 的 ntlm Hash)对 Client-To-Server Ticket 解密,提取出 Client/Server SessionKey、Client ID 等信息。Service Server 使用 Client/Server SessionKey 对 Authenticator3 解密得到 Client ID 和 TimeStamp。

Service Server 发送最后的验证消息——用 Client/Server SessionKey 加密的 Timestamp 和 Service ID 数据包给 Client。

Client 收到之后,使用缓存的 Client/Server SessionKey 解密提取 Timestamp 信息,然后确认该信息与 Client 发送的 Authenticator3 中的 Timestamp 信息是否一致。验证通过后,在定义的通讯周期内,Client 可以使用票据请求 Service。

PAC

微软为了访问控制而引进的一个扩展PAC,以及PAC在历史上出现过的一个严重的,允许普通用户提升到域管的漏洞MS14068。

PAC 介绍

网上很多版本的kerberos的流程是

  1. 1.

    用户向KDC发起AS_REQ,请求凭据是用户hash加密的时间戳,KDC使用用户hash进行解密,如果结果正确返回用krbtgt hash加密的TGT票据

  2. 2.

    用户凭借TGT票据向KDC发起针对特定服务的TGS_REQ请求,KDC使用krbtgt hash进行解密,如果结果正确,就返回用服务hash 加密的TGS票据

  3. 3.

    用户拿着TGS票据去请求服务,服务使用自己的hash解密TGS票据。如果解密正确,就允许用户访问。

上面这个流程看起来没错,却忽略一个最重要的因素,那就是用户有没有权限访问该服务,在上面的流程里面,只要用户的hash正确,那么就可以拿到TGT,有了TGT,就可以拿到TGS,有了TGS,就可以访问服务,任何一个用户都可以访问任何服务。也就是说上面的流程解决了”Who am i?”的问题,并没有解决 “What can I do?”的问题。

为了解决上面的这个问题,微软引进了PAC,引进PAC之后的kerberos流程变成

​ 用户向KDC发起AS_REQ,请求凭据是用户hash加密的时间戳,KDC使用用户hash进行解密,如果结果正确返回用krbtgt hash加密的TGT票据,TGT里面包含PAC,PAC包含用户的sid,用户所在的组

image-20220212235948727

​ 1.用户凭借TGT票据向KDC发起针对特定服务的TGS_REQ请求,KDC使用krbtgt hash进行解密,如果结果正确,就返回用服务hash 加密的TGS票据(这一步不管用户有没有访问服务的权限,只要TGT正确,就返回TGS票据,这也是kerberoating能利用的原因,任何一个用户,只要hash正确,可以请求域内任何一个服务的TGS票据,

​ 2.用户拿着TGS票据去请求服务,服务使用自己的hash解密TGS票据。如果解密正确,就拿着PAC去KDC那边询问用户有没有访问权限,域控解密PAC。获取用户的sid,以及所在的组,再判断用户是否有访问服务的权限,有访问权限(有些服务并没有验证PAC这一步,这也是白银票据能成功的前提,因为就算拥有用户hash,可以制作TGS,也不能制作PAC,PAC当然也验证不成功,但是有些服务不去验证PAC,这是白银票据成功的前提)就允许用户访问。

特别说明的是,PAC对于用户和服务全程都是不可见的。只有KDC能制作和查看PAC。

相关安全问题

1. MS14068

补丁编号是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。

在KERB_VALIDATION_INFO结构里面,我们看到有这两个字段。

image-20220213000156652

其中GroupId是用户所在所在的组,那只要我们把重要组(比如域管组)的sid加进GroupId。那么服务拿这用户的TGS去询问域管用户是否有访问访问改服务的权限的时候,域控会解密PAC,提取里面用户的sid,以及所在的组(GroupId),我们已经把域管加进去了,是的域控把把这个用户当做域管组里面的成员。从而达到提升为域管的效果。pykek加入的是以下组,

  • 域用户(513)

  • 域管理员(512)

  • 架构管理员(518)

  • 企业管理员(519)

  • 组策略创建者所有者(520)

参考:https://daiker.gitbook.io/windows-protocol/kerberos/3


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!