Kerberos协议详解

Posted h领小白帽

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kerberos协议详解相关的知识,希望对你有一定的参考价值。

文章目录


一、前言

Kerberos协议 是一个专注于验证通信双方身份的网络协议,不同于其他网络安全协议的保证整个通信过程的传输安全,Kerberos侧重于通信前双方身份的认定工作,帮助客户端以及服务端验证是真正的自己并非他人,从而使得通信两端能够完全信任对方的身份,在一个不安全的网络中完成一次安全的身份认证继而进行安全的通信。

二、什么是Kerberos协议

Kerberos是一种计算机网络认证协议 ,其设计目标是通过密钥系统为网络中通信的客户机(Client)/服务器(Server)应用程序提供严格的身份验证服务,确保通信双方身份的真实性和安全性。不同于其他网络服务,Kerberos协议中不是所有的客户端向想要访问的网络服务发起请求,他就能建立连接然后进行加密通信,而是在发起服务请求后必须先进行一系列的身份认证,包括客户端和服务端两方的双向认证,只有当通信双方都认证通过对方身份之后,才可以互相建立起连接,进行网络通信。即Kerberos协议的侧重在于认证通信双方的身份,客户端需要确认即将访问的网络服务就是自己所想要访问的服务而不是一个伪造的服务器,而服务端需要确认这个客户端是一个身份真实,安全可靠的客户端,而不是一个想要进行恶意网络攻击的用户。

三、Kerberos协议角色组成

Kerberos协议中存在三个角色,分别是:

  • 客户端(Client):发送请求的一方
  • 服务端(Server):接收请求的一方
  • 密钥分发中心(Key distribution KDC)

密钥分发中心又分为两个部分,分别是:

  • AS(Authentication Server):认证服务器,专门用来认证客户端的身份并发放客户用于访问TGS的TGT(票据授予票据)
  • TGS(Ticket Granting ticket):票据授予服务器,用来发放整个认证过程以及客户端访问服务端时所需的服务授予票据(ticket)

Kerberos认证过程简化描述如下:

客户端在访问每个想要访问的网络服务时,他需要携带一个专门用于访问该服务并且能够证明自己身份的票据,当服务端收到了该票据他才能认定客户端身份正确,向客户端提供服务。所以整个流程可简化为两大步

(1)客户端向KDC请求获取想要访问的目标服务的服务授予票据(Ticket);
(2)客户端拿着从KDC获取的服务授予票据(Ticket)访问相应的网络服务;

简化认证流程图如下:

四、Kerberos认证流程

上面写了简化版的Kerberos认证流程,大致分为两步,虽然只有两步,但是其中还是存在一些问题的,就是:

(1)	KDC怎么知道你(客户端)就是真正的客户端?凭什么给你发放服务授予票据(Ticket)呢?
(2)	服务端怎么知道你带来的服务授予票据(ticket)就是一张真正的票据呢?

到这里就需要我们细节性的描述一下整个Kerberos认证的过程了~

上面提到这个流程可以简化为两大步,但其实在第一步中共做了两件事,这两件事解决了上述问题中的问题1;然后第二步解决了问题2; 最终结束认证过程建立通信。

所以整个Kerberos认证流程可以细化为三个阶段也可以理解为三次通信! 接下来从三个阶段三次通信的角度细说认证过程。

在具体描述整个认证流程之前,我们需要知道几个Kerberos认证的前提条件:

1、 Kerberos协议他是一个“限权”的认证协议,Kerberos中会自带一个数据库,这个数据库会由创建Kerberos的运维人员提前在库中添加好整个系统中拥有使用Kerberos认证权限的用户和网络服务。在后续的认证中也是根据数据库中是否存在该用户和服务来判断该对象是否能够通过认证服务的;

2、 所有使用Kerberos协议的用户和网络服务,在他们添加进Kerberos系统中时,都会根据自己当前的密码(用户密码,人为对网络服务随机生成的密码)生成一把密钥存储在Kerberos数据库中,且Kerberos数据库也会同时保存用户的基本信息(例如: 用户名,用户IP地址等)和网络服务的基本信息(IP,Server Name);

3、 Kerberos中存在的三个角色,只要是发生了两两之间的通信,那么都需要先进行身份认证;

第一次通信

为了获得能够用来访问服务端服务的票据,客户端首先需要来到KDC获得服务授予票据(Ticket)。由于客户端是第一次访问KDC,此时KDC也不确定该客户端的身份,所以第一次通信的目的为KDC认证客户端身份,确认客户端是一个可靠且拥有访问KDC权限的客户端,过程如下:

1、 客户端用户向KDC以明文的方式发起请求,该次请求中携带了自己的用户名主机IP、和当前时间戳

2、 KDC当中的AS(Authentication Server)接收请求(AS是KDC中专门用来认证客户端身份的认证服务器)后去Kerberos认证数据库中根据用户名查找是否存在该用户,此时只会查找是否有相同用户名的用户,并不会判断身份的可靠性;

3、 如果没有该用户名,认证失败,服务结束;如果存在该用户名,则AS认证中心便认为用户存在,此时便会返回给客户端,其中包含两部分内容:

(1) 第一部分内容称为TGT,它叫做票据授予票据,客户端需要使用TGT去KDC中的TGS(票据授予中心)获取访问网络服务所需的Ticket(服务授予票据),TGT中包含的内容有Kerberos数据库中存在的该客户端的NameIP当前时间戳客户端即将访问的TGS的NameTGT的有效时间以及一把用于客户端和TGS间进行通信的Session_key(CT_SK)整个TGT使用TGS密钥加密,客户端是解密不了的,由于密钥从没有在网络中传输过,所以也不存在密钥被劫持破解的情况。

(2) 第二部分内容是使用客户端密钥加密的一段内容,其中包括用于客户端和TGS间通信的Session_key(CT_SK),客户端即将访问的TGS的Name以及TGT的有效时间,和一个当前时间戳。该部分内容使用客户端密钥加密,所以客户端在拿到该部分内容时可以通过自己的密钥解密。如果是一个假的客户端,那么他是不会拥有真正客户端的密钥的,因为该密钥也从没在网络中进行传输过。这也同时认证了客户端的身份,如果是假客户端会由于解密失败从而中断认证流程。

至此,第一次通信完成!!!


第二次通信

此时的客户端收到了来自KDC(其实是AS)的响应,并获取到了其中的两部分内容。客户端会用自己的密钥将第二部分内容进行解密,分别获得时间戳自己将要访问的TGS的信息,和用于与TGS通信时的密钥CT_SK
首先它会根据时间戳判断该时间戳与自己发送请求时的时间的差值是否大于5分钟,如果大于5分钟则认为该AS是伪造的,认证至此失败。如果时间戳合理,客户端便准备向TGS发起请求,其次请求的目的是为了获取能够访问目标网络服务的服务授予票据(Ticket)
在第二次通信请求中,客户端将携带三部分内容交给KDC中的TGS,第二次通信过程具体如下所述:

客户端行为:

1、 客户端使用CT_SK加密将自己的客户端信息发送给KDC,其中包括客户端名IP时间戳
2、 客户端将自己想要访问的Server服务以明文的方式发送给KDC;
3、 客户端将使用TGS密钥加密的TGT也原封不动的也携带给KDC;

TGS行为:

1、 此时KDC中的TGS(票据授予服务器)收到了来自客户端的请求。它首先根据客户端明文传输过来的Server服务IP查看当前Kerberos系统中是否存在可以被用户访问的该服务。如果不存在,认证失败结束。如果存在,则继续接下来的认证。

2、 TGS使用自己的密钥将TGT中的内容进行解密,此时它看到了经过AS认证过后并记录的用户信息,一把Session_Key即CT_SK,还有时间戳信息,他会根据时间戳判断此次通信是否真是可靠有无超出时延。

3、 如果时延正常,则TGS会使用CK_SK对客户端的第一部分内容进行解密(使用CT_SK加密的客户端信息),取出其中的用户信息和TGT中的用户信息进行比对,如果全部相同则认为客户端身份正确,方可继续进行下一步。

4、 此时KDC将返回响应给客户端,响应内容包括:
(1) 第一部分:用于客户端访问网络服务使用Server密码加密的ST(Server Ticket),其中包括客户端的NameIP需要访问的网络服务的地址Server IPST的有效时间时间戳以及用于客户端和服务端之间通信的CS_SK(Session Key)
(2) 第二部分:使用CT_SK加密的内容,其中包括CS_SK时间戳,还有ST的有效时间。由于在第一次通信的过程中,AS已将CT_SK通过客户端密码加密交给了客户端,且客户端解密并缓存了CT_SK,所以该部分内容在客户端接收到时是可以自己解密的。

至此,第二次通信完成!!!


第三次通信

此时的客户端收到了来自KDC(TGS)的响应,并使用缓存在本地的CT_SK解密了第二部分内容(第一部分内容中的ST是由Server密码加密的,客户端无法解密),检查时间戳无误后取出其中的CS_SK准备向服务端发起最后的请求。


客户端:

(1) 客户端使用CK_SK将自己的主机信息和时间戳进行加密作为交给服务端的第一部分内容,然后将ST(服务授予票据)作为第二部分内容都发送给服务端。

服务端:

(1) 服务器此时收到了来自客户端的请求,它会使用自己的密钥,即Server密钥将客户端第二部分内容进行解密,核对时间戳之后将其中的CS_SK取出,使用CS_SK将客户端发来的第一部分内容进行解密,从而获得经过TGS认证过后的客户端信息,此时它将这部分信息和客户端第二部分内容带来的自己的信息进行比对,最终确认该客户端就是经过KDC认证的具有真实身份的客户端,是它可以提供服务的客户端。此时服务端返回一段使用CT_SK加密的表示接收请求的响应给客户端,在客户端收到请求之后,使用缓存在本地的CS_ST解密之后也确定了服务端的身份(其实服务端在通信的过程中还会使用数字证书证明自己身份)。

至此,第三次通信完成。此时也代表着整个Kerberos认证的完成,通信的双方都确认了对方的身份,此时便可以进行整个网络的通信了。

将以上内容进行总结概述就是整个Kerberos认证的过程较为复杂,三次通信中都使用了密钥,且密钥的种类一直在变化,并且为了防止网络拦截密钥,这些密钥都是临时生成的Session key,即它们只在一次Session会话中起作用,即使密钥被劫持,等到密钥被破解可能这次会话都早已结束。这为整个Kerberos认证过程保证了较高的安全性。


五、Kerberos认证图谱

Kerberos认证整体流程图

Kerberos认证时序图

六、Kerberos攻击方式


1、 AS_REQ阶段(三种手法)

(1) 哈希传递攻击(Pass The Hash)
在AS_REQ阶段,捕获用户的密码Hash来做身份验证,获取TGT,横向访问其他主机。 //使用场景:内网渗透中要横向移动,但获取不到明文口令。

(2) 域用户名枚举
AS_REQ的cname值,当域用户不存在时,返回包提示错误

(3) 密码喷洒攻击(Password Sparying)
并且当用户名存在,密码正确和错误时,返回包也不一样,所以可以进行用户名密码爆破,是用固定的密码去跑用户名。

域用户名枚举以及密码喷洒攻击和Web爆破原理类似,根据响应包返回码来判断是否爆破成功!如下图所示:

2、 AS_REP阶段(两种手法)

(1) 黄金票据

针对黄金票据的攻击发生在这一阶段。因为TGT黄金票据是由krbtgt用户的Hash加密的,所以如果我们获取了这个Hash,就可以用Mimikatz伪造任意TGT(可自己制作一个TGT认购权证)。总共需要以下信息:

  • 要伪造的域用户(名称、Hash),一般是域管理员;
  • 域名;
  • 域的SID;
  • krbtgt用户的Hash;

黄金、白银票据主要为了维持权限,尤其是黄金票据,是后门的万能钥匙

此类攻击方法被称为票据传递攻击(Pass The Ticket)

(2) AS_REP Roasting攻击

AS_REP阶段,最外层的enc-part是用用户密码Hash加密的。对于域用户,如果设置了选项 “Do not require Kerberos preauthentication” ,此时向域控器的88端口发送AS_REQ请求,对收到的AS_REP内容(enc-part底下的ciper,因为这部分是使用用户的Hash加密的Login Session Key,我们通过进行离线爆破就可以获得用户Hash)重新组合,能够拼接成 “Kerberos 5 AS_REP etype 23”(18200) 的格式,接下来可以使用hashcat对其破解。

3、 TGS_REQ&TGS_REP阶段(五种手法)

TGS_REP阶段(两种手法)

(1) Kerberoast攻击
在TGS_REP阶段,由于enc-part是用服务密码Hash加密的,所以我们可以通过爆破获得服务的Hash。
域上的任何经过身份验证的用户,只要TGT没问题,就能够从TGS处根据SPN请求ST,然后进行暴力破解。SPN格式:SERVICE/host.name。可以用Windows自带的SetSPN.exe查询SPN, 命令为setspn -Q */*。之后,可以用mimikatz指定服务目标、导出ST,用tgsrepcrack破解,相关工具有很多。

(2) 白银票据
在TGS_REP阶段,TGS_REP里面的ticket的enc-part是使用服务的Hash进行加密的,如果我们拥有服务的Hash,就可以给我们自己签发任意用户的TGS票据,这个票据也称为白银票据。相较于黄金票据,白银票据使用要访问服务的Hash,而不是krbtgt的Hash,由于生成的是TGS票据,不需要跟域控打交道,但是白银票据只能访问特定服务。但是要注意一点的是,伪造的白银票据没有带有有效KDC签名的PAC。如果将目标主机配置为验证KDC PAC签名,则银票将不起作用。

TGS生成的ST虽然只能访问特定的服务,但不依赖KDC,所以相比伪造黄金票据动静更小。ST用AP的Hash加密,如果获取了这个Hash,就可以伪造ST,进而跳过KDC的认证,直接和AP通信。

总共需要以下信息:

  • 域用户名称、Hash;
  • 域名;
  • 域的SID;
  • 目标AP的服务名、NTLM Hash等;

其实这也属于票据传递攻击!!!

S4U阶段(三种手法)

  • 非约束委派攻击
  • 约束委派攻击
  • 基于资源的约束委派攻击

4、 PAC阶段(MS14-068)

PAC介绍

在Kerberos最初设计的几个流程里说明了如何证明Client是Client而不是由其他人来冒充的但并没有声明Client有没有访问Server服务的权限因为在域中不同权限的用户能够访问的资源是有区别的。

所以Microsoft为了解决这个问题在实现Kerberos时加入了PAC的概念,PAC全称是(Privilege Attribute Certificate 特权属性证书)

PAC可以理解为一串校验信息为了防止被伪造和篡改原则上是存放在TGT里并且TGT由KDC Hash加密。同时尾部会有两个数字签名分别由KDC密码和Server密码加密防止数字签名内容被篡改。

MS14-068实战攻击参考文章:点击跳转到文章


七、Kerberos词汇扩展

相关文章
https://cloud.tencent.com/developer/article/1850703
https://seevae.github.io/2020/09/12/%E8%AF%A6%E8%A7%A3kerberos%E8%AE%A4%E8%AF%81%E6%B5%81%E7%A8%8B/

Kerberos 密码更改协议

【中文标题】Kerberos 密码更改协议【英文标题】:Kerberos password change protocol 【发布时间】:2012-12-13 09:11:59 【问题描述】:

我正在尝试使用RFC 3244 中的 Kerberos 密码协议,从 C++/CLI 库(将由另一个服务调用)对 ActiveDirectory 中的用户执行大量密码操作。

我使用 LogonUser 模拟了 C# 中的序列(这是我的首选语言)来模拟管理员,然后使用 DirectoryServices.AccountManagement 命名空间在用户条目上调用 SetPassword。 SetPassword 使用三种方法来尝试更改 LDAPS、Kerberos 和 RPC。如果我查看 Wireshark 跟踪,我可以看到模拟管理员时的 Kerberos 握手,然后是 LDAP 尝试(由于缺少 SSL 而失败),然后是 kerberos 密码交换。

尝试复制这是 C++ LogonUser 不会启动 Kerberos 交换,因此当调用 SetPassword 时,该方法会通过 RPC(成功但不满足我们使用 Kerberos 的要求)。

有没有办法强制使用 Kerberos?

有没有更好的解决方案从 .net 与 Kerberos 密码协议进行交互,而不是依赖于 SetPassword?

最小代码示例:

C#

SafeTokenHandle handle;
LogonUser("serviceAccount", "Test", "aPassw0rd", 2, 0, out handle);
WindowsIdentity.Impersonate(handle.DangerousGetHandle());
DirectoryEntry usr = new DirectoryEntry();
usr.Path = "LDAP://"+"dctest.test.com/"+"CN=testuser,CN=Users,DC=test,DC=com";
usr.AuthenticationType = AuthenticationTypes.Sealing | AuthenticationTypes.Secure;
object ret = usr.Invoke("SetPassword", "aPassw0rd");

usr.CommitChanges();
usr.Close();
Console.WriteLine("Completed");

此方法成功模拟服务帐户,然后使用 KPASSWD over 464 执行 setpassword。

C++/CLI

HANDLE _handle;
LogonUser(L"serviceAccount",L"Test",L"aPassw0rd",LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT,&_handle)
ImpersonateLoggedOnUser(_handle);
DirectoryEntry^ usr = gcnew DirectoryEntry();
usr->Path = "LDAP://"+"dctest.test.com/"+"CN=testuser,CN=Users,DC=test,DC=com";
usr->AuthenticationType = AuthenticationTypes::Sealing | AuthenticationTypes::Secure;
Object^ ret = usr->Invoke("SetPassword", "aPassw0rd");

usr->CommitChanges();
usr->Close();
Console::WriteLine("Completed");

这种方法模拟管理员帐户,然后在调用设置密码时执行 kerberos 交换(超过 88,所以我猜这是 auth),但随后回退到使用 RPC。

如果我获取 C# 代码并从 C++/CLI 包装器中调用它,则行为将更改为代码在 C++ 中时所显示的行为。

【问题讨论】:

澄清一下,.NET 确实使用了 Kerberos(所以这是一个成功的用例)但 C++ 没有? 是的,没错,我在上面添加了一个最小的代码示例。 【参考方案1】:

前几天终于设法通过反射器运行输出来追踪这一点。 C++/CLI 类的项目属性包括将字符集设置为 unicode。使用此设置,输出使用 LogonUserW 方法。但是,如果此设置更改为“未设置”,则会使用 LogonUser 方法,并且 setPassword 的 Kerberos 身份验证路径将正常运行。

【讨论】:

以上是关于Kerberos协议详解的主要内容,如果未能解决你的问题,请参考以下文章

kerberos 配置解决常见问题

Kerberos简介

Kerberos协议原理

Kerberos 密码更改协议

Kerberos介绍

Kerberos协议