现在的位置: 首页 Sql >正文

MSSQL链接数据库密码获取工具

老外的原文:https://www.netspi.com/blog/entryid/221/decrypting-mssql-database-link-server-passwords

已经被和谐了的乌云的译文:http://drops.wooyun.org/tips/1067

在测试时发现了一些问题,于是把解决方案与一些结论共享出来,以供需要。


0x01. 原文提供的powershell脚本出错的解决方案

原文提供的脚本在中文环境下直接导入执行会抛出“数据无效”的错误,测试系统在查询分析器中获取到的密文为:

[ 0x01, 0x00, 0x00, 0x00, 0xD0, 0x8C, 0x9D, 0xDF, 0x01, 0x15, 0xD1, 0x11, 0x8C, 0x7A, 0x00, 0xC0,  
    0x4F, 0xC2, 0x97, 0xEB, 0x01, 0x00, 0x00, 0x00, 0xCD, 0x2A, 0x0B, 0x54, 0x64, 0x6A, 0xBC, 0x4B, 
    0xB0, 0x99, 0xF1, 0xE6, 0x3D, 0x93, 0x9E, 0x6E, 0x04, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 
    0x5F, 0x00, 0x5F, 0x00, 0x53, 0x00, 0x51, 0x00, 0x4C, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00,  
    0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x39, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x20, 0x00, 
    0x4D, 0x00, 0x61, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x4B, 0x00, 
    0x65, 0x00, 0x79, 0x00, 0x20, 0x00, 0x45, 0x00, 0x6E, 0x00, 0x63, 0x00, 0x72, 0x00, 0x79, 0x00, 
    0x70, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x03, 0x66, 0x00, 0x00, 
    0xA8, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3F, 0xDA, 0x14, 0xC2, 0xE9, 0x3E, 0xC7, 0xDE, 
    0x3A, 0x5C, 0xC2, 0xAE, 0x0F, 0x27, 0xAB, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 
    0xA0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4C, 0xED, 0x01, 0x57, 0x33, 0x68, 0x06, 0x7E, 
    0x81, 0x4D, 0xB9, 0xF7, 0x13, 0xC6, 0x30, 0x34, 0x18, 0x00, 0x00, 0x00, 0xE9, 0x20, 0xD6, 0x91, 
    0xCD, 0x70, 0xF2, 0xC4, 0x0F, 0xB4, 0x97, 0xF7, 0xBB, 0x7C, 0x68, 0x76, 0x4D, 0x1F, 0x9D, 0x6D, 
    0xC8, 0x56, 0xBF, 0x04, 0x14, 0x00, 0x00, 0x00, 0xEA, 0x60, 0x44, 0xBC, 0xAC, 0xA3, 0x92, 0x1D, 
    0x04, 0xCC, 0x5E, 0x6C, 0x47, 0xE5, 0x1B, 0x0D, 0xE1, 0xF9, 0x29, 0xD8]

长度为236字节。而在powershell中获取到的密文为:

[ 0x01, 0x00, 0x00, 0x00, 0xD0, 0x8C, 0x9D, 0xDF, 0x01, 0x15, 0xD1, 0x11, 0x8C, 0x7A, 0x00, 0xC0, 
  0x4F, 0xC2, 0x97, 0xEB, 0x01, 0x00, 0x00, 0x00, 0xCD, 0x2A, 0x0B, 0x54, 0x64, 0x6A, 0xBC, 0x4B, 
  0xB0, 0x99, 0xF1, 0xE6, 0x3D, 0x93, 0x9E, 0x6E, 0x04, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 
  0x5F, 0x00, 0x5F, 0x00, 0x53, 0x00, 0x51, 0x00, 0x4C, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 
  0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x39, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x20, 0x00, 
  0x4D, 0x00, 0x61, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x4B, 0x00, 
  0x65, 0x00, 0x79, 0x00, 0x20, 0x00, 0x45, 0x00, 0x6E, 0x00, 0x63, 0x00, 0x72, 0x00, 0x79, 0x00, 
  0x70, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x03, 0x66, 0x00, 0x00, 
  0xA8, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3F, 0xDA, 0x14, 0xC2, 0xE9, 0x3E, 0xC7, 0xDE, 
  0x3A, 0x5C, 0xC2, 0xAE, 0x0F, 0x27, 0xAB, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 
  0xA0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4C, 0xED, 0x01, 0x57, 0x33, 0x68, 0x06, 0x7E, 
  0x81, 0x4D, 0xB9, 0xF7, 0x13, 0xC6, 0x30, 0x34, 0x18, 0x00, 0x00, 0x00, 0xE9, 0x20, 0xD6, 0x91, 
  0xCD, 0x70, 0xF2, 0xC4]

长度为196字节,少了40字节。经过多次测试发现问题处于查询语句中的len函数。

msdn(http://technet.microsoft.com/zh-cn/library/ms190329.aspx)上对len函数的说明是:返回指定字符串表达式的字符数,其中不包含尾随空格。

由于函数返回的是字符数,自然要涉及编码。老外的操作系统是英文,默认使用IBM437 (OEM United States) 编码,这个编码是单字节字符集(sbcs),0x00-0xff都被认为是一个字符,所以获取的字符长度与字节长度相等。

而中文操作系统默认使用gb2312编码,属于双字节字符集(dbcs),大于0x7f的字节都将与其后的一个字节合并作为一个字符,于是在处理含有大于0x7f的字节数组时,len函数获取的长度与字节数并不相等,最终会由于密文不完整而出错。

统计完整密文中大于0x7f的字节总数并排除连续两个的情况,正好是40。


解决方案:
将原脚本第74行、第100行共计三个len函数换为datalength函数(http://technet.microsoft.com/zh-cn/library/ms173486.aspx)。


0x02. 原文错误的定论与拓展利用

原文中有两个错误的定论,而这两个定论在某些条件下恰好可以组合成为一个新的获取途径。


第一个错误的定论是存放熵的注册表键所属项权限,原文:“Once again, local administrator privileges are needed to access the registry key.(再次提醒,访问此表项需要本地系统的管理员权限。)”的说法是完全错误的。

可以想象,如果sqlserver以低权限运行时不能访问这个注册表项,那么执行sp_addlinkedsrvlogin必然会抛出错误,而实际上无论sqlserver的权限是什么,这个键总可以被sqlserver所访问。


在默认情况下,此键所在项(HKLM\SOFTWARE\Microsoft\Microsoft SQL Server\[instancename]\Security)有以下四个权限:

system:完全控制,子项继承
administrators:完全控制,子项继承
creator owner:完全控制
SQLServerMSSQLUser$[机器名]$[实例名]:查询,修改,创建子项,枚举子项,通知,读取DAC

前两个权限不必解释,第三个权限代表所有者,一般为system/administrator。

第四项代表sqlserver实例用户组,每个实例对应一个。在修改sqlserver某个实例的服务登录帐户时,会自动将这个账户加入此组。所以当某个程序的运行账户与sqlserver某实例服务账户相同,那么这个程序可以打开注册表并读取此实例的Entropy Key。实际上,真正起到验证作用的实际上只有第四项。


第二个错误的定论是DAC连接的条件,原文:“The user executing the script must also have sysadmin access to all the database instances (for the DAC connection) and local admin privileges on the Windows server (to access the entropy bytes in registry).(打开专用管理员连接有两个条件:一是需要有sqlserver的Sysadmin权限,二是本地服务器的管理员权限。)”。而实际上,打开DAC链接只需要属于sysadmin固定服务器角色成员,DAC是支持远程连接的。


于是有以下推论:

1.当某个程序的运行账户与sqlserver某实例服务账户相同,那么这个程序可以打开注册表并读取此实例的Entropy Key。
2.由于实例服务账户所在用户组SQLServerMSSQLUser$[机器名]$[实例名]存在于sqlserver登陆名列表中并属于sysadmin固定服务器角色成员,于是这个凭据可用来打开DAC链接。
3.由于实例服务账户属于本地组成员,所以可以打开MachineKey进行加密解密。
4.连接服务器密码解密只依赖于Entropy Key,MachineKey和DAC链接

最后得出结论:

当某个程序的运行账户与sqlserver某实例服务账户相同,则此程序可以获取到此sqlserver实例中所有链接服务器的密码。


在实际利用上,一个典型例子就是iis6。众所周知2003的iis默认应用程序池权限是Network Service,而一些管理员对sqlserver降权的做法也是将sqlserver实例服务账户更改为Network Service。于是在上述情况下,任何asp.net应用程序都可以通过windows验证方式直接作为sa登陆,同时解密并导出链接服务器密码。对于asp等由于使用了来宾帐户作为默认凭据,则需要执行额外的程序来获取。


如图:当sqlserver实例服务账户配置为network service运行时,默认iis应用程序池账户network service具有读取注册表的权限:


于是可以通过工具直接获取到链接服务器密码:


0x03. 工具与源码

有了以上结论与原始的Powershell脚本,工具也就不难写了,Powershell脚本实际上就是.net类库的调用,略作修改即可。

lspwd.exe为一键获取工具,lspwd.js为源码,编译命令行:

jsc /r:system.xml.dll lspwd.js

在asp.net中利用的工具以AspxSpy插件形式提供,GetMSSQLLinkedServerPasswordPlugin.cs为源码,编译命令行:

csc /t:library GetMSSQLLinkedServerPasswordPlugin.cs

GetMSSQLLinkedServerPasswordPlugin.dll和GetMSSQLLinkedServerPasswordPlugin.dll.Deflated分别为未压缩和压缩后的插件。插件信息如下:

TypeName:Zcg.Test.AspxSpyPlugins.GetMSSQLLinkedServerPasswordPlugin
MethodName:Run
HTML Result:true
Params:null

输出:当前服务器所有能成功连接的sqlserver实例中所有链接数据库信息。

调用结果如图:


下载地址:         mssql_linkedserver_pwd.zip

解压密码见注释。

Github: https://github.com/zcgonvh/LinkedServerPwdDumper