从关键系统提取明文凭证一直以来都是那么的有趣和富有挑战性。然而,MSSQL服务器在数据库中存储了本地SQL凭证的哈希值,链接服务器的凭证以加密形式存储。如果MSSQL可以解密该凭证,那么使用本文中的PowerShell脚本能够使你也能做到,这篇文章中我们就来共同解密MSSQL凭证及链接服务器密码。
从攻击的角度来看,这距离深入利用它还很远,因为这需要SQL数据库系统管理员权限和服务器本地系统管理员权限。从防守的角度来看,这又给我们敲响了警钟,提醒我们注意不必要的数据库链接和数据库链接权限是否过度,以及使用SQL服务器身份认证而不是集成身份验证可能会导致不必要的风险。这篇文章对于专攻数据库的黑客和想学习更多的数据库管理员来说,将会很有趣。
微软SQL Server允许用户创建连向外部数据源的链接,一般是其他的MSSQL服务器。当这些链接被创建后,可以配置它们使用当前的安全环境或静态SQL server凭证。如果使用了SQL server凭证,用户账号和密码就会被加密并保存在数据库中,所以它们以一种可逆的格式存储。此时,不能使用单方向的哈希算法,因为为了在其他服务器上验证身份,SQL server必须能够得到明文凭证。所以,如果凭证被加密了而未被进行哈希运算,那么对SQL server来说在使用之前一定有一种方法来解密它们。本文接下来的部分将重点关注凭证加密解密的过程。
MSSQL在“master.sys.syslnklgns”表中存储了链接服务器的信息,包括加密过的密码。具体来说,加密后的密码存储在“pwdhash”栏中(即使它并不是一个哈希)。示例如下图所示:
使用普通的SQL链接并不能访问“master.sys.syslnklgns”表,而是需要一个专用管理连接(DAC),关于DAC的更多信息可以点击这里。
需要系统管理员权限来开启一个DAC连接,但是因为一直需要本地管理员权限,所以得到系统管理员权限应该不是问题。如果本地管理员没有系统管理员权限,那么你将不得不冒充MSSQL server账户或者本地系统账户。有关这部分的更多细节你可以在Scott的博客中找到。
是时候介绍一些MSSQL加密的基础知识啦。为了顺利进行,需要访问服务主密钥(SMK),关于SMK的更多信息可以点击这里。根据微软官网中“SMK是SQL server加密层次结构的根,当首次需要SMK加密其他密钥的时候,SMK会自动产生。”SMK存储在表“master.sys.key_encryptions”中并可使用key_id 102来唯一指定。
通过使用Windows数据保护API(DPAPI)将SMK加密,而在数据库中有两个版本的SMK,一个作为本地机器加密,另一个则在当前用户(这里指SQL server服务账户)的环境中加密。因为本地机器加密使用了Machinekey,并且它可以无需冒充服务账户就能被解密,所以我们将选择前者来提取密钥。下面是一个例子来展示本地机器加密的形式:
为了增强加密的复杂性,需要额外的entropy,但是entropy字节可以在注册表“HKLM:SOFTWAREMicrosoftMicrosoft SQL Server[instancename]SecurityEntropy”中找到。再次地,我们需要本地管理员权限来访问注册表键。Entropy为每个MSSQL实例在注册表中存储,如下图所示:
在这之后(从加密的值中删除一些填充或元数据),我们就可以使用DPAPI来解密SMK。
根据SMK的长度(或MSSQL版本)我们就能确定加密算法:MSSQL 2012使用AES,早期版本使用3DES。此外,必须解析“pwdhash”值来找到加密后的密码。可以参考这里来了解更多。尽管字节格式似乎并不完全匹配该网页上的给出的详情,但是找到加密中正确的字节并不会太难。所以,现在使用SMK就很有可能提取所有的明文链接凭证(当使用SQL server账户时不需要Windows身份验证)。
使用PowerShell脚本文件Get-MSSQLLinkPasswords.psm1解密链接服务器密码
为了实现链接服务器凭证的自动解密,我写了一个名为“Get-MSSQLLinkPasswords.psm1”的PowerShell脚本。你可以在GitHub上下载该脚本文件。
该脚本必须在MSSQL server服务器上运行(因为DPAPI需要访问本机的键)。执行脚本的用户还必须有系统管理员权限来访问所有的数据库实例(DAC连接)和Windows服务器本地管理员权限(访问注册表中的entropy字节)。此外,如果启用了UAC,那么必须以管理员身份运行该脚本。
下面是一个该脚本执行流程的总结:
下面是一个最终结果的例子:
我已经在MSSQL 2005、2008、2012、2008Express版、2012Express版上测试了该脚本。可能脚本中还有一些bug,但是好像还是能够可靠地工作。
通过使用相同的技术我们也可以解密SQL Server凭证密码。我通过修改上面提到的密码解密脚本,即仅改变加密密码存储的位置,就可以得到一个更新的用于凭证解密的PowerShell脚本。
微软的SQL Server数据库允许用户向数据库中添加凭证。这些凭证通常是Windows用户名和密码,它可以用来访问SQL Server的外部资源。一个凭证可以被多个SQL登录用户用来进行外部访问。
SQL server代理账户就是一个简单的凭证使用例子。当xp_cmdshell被执行时,它默认使用SQL server服务账户权限。然而,通过为SQL server配置代理账户,我们就可以设置xp_cmdshell使用最低权限的账户来访问操作系统,而不是服务账户权限(该权限通常都有些过大)。
当凭证被添加到SQL server时,考虑到凭证的正确使用,密码必须以可逆的加密方式存储在数据库中。所以,解密被存储的凭证密码将会在本文中讨论。
MSSQL数据库会将凭证密码存储到master.sys.sysobjvalues表中。通过利用下面的语句查看master.sys.credentials视图的定义,我们就可以找出加密密码的存储位置。
SELECT object_definition(OBJECT_ID('sys.credentials'))
对于该表,微软给出了一个相当模糊的描述:“它存在于每个数据库中,对于一个实体的每个普通值属性来说都包含一行。”Master.sys.sysobjvalues表中有大量数据,但是凭证信息似乎有一个valueclass 为28。并且加密后的密码存储在valclass=28和valnum=2的imageval列。我找不到关于valclass和valnum的文档资料,但是这些值在我的测试系统上似乎能够正常工作。
然而,使用普通的SQL连接并不能访问master.sys.sysobjvalues表,而是需要特定管理连接(DAC)才可以,关于DAC的更多信息,可以点击这里。
根据MSSQL服务器版本的不同,凭证密码可能使用AES加密(MSSQL 2012+)或3DES(MSSQL 2008以及更早版本)。存储在sys.sysobjvalues表中imageval列的密码必须首先进行 一点解析处理,然后才能进行解密(幸运的是,跟链接服务器密码的解密方式完全相同)。在解析凭证密码之后,就可以使用SMK来解密该凭证了。
使用PowerShell脚本Get-MSSQLCredentialPasswords.psm1解密凭证密码
对Get-MSSQLLinkPasswords.psm1脚本稍微修改之后,就得到了针对MSSQL凭证密码的新版本,将其命名为Get-MSSQLCredentialPasswords.psm1,使用该脚本就能自动地解密凭证密码。该脚本可以从GitHub上下载。
该脚本必须在MSSQL服务器本地运行(因为DPAPI需要访问本地机器键)。执行该脚本的用户必须拥有系统管理员权限来访问所有的数据库实例(使用DAC连接),还需要windows服务器的本地管理员权限(需要访问注册表中的entropy字节)。此外,如果启用了UAC,那么脚本必须以管理员身份运行。下面是该脚本中执行流程的总结:
下面是一个最终结果的例子:
PS C:\> Get-MSSQLCredentialPasswords | out-gridview
我已经在MSSQL 2008和MSSQL 2012上测试了该脚本。可能脚本中还有一些bug,但是好像还是能够可靠地工作。