簡介
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 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「資訊:沒有執行個體。」 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「資訊: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「錯誤:無法讀取安裝在機器上的 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「資訊: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「資訊:Successfully applied the workaround on " + strInstance + " (" + strBuildVersion + ")."+ vbCRLF ApplyFix = TRUE End If Else WScript.Echo「資訊: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「錯誤: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 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 ' 如果我們執行到這個步驟,就表示該部機器為叢集節點。 ' 因此,請讓查詢登錄決定 SQL 執行個體是否為叢集。 ' 如為 SQL 2000,請查詢下列值 ' 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 ' 針對 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」,因為它不存在,或是您沒有權限。 錯誤:無法將因應措施套用至 <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。 |