簡介

Microsoft 已針對 Microsoft SQL Server 的弱點發佈資訊安全諮詢,該弱點可能會允許遠端程式碼執行。此資訊安全諮詢包含其他安全性相關資訊。如果要檢視此資訊安全諮詢,請造訪下列 Microsoft 網站:

http://www.microsoft.com/taiwan/technet/security/advisory/961040.mspx本文包含 VB 指令碼,可讓您將因應措施套用至本機電腦上 SQL Server 之所有執行中的執行個體。

您可用於套用因應措施的 VB 指令碼範例

在本機電腦上執行之所有受影響版本的 SQL Server 上,您可以使用此 VB 指令碼來拒絕 sp_replwritetovarbin 延伸預存程序中「公用」角色的「執行」權限。Microsoft 僅提供示範性的程式設計範例,不做任何明示或默示的保證。其中包括 (但不限於) 其適售性與適合某特定用途之默示擔保。本文將假設您已相當熟悉示範所使用的程式設計語言,以及用於建立和偵錯程序的工具。Microsoft 技術支援工程師可以協助說明特定程序的功能,但不會修改這些範例以提供附加功能或建構程序來滿足您的特定需求。 將此程式碼複製到文字檔案,再使用 .vbs 副檔名來儲存該檔案,接著執行使用 CScript.exe 的指令檔案。該指令碼會逐一查看本機電腦上 SQL Server 之執行中的執行個體,並將因應措施套用至受影響的版本。在 SQL Server 之各執行個體上,您必須為系統管理員 (sysadmin) 角色的成員,才能套用因應措施。在所有執行 SQL Server 之受影響的伺服器上,如果您沒有系統管理員 (sysadmin) 角色成員的 Windows 帳戶,則您可能必須從多個帳戶執行此指令碼。在 Windows Server 2008 和 Windows Vista 上,如果您使用的是系統管理員 (sysadmin) 角色成員的 Windows 系統管理員帳戶,則您必須從「提高權限」的命令提示字元中執行此指令碼。

'*************************************************************************************'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 EXPLICITON ERROR RESUME NEXT' Constant valuesCONST 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) +』上套用因應措施。」+ vbCRLFVBMain = EXIT_FAILUREEnd IfNextWScript.Echo「資訊:Completed processing all the running SQL instances."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「資訊:No instances present."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' Initialize return valueApplyFix = FALSEstrConn = "Provider=sqloledb;Initial Catalog=master;Integrated Security=SSPI;Data Source=" + strServerName + ";"' Error checking is intentionally left to keep the code shortSet objConn = CreateObject("ADODB.Connection")Set objCmd = CreateObject("ADODB.Command")Set objCmd1 = CreateObject("ADODB.Command")' Open a Connection to the master DatabaseobjConn.Open strConn If ErrorOccurred("Error:Could not connect to " + strInstance) ThenSet objConn = NothingExit FunctionEnd If' Validate the version before applying the fixstrCommand = "select SERVERPROPERTY('ProductVersion') as version, SERVERPROPERTY('productlevel') as productlevel"objCmd.ActiveConnection = objConnobjCmd.CommandType = adCmdTextobjCmd.CommandText = strCommandSet objRS = objCmd.Execute()If ErrorOccurred("ERROR:Could not execute """ + strCommand + """ on " + strInstance) = TRUE ThenobjConn.Close()Set objConn = NothingApplyFix = FALSEExit Function   End IfstrBuildVersion = objRS("version")strProductLevel = UCase(objRS("productlevel"))bApplyFix = FALSE' Apply the workaround only for SQL 2000 and SQL 2005 (RTM, SP1 and SP2) versionsIf (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 IfIf bApplyFix = TRUE ThenstrCommand = "deny execute on sp_replwritetovarbin to public"objCmd1.ActiveConnection = objConnobjCmd1.CommandType = adCmdTextobjCmd1.CommandText = strCommandSet objRS1 = objCmd1.Execute()If ErrorOccurred("ERROR:Could not execute """ + strCommand + """ on " + strInstance) = FALSE ThenWScript.Echo「資訊:Successfully applied the workaround on " + strInstance + " (" + strBuildVersion + ")."+ vbCRLFApplyFix = TRUEEnd IfElseWScript.Echo「資訊:Skipping collecting information for " + strInstance + " (" + strBuildVersion + ") as this instance is not vulnerable."+ 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'Connect to WMI and get an object to STDREGPROV class.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 ("ERROR:Could not connect to WMI namespace " + DEFAULTNAMESPACE) ThenGetRegValue = FALSEExit FunctionEnd IflRc = 0Select Case iValueType' We only care about 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 NEXTDim 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 ThenFor each objCluster in objClustersisEmpty = 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' ClusterNamestrKey = "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」,因為它不存在,或是您沒有權限。錯誤:無法將因應措施套用至 <instancename>。

原因 1

如果您沒有具備套用此變更的權限,則會收到此錯誤訊息。此錯誤訊息表示您能夠成功登入執行個體「<instancename>」。此錯誤訊息通常在 SQL Server Express 中發生,其中的「Built-In\Users」群組在預設情況下會擁有一筆資料庫登入。但是,此群組並非系統管理員 (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>」登入失敗。錯誤:無法將因應措施套用至 <instancename>。

原因 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) 角色的成員。在 SQL Server 2005 和較舊版本中,「Built-in\Administrators」群組在預設情況下是系統管理員 (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)」。您可以使用如 SharePoint Services 這類的 Microsoft 產品來安裝此資料庫。

解決方式 3

此因應措施指令碼專為與 Windows Internal Database 搭配運作所設計。您不須執行任何動作。解除安裝某些應用程式時,它們並沒有移除 Windows Internal Database。 如需有關如何移除 Windows Internal Database 的詳細資訊,請按一下下面的文件編號,檢視「Microsoft 知識庫」中的文件:

920277[新增或移除程式] 工具中沒有列出 Windows Internal Database,且當您從電腦中移除 Windows SharePoint Services 3.0 時,沒有將其移除。

問題 4

當您執行指令碼時,收到下列錯誤訊息:

錯誤:無法連線到 .\<instancename>錯誤:0x-2147467259 - [DBNETLIB][ConnectionOpen (Connect()).]SQL Server 不存在或拒絕存取

原因 4

當下列情況成立時,您會收到錯誤訊息:

  • 您已將 32 位元版本的 SQL Server 2000 安裝在 x64 位元作業系統。

  • 您已將 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。

需要更多協助嗎?

想要其他選項嗎?

探索訂閱權益、瀏覽訓練課程、瞭解如何保護您的裝置等等。