当Service Principal Names(SPNs)泄露时,可能会引发严重的安全风险,特别是在使用Kerberos身份验证的环境中。
服务主体名称(SPN)在利用Kerberos身份验证的服务发现中是必需的。
Microsoft Kerberos规范:
SPN = serviceclass “/” hostname [“:”port] [“/” servicename]
serviceclass 是标识服务类别的字符串,例如Web服务的“www”或目录服务的“ldap”。
hostname 是系统名称的字符串,完全限定的域名(FQDN)。
port 是服务的端口号的数字。
servicename 段是服务的区别名称(DN)、objectGuid、Internet主机名或完全限定域名(FQDN)的字符串。
SPN 示例:
SQL 服务器、实例、端口:
MSSQLSvc/host01.example.com:1433
Exchange:
exchangeMDB/EXCAS01.example.com
RDP:
TERMSERV/EXCAS01.example.com
WSMan / WinRM / PS Remoting:
WSMAN/EXCAS01.example.com
Hyper-V主机:
Microsoft Virtual Console Service/HV01.example.com
VMWare VCenter:
STS/VC01.example.com
相对于网络端口扫描,SPN扫描对攻击者的主要优势在于不需要连接到网络上的每个IP来检查服务端口。SPN扫描通过LDAP查询到域控制器对服务扫描。由于SPN查询是正常Kerberos票证行为的一部分,很难发现。而网络端口扫描就很容易发现了。
# 服务器和域接口
$search = New-Object DirectoryServices.DirectorySearcher([ADSI]"")
$search.filter = "(servicePrincipalName=*)"
$results = $search.Findall()
# 遍历SPN
foreach($result in $results)
{
$userEntry = $result.GetDirectoryEntry()
Write-host "Object Name = " $userEntry.name -backgroundcolor "yellow" -foregroundcolor "black"
Write-host "DN = " $userEntry.distinguishedName
Write-host "Object Catch = " $userEntry.objectCategory
Write-host "servicePrincipalNames:"
$i=1
foreach($SPN in $userEntry.servicePrincipalName)
{
Write-host "SPN(" $i ") = " $SPN $i+=1
}
Write-host ""
}
用户账户控制发现服务账户
在搜索Active Directory中寻找服务账户的另一种隐秘方法是账户控制设置,因为服务账户一般与常规用户账户设置不同。
一个不错的案例: “密码永不过期”设置
— 服务账户的密码被设置为永不过期,因为重置很麻烦,而且会导致应用程序或服务中断。
$ldapFilter = "(&(servicePrincipalName=*)(useraccountcontrol:1.2.840.113556.1.4.803:=65536))"
$search = New-Object DirectoryServices.DirectorySearcher([ADSI]"")
$search.Filter = $ldapFilter
$results = $search.Findall()