导语:本文介绍了作者自己完成的c#工具Rubeus的更多功能。

Rubeus是具备@gentilkiwiKekeo工具集部分功能的C#版本,1.1.0版本中已经有了一些新的更新,1.2.0版本中又有了另一个新功能。本文将介绍其主要的新功能以及其他一些更改,并深入探讨酷炫的新功能——基于密码更改的假代理TGT和Kerberos。

我想强调@gentilkiwi是这些技术的创始人,而本项目只是他工作的重新实现。我将重新实现Kekeo的部分功能。并且会尽力解释tgt :: deleg / tgtdeleg和misc :: changepw / changepw函数(Kekeo和Rubeus)的内幕,以便每个人都了解Benjamin所实现的内容。

首先来看一些背景知识,这有助于澄清一些事情。

一、从TGTs到.kirbis

正如大多数人传统上所理解的那样,在Kerberos交换中,使用哈希(ntlm / rc4_hmac,aes128_cts_hmac,aes256_cts_hmac .etc)从域控制器(也称为KDC,密钥分发中心)获取票据授予票证。Kerberos交换中涉及对KDC / DC进行身份验证的AS-REQ(身份验证服务请求),如果成功,则会生成AS-REP(身份验证服务响应),其中包含TGT。但是,这并不是它所包含的全部内容。

注释:TGT本身没用。TGT是使用Kerberos服务(krbtgt)哈希加密/签名的不透明blob,因此作为普通用户是无法解码的。那么TGT实际上是如何使用的呢?

在验证用户(AS-REQ)时,TGT并不是AS-REP中返回的唯一数据块。还有一个“enc-part”,它是一个标记的EncKDCRepPart结构,使用用户的哈希加密。所使用的哈希(rc4_hmac,aes256_cts_hmac_sha1等)在初始交换期间进行协商。解密此blob,其中包含一组元数据,包括启动时间、结束时间和票据的续订,但最重要的是还包含一个会话密钥,此会话密钥也包含在不透明的TGT blob中(它再次用krbtgt哈希加密)。

那么用户/机器如何“使用”TGT?

与TGT一起提供了使用会话密钥加密的Authenticator,证明客户端知道在初始认证交换中返回的会话密钥(包含在TGT中)。这是TGT续订,服务票据请求,S4U请求等所必需的。

所有这些数据都包含在KRB-CRED结构中,也就是Mimikatz中的.kirbi文件,这表明可通过已建立的LSA API提交的完整Kerberos凭证重现编码结构。因此,当我们谈论“TGT”时,实际上意味着可用的TGT .kirbi文件(包含明文会话密钥),而不仅仅是TGT blob。这是一个重要的区别,我们将更深入的介绍一下。

此外,我想快速介绍一下从权限提升(高完整性)和非提升中提取Kerberos票据的差异。Rubeus.exe dump命令将采取适当的方法,具体取决于Rubeus正在执行的完整性级别。

如果权限高,一般方法为:

1.提升到SYSTEM权限。

2.使用LsaRegisterLogonProcess注册虚假登录进程,返回LSA服务器的特权句柄。

3.使用LsaEnumerateLogonSessions枚举当前登录会话。

4.对于每个登录会话,构建一个KERB_QUERY_TKT_CACHE_REQUEST结构,指定登录会话的ID和KerbQueryTicketCacheMessage的消息类型。这将返回有关指定用户登录会话的所有缓存Kerberos票据的信息。

5.使用KERB_QUERY_TKT_CACHE_REQUEST调用LsaCallAuthenticationPackage并解析生成的票据缓存信息。

6.对于来自缓存的每一条票据信息,构建一个KERB_RETRIEVE_TKT_REQUEST结构,其消息类型为KerbRetrieveEncodedTicketMessage。这表明想要从缓存中为特票据证编码KRB-CRED(.kirbi)blob。 PS-在C#中弄清楚这有点烦人;

7.使用KERB_RETRIEVE_TKT_REQUEST调用LsaCallAuthenticationPackage并解析生成的票据信息.kirbi。

这将返回当前系统上所有用户的TGT和服务票据的完整.kirbi blob,而无需打开LSASS的读取句柄。或者可以使用Mimikatz'sekurlsa :: tickets / export命令直接从LSASS的内存中导出所有Kerberos票据,但请记住,这不是唯一的方法。

如果权限不够,则方法略有不同:

1.使用LsaConnectUntrusted打开与LSA的不可信连接。

2.使用消息类型KerbQueryTicketCacheMessage构建KERB_QUERY_TKT_CACHE_REQUEST,返回有关当前用户的登录会话的所有缓存Kerberos票据的信息。

3.使用KERB_QUERY_TKT_CACHE_REQUEST调用LsaCallAuthenticationPackage并解析生成的票据缓存信息。

4.对于来自缓存的每一条票据信息,构建一个KERB_RETRIEVE_TKT_REQUEST结构,其消息类型为KerbRetrieveEncodedTicketMessage。这表明我们想要从缓存中为特定票据编码KRB-CRED(.kirbi)blob。

5.使用KERB_RETRIEVE_TKT_REQUEST调用LsaCallAuthenticationPackage并解析生成的票据信息.kirbi。

如果未提升权限,则从逻辑上仅可以查询当前登录会话中的票据。此外,在Win7 +中,Windows在从userland查询时限制了TGT会话密钥的检索,因此在转储TGT时会得到类似的东西:

这意味着如果没有提升权限,则无法为当前用户提取可用的TGT .kirbis,因为所需的会话密钥为空。此外,如上面的Mimikatz输出中所述,Microsoft引入了一个允许返回TGT会话密钥的注册表项(allowtgtsessionkey)。但是,默认情况下并未启用,并且需要更高权限。

后面的tgtdeleg一章解释了Benjamin绕过这个限制的诀窍。

但是,会为服务票据返回会话密钥。这在以后很重要。

二、asktgs

第一个“大”的新功能是通用服务票据请求:

Rubeus.exe asktgs </ticket:BASE64 | /ticket:FILE.KIRBI> </service:SPN1,SPN2,...> [/dc:DOMAIN_CONTROLLER] [/ptt]

asktgs与asktgt接受相同的/ dc:X / ptt参数。 / ticket:X接受相同base64编码的.kirbi文件或磁盘上.kirbi文件的路径。此票据需要是TGT的.kirbi(包括会话密钥,如前所述),因此可以在TGS-REQ / TGS-REP交换中正确请求服务票据。

/ service:SPN参数是必需的,并指定要为其提供服务票据的服务主体名称(SPN)。此参数接受一个或多个常见的分隔SPN,因此以下内容将起作用:

C:\Temp\tickets>Rubeus.exe asktgt /user:harmj0y /rc4:2b576acbe6bcfda7294d6bd18041b8fe

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

  v1.0.0
  
[*] Action: Ask TGT

[*] Using rc4_hmac hash: 2b576acbe6bcfda7294d6bd18041b8fe
[*] Using domain controller: PRIMARY.testlab.local (192.168.52.100)
[*] Building AS-REQ (w/ preauth) for: 'testlab.local\harmj0y'
[*] Connecting to 192.168.52.100:88
[*] Sent 232 bytes
[*] Received 1405 bytes
[+] TGT request successful!
[*] base64(ticket.kirbi):

      doIFFjCCBRKgAwIBBa...(snip)...
      
C:\Temp\tickets>Rubeus.exe asktgs /ticket:doIFFjCCBRKgAwIBBa...(snip...)== /service:LDAP/primary.testlab.local,cifs/primary.testlab.local /ptt

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

    v1.0.0
    
[*] Action: Ask TGS

[*] Using domain controller: PRIMARY.testlab.local (192.168.52.100)
[*] Building TGS-REQ request for: 'LDAP/primary.testlab.local'
[*] Connecting to 192.168.52.100:88
[*] Sent 1384 bytes
[*] Received 1430 bytes
[+] TGS request successful!
[*] base64(ticket.kirbi):

      doIFSjCCBUagAwIBBaEDA...(snip)...
      
[*] Action: Import Ticket
[+] Ticket successfully imported!

[*] Action: Ask TGS

[*] Using domain controller: PRIMARY.testlab.local (192.168.52.100)
[*] Building TGS-REQ request for: 'cifs/primary.testlab.local'
[*] Connecting to 192.168.52.100:88
[*] Sent 1384 bytes
[*] Received 1430 bytes
[+] TGS request successful!
[*] base64(ticket.kirbi):

      doIFSjCCBUagAwIBBaEDAgE...(snip)...
      
[*] Action: Import Ticket
[+] Ticket successfully imported!

C:\Temp\tickets>C:\Windows\System32\klist.exe tickets

Current LogonId is 0:0x570ba

Cached Tickets: (2)

#0>     Client: harmj0y @ TESTLAB.LOCAL
        Server: cifs/primary.testlab.local @ TESTLAB.LOCAL
        KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
        Ticket Flags 0x40a50000 -> forwardable renewable pre_authent ok_as_delegate name_canonicalize
        Start Time: 9/30/2018 18:17:55 (local)
        End Time:   9/30/2018 23:17:01 (local)
        Renew Time: 10/7/2018 18:17:01 (local)
        Session Key Type: AES-128-CTS-HMAC-SHA1-96
        Cache Flags: 0
        Kdc Called:
        
#1>     Client: harmj0y @ TESTLAB.LOCAL
        Server: LDAP/primary.testlab.local @ TESTLAB.LOCAL
        KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
        Ticket Flags 0x40a50000 -> forwardable renewable pre_authent ok_as_delegate name_canonicalize
        Start Time: 9/30/2018 18:17:55 (local)
        End Time:   9/30/2018 23:17:01 (local)
        Renew Time: 10/7/2018 18:17:01 (local)
        Session Key Type: AES-128-CTS-HMAC-SHA1-96
        Cache Flags: 0
        Kdc Called:

如果没有提升权限,也不想通过应用上周描述的新TGT来stomp现有登录会话的TGT,则可以请求给定帐户的TGT并使用此blob与asktgs请求/应用需要的服务票据。

有关服务票据接管的更多信息,请参阅Sean Metcalf的“How Attackers Use Kerberos Silver Tickets to Exploit Systems”帖子中的“Service to Silver Ticket Reference”。

三、tgtdeleg

tgtdeleg功能是Kekeo的tgt ::deleg功能的重新编码,允许从当前用户中提取可用的TGT .kirbi,而无需提升系统权限。这是Benjamin发现的一个非常酷的技巧,我将尝试详细解释,最后介绍操作用例。

有一个称为通用安全服务应用程序接口(GSS-API)的东西,它是应用程序用来与安全服务交互的通用API。虽然Microsoft没有正式支持GSS-API,但它确实实现了与Kerberos GSS-API兼容的Kerberos安全服务提供程序接口(SSPI),这意味着它应该支持所有常见的Kerberos GSS-API结构/方法。我们将在本文中使用RFC4121作为参考。

基本上,可以使用Windows API来请求委托TGT,此TGT通过SSPI / GSS-API发送到远程主机/ SPN。这些结构中包含一个当前用户的转发TGT,它使用了用KRB-CRED(.kirbi)结构,该结构在AP-REQ内被加密并发送到目标服务器。用于加密Authenticator / KRB-CRED的会话密钥包含在目标SPN的可访问的缓存服务票据中。将这些结合在一起,就能为当前用户提取这个可用的TGT而无需任何权限提升!

首先,使用AcquireCredentialsHandle来获取当前用户现有Kerberos票据的句柄。指定fCredentialUse参数为SECPKG_CRED_OUTBOUND,该参数将“允许本地客户端凭证准备一个传出令牌”。

然后使用InitializeSecurityContext从AcquireCredentialsHandle返回的票据句柄中启动“客户端,出站安全上下文”。此处的技巧是为fContextReq参数指定ISC_REQ_DELEGATE和ISC_REQ_MUTUAL_AUTH标志。请求委托TGT,意思是服务器可以使用上下文作为客户端向其他服务器进行身份验证。我们还为具有无约束委派(默认为HOST / DC.domain.com)的服务器指定SPN。这是我们假装准备委派请求的SPN /服务器。

那么当触发这个API调用时会发生什么?

首先,进行TGS-REQ / TGS-REP交换以请求我们假装委托的SPN的服务票据。这样就可以在目标服务器和我们正在通信的机器之间建立共享会话密钥。此服务票据存储在本地Kerberos缓存中,这意味着我们以后可以提取共享会话密钥。

接下来,为当前用户请求转发的TGT。有关转发票据的更多信息,请参阅此处(section here)的“What are forwarded tickets?”部分。KDC将使用当前TGT的单独会话密钥返回新TGT。然后,系统使用该转发的TGT为目标服务器构建AP-REQ,其请求中的Authenticator包含转发TGT的可用KRB-CRED编码。这将在RFC4121的4.1.1 Authenticator Checksum中解释:

如果一切都成功,我们将得到传递给InitializeSecurityContext的pOutput指针所指向的SSPI SecBufferstructure中编码的AP-REQ(包括新TGT的.kirbi)。我们可以在输出流中搜索KerberosV5 OID,并从GSS-API输出中提取AP-REQ。

然后,从缓存中提取服务票据会话密钥,并使用它来解密从AP-REQ中提取的Authenticator。最后,从Authenticator校验和中提取编码的KRB-CRED,并将其输出为可用的TGT .kirbi:

成功!

从操作的角度来看,这是一个小众特色。主要情况是,在一个至少有一台无法提升权限的机器的环境中你有多个客户端。在这台机器上,可以使用Rubeus的tgtdeleg提取当前用户的TGT,并将传递给另一台机器上运行的Rubeus renew函数。这将允许在没有提升权限的情况下提取用户凭证,并将其保留在另一个系统上以供重复使用,有限期最多7天(默认情况下)。

无论这个TTP是否非常有用,理解和重新编码都有很多乐趣:)

四、changepw

changepw(Kekeo中的misc :: changepw)实现了@Aorato POC,允许攻击者从TGT .kirbi中更改用户的明文密码(不知道之前的值)。将此与asktgt以及用户的rc4_hmac / aes128_cts_hmac_sha1 / aes256_cts_hmac_sha1哈希相结合,意味着攻击者可以轻松的强制重置用户的纯文本密码。或者,如果使用Rubeus dump命令(来自权限提升的上下文),攻击者只能通过LSA API强制重置用户密码。

解释此过程的RFC是RFC3244(Microsoft Windows 2000 Kerberos更改密码和设置密码协议)。下面是发送到域控制器上端口464(kpasswd)的图表:

有两个主要的部分:AP-REQ以及特殊构造的KRB-PRIVASN.1结构。AP-REQ消息包含用户的TGT blob,以及使用来自TGT .kirbi的TGT会话密钥加密的Authenticator。Authenticator必须具有随机的子会话密钥集,用于加密KRB-PRIV结构。KRB-PRIV包含新的明文密码,序列/随机数和发件人的主机地址(可以是任何东西。)

如果密码设置成功,则返回KRB-PRIV结构,结果代码为0(KRB5_KPASSWD_SUCCESS)。错误反映在KRB-ERROR或其他错误代码中(在RFC3244的第2节末尾指定)。

注意:我不确定原因,但使用tgtdeleg技巧获取的票据不能与此changepw方法一起使用,会返回一个KRB5_KPASSWD_MALFORMED错误。我用Rubeus和Kekeo测试了这个,均返回相同的结果¯\ _ _(ツ)_ /¯

五、其它改动

其他更改/修复:

· s4u操作现在接受多个备用snames(/altservice:X,Y,…)

只执行一次S4U2self / S4U2proxy进程,并将多个备用服务名称替换为最终生成的服务票据结构,获得指定数量的sname。

· 修正了kerberoast的哈希输出中的encType,来自@machosec的KerberosRequestorSecurityToken.GetRequest方法。

· 修复了asreproast哈希的salt分界线,添加了最终的Hashcat哈希输出格式。

· 修复了dump操作中的一个 bug – 完整的ServiceName / TargetName字符串,现在已正确提取。

· 添加了一个基于CHANGELOG.md的Keep a Changelog来跟踪当前和未来的变化。

源链接

Hacking more

...