原文链接:http://drops.wooyun.org/tips/1067

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

0x00 原文提供的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)。

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


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

第一个错误的定论是存放熵的注册表键所属项权限,原文:“再次提醒,访问此表项需要本地系统的管理员权限。”的说法是完全错误的。

可以想象,如果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连接的条件,原文:“打开专用管理员连接有两个条件:一是需要有sqlserver的Sysadmin权限,二是本地服务器的管理员权限。”。

而实际上,打开DAC链接只需要属于sysadmin固定服务器角色成员,DAC是支持远程连接的。

于是有以下推论:

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

最后得出结论:

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

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

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

2014042501272227571.png

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

2014042501284933549.png

有了以上结论与原始的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.dllGetMSSQLLinkedServerPasswordPlugin.dll.Deflated分别为未压缩和压缩后的插件。

插件信息如下:

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

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

调用结果如图:

2014042501315093112.png

附件:mssql_linkedserver_pwd.7z