使用 Microsoft 登录
登录或创建帐户。
你好,
使用其他帐户。
你有多个帐户
选择要登录的帐户。

简介

Microsoft 已发布一个安全公告,Microsoft SQL Server 中的漏洞允许远程代码执行。该安全公告包含其他与安全相关的信息。若要查看该安全公告,请访问下面的 Microsoft 网站:

http://www.microsoft.com/china/technet/security/advisory/961040.mspx本文包含一个 VB 脚本,您可以使用该脚本将一个替代方法应用于本地计算机上 SQL Server 的所有运行实例。

可用于应用该替代方法的 VB 脚本示例

对于本地计算机上运行的所有受影响版本的 SQL Server,您可以使用此 VB 脚本拒绝 sp_replwritetovarbin 扩展存储过程上的 Public 角色的执行权限。

Microsoft 提供的编程示例只用于演示目的,不带任何明示或暗示担保。这包括但不限于对适销性或特定用途适用性的暗示担保。本文假定您熟悉演示的编程语言以及用于创建和调试过程的工具。Microsoft 支持工程师可以帮助解释某个特定过程的功能。但是他们不会修改这些示例以提供额外的功能,也不会构建过程以满足您的特定要求。


将此代码复制到文本文件,使用 .vbs 文件扩展名保存该文件,然后使用 CScript.exe 运行脚本文件。该脚本在本地计算机上 SQL Server 的运行实例中迭代,并在受影响版本上应用该替代方法。您必须是每个 SQL Server 实例上 sysadmin 角色的成员才能应用此替代方法。如果您的 Windows 帐户不是运行 SQL Server 的所有受影响服务器上 sysadmin 角色的成员,则您必须从多个帐户运行此脚本。在 Windows Server 2008 和 Windows Vista 上,如果使用的 Windows 管理员帐户是 sysadmin 角色的成员,则必须从“提升权限”命令提示符运行此脚本。

'*************************************************************************************
'描述:此脚本在 SQL Server 的所有运行实例中迭代
' 并且在所有受影响的版本上拒绝 Public 角色在 sp_replwritetovarbin 上的
' 执行权限。
' 该方法作为一个替代方法提供,当提供并安装安全更新时,
' 不应使用此方法。
'*************************************************************************************

OPTION EXPLICIT
ON ERROR RESUME NEXT

' 常数值
CONST EXIT_SUCCESS = 0
CONST EXIT_FAILURE = 1
CONST EXIT_NOINSTANCES = -1
CONST DEFAULTNAMESPACE = "root\default"
CONST STDREGPROV = "stdregprov"
CONST HKEY_LOCAL_MACHINE = &H80000002
CONST REG_MULTI_SZ = 7
CONST REG_SZ = 1
CONST adCmdText = 1


Call VBMain()

Function VBMain()
Err.Clear
ON ERROR RESUME NEXT

Dim sInstances(), strInstance, i, TotalCount
VBMain = EXIT_SUCCESS
If GetInstances(sInstances, TotalCount) = FALSE Then
WScript.Quit EXIT_FAILURE
End If

If IsEmptyNull(sInstances) Then
WScript.Echo "信息: 不存在任何实例。"
VBMain = EXIT_NOINSTANCES
Exit Function
End If

For i = 0 To TotalCount-1
strInstance = sInstances(i,0)
GetFullInstance strInstance, sInstances(i,1)
If ApplyFix(sInstances(i,0), strInstance) = FALSE Then
WScript.Echo "错误: 无法在 " + sInstances(i,0) + "."+ vbCRLF 上应用替代方法
VBMain = EXIT_FAILURE
End If
Next

WScript.Echo "信息:已完成处理所有运行的 SQL 实例。"
End Function

Function GetInstances(ByRef sInstances, ByRef TotalCount)
Err.Clear
ON ERROR RESUME NEXT

Dim sInstances1, sInstances2, i
Dim instCount1, instCount2
GetInstances = FALSE

If NOT GetRegValue ("", HKEY_LOCAL_MACHINE, "Software\Microsoft\Microsoft SQL Server", "InstalledInstances", sInstances1, REG_MULTI_SZ, TRUE) Then
WScript.Echo "错误: 读取计算机上安装的 SQL 实例失败。"
Exit Function
End If

sInstances2 = NULL
If IsOs64Bit() = TRUE Then
If NOT GetRegValue ("", HKEY_LOCAL_MACHINE, "Software\Microsoft\Microsoft SQL Server", "InstalledInstances", sInstances2, REG_MULTI_SZ, FALSE) Then
WScript.Echo "错误: 读取计算机上安装的 SQL 实例失败。"
Exit Function
End If
End If

If IsEmptyNull(sInstances1) AND IsEmptyNull(sInstances2) Then
WScript.Echo "信息:不存在任何实例。"
WScript.Quit EXIT_SUCCESS
End If

instCount1 = 0
instCount2 = 0
TotalCount = 0
If IsEmptyNull(sInstances1) = FALSE Then
instCount1 = UBound(sInstances1) + 1
TotalCount = instCount1
End If

If IsEmptyNull(sInstances2) = FALSE Then
instCount2 = UBound(sInstances2) + 1
TotalCount = TotalCount + instCount2
End If

ReDim PRESERVE sInstances(TotalCount,1)
if instCount1 > 0 Then
For i = 0 To UBound(sInstances1)
sInstances(i,0) = sInstances1(i)
sInstances(i,1) = True
Next
End If
If instCount2 >0 Then
For i = 0 To UBound(sInstances2)
sInstances(i+instCount1,0) = sInstances2(i)
sInstances(i+instCount1,1) = FALSE
Next
End If
GetInstances = TRUE
End Function


Function ApplyFix(ByVal strInstance, ByVal strServerName)
Err.Clear
ON ERROR RESUME NEXT

Dim objConn, objCmd, objCmd1, objRS, objRS1
Dim strCommand, strConn
Dim strBuildVersion, strProductLevel, bApplyFix

' 初始化返回值
ApplyFix = FALSE

strConn = "Provider=sqloledb;Initial Catalog=master;Integrated Security=SSPI;Data Source=" + strServerName + ";"
' 为使代码较短,故意保留了错误检查
Set objConn = CreateObject("ADODB.Connection")
Set objCmd = CreateObject("ADODB.Command")
Set objCmd1 = CreateObject("ADODB.Command")

' 打开到主数据库的连接
objConn.Open strConn
If ErrorOccurred("错误:无法连接到 " + strInstance) Then
Set objConn = Nothing
Exit Function
End If

' 在应用修补程序前验证版本
strCommand = "select SERVERPROPERTY('ProductVersion') as version, SERVERPROPERTY('productlevel') as productlevel"
objCmd.ActiveConnection = objConn
objCmd.CommandType = adCmdText
objCmd.CommandText = strCommand

Set objRS = objCmd.Execute()
If ErrorOccurred("错误:无法在以下实例上执行 """ + strCommand + """:" + strInstance) = TRUE Then
objConn.Close()
Set objConn = Nothing
ApplyFix = FALSE
Exit Function
End If

strBuildVersion = objRS("version")
strProductLevel = UCase(objRS("productlevel"))

bApplyFix = FALSE
' 仅对 SQL 2000 和 SQL 2005(RTM、SP1 和 SP2)版本应用替代方法
If (CInt(Mid(strBuildVersion,1,1)) = 8) Then
bApplyFix = TRUE
ElseIf CInt(Mid(strBuildVersion,1,1)) = 9 AND (StrComp(strProductLevel,"RTM") = 0 OR StrComp(strProductLevel,"SP1") = 0 OR StrComp(strProductLevel,"SP2") = 0) Then
bApplyFix = TRUE
End If

If bApplyFix = TRUE Then
strCommand = "deny execute on sp_replwritetovarbin to public"
objCmd1.ActiveConnection = objConn
objCmd1.CommandType = adCmdText
objCmd1.CommandText = strCommand
Set objRS1 = objCmd1.Execute()
If ErrorOccurred("错误:无法在以下实例上执行 """ + strCommand + """:" + strInstance) = FALSE Then
WScript.Echo "信息:已成功将该替代方法应用于 " + strInstance + " (" + strBuildVersion + ")。"+ vbCRLF
ApplyFix = TRUE
End If
Else
WScript.Echo "信息:正在跳过对 " + strInstance + " (" + strBuildVersion + ") 收集信息,因为此实例无漏洞。"+ vbCRLF
ApplyFix = TRUE
End If

objConn.Close()
Set objConn = Nothing
Set objCmd = Nothing
Set objCmd1 = Nothing
Set objRS = Nothing
Set objRS1 = Nothing
End Function

Private Function GetRegValue (ByVal strMachineName, ByVal hMainKey, ByVal strPath, ByVal strValueName, ByRef strValue, ByVal iValueType, ByVal b32bit)
Err.Clear
ON ERROR RESUME NEXT

Dim objLocator, objServices, objRegistry, objCtx
Dim sMultiStrings, lRc
GetRegValue = TRUE

'连接到 WMI 并获取 STDREGPROV 类的对象。
Set objCtx = CreateObject("WbemScripting.SWbemNamedValueSet")
If b32bit = TRUE Then
objCtx.Add "__ProviderArchitecture", 32
Else
objCtx.Add "__ProviderArchitecture", 64
End If
objCtx.Add "__RequiredArchitecture", TRUE
set objLocator = createobject("wbemscripting.swbemlocator")
set objServices = objLocator.connectserver(strMachineName,DEFAULTNAMESPACE, "", "",,,,objCtx)
set objRegistry = objServices.get(STDREGPROV)
If ErrorOccurred ("错误: 无法连接到 WMI 命名空间" + DEFAULTNAMESPACE) Then
GetRegValue = FALSE
Exit Function
End If

lRc = 0
Select Case iValueType
' 我们仅关心 REG_MULTI_SZ
Case REG_MULTI_SZ
strValue = ""
lRC = objRegistry.GetMultiStringValue(hMainKey, strPath, strValueName, sMultiStrings)
strValue = sMultiStrings
Case REG_SZ
strValue = ""
lRC = objRegistry.GetStringValue(hMainKey, strPath, strValueName, strValue)
Case Else
GetRegValue = FALSE
End Select

If lRc = 2 Or lRc = 3 Then
GetRegValue = TRUE
strValue = ""
ElseIf Err.Number OR lRc <> 0 Then
GetRegValue = FALSE
End If

Set objLocator = Nothing
Set objServices = Nothing
Set objRegistry = Nothing
End Function

Function IsEmptyNull(sCheck)
IsEmptyNull = FALSE
If IsObject(sCheck) Then Exit Function
If IsArray(sCheck) Then Exit Function
If VarType(sCheck) = vbEmpty Then IsEmptyNull = TRUE :Exit Function
If VarType(sCheck) = vbNull Then IsEmptyNull = TRUE :Exit Function
If sCheck = "" Then IsEmptyNull = TRUE
End Function

Private Function ErrorOccurred (ByVal strIn)
If Err.Number <> 0 Then
WScript.Echo strIn
WScript.Echo "错误:0x" & Err.Number & " - " & Err.Description
Err.Clear
ErrorOccurred = TRUE
Else
ErrorOccurred = FALSE
End If
End Function

Function IsOs64Bit()
Err.Clear
ON ERROR RESUME NEXT

Dim objProc
Set objProc = GetObject("winmgmts:root\cimv2:Win32_Processor='cpu0'")
If objProc.Architecture = 0 Then
IsOs64Bit = FALSE
Else
IsOs64Bit = TRUE
End If
End Function

Function GetFullInstance (ByRef strInstanceName, ByVal b32bit)
Err.Clear
ON ERROR RESUME NEXT

Dim objServices, objClusters, objCluster
Dim strMacName, isEmpty
Dim strKey, strInstID

GetFullInstance = TRUE

If strComp(UCase(strInstanceName), "MICROSOFT##SSEE", 1) = 0 Then
strInstanceName = "np:\\.\pipe\mssql$microsoft##ssee\sql\query"
Exit Function
End if

strMacName = ""
Set objServices = GetObject("winmgmts:root\cimv2")

' 查询群集服务
Set objClusters = objServices.ExecQuery ("select * from win32_service where Name='ClusSvc' AND Started = TRUE")
isEmpty = TRUE
If Err.Number = 0 Then
对于 objClusters 中的每个 objCluster
isEmpty = FALSE
Next
End If

Set objServices = Nothing
Set objClusters = Nothing

If isEmpty = TRUE Then
strInstanceName = BuildInstanceName (".", strInstanceName)
Exit Function
End If

' 如果执行到此步,则表明计算机为群集节点。
' 因此查询注册表可确定是否群集 SQL 实例。
' 对于 SQL 2000,查询以下值
' HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\<InstanceName>\Cluster
' 群集名称
strKey = "SOFTWARE\Microsoft\Microsoft SQL Server\" + strInstanceName + "\Cluster"
GetRegValue "", HKEY_LOCAL_MACHINE, strKey, "ClusterName", strMacName, REG_SZ, b32bit

If StrComp(strMacName, "") <> 0 Then
strInstanceName = BuildInstanceName (strMacName, strInstanceName)
Exit Function
End If

strKey = "SOFTWARE\Microsoft\" + strInstanceName + "\Cluster"
GetRegValue "", HKEY_LOCAL_MACHINE, strKey, "ClusterName", strMacName, REG_SZ, b32bit

If StrComp(strMacName, "") <> 0 Then
strInstanceName = BuildInstanceName (strMacName, strInstanceName)
Exit Function
End If

' 请尝试查询 2005/2008 实例的注册表值
'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL
' RegValue = InstanceName
strInstID = ""
strKey = "SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL"
GetRegValue "", HKEY_LOCAL_MACHINE, strKey, strInstanceName, strInstID, REG_SZ, b32bit

If StrComp(strInstID, "") = 0 Then
' 如果此密钥不存在,则返回作为 SQL 2000 本地实例
strInstanceName = BuildInstanceName (".", strInstanceName)
Exit Function
End If

strKey = "SOFTWARE\Microsoft\Microsoft SQL Server\" + strInstID + "\Cluster"
GetRegValue "", HKEY_LOCAL_MACHINE, strKey, "ClusterName", strMacName, REG_SZ, b32bit

If StrComp(strMacName, "") = 0 Then
strMacName = "."
End If

strInstanceName = BuildInstanceName (strMacName, strInstanceName)
End Function

Function BuildInstanceName (ByVal strMachineName, ByVal strInstanceName)
Dim strPrefix

strPrefix = ""
If StrComp(strMachineName, ".")= 0 Then
strPrefix = "lpc:"
End If

If strComp(UCase(strInstanceName), "MSSQLSERVER", 1) = 0 Then
BuildInstanceName = strPrefix + strMachineName
Else
BuildInstanceName = strPrefix + strMachineName + "\" + strInstanceName
End if
End Function

有关 CScript.exe 的更多信息,请访问下面的 Microsoft 网站:

http://technet.microsoft.com/en-us/library/bb490887.aspx注意 如果已提供安全更新且已安装,则建议不要使用该脚本。

在运行此脚本时可能发生的已知问题

问题 1

运行脚本时,会接收到以下错误消息:

错误:无法在 <instancename> 上执行“deny execute on sp_replwritetovarbin to public”
错误:0x-2147217900 - 找不到对象“sp_replwritetovarbin”,原因是该对象不存在或者您没有相应权限。
错误: 无法应用 <实例名> 上的替代方法。

原因 1

如果没有应用此更改所需的权限,则会接收到此错误消息。此错误消息表明您能够成功登录到实例“<instancename>”。


默认情况下,其中 "Built-In\Users" 组登录到数据库的 SQL Server Express 通常会出现此错误消息。但是,该组不是 sysadmin 角色的成员。

如果您丢弃 sp_replwritetovarbin 过程,也可能出现此错误消息。第三方报告中建议丢弃此过程。我们不建议丢弃存储的过程。相反,建议您应用该解决方案。

解决方案 1

确保所连接的帐户为该数据库实例上 sysadmin 角色的成员。如果该帐户不是此角色的成员,那么,或者将您用来连接的用户添加到 sysadmin 角色或者使用其他用户帐户。对于 SQL Server 2005 和更低版本,“Built-in\Administrators”组在默认情况下是 sysadmin 角色的成员。在 Windows Vista 或 Windows Server 2008 上运行该脚本时,请确保从“提升权限”命令提示符运行。

问题 2

如果在 SQL Server 2005 中运行该脚本,则会接收到以下错误消息:

错误:无法连接到 <instancename>
错误:0x-2147217843 - 用户“<user>”的登录失败。
错误: 无法应用 <实例名> 上的替代方法。

原因 2

如果无法连接到实例“<instancename>”(即使该实例存在),则会收到此错误消息。


当您连接到 Windows Internal Database 或 Microsoft SQL Server 2000 Desktop Edition (Windows) 实例时通常会出现此错误消息。通常,用户帐户没有登录到这些数据库。

解决方案 2

确保用于运行脚本的帐户登录的数据库属于 sysadmin 角色成员。

建议您不要将单个用户添加到 Windows Internal Database 和 Microsoft SQL Server 2000 Desktop Edition (Windows) 数据库。如果执行此操作,所添加的用户可能会妨碍这些数据库的常规操作。在这种情况下,请确保连接的帐户为 sysadmin 角色的成员。默认情况下,Windows 中的 "Built-in\Administrators" 组通常为 SQL Server 2005 以及较早版本中 sysadmin 角色的成员。在 Windows Vista 或 Windows Server 2008 中运行此脚本时,请确保从“提升权限”命令提示符运行。

问题 3

可能会注意到名为 MICROSOFT##SSEE 的数据库实例。不过,您并没有安装此数据库。

原因 3

此数据库为 Windows Internal Database,也称为 "SQL Server Embedded Edition",有时称为 "Windows Internal Database" 或 "Microsoft SQL Server 2000 Desktop Edition (Windows)"。它是随 Microsoft 的某些产品(包括 SharePoint Services)安装的。

解决方案 3

该替代方法脚本旨在与 Windows Internal Database 一起使用。您无需执行任何操作。

某些应用程序在卸载时没有删除 Windows Internal Database。
有关如何删除 Windows Internal Database 的更多信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:

920277Windows Internal Database 未列在“添加或删除程序”工具中,从计算机删除 Windows SharePoint Services 3.0 时未删除 Windows Internal Database

问题 4

运行脚本时,会接收到以下错误消息:


错误: 无法连接到 .\<实例名>
错误:0x-2147467259 - [DBNETLIB][ConnectionOpen (Connect()).]SQL Server 不存在或拒绝访问

原因 4

如果以下条件为真,会接收到该错误消息:

  • 在 X64 位操作系统上安装 32 位版本的 SQL Server 2000。

  • 在计算机上安装 64 位版本的 SQL Server 2005 或 SQL Server 2008。


如果脚本使用 64 位版本的 dbmslpcn.dll 文件,则会出现此错误消息。该版本无法与 SQL Server 2000 的 WoW 实例进行通信。

解决方案 4

使用 %WINDOWS%\SysWOW64 文件夹中 32 位版本的 cscript.exe 文件启动脚本。这样加载的 32 位版本的 dbmslpcn.dll 文件可以检测到 WoW 实例。

参考

有关如何识别 SQL Server 版本的更多信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:

321185 如何识别 SQL Server 的版本

更多信息

下表列出本文的重要技术修订。本文中的版本号和最后检查日期可能表明该表中不包括的本文的次要编辑修订或结构修订。

日期

修订

2008 年 12 月 31 日

包含的更新脚本可检测 SQL Server 故障转移群集实例。

2008 年 12 月 30 日

包含的更新脚本检测到 64 位版本的 Windows 中运行的 32 位版本的 SQL Server。

需要更多帮助?

需要更多选项?

了解订阅权益、浏览培训课程、了解如何保护设备等。

社区可帮助你提出和回答问题、提供反馈,并听取经验丰富专家的意见。

此信息是否有帮助?

你对语言质量的满意程度如何?
哪些因素影响了你的体验?
按“提交”即表示你的反馈将用于改进 Microsoft 产品和服务。 你的 IT 管理员将能够收集此数据。 隐私声明。

谢谢您的反馈!

×