简介
Microsoft发布了有关Microsoft SQL Server中可能允许远程执行代码的漏洞的安全公告。 安全公告包含其他与安全相关的信息。 若要查看安全公告,请访问以下Microsoft网站:
http://www.microsoft.com/technet/security/advisory/961040.mspx本文包含一个 VB 脚本,可用于将解决方法应用于本地计算机上所有正在运行的 SQL Server 实例。
可用于应用解决方法的 VB 脚本示例
可以使用此 VB 脚本拒绝对本地计算机上运行的所有受影响SQL Server版本的sp_replwritetovarbin扩展存储过程的公共角色执行权限。
Microsoft 的编程示例仅用于说明,不做任何明示或暗示的保证。 这包括但不限于特定用途的适销性或适用性的隐含保证。 本文假定你熟悉所演示的编程语言以及用于创建和调试过程的工具。 Microsoft 支持工程师可以帮助解释特定过程的功能。 但他们不会修改这些示例以提供附加功能或构造满足你的特定要求的过程。
将此代码复制到文本文件,使用 .vbs 文件扩展名保存文件,然后使用 CScript.exe 运行脚本文件。 该脚本循环访问本地计算机上正在运行的 SQL Server 实例,并在受影响的版本上应用解决方法。 你必须是每个 SQL Server 实例的 sysadmin 角色的成员才能应用解决方法。 如果 Windows 帐户不是运行 SQL Server 的所有受影响的服务器上的 sysadmin 角色的成员,则可能必须从多个帐户运行此脚本。 在 Windows Server 2008 和 Windows Vista 上,如果使用的 Windows 管理员帐户是 sysadmin 角色的成员,则必须从“提升的”命令提示符运行此脚本。
'*************************************************************************************
'Description: This script iterates through all the running instances of SQL Server
' and denies execute permission on sp_replwritetovarbin to public on all
' the affected versions.
' THIS IS PROVIDED AS A WORKAROUND AND SHOULD NOT BE USED IN THE EVENT THAT
' A SECURITY UPDATE IS PROVIDED AND INSTALLED.
'*************************************************************************************
OPTION EXPLICIT
ON ERROR RESUME NEXT
' Constant values
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 "INFO: No instances are present."
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 "ERROR: Could not apply the workaround on " + sInstances(i,0) + "." + vbCRLF
VBMain = EXIT_FAILURE
End If
Next
WScript.Echo "INFO: Completed processing all the running SQL instances."
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 "ERROR:Failed to read SQL instances installed on the machine."
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 "ERROR:Failed to read SQL instances installed on the machine."
Exit Function
End If
End If
If IsEmptyNull(sInstances1) AND IsEmptyNull(sInstances2) Then
WScript.Echo "INFO: No instances present."
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
' Initialize return value
ApplyFix = FALSE
strConn = "Provider=sqloledb;Initial Catalog=master;Integrated Security=SSPI;Data Source=" + strServerName + ";"
' Error checking is intentionally left to keep the code short
Set objConn = CreateObject("ADODB.Connection")
Set objCmd = CreateObject("ADODB.Command")
Set objCmd1 = CreateObject("ADODB.Command")
' Open a Connection to the master Database
objConn.Open strConn
If ErrorOccurred("Error: Could not connect to " + strInstance) Then
Set objConn = Nothing
Exit Function
End If
' Validate the version before applying the fix
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("ERROR: Could not execute """ + strCommand + """ on " + strInstance) = TRUE Then
objConn.Close()
Set objConn = Nothing
ApplyFix = FALSE
Exit Function
End If
strBuildVersion = objRS("version")
strProductLevel = UCase(objRS("productlevel"))
bApplyFix = FALSE
' Apply the workaround only for SQL 2000 and SQL 2005 (RTM, SP1 and SP2) versions
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("ERROR: Could not execute """ + strCommand + """ on " + strInstance) = FALSE Then
WScript.Echo "INFO: Successfully applied the workaround on " + strInstance + " (" + strBuildVersion + ")." + vbCRLF
ApplyFix = TRUE
End If
Else
WScript.Echo "INFO: Skipping collecting information for " + strInstance + " (" + strBuildVersion + ") as this instance is not vulnerable." + 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
'Connect to WMI and get an object to STDREGPROV class.
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 ("ERROR: Could not connect to WMI namespace " + DEFAULTNAMESPACE) Then
GetRegValue = FALSE
Exit Function
End If
lRc = 0
Select Case iValueType
' We only care about 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 "ERROR: 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")
' Query Cluster service
Set objClusters = objServices.ExecQuery ("select * from win32_service where Name='ClusSvc' AND Started = TRUE")
isEmpty = TRUE
If Err.Number = 0 Then
For each objCluster in objClusters
isEmpty = FALSE
Next
End If
Set objServices = Nothing
Set objClusters = Nothing
If isEmpty = TRUE Then
strInstanceName = BuildInstanceName (".", strInstanceName)
Exit Function
End If
' If we reach here that means the machine is a clustered node.
' So lets query registry to determine whether the SQL instance is clustered or not.
' For SQL 2000 query the following value
' HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\<InstanceName>\Cluster
' ClusterName
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
' Lets try querying the registry value for 2005/2008 instances
'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
' If this key doesnt exist, then return back as a SQL 2000 local instance
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 执行“拒绝执行sp_replwritetovarbin到公共”>
错误:0x-2147217900 - 找不到对象“sp_replwritetovarbin”,因为它不存在或您没有权限。
错误:无法对 <instancename> 应用解决方法。
原因 1
如果你没有应用更改所需的权限,则会收到此错误消息。 此错误消息指示你能够成功登录到实例“<instancename>”。
此错误消息通常出现在“内置\用户”组默认登录到数据库的SQL Server Express中。 但是,此组不是 sysadmin 角色的成员。
如果删除了sp_replwritetovarbin过程,也可能会出现此错误消息。 这是第三方报告的建议。 建议不要删除存储过程。 相反,建议应用此解决方法。
解决方案 1
确保连接到的帐户是该数据库实例上的 sysadmin 角色的成员。 如果帐户不是成员,请将要连接的用户添加为 sysadmin 角色,或使用另一个用户帐户。 对于 SQL Server 2005 及更早版本,默认情况下,“内置\管理员”组是 sysadmin 角色的成员。 在 Windows Vista 或 Windows Server 2008 上运行此脚本时,请确保从“提升的”命令提示符运行该脚本。
问题 2
如果在 SQL Server 2005 中运行此脚本,则会收到以下错误消息:
注意
错误:无法连接到 <instancename>
错误:0x-2147217843 - 用户“user>”<登录失败。
错误:无法对 <instancename> 应用解决方法。
原因 2
如果无法连接到实例“instancename>”<,即使此实例存在,也会收到此错误消息。
连接到 Windows 内部数据库 或 Microsoft SQL Server 2000 Desktop Edition (Windows) 实例时,通常会出现此错误消息。 通常,没有用户帐户具有这些数据库的登录名。
解决方案 2
确保用于运行脚本的帐户在数据库上具有登录名,该登录名是 sysadmin 角色的成员。
建议不要将单个用户添加到 Windows 内部数据库 和 Microsoft SQL Server 2000 Desktop Edition (Windows) 数据库。 如果执行此操作,则添加的用户可能会干扰这些数据库的正常操作。 在这种情况下,请确保从作为 sysadmin 角色成员的帐户进行连接。 默认情况下,Windows 中的“内置\管理员”组在 SQL Server 2005 和早期版本中通常是 sysadmin 角色的成员。 在 Windows Vista 或 Windows Server 2008 上运行此脚本时,请确保从“提升的”命令提示符运行该脚本。
问题 3
你可能会注意到名为 MICROSOFT##SSEE 的数据库实例。 但是,未安装此数据库。
原因 3
此数据库是Windows 内部数据库,也称为“SQL Server Embedded Edition”,有时也称为“Windows 内部数据库”或“Microsoft SQL Server 2000 桌面版 (Windows) ”。它随Microsoft中的某些产品一起安装,包括 SharePoint Services。
解决方法 3
解决方法脚本旨在通过Windows 内部数据库运行。 你无需执行任何操作。
某些应用程序在卸载时不会删除Windows 内部数据库。
有关如何删除Windows 内部数据库的详细信息,请单击下面的文章编号以查看Microsoft知识库中的文章:
920277 Windows 内部数据库未在“添加或删除程序”工具中列出,并且从计算机中删除 Windows SharePoint Services 3.0 时不会删除
问题 4
运行脚本时,会收到以下错误消息:
注意
错误:无法连接到 .\<instancename>
错误: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 文件启动脚本。 这将加载可以检测 WoW 实例的 32 位版本的 dbmslpcn.dll 文件。
参考资料
有关如何标识SQL Server版本的详细信息,请单击下面的文章编号以查看Microsoft知识库中的文章:
321185如何标识SQL Server版本
详细信息
下表列出了本文的重要技术修订。 本文中的修订号和上次审阅日期可能表示表中未包括的对本文的次要编辑修订或结构修订。
| 日期 | 修订 |
|---|---|
| 2008 年 12 月 31 日 | 包括一个更新的脚本,用于检测SQL Server故障转移聚类分析实例。 |
| 2008 年 12 月 30 日 | 包括一个更新的脚本,用于检测在 64 位版本的 Windows 上运行的 32 位版本的SQL Server。 |