KB961040 - Microsoft セキュリティ アドバイザリ: SQL Serverの脆弱性により、リモートでコードが実行される

適用先
Microsoft SQL Server 2005 Service Pack 2 Microsoft SQL Server 2005 Standard Edition Microsoft SQL Server 2005 Enterprise Edition Microsoft SQL Server 2005 Express Edition Microsoft SQL Server 2005 Express Edition with Advanced Services Microsoft SQL Server 2005 Enterprise X64 Edition Microsoft SQL Server 2005 Standard X64 Edition

はじめに

Microsoft は、リモートでコードを実行できる可能性がある Microsoft SQL Serverの脆弱性に関するセキュリティ アドバイザリをリリースしました。 セキュリティ アドバイザリには、この問題に関連する追加のセキュリティ関連の情報が含まれています。 セキュリティ アドバイザリを参照するには、次のマイクロソフト Web サイトにアクセスしてください。

http://www.microsoft.com/technet/security/advisory/961040.mspxこの記事には、ローカル コンピューター上のSQL Serverのすべての実行中のインスタンスに回避策を適用するために使用できる VB スクリプトが含まれています。

回避策を適用するために使用できる VB スクリプトの例

この VB スクリプトを使用すると、ローカル コンピューターで実行されているすべての影響を受けるバージョンのSQL Serverで、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 "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 Web サイトを参照してください。

http://technet.microsoft.com/en-us/library/bb490887.aspx 注 このスクリプトは、セキュリティ更新プログラムが提供されていてインストールされている場合は使用しないことをお勧めします。

このスクリプトを実行するときに発生する可能性がある既知の問題

問題 1

スクリプトを実行すると、次のエラー メッセージが表示されます。

エラー: <instancename で "sp_replwritetovarbin からパブリックへの実行を拒否する" を実行できませんでした>
 ERROR: 0x-2147217900 - オブジェクト 'sp_replwritetovarbin' が見つかりません。存在しないか、アクセス許可がないためです。
エラー: <instancename>で回避策を適用できませんでした。

原因 1

変更を適用するために必要なアクセス許可がない場合は、このエラー メッセージが表示されます。 このエラー メッセージは、インスタンス "<instancename>" に正常にログインできたことを示します。

このエラー メッセージは、通常、"組み込み\Users" グループが既定でデータベースにログインしているSQL Server Expressで発生します。 ただし、このグループは sysadmin ロールのメンバーではありません。

このエラー メッセージは、sp_replwritetovarbin プロシージャを削除した場合にも発生する可能性があります。 これは、サード パーティのレポートからの推奨事項でした。 ストアド プロシージャを削除することはお勧めしません。 代わりに、この解決策を適用することをお勧めします。

解決方法 1

接続するアカウントが、データベースのそのインスタンスの sysadmin ロールのメンバーであることを確認します。 アカウントがメンバーでない場合は、接続しているユーザーを sysadmin ロールに追加するか、別のユーザー アカウントを使用します。 SQL Server 2005 以前の場合、"組み込み\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 ロールのメンバーであるアカウントから接続してください。 Windows の "組み込み\Administrators" グループは、通常、SQL Server 2005 以前のバージョンでは既定で sysadmin ロールのメンバーです。 Windows Vista または Windows Server 2008 でこのスクリプトを実行する場合は、"管理者特権" コマンド プロンプトから実行してください。

問題 3

MICROSOFT##SSEE という名前のデータベースのインスタンスに気付く場合があります。 ただし、このデータベースはインストールされませんでした。

原因 3

このデータベースは、"SQL Server Embedded Edition" とも呼ばれるWindows Internal Databaseであり、"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

次の条件に該当する場合、このエラー メッセージが表示されます。

  • x64 ビット オペレーティング システムに 32 ビット バージョンの SQL Server 2000 がインストールされています。
  • コンピューターには、64 ビット バージョンの SQL Server 2005 または SQL Server 2008 がインストールされています。

このエラー メッセージは、スクリプトが dbmslpcn.dll ファイルの 64 ビット バージョンを使用している場合に発生します。 このバージョンは、SQL Server 2000 の WoW インスタンスと通信できません。

解決策 4

スクリプトを開始するには、%WINDOWS%\SysWOW64 フォルダーの cscript.exe ファイルの 32 ビット バージョンを使用します。 これにより、WoW インスタンスを検出できる 32 ビット バージョンの dbmslpcn.dll ファイルが読み込まれます。

参考資料

SQL Serverのバージョンとエディションを識別する方法の詳細については、次の記事番号をクリックして、Microsoft サポート技術情報の記事を参照してください。

321185 SQL Server のバージョンとエディションを識別する方法

追加情報

次の表に、この記事の重要な技術的なリビジョンを示します。 この記事のリビジョン番号と最終レビュー日は、表に含まれていない、この記事の軽微な編集リビジョンまたは構造リビジョンを示している可能性があります。

日付 リビジョン
2008 年 12 月 31 日 フェールオーバー クラスタリング インスタンスSQL Server検出する更新されたスクリプトが含まれています。
2008 年 12 月 30 日 64 ビット バージョンの Windows で実行されている 32 ビット バージョンのSQL Serverを検出する更新されたスクリプトが含まれています。