简介
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 EXPLICITON ERROR RESUME NEXT' 常数值CONST EXIT_SUCCESS = 0CONST EXIT_FAILURE = 1CONST EXIT_NOINSTANCES = -1CONST DEFAULTNAMESPACE = "root\default"CONST STDREGPROV = "stdregprov"CONST HKEY_LOCAL_MACHINE = &H80000002CONST REG_MULTI_SZ = 7CONST REG_SZ = 1CONST adCmdText = 1Call VBMain()Function VBMain()Err.ClearON ERROR RESUME NEXTDim sInstances(), strInstance, i, TotalCountVBMain = EXIT_SUCCESSIf GetInstances(sInstances, TotalCount) = FALSE ThenWScript.Quit EXIT_FAILUREEnd IfIf IsEmptyNull(sInstances) Then WScript.Echo "信息: 不存在任何实例。"VBMain = EXIT_NOINSTANCESExit FunctionEnd IfFor i = 0 To TotalCount-1strInstance = sInstances(i,0)GetFullInstance strInstance, sInstances(i,1)If ApplyFix(sInstances(i,0), strInstance) = FALSE ThenWScript.Echo "错误: 无法在 " + sInstances(i,0) + "."+ vbCRLF 上应用替代方法VBMain = EXIT_FAILUREEnd IfNextWScript.Echo "信息:已完成处理所有运行的 SQL 实例。"End FunctionFunction GetInstances(ByRef sInstances, ByRef TotalCount)Err.ClearON ERROR RESUME NEXTDim sInstances1, sInstances2, iDim instCount1, instCount2GetInstances = FALSEIf NOT GetRegValue ("", HKEY_LOCAL_MACHINE, "Software\Microsoft\Microsoft SQL Server", "InstalledInstances", sInstances1, REG_MULTI_SZ, TRUE) ThenWScript.Echo "错误: 读取计算机上安装的 SQL 实例失败。"Exit FunctionEnd IfsInstances2 = NULLIf IsOs64Bit() = TRUE ThenIf NOT GetRegValue ("", HKEY_LOCAL_MACHINE, "Software\Microsoft\Microsoft SQL Server", "InstalledInstances", sInstances2, REG_MULTI_SZ, FALSE) ThenWScript.Echo "错误: 读取计算机上安装的 SQL 实例失败。"Exit FunctionEnd IfEnd IfIf IsEmptyNull(sInstances1) AND IsEmptyNull(sInstances2) Then WScript.Echo "信息:不存在任何实例。"WScript.Quit EXIT_SUCCESSEnd IfinstCount1 = 0instCount2 = 0 TotalCount = 0If IsEmptyNull(sInstances1) = FALSE TheninstCount1 = UBound(sInstances1) + 1TotalCount = instCount1End IfIf IsEmptyNull(sInstances2) = FALSE TheninstCount2 = UBound(sInstances2) + 1TotalCount = TotalCount + instCount2End IfReDim PRESERVE sInstances(TotalCount,1)if instCount1 > 0 ThenFor i = 0 To UBound(sInstances1)sInstances(i,0) = sInstances1(i)sInstances(i,1) = TrueNextEnd IfIf instCount2 >0 ThenFor i = 0 To UBound(sInstances2)sInstances(i+instCount1,0) = sInstances2(i)sInstances(i+instCount1,1) = FALSENextEnd IfGetInstances = TRUEEnd FunctionFunction ApplyFix(ByVal strInstance, ByVal strServerName)Err.ClearON ERROR RESUME NEXTDim objConn, objCmd, objCmd1, objRS, objRS1Dim strCommand, strConnDim strBuildVersion, strProductLevel, bApplyFix' 初始化返回值ApplyFix = FALSEstrConn = "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) ThenSet objConn = NothingExit FunctionEnd If' 在应用修补程序前验证版本strCommand = "select SERVERPROPERTY('ProductVersion') as version, SERVERPROPERTY('productlevel') as productlevel"objCmd.ActiveConnection = objConnobjCmd.CommandType = adCmdTextobjCmd.CommandText = strCommandSet objRS = objCmd.Execute()If ErrorOccurred("错误:无法在以下实例上执行 """ + strCommand + """:" + strInstance) = TRUE ThenobjConn.Close()Set objConn = NothingApplyFix = FALSEExit FunctionEnd IfstrBuildVersion = objRS("version")strProductLevel = UCase(objRS("productlevel"))bApplyFix = FALSE' 仅对 SQL 2000 和 SQL 2005(RTM、SP1 和 SP2)版本应用替代方法If (CInt(Mid(strBuildVersion,1,1)) = 8) ThenbApplyFix = TRUEElseIf CInt(Mid(strBuildVersion,1,1)) = 9 AND (StrComp(strProductLevel,"RTM") = 0 OR StrComp(strProductLevel,"SP1") = 0 OR StrComp(strProductLevel,"SP2") = 0) ThenbApplyFix = TRUEEnd If If bApplyFix = TRUE ThenstrCommand = "deny execute on sp_replwritetovarbin to public"objCmd1.ActiveConnection = objConnobjCmd1.CommandType = adCmdTextobjCmd1.CommandText = strCommandSet objRS1 = objCmd1.Execute()If ErrorOccurred("错误:无法在以下实例上执行 """ + strCommand + """:" + strInstance) = FALSE ThenWScript.Echo "信息:已成功将该替代方法应用于 " + strInstance + " (" + strBuildVersion + ")。"+ vbCRLFApplyFix = TRUEEnd IfElseWScript.Echo "信息:正在跳过对 " + strInstance + " (" + strBuildVersion + ") 收集信息,因为此实例无漏洞。"+ vbCRLFApplyFix = TRUEEnd IfobjConn.Close()Set objConn = NothingSet objCmd = NothingSet objCmd1 = NothingSet objRS = NothingSet objRS1 = NothingEnd FunctionPrivate Function GetRegValue (ByVal strMachineName, ByVal hMainKey, ByVal strPath, ByVal strValueName, ByRef strValue, ByVal iValueType, ByVal b32bit)Err.ClearON ERROR RESUME NEXTDim objLocator, objServices, objRegistry, objCtxDim sMultiStrings, lRcGetRegValue = TRUE'连接到 WMI 并获取 STDREGPROV 类的对象。Set objCtx = CreateObject("WbemScripting.SWbemNamedValueSet")If b32bit = TRUE ThenobjCtx.Add "__ProviderArchitecture", 32ElseobjCtx.Add "__ProviderArchitecture", 64End IfobjCtx.Add "__RequiredArchitecture", TRUEset objLocator = createobject("wbemscripting.swbemlocator")set objServices = objLocator.connectserver(strMachineName,DEFAULTNAMESPACE, "", "",,,,objCtx)set objRegistry = objServices.get(STDREGPROV)If ErrorOccurred ("错误: 无法连接到 WMI 命名空间" + DEFAULTNAMESPACE) ThenGetRegValue = FALSEExit FunctionEnd If lRc = 0Select Case iValueType' 我们仅关心 REG_MULTI_SZCase REG_MULTI_SZstrValue = ""lRC = objRegistry.GetMultiStringValue(hMainKey, strPath, strValueName, sMultiStrings)strValue = sMultiStringsCase REG_SZstrValue = ""lRC = objRegistry.GetStringValue(hMainKey, strPath, strValueName, strValue)Case ElseGetRegValue = FALSEEnd SelectIf lRc = 2 Or lRc = 3 ThenGetRegValue = TRUEstrValue = ""ElseIf Err.Number OR lRc <> 0 ThenGetRegValue = FALSEEnd IfSet objLocator = NothingSet objServices = NothingSet objRegistry = NothingEnd FunctionFunction IsEmptyNull(sCheck)IsEmptyNull = FALSEIf IsObject(sCheck) Then Exit FunctionIf IsArray(sCheck) Then Exit FunctionIf VarType(sCheck) = vbEmpty Then IsEmptyNull = TRUE :Exit FunctionIf VarType(sCheck) = vbNull Then IsEmptyNull = TRUE :Exit FunctionIf sCheck = "" Then IsEmptyNull = TRUEEnd FunctionPrivate Function ErrorOccurred (ByVal strIn)If Err.Number <> 0 ThenWScript.Echo strInWScript.Echo "错误:0x" & Err.Number & " - " & Err.DescriptionErr.ClearErrorOccurred = TRUEElseErrorOccurred = FALSEEnd IfEnd FunctionFunction IsOs64Bit()Err.ClearON ERROR RESUME NEXTDim objProcSet objProc = GetObject("winmgmts:root\cimv2:Win32_Processor='cpu0'")If objProc.Architecture = 0 ThenIsOs64Bit = FALSEElseIsOs64Bit = TRUEEnd IfEnd FunctionFunction GetFullInstance (ByRef strInstanceName, ByVal b32bit)Err.ClearON ERROR RESUME NEXT Dim objServices, objClusters, objClusterDim strMacName, isEmptyDim strKey, strInstIDGetFullInstance = TRUEIf strComp(UCase(strInstanceName), "MICROSOFT##SSEE", 1) = 0 ThenstrInstanceName = "np:\\.\pipe\mssql$microsoft##ssee\sql\query"Exit FunctionEnd ifstrMacName = ""Set objServices = GetObject("winmgmts:root\cimv2")' 查询群集服务Set objClusters = objServices.ExecQuery ("select * from win32_service where Name='ClusSvc' AND Started = TRUE")isEmpty = TRUEIf Err.Number = 0 Then对于 objClusters 中的每个 objClusterisEmpty = FALSENextEnd IfSet objServices = NothingSet objClusters = NothingIf isEmpty = TRUE ThenstrInstanceName = BuildInstanceName (".", strInstanceName)Exit FunctionEnd 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, b32bitIf StrComp(strMacName, "") <> 0 ThenstrInstanceName = BuildInstanceName (strMacName, strInstanceName)Exit FunctionEnd IfstrKey = "SOFTWARE\Microsoft\" + strInstanceName + "\Cluster"GetRegValue "", HKEY_LOCAL_MACHINE, strKey, "ClusterName", strMacName, REG_SZ, b32bitIf StrComp(strMacName, "") <> 0 ThenstrInstanceName = BuildInstanceName (strMacName, strInstanceName)Exit FunctionEnd If' 请尝试查询 2005/2008 实例的注册表值'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL' RegValue = InstanceNamestrInstID = ""strKey = "SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL"GetRegValue "", HKEY_LOCAL_MACHINE, strKey, strInstanceName, strInstID, REG_SZ, b32bitIf StrComp(strInstID, "") = 0 Then' 如果此密钥不存在,则返回作为 SQL 2000 本地实例strInstanceName = BuildInstanceName (".", strInstanceName)Exit FunctionEnd IfstrKey = "SOFTWARE\Microsoft\Microsoft SQL Server\" + strInstID + "\Cluster"GetRegValue "", HKEY_LOCAL_MACHINE, strKey, "ClusterName", strMacName, REG_SZ, b32bitIf StrComp(strMacName, "") = 0 ThenstrMacName = "."End IfstrInstanceName = BuildInstanceName (strMacName, strInstanceName)End FunctionFunction BuildInstanceName (ByVal strMachineName, ByVal strInstanceName)Dim strPrefixstrPrefix = ""If StrComp(strMachineName, ".")= 0 ThenstrPrefix = "lpc:"End IfIf strComp(UCase(strInstanceName), "MSSQLSERVER", 1) = 0 ThenBuildInstanceName = strPrefix + strMachineNameElseBuildInstanceName = strPrefix + strMachineName + "\" + strInstanceNameEnd ifEnd 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。 |