Microsoft Security Advisory: Luka w zabezpieczeniach programu SQL Server umożliwia zdalne wykonywanie kodu

WPROWADZENIE

Firma Microsoft wydała dokument Security Advisory dotyczący luki w zabezpieczeniach programu Microsoft SQL Server umożliwiającej zdalne wykonywanie kodu. Zawiera on dodatkowe informacje związane z zabezpieczeniami. Aby przeczytać ten dokument, odwiedź następującą witrynę firmy Microsoft w sieci Web: Niniejszy artykuł zawiera skrypt w języku Visual Basic umożliwiający zastosowanie obejścia problemu we wszystkich działających wystąpieniach programu SQL Server na komputerze lokalnym.

PRZYKŁADOWY SKRYPT VISUAL BASIC UMOŻLIWIAJĄCY OBEJŚCIE PROBLEMU

Ten skrypt w języku Visual Basic odmawia uprawnienia do wykonywania roli publicznej (Public) w rozszerzonej procedurze składowanej sp_replwritetovarbin dla wszystkich wersji programu SQL Server, których dotyczy ten problem, uruchomionych na komputerze lokalnym.

Firma Microsoft podaje przykłady programowania wyłącznie do celów informacyjnych, bez jakichkolwiek gwarancji wyrażonych wprost lub domyślnie. Dotyczy to także, ale nie ograniczając się do tego zapisu, gwarancji przydatności handlowej lub do określonego celu. W tym artykule zakłada się, że czytelnik zna prezentowany język programowania oraz narzędzia używane do tworzenia i debugowania procedur. Pracownicy pomocy technicznej firmy Microsoft mogą służyć pomocą, wyjaśniając funkcję konkretnej procedury. Nie będą jednak modyfikować tych przykładów ani dodawać żadnej funkcji i konstruować nowych procedur w celu dostosowania ich do konkretnych potrzeb użytkownika.


Należy skopiować poniższy kod do pliku tekstowego, zapisać plik z rozszerzeniem vbs, a następnie uruchomić skrypt za pomocą programu CScript.exe. Skrypt będzie wykonywany (jako iteracja) kolejno dla wszystkich wystąpień programu SQL Server na komputerze lokalnym i spowoduje zastosowanie obejścia problemu w odpowiednich wersjach. Aby można było zastosować obejście, użytkownik musi mieć uprawnienia administratora systemu (rola sysadmin) w poszczególnych wystąpieniach programu SQL Server. W przypadku braku dostępu do konta systemu Windows z uprawnieniami roli sysadmin na wszystkich serwerach z uruchomionym programem SQL Server, których dotyczy problem, może być konieczne uruchomienie tego skryptu z kilku różnych kont. W systemach Windows Server 2008 i Windows Vista uruchomienie tego skryptu za pomocą konta administratora systemu Windows z uprawnieniami roli sysadmin wymaga podniesienia poziomu uprawnień w wierszu polecenia.
'*************************************************************************************
'Opis: Ten skrypt będzie wykonywany (jako iteracja) kolejno dla wszystkich uruchomionych wystąpień programu SQL Server
' i odmówi uprawnienia do wykonywania roli publicznej w procedurze sp_replwritetovarbin
' dla wszystkich wersji, których dotyczy ten problem.
' JEST TO TYLKO OBEJŚCIE PROBLEMU I NIE NALEŻY GO UŻYWAĆ W PRZYPADKU
' UDOSTĘPNIENIA I ZAINSTALOWANIA AKTUALIZACJI ZABEZPIECZEŃ.
'*************************************************************************************

OPTION EXPLICIT
ON ERROR RESUME NEXT

' Wartości stałych
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 "INFORMACJA: Brak wystąpień."
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 "BŁĄD: nie można zastosować obejścia dla wystąpienia " + sInstances(i,0) + "." + vbCRLF
VBMain = EXIT_FAILURE
End If
Next

WScript.Echo "INFORMACJA: Przetwarzanie wszystkich uruchomionych wystąpień programu SQL zostało zakończone."
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 "BŁĄD: nie można odczytać wystąpień programu SQL zainstalowanych na komputerze."
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 "BŁĄD: nie można odczytać wystąpień programu SQL zainstalowanych na komputerze."
Exit Function
End If
End If

If IsEmptyNull(sInstances1) AND IsEmptyNull(sInstances2) Then
WScript.Echo "INFORMACJA: Brak wystąpień."
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

' Inicjowanie wartości zwracanej
ApplyFix = FALSE

strConn = "Provider=sqloledb;Initial Catalog=master;Integrated Security=SSPI;Data Source=" + strServerName + ";"
' Sprawdanie błędów zostało celowo pominięte, aby nie przedłużać kodu
Set objConn = CreateObject("ADODB.Connection")
Set objCmd = CreateObject("ADODB.Command")
Set objCmd1 = CreateObject("ADODB.Command")

' Otwieranie połączenia z wzorcem bazy danych
objConn.Open strConn
If ErrorOccurred("Błąd: nie można nawiązać połączenia z " + strInstance) Then
Set objConn = Nothing
Exit Function
End If

' Sprawdzanie wersji przed zastosowaniem poprawki
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("BŁĄD: nie można wykonać polecenia """ + strCommand + """ dla " + strInstance) = TRUE Then
objConn.Close()
Set objConn = Nothing
ApplyFix = FALSE
Exit Function
End If

strBuildVersion = objRS("version")
strProductLevel = UCase(objRS("productlevel"))

bApplyFix = FALSE
' Zastosowanie obejścia tylko dla wersji SQL 2000 i SQL 2005 (RTM, SP1 i SP2)
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("BŁĄD: nie można wykonać polecenia """ + strCommand + """ dla " + strInstance) = FALSE Then
WScript.Echo "INFORMACJA: pomyślnie zastosowano obejście dla wystąpienia " + strInstance + " (" + strBuildVersion + ")." + vbCRLF
ApplyFix = TRUE
End If
Else
WScript.Echo "INFORMACJA: pomijanie gromadzenia informacji dla wystąpienia " + strInstance + " (" + strBuildVersion + "), ponieważ nie dotyczy go problem." + 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

'Połączenie z usługą WMI i pobranie obiektu do klasy STDREGPROV.
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("BŁĄD: nie można nawiązać połączenia z obszarem nazw WMI " + DEFAULTNAMESPACE) Then
GetRegValue = FALSE
Exit Function
End If

lRc = 0
Select Case iValueType
' Chodzi tylko o wartość 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 "BŁĄD: 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

' Osiągnięcie tego miejsca oznacza, że komputer jest węzłem klastrowanym.
' Dlatego należy wykonać kwerendę w rejestrze sprawdzającą, czy wystąpienie programu SQL jest klastrowane.
' Dla wersji SQL 2000 kwerenda powinna dotyczyć następującej wartości:
' HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\<nazwa_wystąpienia>\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

' Kwerenda zwracająca wartości rejestru dla wystąpień programu SQL 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
' Jeśli ten klucz nie istnieje, przyjmowane jest założenie, że jest to lokalne wystąpienie programu 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
Aby uzyskać więcej informacji o programie CScript.exe, odwiedź następującą witrynę firmy Microsoft w sieci Web: Uwaga Nie zaleca się korzystania z tego skryptu w przypadku zainstalowania wydanej aktualizacji zabezpieczeń.

ZNANE PROBLEMY MOGĄCE WYSTĄPIĆ PO URUCHOMIENIU TEGO SKRYPTU

Problem 1

Komunikat o błędzie po uruchomieniu skryptu:
BŁĄD: nie można wykonać polecenia "deny execute on sp_replwritetovarbin to public" dla <nazwa_wystąpienia>
BŁĄD: 0x-2147217900 - Nie można odnaleźć obiektu „sp_replwritetovarbin”, ponieważ obiekt nie istnieje lub nie masz do niego uprawnień dostępu.
BŁĄD: nie można zastosować obejścia dla wystąpienia <nazwa_wystąpienia>.

Przyczyna 1

Ten komunikat o błędzie pojawia się w przypadku braku uprawnień wymaganych do wprowadzenia zmiany. Oznacza on, że udało się pomyślnie zalogować do wystąpienia <nazwa_wystąpienia>.


Ten błąd występuje zwykle w programie SQL Server Express, w którym grupa „Built-In\Users” ma domyślnie uprawnienia do logowania w bazie danych. Nie ma ona jednak uprawnień roli sysadmin.

Ten komunikat o błędzie może także pojawić się po porzuceniu procedury sp_replwritetovarbin. Takie zalecenie zostało zgłoszone zewnętrznie. Firma Microsoft nie zaleca porzucania tej procedury składowanej. Zamiast tego zaleca się zastosowanie następującego rozwiązania.

Rozwiązanie 1

Upewnij się, że konto, z którego nawiązujesz połączenie, ma uprawnienia roli sysadmin w tym wystąpieniu bazy danych. Jeśli tak nie jest, dodaj nazwę użytkownika konta do grupy roli sysadmin lub użyj innego konta. W przypadku programu SQL Server 2005 i starszych wersji grupa „Built-in\Administrators” domyślnie należy do roli sysadmin. W systemach Windows Server 2008 i Windows Vista pamiętaj, aby uruchamiać ten skrypt z podniesionym poziomem uprawnień w wierszu polecenia.

Problem 2

Komunikat o błędzie po uruchomieniu skryptu w programie SQL Server 2005:
Błąd: nie można nawiązać połączenia z <nazwa_wystąpienia>
BŁĄD: 0x-2147217843 - Logowanie użytkownika <użytkownik> nie powiodło się.
BŁĄD: nie można zastosować obejścia dla wystąpienia <nazwa_wystąpienia>.

Przyczyna 2

Ten komunikat o błędzie pojawia się, gdy nie można połączyć się z wystąpieniem <nazwa_wystąpienia>, nawet jeśli to wystąpienie istnieje.


Ten błąd występuje zwykle w przypadku połączenia z wystąpieniem wewnętrznej bazy danych systemu Windows lub bazy danych programu Microsoft SQL Server 2000 Desktop Edition (Windows). Najczęściej żadne konta użytkowników nie mają uprawnień do logowania do tych baz danych.

Rozwiązanie 2

Upewnij się, że konto, z którego uruchamiasz skrypt, ma uprawnienia do logowania w bazie danych na poziomie roli sysadmin.

Nie zaleca się dodawania pojedynczych użytkowników do wewnętrznej bazy danych systemu Windows lub do bazy danych programu Microsoft SQL Server 2000 Desktop Edition (Windows). Dodanie użytkowników może zakłócić normalne działanie tych baz danych. W takim przypadku należy pamiętać, aby nawiązywać połączenie z konta należącego do grupy roli sysadmin. W programie SQL Server 2005 i starszych wersjach grupa „Built-in\Administrators” w systemie Windows zazwyczaj domyślnie należy do roli sysadmin. W systemach Windows Server 2008 i Windows Vista pamiętaj, aby uruchamiać ten skrypt z podniesionym poziomem uprawnień w wierszu polecenia.

Problem 3

Może pojawić się wystąpienie bazy danych o nazwie MICROSOFT##SSEE. Taka baza danych nie została jednak zainstalowana.

Przyczyna 3

Ta baza danych to wewnętrzna baza danych systemu Windows (inne jej nazwy to SQL Server Embedded Edition lub Microsoft SQL Server 2000 Desktop Edition). Jest ona instalowana wraz z niektórymi produktami firmy Microsoft, na przykład z programem SharePoint Services.

Rozwiązanie 3

Skrypt obejścia został zaprojektowany pod kątem współpracy z wewnętrzną bazą danych systemu Windows. Nie jest wymagana żadna akcja ze strony użytkownika.

Niektóre aplikacje podczas dezinstalacji nie usuwają wewnętrznej bazy danych systemu Windows.
Aby uzyskać więcej informacji dotyczących usuwania wewnętrznej bazy danych systemu Windows, kliknij następujący numer artykułu w celu wyświetlenia tego artykułu z bazy wiedzy Microsoft Knowledge Base:
920277 Wewnętrzna baza danych systemu Windows nie jest wymieniona na liście narzędzia Dodaj lub usuń programy i nie jest usuwana po usunięciu z komputera programu Windows SharePoint Services 3.0 (j. ang.)

Problem 4

Komunikat o błędzie po uruchomieniu skryptu:

Błąd: nie można nawiązać połączenia z .\<nazwa_wystąpienia>
BŁĄD: 0x-2147467259 - [DBNETLIB][ConnectionOpen (Connect()).]Serwer SQL nie istnieje lub odmówiono do niego dostępu.

Przyczyna 4

Ten komunikat o błędzie jest wyświetlany, gdy są spełnione następujące warunki:
  • W 64-bitowym systemie operacyjnym jest zainstalowana 32-bitowa wersja programu SQL Server 2000.
  • Na komputerze jest zainstalowana 64-bitowa wersja programu SQL Server 2005 lub SQL Server 2008.

Ten błąd występuje, gdy skrypt używa 64-bitowej wersji pliku dbmslpcn.dll. Ta wersja nie komunikuje się z wystąpieniami programu SQL Server 2000 w trybie WoW.

Rozwiązanie 4

Należy uruchomić skrypt przy użyciu 32-bitowej wersji pliku cscript.exe z folderu %WINDOWS%\SysWOW64. Zostanie wówczas załadowana 32-bitowa wersja pliku dbmslpcn.dll, która wykrywa wystąpienia w trybie WoW.

Materiały referencyjne

Aby uzyskać więcej informacji dotyczących sposobu ustalania wersji i wydania programu SQL Server, kliknij następujący numer artykułu w celu wyświetlenia tego artykułu z bazy wiedzy Microsoft Knowledge Base:
321185 JAK: Identyfikowanie wersji i wydania dodatku Service dla programu SQL Server

Więcej informacji

W poniższej tabeli wymieniono ważne poprawki techniczne w tym artykule. Numer poprawki i data ostatniej korekty tego artykułu mogą dotyczyć drobnych poprawek redakcyjnych lub strukturalnych, które nie zostały wymienione w tabeli.
DataPoprawki
31 grudnia 2008Zaktualizowany skrypt wykrywa wystąpienia programu SQL Server w ramach klastra pracy awaryjnej.
30 grudnia 2008Zaktualizowany skrypt wykrywa 32-bitowe wersje programu SQL Server działające w 64-bitowych wersjach systemu Windows.
Właściwości

Identyfikator artykułu: 961040 — ostatni przegląd: 06.01.2009 — zmiana: 1

Opinia