如何查找活动目录委派

 

一、前言

活动目录(AD)委派(delegation)是一个非常有趣的话题,我们之前已经在一篇文章和某次研讨会中讨论过这方面内容。总而言之,活动目录可以将某些权限委派给非(域/森林/企业)管理员用户,以便管理员对特定AD区域执行管理任务。如果配置不当,那么攻击者也可以利用这种功能入侵AD。

先前我们只讨论了如何手动查找这类委派,大家可以阅读另一篇文章,了解其他一些工具在手动分析中的应用。在本文中,我们将介绍如何利用脚本,以(半)自动化的方式在网络中搜索这类委派。

 

二、实验环境

假设我们面临的是如下场景:

  • 具备某个低权限域用户访问权限,该账户有一些限制条件,比如AppLocker禁用此账户执行powershell。
  • 在加入域的某台主机上具备本地管理员访问权限。
  • 可以利用这个本地管理员权限运行未受限的powershell脚本,然而我们需要登录域才能在AD域上执行枚举操作。

为了完成任务,我们准备使用两种不同的方法:

  • 使用AD ACLScanner(半自动化方法)
  • 使用NSS提供的自定义Powershell脚本(全自动化方法)

 

三、ADACLScanner

这款工具由canix1开发,可以用于通用型ACL扫描,大家可以访问GitHub下载该工具。我们可以利用该工具来搜索AD委派配置情况。下面让我们以某个例子来演示整个操作过程:

运行powershell脚本后,ADACLScanner为我们提供了一个非常友好的GUI(很少有powershell脚本能提供这么友好的GUI)。

图1. ADACLScanner

假设我们接入的AD名为plum,地址为192.168.3.215,如下图所示:

图2. 连接AD

当我们点击第一列的“Connect”按钮时,就会弹出对话框提示输入域凭据,以便枚举目标节点。需要注意的是,这里可以使用低权限域用户的凭据。

图3. 请求域凭据

输入正确的域凭据后,我们就能看到可用的节点,如下图所示:

图4. 枚举AD节点

现在我们需要选中第一列节点,确保已勾掉“Inherited Permissions”复选框,然后点击“Run Scan”按钮开始扫描。在上图中,我们选中的是DC=plum,DC=local。扫描完成后就会生成扫描报告,如下图所示:

图5. ACL Scanner报告

如果我们选中“Resions”节点,再次扫描,那么最终报告有所不同。在报告中,我们可以发现Object列中代表我们扫描的目标节点,因此这里OU的值为Regions

图6. Regions OU的ACL报告

同样,如果我们扫描USA OU,可以看到如下报告,了解USA OU的委派权限信息。

图7. AD ACL Scanner对OU USA的扫描结果

这里比较麻烦的是我们需要手动搜索每个节点,分析每个条目,才能找到正确的委派信息。这个过程对小型网络来说问题不大,但如果面对的是大型网络,这将变成分析人员的噩梦。此时我们第二种方法就可以派上用场。

 

四、使用自定义Powershell脚本

大家可以先看一下我们团队定制的这个脚本的工作过程,请参考此处视频。如果想直接使用这个自动化脚本,可以访问此处下载。如果大家对脚本的具体实现非常感兴趣,接下来我们就一步一步来分析:

1、获取用户凭据

我们使用的是一个本地管理员用户(非域用户),因此每当我们尝试挂载AD驱动或者导入活动目录模块时,都会看到如下错误:

图8. 导入AD模块错误

为了解决这个问题,我们可以传入-WarningAction SilentlyContinue参数。

接下来具体看看脚本实现,第一部分代码如下:

Import-Module ActiveDirectory -WarningAction SilentlyContinue
# force use of specified credentials everywhere
$creds=Get-Credential
$PSDefaultParameterValues = @{"*-AD*:Credential"=$creds}
# GET DC Name
$dcname=(Get-ADDomainController).Name
New-PSDrive -Name AD -PSProvider ActiveDirectory -Server $dcname -Root //RootDSE/ -Credential $creds Set-Location AD:

由于我们没有使用域用户来执行操作,因此代码开头处我们使用-WarningAction SilentlyContinue参数导入ActiveDirectory模块,这样我们就能在不挂载AD驱动的情况下导入该模块。接下来我们尝试获取用户凭据,添加用户凭据后,我们为包含-AD的所有命令设置PSDefaultParameterValues。接下来我们使用Get-ADDomainController命令获取服务器名称,然后配合新获取的凭据来挂载AD驱动。

其实如果我们已经以域用户身份登录,就无需执行这些操作。然而我们还是考虑了最坏的情况,比如我们可能以本地管理员身份访问目标系统,因此不受powershell访问限制,但所使用的域用户凭据权限较低。

2、遍历整个OU

获取所有域名、组织单元(Organization Units)以及ADObject。

$OUs  = @(Get-ADDomain | Select-Object -ExpandProperty DistinguishedName)
$OUs += Get-ADOrganizationalUnit -Filter * | Select-Object -ExpandProperty DistinguishedName
$OUs += Get-ADObject -SearchBase (Get-ADDomain).DistinguishedName -SearchScope OneLevel -LDAPFilter '(objectClass=container)' | Select-Object -ExpandProperty DistinguishedName

这里代码第一行执行Get-ADDomain,获取DistinguishedName列,第二行执行Get-ADOrganizationalUnit(过滤器为*),获取这些对象的DistinguishedName,将其添加到OUs对象中。第三行获取匹配AD域DistinguishedName的AD对象,使用LdapFilter(过滤objectClass=container的对象),只筛选一层,然后再打印DistinguishedName列。

3、添加例外

$domain = (Get-ADDomain).Name
$groups_to_ignore = ( "$domain\Enterprise Admins", "$domain\Domain Admins")
# 'NT AUTHORITY\SYSTEM', 'S-1-5-32-548', 'NT AUTHORITY\SELF'

在这几行代码中我们排除了一些例外情况。我们首先获取域名,然后以此为基础添加待忽略的一些组。

4、提取相关的域用户/组权限

ForEach ($OU in $OUs) {
    $report += Get-Acl -Path "AD:$OU" |
     Select-Object -ExpandProperty Access | ? {$_.IdentityReference -match "$domain*" -and $_.IdentityReference -notin $groups_to_ignore} |
     Select-Object @{name='organizationalUnit';expression={$OU}}, `
                   @{name='objectTypeName';expression={if ($_.objectType.ToString() -eq '00000000-0000-0000-0000-000000000000') {'All'} Else {$schemaIDGUID.Item($_.objectType)}}}, `
                   @{name='inheritedObjectTypeName';expression={$schemaIDGUID.Item($_.inheritedObjectType)}}, `
                   *
}

在前文第二步骤中,我们将所有信息存储在$OUs中,现在我们使用ForEach循环来提取这些信息并进一步处理。

前三行通过ForEach循环获取$OUs中所有单位的ACL路径,确保其中有与目标域匹配的IdentityReference,同时不属于需排除的组中(例外组参考第4个步骤)。从第4行开始,代码提取了某些对象,如organizationalUnit(使用$OU表达式)、ObjectTypeName(如果对象类型不等于root GUID,则只根据对象类型的值提取SchemaIDGUID的详细信息)。

5、Inheritance == False

Inheritance为false是非常关键的一点。我们只需要筛选出满足该条件的那些行。

$filterrep= $report | Where-Object {-not $_.IsInherited}

这样就能确保输出结果中不包含继承的对象。

6、格式化输出结果

Write-Output ( $filterrep | Select-Object OrganizationalUnit,ObjectTypeName,ActiveDirectoryRights,IdentityReference | Format-Table | Out-String)

以上代码能以表格的形式格式化输出结果,列出在特定对象上未继承权限(即具备委派权限)的用户。默认情况下,委派权限会应用到整颗OU树上,因此如果上层OU具备该权限,那么除非显式移除权限,否则下层OU就会自动具备该权限。

图9. 自动化脚本处理结果

我们已经在最新的2019版高级基础设施黑客教程中介绍了这种技术(还包括其他有用的技术),我们也会为内部安全和SOC团队提供内部培训和CTF服务,帮助大家提高技术水平。

(完)