SPN(ServicePrincipal Names)服务主体名称,是服务实例(如:HTTP、SMB、Exchange等服务)的唯一标识符。Kerberos身份验证使用SPN将服务实例与服务登录帐户相关联。如果在整个林或域中的计算机上安装多个服务实例,则每个实例都必须具有自己的 SPN。如果客户端使用多个名称进行身份验证,则给定服务实例可以具有多个SPN。SPN 始终包含运行服务实例的主机名,因此服务实例可以为其主机的每个名称或别名注册SPN。一个用户账户下可以有多个SPN,但一个SPN只能注册到一个账户。如果想使用 Kerberos 协议来认证服务,那么必须正确配置SPN。
在 Kerberos 身份验证服务使用 SPN 对服务进行身份验证之前,必须在服务实例用于登录的帐户对象上注册 SPN。 只能在一个帐户上注册给定的 SPN。 对于 Win32 服务,服务安装程序在安装服务实例时指定登录帐户。 然后,安装程序将编写 SPN,并作为帐户对象的属性写入 Active Directory 数据库中。 如果服务实例的登录帐户发生更改,则必须在新帐户下重新注册 SPN。 当客户端想要连接到某个服务时,它将查找该服务的实例,然后连接到该服务并显示该服务的 SPN 以进行身份验证。
在内网中,SPN扫描通过查询向域控服务器执行服务发现。这对于红队而言,可以帮助他们识别正在运行重要服务的主机,如Exchange服务器等。SPN的识别是kerberoasting攻击的第一步。
下面通过一个例子来说明SPN的作用:
Exchange邮箱服务在安装的过程中,就会在活动目录中注册一个Exchange的SPN。当用户需要访问Exchange邮箱服务时,系统会以当前用户的身份向域控查询SPN为Exchange的记录。当找到该SPN记录后,用户会再次与KDC通信,将KDC发放的TGT作为身份凭据发送给KDC,并将需要访问的SPN发送给KDC。KDC中的TGS服务对TGT进行解密。确认无误后,由TGS将一张允许访问该SPN所对应的服务的ST服务票据和该SPN所对应的服务的地址发送给用户,用户使用该票据即可访问Exchange邮箱服务。
SPN分为两种类型:
- 一种是注册在活动目录的机器帐户(Computers)下,当一个服务的权限为 Local System 或 Network Service时,则SPN注册在机器帐户(Computers)下。域中的每个机器账户都会注册两个SPN:HOST/主机名 和 HOST/主机名.xie.com
- 另一种是注册在活动目录的域用户帐户(Users)下,当一个服务的权限为一个域用户,则SPN注册在域用户帐户下。
如图所示,可以看到机器win2008下注册的SPN和hack用户下注册的SPN。
这里以SQLServer服务注册为例:
SQLServer在每次启动的时候,都会去尝试用自己的启动账号注册SPN。但是在Windows域里,默认普通机器账号有权注册SPN,但是普通域用户账号是没有权注册SPN的。这就会导致这样一个现象,SQL Server如果使用“Local System account”来启动,Kerberos就能够成功,因为SQL Server这时可以在DC上注册SPN。如果用一个域用户来启动,Kerberos就不能成功,因为这时SPN注册不上去。
解决办法:
- 可以使用工具Setspn来手动注册SPN。但是这不是一个最好的方法,毕竟手工注册不是长久之计。如果SPN下次丢了,又要再次手动注册。
- 所以比较好的方法,是让SQL Server当前启动域账号有注册SPN的权力。要在DC上为域账号赋予 “Read servicePrincipalName” 和 “Write serverPrincipalName” 的权限即可。
SPN的配置
SPN 在注册它的林中必须是唯一的。 如果它不唯一,身份验证将失败。 SPN 语法包含四个元素:两个必需的元素和两个其他元素,其中<service class> 和 <host> 为必须元素。如有必要,可以使用这些元素生成下表中列出的唯一名称。如下是SPN的格式:
代码语言:javascript复制<service class>/<host>:<port>/<service name>
- <service class>:服务类,用于标识服务常规类的字符串;例如,"HOST"。 有一些已知的服务类名称,例如 Web 服务的"WWW"或目录服务的"LDAP"。 一般情况下,可以是服务类唯一的任何字符串。 请注意,SPN 语法使用正斜杠 (/) 分隔元素,因此此字符不能出现在服务类名中。
- <host>:运行服务的计算机的名称。 这可以是完全限定的 DNS 名称(FQDN)或 NetBIOS 名称。 请注意,不确保 NetBIOS 名称在林中是唯一的,因此,包含 NetBIOS 名称的 SPN 可能不是唯一的。
- <port>:一个可选端口号,用于区分单个主计算机上同一服务类的多个实例。 如果服务使用服务类的默认端口,则省略此组件。
- <service name>:可复制服务SPN中使用的可选名称,用于标识服务或服务所服务的域提供的数据或服务。 此组件可以具有以下格式之一:
- 服务中对象的可分辨名称或 objectGUID Active Directory 域,例如 SCP (连接) 。
- 为整个域提供指定服务的服务的域的 DNS 名称。
- SRV 或 MX 记录的 DNS 名称。
基于主机的服务
对于基于主机的服务,将省略"服务名称"组件,因为服务由服务类和安装该服务的主机名唯一标识。
代码语言:javascript复制<service class>/<host>
仅服务类就足以为客户端标识服务提供的功能。可以在多台计算机上安装服务类的实例,并且每个实例都提供使用其主计算机标识的服务。 FTP 和 Telnet 是基于主机的服务的示例。 如果服务使用非默认端口,或者主机上有多个服务实例,则基于主机的服务实例的 SPN 可以包含端口号。
代码语言:javascript复制<service class>/<host>:<port>
如下所示,是系统自动注册的基于主机服务的SPN。
各个SPN代表的服务如下:
代码语言:javascript复制#Host服务
HOST/WIN2008
HOST/WIN2008.xie.com
#RDP服务
TERMSRV/WIN2008
TERMSRV/Win2008.xie.com
#WSMan/WinRM/PSRemoting服务
WSMAN/Win2008
WSMAN/Win2008.xie.com
#RestrictedKrbHost服务
RestrictedKrbHost/WIN2008
RestrictedKrbHost/WIN2008.xie.com
可复制的服务
对于可复制服务,可以有一个或多个服务实例 (副本) ,并且客户端不会区分它们连接到哪个副本,因为每个实例都提供相同的服务。 每个副本的 SPN具有相同的"服务类"和"服务名称"组件,其中"服务名称"更明确地标识服务提供的功能。 只有<host>和可选的<port>组件因 SPN 而异。
代码语言:javascript复制<service class>/<host>:<port>/<service name>
可复制服务的一个示例是提供对指定数据库的访问的数据库服务的实例。 在这种情况下,标识数据库应用程序,标识特定数据库。 可以是包含数据库连接数据的 SCP的可分辨名称。
代码语言:javascript复制MyDBService/host1.example.com/CN=hrdb,OU=mktg,DC=example,DC=com
MyDBService/host2.example.com/CN=hrdb,OU=mktg,DC=example,DC=com
MyDBService/host3.example.com/CN=hrdb,OU=mktg,DC=example,DC=com
可复制服务的另一个示例是向整个域提供服务。 在这种情况下,<service name>组件是所服务的域的 DNS 名称。 Kerberos KDC 是此类可复制服务的示例。
请注意,如果计算机的 DNS 名称发生更改,系统将更新林中该主机的所有已注册的SPN的元素。
使用SetSPN注册SPN
在客户端使用 SPN 对服务实例进行身份验证之前,必须在服务实例上将用于登录的用户或计算机帐户注册 SPN。 通常,SPN 注册由通过域管理员权限运行的服务安装程序来完成。但是我们也可以手动使用Setspn.exe程序来注册SPN,setspn.exe是Windows自带的一个二进制文件,该二进制文件可以用于SPN的查看、添加和删除。
如图所示,查看setspn的帮助说明。
我们可以使用 -S 参数来注册SPN。
- 如果是注册在域用户下,还可以使用-U -A参数注册
- 如果是注册在机器用户下,可以使用-C -A参数注册
SPN注册权限
只有机器账号或域管理员账号有权限注册SPN,普通域用户注册SPN会提示特权不够,不能执行该操作。而使用机器账号注册SPN时,注册的SPN的必须为当前主机的DNS名称。使用域管理员账号注册SPN无任何限制!
如图所示,通过切换到本地System权限窗口来使用机器账号注册SPN,注册的SPN的为当前主机的DNS名称。
如果使用机器账号注册SPN,注册的SPN的不为当前主机的DNS名称,会提示失败。如图所示:
使用普通域用户执行如下命令注册SPN,由于权限不足,所以提示失败!
代码语言:javascript复制setspn -S MySQL/win7.xie.com:3306 hack
如图所示,使用普通域用户注册SPN。
如果想让普通域用户也拥有注册SPN权限的话,需要在域控上手动为域账号赋予“读取 servicePrincipalName” 和 “写入serverPrincipalName” 的权限。具体操作如下:
如图所示,打开ADSI 编辑器(adsiedit.msc),找到要修改的hack用户,右键——>属性。
找到安全选项卡,点击高级。如图所示:
然后点击添加,如图所示:
点击选择主体,然后输入SELF。如图所示:
接着勾选“读取 servicePrincipalName” 和 “写入serverPrincipalName” 选项。如图所示:
然后应用、确定即可。如图所示:
然后就可以使用普通域用户注册SPN了。执行如下命令在hack域账号下注册SPN。
代码语言:javascript复制setspn -S MySQL/win7.xie.com:3306 hack
如图所示,可以看到hack用户成功注册SPN。
但是即使为域账号了赋予“Read servicePrincipalName” 和 “Write serverPrincipalName” 的权限,当前账户也只能将SPN注册在当前用户下,而无法注册在其他用户下。
如图所示,hack用户将SPN注册于其它用户下,提示权限拒绝。
以下我们注册SPN均是在域管理员administrator身份下进行注册。
将SPN注册在域账号下
运行如下命令使用-S参数或-U -S参数将SPN注册在域用户hack下:
代码语言:javascript复制setspn -S SQLServer/win7.xie.com:1433/MSSQL hack
或
setspn -U -S SQLServer/win7.xie.com:1443/MSSQL hack
#查询hack用户注册的SPN
setspn -L hack
如图所示,使用不同命令将SPN注册于hack用户下。
将SPN注册在机器账号下
运行如下命令使用-S参数或-C -S参数将SPN注册在机器账号win7下:
代码语言:javascript复制setspn -S SQLServer/win7.xie.com:1433/MSSQL win7
或
setspn -C -S SQLServer/win7.xie.com:1443/MSSQL win7
#查询win7机器注册的SPN
setspn -L win7
如图所示,使用不同命令将SPN注册于机器账号win7下。
SPN的查询和发现
由于每台服务器都需要注册用于Kerberos身份验证服务的SPN,因此这为在不进行大规模端口扫描的情况下收集有关内网域环境的信息提供了一个更加隐蔽的方法。
如下使用不同的工具探测域内的SPN:
setspn
可以通过Windows系统自带的setspn执行如下命令查询域内的SPN。
代码语言:javascript复制#查看当前域内所有的SPN
setspn -Q */*
#如果在域林中,则可以使用如下命令查看指定域xie.com内的SPN。如果指定域不存在,则默认切换到查找本域的SPN
setspn -T xie.com -Q */*
#查找本域内重复的SPN
setspn -X
#删除指定SPN:
setspn -D MySQL/win7.xie.com:1433/MSSQL hack
#查找指定用户/主机名注册的SPN
setspn -L username/hostname
如图所示,查询域内所有的SPN。
如图所示,查询注册于hack用户和win7机器帐户下的SPN。
impacket
impacket中的GetUserSPNs.py脚本可以在域外查询指定域的SPN,使用该脚本只需要提供一个有效的域凭据。
使用GetUserSPNs.py脚本执行如下命令查询域控10.211.55.4所在的域内注册于用户下的SPN。
代码语言:javascript复制python3 GetUserSPNs.py -dc-ip 10.211.55.4 xie.com/hack:P@ss1234
如图所示,查询到注册域用户下的SPN两2个,分别为 MySQL/win7.xie.com:3306和SQLServer/win7.xie.com:1533/MSSQL。
PowerShellery
PowerShellery下有各种各样针对服务SPN探测的脚本。其中一些只需要PowerShell v2.0的环境,而有一些则需要PowerShell v3.0环境。如下是PowerShellery下不同脚本的使用:
代码语言:javascript复制#Powershellery/Stable-ish/Get-SPN/ 下Get-SPN.psm1脚本的使用,需要powershell3.0及以上版本才能使用
Import-Module .Get-SPN.psm1
Get-SPN -type service -search "*"
Get-SPN -type service -search "*" -List yes | Format-Table
#Powershellery/Stable-ish/ADS/ 下Get-DomainSpn.psm1脚本的使用
Import-Module .Get-DomainSpn.psm1
Get-DomainSpn
如图所示,使用Get-SPN.psm1脚本查询域内所有的SPN。
PowerShell-AD-Recon
该工具包提供了一些发现指定SPN的脚本,例如指定Exchange,Microsoft SQLServer等服务的SPN。但其实对于Kerberoasting攻击第一步SPN的发现意义不大,因为该脚本并没过滤出注册于域用户下的SPN。
如下是 不同脚本的使用。
代码语言:javascript复制#探测域内注册的SQLServer服务的SPN
Import-Module .Discover-PSMSSQLServers.ps1;
Discover-PSMSSQLServers
#探测域内注册的Exchannge服务的SPN
Import-Module .Discover-PSMSExchangeServers.ps1;
Discover-PSMSExchangeServers
#扫描域中所有的SPN信息
Import-Module .Discover-PSInterestingServices.ps1;
Discover-PSInterestingServices
如图所示,是PowerShell-AD-Recon中不同脚本的使用。
END