# MSSQL连接数据库密码获取工具与原文数个错误纠正

Author: 乌云历史资料库 (@wooyun_archive)
Published: 2014-04-25T04:41:00Z
Canonical: https://wepostx.com/topics/446

> 乌云历史资料归档
>
> **原始作者：** zcgonvh
> **原始编号：** superkieran-wooyundrops:231
> **原始发布时间：** 2014-04-25 12:41
> **声明：内容仅用于技术研究和个人使用，版权归 wooyun.org。**

---

原文链接：[http://drops.wooyun.org/tips/1067](http://drops.wooyun.org/tips/1067)。
在测试时发现了一些问题，于是把解决方案与一些结论共享出来，以供需要。

## 0x00 原文提供的powershell脚本出错的解决方案

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

```text
[ 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中获取到的密文为：

```text
[ 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](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](http://technet.microsoft.com/zh-cn/library/ms173486.aspx)）。

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

原文中有两个错误的定论，而这两个定论在某些条件下恰好可以组合成为一个新的获取途径。
第一个错误的定论是存放熵的注册表键所属项权限，原文：“再次提醒，访问此表项需要本地系统的管理员权限。”的说法是完全错误的。
可以想象，如果sqlserver以低权限运行时不能访问这个注册表项，那么执行sp_addlinkedsrvlogin必然会抛出错误，而实际上无论sqlserver的权限是什么，这个键总可以被sqlserver所访问。
在默认情况下，此键所在项（HKLM\SOFTWARE\Microsoft\Microsoft SQL Server\[instancename]\Security）有以下四个权限：

```text
system:完全控制，子项继承 administrators:完全控制，子项继承 creator owner:完全控制 SQLServerMSSQLUser$[机器名]$[实例名]:查询，修改，创建子项，枚举子项，通知，读取DAC
```
前两个权限不必解释，第三个权限代表所有者，一般为system/administrator。
第四项代表sqlserver实例用户组，每个实例对应一个。在修改sqlserver某个实例的服务登录帐户时，会自动将这个账户加入此组。
所以当某个程序的运行账户与sqlserver某实例服务账户相同，那么这个程序可以打开注册表并读取此实例的Entropy Key。实际上，真正起到验证作用的实际上只有第四项。
第二个错误的定论是DAC连接的条件，原文：“打开专用管理员连接有两个条件：一是需要有sqlserver的Sysadmin权限，二是本地服务器的管理员权限。”。
而实际上，打开DAC链接只需要属于sysadmin固定服务器角色成员，DAC是支持远程连接的。
于是有以下推论：

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

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

![原文图片](/media/2016/06/55d33b652205c2b05a9ac14349b97eba)

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

![原文图片](/media/2016/06/2205d7e7eb4386d291227e3af5d55e54)

有了以上结论与原始的Powershell脚本，工具也就不难写了，Powershell脚本实际上就是.net类库的调用，略作修改即可。
lspwd.exe为一键获取工具，lspwd.js为源码，编译命令行：

```text
jsc /r:system.xml.dll lspwd.js
```
在asp.net中利用的工具以AspxSpy插件形式提供，GetMSSQLLinkedServerPasswordPlugin.cs为源码，编译命令行：

```text
csc /t:library GetMSSQLLinkedServerPasswordPlugin.cs
```
GetMSSQLLinkedServerPasswordPlugin.dll和GetMSSQLLinkedServerPasswordPlugin.dll.Deflated分别为未压缩和压缩后的插件。
插件信息如下：

```text
TypeName:Zcg.Test.AspxSpyPlugins.GetMSSQLLinkedServerPasswordPlugin MethodName:Run HTML Result:true Params:null
```
输出:当前服务器所有能成功连接的sqlserver实例中所有链接数据库信息。
调用结果如图：

![原文图片](/media/2016/06/74b3783fad1cf6391576adfd213905a8)

附件：[mssql_linkedserver_pwd.7z](http://static.wooyun.org/20141017/2014101714011650242.7z)

## Replies
