在Netwrix安全研究中,我们帮助客户进行Entra ID评估,知道他们已在本地完成了AD分层。我们也知道他们在Azure中虚拟化了域控制器,但很难判断哪个Windows服务器是真正的DC。我们发现Azure Run Command允许以SYSTEM身份运行命令,于是利用它进行侦察并识别DC。
本帖中展示的所有内容(截图、机器名称、用户名)均来自我们事后重建的实验室环境,用于演示该技术。无任何客户数据。
作为背景,微软官方文档将 Run Command 描述为通过 VM 代理在 Windows 虚拟机内运行 PowerShell 脚本的一种方式,主要用于修复访问或网络问题等一般管理任务。不过,它不会检查所连接的虚拟机类型。它可以像在其他虚拟机上一样,顺利运行在域控制器上。
我们决定与Claude一起编写一个PowerShell脚本,允许我们一次对多台Windows机器运行命令,而不是在运行命令控制台中逐个点击虚拟机。您只需拥有Microsoft.Compute/virtualMachines/runCommand/action权限,该权限包含在虚拟机贡献者或更高级别中。
关键是在ThreadJob中运行每个Invoke-AzVMRunCommand调用,而不是使用常规的Start-Job。Start-Job会为每台虚拟机启动一个新的PowerShell进程,并每次重新加载Az模块,因此一旦超过几台机器,速度就会迅速变慢。ThreadJob则在同一进程的线程中运行,同时启动多个几乎不额外消耗资源。我们维护一个所有找到的虚拟机队列,并保持同时运行的数量固定(默认20台)。一旦有一个完成,就立即补充,不用等整个批次完成后再开始下一个。
通过门户一次对一台机器运行 whoami 的效果如下:
现在通过 PowerShell 脚本,对订阅中的所有虚拟机同时运行相同的命令:
完成此步骤后,下一步是找到域控制器。检查很简单:通过运行命令在每个运行中的Windows虚拟机上执行Get-Service NTDS。NTDS是运行AD DS数据库的服务,因此如果它存在且正在运行,该机器就是域控制器。
有一台虚拟机明显在运行,但Run Command对它报错而未响应,而订阅中的其他虚拟机均正常响应。仅凭名称就是强烈的提示。Run Command失败正符合对严格锁定的域控制器的预期,较严格的安全工具阻碍了操作。若照字面接受该错误并继续,将意味着报告订阅中没有域控制器。
我们没有信任错误,而是直接询问了AD。任何Run Command仍能访问的域加入虚拟机都会被用来通过LDAP查询AD。不需要Active Directory模块,只需[adsisearcher],这是一个内置的PowerShell类型加速器,已在任何域加入的计算机上运行。
该检查是名为 SERVER_TRUST_ACCOUNT 的 userAccountControl 位,值为 8192。AD 仅在计算机提升为 DC 后设置此值。我们在此基础上添加了两个检查:主组 516(“Domain Controllers”组)和配置分区中匹配的 NTDS 设置对象。
不过有一点:仅凭匹配的主机名并不能证明这是该虚拟机,甚至不能证明它在Azure中。名称会变化、被截断,有时还会被重复使用。因此我们先检查IP,从域内解析DC的主机名到IP,并与Azure自己报告的该虚拟机的私有IP进行比较。只有当机器确实运行在Azure中时,该IP才存在,所以这里的匹配几乎是最可靠的。当DNS无法解析时,我们会退而求其次比较主机名,但我们认为这更弱,因为名称的变化方式是IP所不具备的。
一旦我们能够可靠地确认之前出错的机器是域控制器,真正的问题是谁能以SYSTEM身份对其运行任意命令,因为这种访问属于Tier-0,毫无疑问。不是拥有域管理员权限的人;客户已经通过分级处理了这个问题。是拥有该特定虚拟机Azure权限的人,因为Run Command不关心AD中的任何内容,只关心Azure RBAC。
情况是这样的。一些用户出现多次(每个角色一行),还有一个组和两个服务主体,链中某处都有所有者或贡献者权限,大部分是从订阅继承的,而非直接分配给虚拟机。每个人都可以针对域控制器调用运行命令,并以SYSTEM身份执行代码。这与实际在本地域管理员中的人员无关。
此时,脚本还会询问您是否希望从这些数据生成类似BloodHound的图表,以可视化哪些安全主体拥有通往Azure虚拟化域控制器的直接或间接路径。对于我们的实验室,有12个主体有通往DC的路径,其中大部分是从订阅继承的,而非直接分配给虚拟机。点击任意节点,侧边栏会详细说明它是什么以及如何到达那里,包括组成员身份,因此显示“3个成员”的组节点不仅仅是计数。您可以实际看到里面是谁。
推荐
在 Azure 中为您的域控制器的 VM 资源打上类似 Tier-0-OnPrem-AD 的标签。这看似显而易见,但我们查看的大多数环境都没有这样做,因此最初识别它们需要真正的工作,而不仅仅是通过标签过滤。标签一旦设置,您可以围绕它制定策略:拒绝对带有该标签的任何对象的新所有者或贡献者分配,或在更改时发出警报。
标签只能告诉你未来会发生什么。它不能修复已经存在的问题。我们查看的每个环境中,这些虚拟机随着时间积累了权限,大多数是从资源组或订阅继承的,而非直接分配的。这正是容易被忽视的原因。检查当前谁有访问权限,并询问每个人和服务主体,他们是否仍然需要在作为域控制器的机器上拥有所有者或贡献者权限。
设置完成后,过滤只需添加层级列或在过滤栏输入Tier-0-OnPrem-AD,所有DC都会显示。无需脚本。曾经需要整个枚举流程才能完成的,现在团队任何人都能两次点击完成过滤。
参考资料
- Microsoft Learn:使用运行命令操作在 Azure 中的 Windows 虚拟机上运行脚本
https://learn.microsoft.com/en-us/azure/virtual-machines/windows/run-command - Microsoft Learn:适用于虚拟机的 Azure 实例元数据服务
https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service
分享到
了解更多
关于作者
Huy Kha
安全研究总监
Huy 是 Netwrix 的安全研究总监,带领安全研究团队,并推动整个安全产品组合的改进,帮助客户提升韧性。他还是 Microsoft MVP(Windows & Devices)。凭借事件响应、安全运营和系统优化方面的经验,他专注于实用、可重复的方法,将复杂问题转化为清晰、精简的流程。