作者:三体虫子
预估稿费:400
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
前言
域同步元数据是域内对象的部分属性合集,当域服务器之间需要同步时,服务器通过元数据监视哪些数据发生了变更,并告知其他服务器哪些数据需要同步。元数据包含了大量的信息,为安全监管提供一种有效的手段,快速跟踪域森林中哪些对象属性发生了变更、变更发生在域森林中哪个域服务器、变更的历史记录及大概时间,配合域和其他安全产品的安全日志,还原、取证恶意行为。
使用该方式可以检测对抗Black Hat 2017中谈到的基于ACL的隐蔽后门方式。
0x00 测试环境
图中1为森林的根服务器,域名为adsec.com,服务器安装Windows Server 2016 Standard英文桌面版,机器名为Win2016-DC01;本文的大部分实验在该服务器上进行。4为adsec.com森林域的信任域的第一服务器,域名为testlab.com,安装Windows Server 2008 R2中文版。
0x01 元数据的基础知识
域同步元数据是域内对象的部分属性合集,可以使用PowerShell代码查看元数据集合中具体有哪些属性,这些属性决定了基于元数据可以进行哪些安全监管和检测。查询代码如下:
Get-ADObject -SearchBase "CN=Administrator,CN=Users, DC=adsec,DC=com" -Filter * -SearchScope Subtree -Properties * | Select-Object msDS-ReplAttributeMetaData
上面的查询代码如果Properties参数不指定查询msDS-ReplAttributeMetaData,则结果为空,如果明确指出查询msDS-ReplAttributeMetaData时,有详细结果,如下图:
所有对象的复制元数据格式类似,这里举Administrator这个用户对象为例进行说明。从上图中可看出查询结果为XML格式化数据,格式化数据的含义如下:
pszAttributeName ,属性名;
dwVersion ,变更次数,逐次累加,即使该属性复原,这个数也会累加;
ftimeLastOriginatingChange,上次被修改的时间戳;
uuidLastOriginatingDsaInvocationID ,属性发生变更的所在域服务器的InvocationID,可以通过系统自带的复制管理工具repadmin /showrepl <ServerName>来查看域服务器的InvocationID,如下图:
pszLastOriginatingDsaDN,即 NTDS object所在域服务器。
元数据包括两类,分别由msDS-ReplAttributeMetaData查询和msDS-ReplValueMetaData查询,前者是具体的属性,例如登录时间、口令修改时间等;后者是链接类属性,最典型的链接类属性是用户组和组成员的关系,一个组包含组成员(member),一个对象是组的成员(member of),关于链接属性在后面有更具体的说明。
0x02 检测管理员组成员变更
应用场景:某用户被临时加入管理员组,使用该用户做了一些事情,事后该用户将自己挪出管理员组;域服务器已开启相应的审计策略。希望基于元数据追踪哪个用户发生了权限变更、什么时间、哪个域服务器、在哪台主机和由谁发起变更。
域服务器已开启安全日志,并有相应的第三方产品及相应的策略严格监控管理员组,一旦发生此类情况,则会当场被检测出,第三方产品有SPLUNK等,不过价格昂贵。如果服务器已开启安全日志,但没有第三方产品及策略严格监控管理员组的变更,则只能是事后对该种行为进行还原、定位和取证。
系统日志默认不会审计组成员的变化,需要在组策略中开启,具体位置如下:
Local Computer Policy -> Computer Configuration -> Windows Settings -> Security Settings -> Advanced Audit Policy Configuration -> Account Management -> Audit Security Group Management
将某个用户加入某个组时,不需要具备对该用户的任何权限,只需要具备对该组的权限即可,该组会添加一个到用户的链接属性,域服务器需要维持、跟踪这些链接。系统会自动根据这个链接属性,为用户计算出一个只读的反向链接属性(Back Link),方便用户信息的查询,这个属性在域之间不需要复制,因为可以通过计算得出。
链接类属性由msDS-ReplValueMetaData表示,有不同的查询方式。使用repadmin工具的命令和对应的结果如下:
repadmin /showobjmeta “win2016-dc01” CN=Administrators,CN=Builtin, DC=adsec,DC=com
上图的member属性,即链接类属性,圈红的为PRESENT或者ABSENET,三种状态之一,其含义分别如下:
PRESENT,对象目前是组成员,如图中的administrator用户,当前隶属于administrators组;
LEGACY ,表示对象在林功能等级升级至2003以前就是组成员,说明域里面层级是操作系统版本比较低的服务器,例如Windows 2000或者NT等等,没有具体作用;
ABSENT ,对象曾经是组成员,但目前不再是组成员,如图中的win10x64user用户。
Ver这一栏,表示用户与该组的隶属关系发生了几次变更,起始值为1,每变动一次累加1,所以偶数表示曾经是但目前不是,奇数表示目前是成员。
ABSENT表示用户已经不再隶属于组,这个状态会维护多久?这个时间由tombstoneLifeTime决定,这个值在这里可以查询:CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration, DC=adsec,DC=com(ADSI的configuration模式)的属性中有tombstoneLifeTime,如果没有设置,2003系统默认是60天,更高的系统默认是180天。意味着事后追踪往前的最长时间为180天。
使用PowerShell提供的ActiveDirectory模块同样可以获取链接属性,具体脚本和结果截图如下:
Get-ADObject -SearchBase "CN=Administrators,CN=Builtin,DC=adsec,DC=com" -Filter * -SearchScope Subtree -Properties msDS-ReplValueMetaData
pszAttributeName ,属性名,为member,即组成员属性;
pszObjectDN,DistinguishedName,例如CN=win10x64user,CN=Users,DC=adsec,DC=com;
ftimeDeleted,成员被删除出组的时间;
ftimeCreated ,成员被加入组的时间;
dwVersion ,成员的变更次数,起始值为1,每变动一次累加1;
ftimeLastOriginatingChange ,成员链接的最后一次变更时间;
uuidLastOriginatingDsaInvocationID ,变更发生的所在域服务器的InvocationID ;
pszLastOriginatingDsaDN,最后变更组的域服务器的NTDS的DN,例如CN=NTDS Settings,CN=WIN2016-DC01,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=adsec,DC=com。
基于这些属性,可以实现预设应用场景中想达到的追踪效果。下面以具体的例子演示如何进行追踪。
演示场景:普通域用户eviluser被加入域管理员组,eviluser自己退出管理员组。利用元数据追踪该事件。
上图表示当前eviluser已经不是管理员组成员,查看组成员的方式无法追踪哪个用户曾经进入过管理员组。
第一步,本文前面repadmin的代码可以查看不同管理员组的变化情况,但是一个大型的域往往有很多的特权组,逐个查看会比较麻烦,使用系统自带的Get-ADGroup会更方便。如下代码可以查看所有特权组的成员变化情况:
Get-ADGroup -LDAPFilter "(&(objectClass=group)(adminCount=1))" -Server Win2016-dc01 -Properties msDS-ReplValueMetaData
代码使用的过滤器是group,设置adminCount=1,表示将所有受adminSDHolder保护的特权组全纳入追踪范围。一般情况下,为了筛选数据,会选择查看某一个时间范围内的数据,以减少数据量,由于是示例演示,演示代码中不加入时间限制。
结果截图可以看出“Domain Admins”组的链接型元数据中有变更记录,可以获取具体时间和所在服务器的InvocationID。
第二步,根据InvocationID找到对应的域服务器,从服务器中分析安全日志。LogParser + LogParser Wizard GUI工具是非常方便的Windows日志分析软件。当组成员发生变更时,对应安全日志的Event ID分别是4728、4729。以4728、4729作为索引、时间作为筛选条件,在日志中查找相应的事件,可以获得日志记录编号。查询筛选结果如下图:
SELECT TOP 100 * FROM 'C:\Users\Administrator\Desktop\change.evtx' WHERE EventID=4728 or EventID=4729
第三步,以日志编号在系统日志中查询具体的事件,2个事件的详细信息截图分别如下:
第四步,从安全日志中可以清晰看出组成员变化事件的发起者为administrator,具体的组为domain admins,添加/删除的成员为eviluser,发生的时间,以及事件发生所在的计算机。演示在域服务器上执行,所以上图的Computer显示是Win2016-dc01,如果在被恶意控制的主机上执行事件,则会显示主机的机器名,可定位事件发生的具体执行位置。细心的读者可能会看到截图中事件的时间和上面PowerShell脚本的结果时间并不完全相同,这是因为时间格式不同。
0x03 检测用户对象ACL变更
应用场景:用户对象的ACL属性发生变更,例如赋予普通域用户对域管理员对象的ACL写权限,使得该普通用户随时可获取域管理员权限。基于元数据追踪哪个用户对象的ACL发生了变更、什么时间、在哪个服务器、哪个主机和谁发起变更事件。
ACL属性变更时,会反应在msDS-ReplAttributeMetaData中的ntSecurityDescriptor属性的时间和Ver上。使用PowerShell脚本可以获取指定用户对象的该属性。代码和结果截图如下:
repadmin /showobjmeta win2016-dc01 cn=administrator,cn=users,dc=adsec,dc=com
其中DSA可以查看事件发生所在的域服务器。获取所有用户ntSecurityDescriptor属性的代码及结果截图如下,执行结果后可以添加任意筛选策略:
Get-ADObject -LDAPFilter "(&(objectCategory=user)(sAMAccountName=*))" -SearchBase "cn=users,dc=adsec,dc=com" -SearchScope Subtree -Properties msDS-ReplAttributeMetaData,distinguishedname | ForEach-Object {
Write-Host "DN: $($_.DistinguishedName)"
$_."msDS-ReplAttributeMetaData" | ForEach-Object{
$_Metadata = [XML] $_.Replace("`0","")
$_Metadata.DS_REPL_ATTR_META_DATA | ForEach-Object {
If ( $_.pszAttributeName -eq "ntSecurityDescriptor" )
{
Write-Host "ntSecurityDescriptor last modification: $($_.ftimeLastOriginatingChange)"
}
}
}
}
从图中得知administrator对象的ntSecurityDescriptor发生了变更,此类事件在安全日志中的Event ID为4662。对事件的追踪、定位和取证流程和上一节类似。根据Event ID和时间从系统日志中查找对应的记录,截图如下:
0x04 检测口令策略的欺骗绕过
应用场景:域制定了口令策略,强制用户每30天修改一次口令。有人使用欺骗方式,始终不触发强制的口令策略,保持口令不变;有人频繁恶意重置口令(重置口令是攻击的重要手段),我们将利用元数据检测、追踪这2种恶意行为。
欺骗方式如下图,选择用户属性的“下次登录时修改口令”,点击应用,然后取消选择,点击确定,口令可以继续使用30天,周而复始,系统始终不会提醒用户口令过期。
该方式,在不变更用户口令的前提下,口令的最后设置时间发生了变更。这种方式之所以能欺骗绕过口令策略,是因为口令过期与否,由pwdLastSet和周期阀值时间相加,与当前时间比较得来。如果前面相加的值大于当前时间,则说明未过期,否则是过期口令,必须强制修改。修改完后,pwdLastSet会被设置会当前时间。使用上图中的选项,会导致pwdLastSet被设置为当前时间,而不管用户是否真的修改了口令,从而可以欺骗绕过口令策略。
用户的口令散列值存储在2个地方:一是unicodePwd存储NTHash;dBCSPwd存储LMHash,即使系统没有存储LMHash,这里也会设置一个随机的散列值;如果NTHash发生了变更,这里也会相应地发生变更。
在Windows系统中,没有向用户层提供API接口来读取这些属性,但是这些属性又必须在域服务器之间进行同步,所以肯定有元数据与之相关联。我们通过获取版本号,确认发生过多少次的口令变更。
使用如下指令可以查看unicodePwd和dBCSPwd的版本:
repadmin /showobjmeta Win2016-dc01 "CN=win10x64user,CN=Users,DC=adsec,DC=com"
图中可以看到unicodePwd、dBCSPwd属性的版本Ver值,变更时间及所在服务器的DSA ID号。
Windows系统中,如果使用net user命令初始创建的用户,unicodePwd等属性的Ver起始值是2;使用ADSI工具创建的用户,起始值是1。但不管怎么说,起始值都比较小,每变更一次,他们的值会增加1。当使用上面的欺骗方式时,这2个属性的Ver不会增加,且时间不发生变化,但是pwdLastSet会发生变化,这几个时间不一致,则说明发生此种恶意欺骗行为。为了更精确,可以通过Get-ADObject获取用户的登录次数,以确定用户的活跃度。这种欺骗方式在系统日志中的Event ID为4738。
一旦确定发生了上述欺骗方式,追踪定位的流程和上一节所述类似,安全日志的截图如下,通过安全日志可以追踪事件发起人、对象、事件和主机位置。
如果一个用户的口令修改次数,远超周期时间内口令过期次数,则可能发生口令被恶意频繁重置。将获取的unicodePwd属性的Ver值,与正常情况下口令过期次数进行比较,超过一定的阀值,即认为用户被恶意重置。这种恶意重置事件的安全日志Event ID为4724。
一旦确定发生了上述欺骗方式,追踪定位的流程和上一节所述类似,安全日志的截图如下,通过安全日志可以追踪事件发起人、对象、事件和主机位置。
在大型网络中,用户成千上万,因此需要有批量的方法完成检测,下面的代码可以做到。只需在检测结果上加上筛选策略即可。
Get-ADObject -SearchBase "CN=Users,DC=adsec,DC=com" -SearchScope Subtree -LDAPFilter "(&(objectCategory=person)(sAMAccountName=*))" -Properties msDS-ReplAttributeMetaData | Get-ADReplicationAttributeMetadata -Server Win2016-dc01 | Where-Object { $_.AttributeName -eq "unicodePwd" -or $_.AttributeName -eq "dBCSPwd" -or $_.AttributeName -eq "ntPwdHistory" -or $_.AttributeName -eq "lmPwdHistory" } | Out-GridView
0x05 小结
域同步的元数据在检测、定位、取证恶意行为上有很大的帮助,本文以3个例子演示了使用的具体方法。这种检测方法可以对抗许多非常隐蔽的后门方式、恶意攻击行为等。
参考文献:
[1] https://www.slideshare.net/harmj0y/ace-up-the-sleeve
[2] https://blogs.technet.microsoft.com/pie/2014/08/25/metadata-0-metadata-what-is-it-and-why-do-we-care/
[3] https://blogs.technet.microsoft.com/pie/2014/08/25/metadata-1-when-did-the-delegation-change-how-to-track-security-descriptor-modifications/
[4] https://blogs.technet.microsoft.com/pie/2014/08/25/metadata-2-the-ephemeral-admin-or-how-to-track-the-group-membership/
[5] https://blogs.technet.microsoft.com/pie/2016/10/20/metadata-3-spot-who-is-cheating-with-the-password-policy/
[6] http://www.harmj0y.net/blog/defense/hunting-with-active-directory-replication-metadata/