نصائح إرشادية للأمان من Microsoft: وجود ثغرة أمنية في SQL Server مما قد يسمح بتنفيذ التعليمات البرمجية عن بُعد


مقدمة


أصدرت Microsoft نصائح إرشادية للأمان حول الثغرة الأمنية الموجودة في Microsoft SQL Server مما قد يسمح بتنفيذ التعليمات البرمجية عن بُعد. وتحتوي النصائح الإرشادية للأمان على معلومات إضافية ذات صلة بالأمان. لعرض "النصائح الإرشادية للأمان"، قم بزيارة موقع Microsoft التالي على الويب: تتضمن هذه المقالة VB script الذي يمكنك استخدامه لتطبيق الحل البديل على كافة مثيلات SQL Server قيد التشغيل على جهاز كمبيوتر محلي.

مثال على VB SCRIPT الذي يمكنك استخدامه لتطبيق الحل البديل


يمكنك استخدام VB script لرفض إذن "التنفيذ" إلى الدور "عام" على الإجراء المخزن الموسع sp_replwritetovarbin على كافة إصدارات SQL Server المتأثرة قيد التشغيل على جهاز كمبيوتر محلي.

تقدم Microsoft الأمثلة البرمجية بغرض التوضيح فقط، دون أي ضمان صريح أو ضمني. ويتضمن ذلك، على سبيل المثال لا الحصر، الضمانات الضمنية الخاصة بالتسويق أو الملاءمة لغرض معين. تفترض هذه المقالة أنك معتاد على لغة البرمجة التي يتم شرحها والأدوات المستخدمة لإنشاء الإجراءات وتصحيحها. يمكن لمهندسي الدعم لدى Microsoft شرح وظيفة إجراء محدد. على الرغم من ذلك، لن يقوموا بتعديل هذه الأمثلة لتقديم وظيفة إضافية أو إنشاء إجراءات لاستيفاء متطلباتك الخاصة.


قم بنسخ هذه التعليمة البرمجية إلى ملف نصي، احفظ الملف باستخدام ملحق اسم ملف ‎.vbs، ثم قم بتشغيل ملف البرنامج النصي باستخدام CScript.exe. يتكرر البرنامج النصي خلال مثيلات SQL Server قيد التشغيل على جهاز الكمبيوتر المحلي ويقوم بتطبيق الحل البديل على الإصدارات المتأثرة. يجب أن تكون عضوًا في دور sysadmin على كل مثيل من SQL Server لتطبيق الحل البديل. إذا لم يكن لديك حساب Windows والذي يكون عضوًا في دور sysadmin على كافة الملقمات المتأثرة التي تعمل على تشغيل SQL Server، قد تضطر إلى تشغيل هذا البرنامج النصي من حسابات متعددة. على نظامي التشغيل Windows Server 2008 وWindows Vista، إذا كنت تستخدم حساب مسؤول Windows والذي يكون عضوًا في دور sysadmin، يجب تشغيل هذا البرنامج النصي من موجه أوامر "غير مقيد".
'*************************************************************************************
'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 التالي على الويب: ملاحظة نوصي بعدم استخدام هذا البرنامج النصي في حالة توفر تحديث أمني وتثبيته.

مشكلات معروفة قد تحدث عند تشغيل هذا البرنامج النصي


المشكلة الأولى

عند تشغيل البرنامج النصي، تظهر رسالة الخطأ التالية:
خطأ: تعذر تنفيذ "deny execute on sp_replwritetovarbin to public" على <اسم المثيل>
خطأ: 0x-2147217900 - لا يمكن العثور على الكائن 'sp_replwritetovarbin'، لأنه غير موجود أو أنك ليس لديك الإذن.
خطأ: تعذر تطبيق الحل البديل على <اسم المثيل>.

السبب الأول

تظهر لك رسالة الخطأ هذه إذا لم تكن لديك الأذونات المطلوبة لتطبيق هذا التغيير. تشير رسالة الخطأ هذه إلى أنك تمكنت من تسجيل الدخول بنجاح إلى المثيل "<اسم المثيل>."


تظهر رسالة الخطأ هذه عادةً في SQL Server Express حيث يتوفر لمجموعة "Built-In\Users" تسجيل الدخول إلى قاعدة البيانات افتراضيًا. على الرغم من ذلك، لا تعد هذه المجموعة من أعضاء دور sysadmin.

قد تظهر رسالة الخطأ هذه أيضًا في حالة إسقاط الإجراء sp_replwritetovarbin. كانت هذه هي توصية من تقرير تابع لجهة أخرى. لا نوصي بإسقاط الإجراء الذي تم تخزينه. بدلاً من ذلك، نوصي بتطبيق هذا الحل.

الحل الأول

تأكد من أن الحساب الذي ترتبط به عضو في دور sysadmin على ذلك المثيل لقاعدة البيانات. إذا لم يكن الحساب عضوًا، قم بإضافة حساب المستخدم، الذي ترتبط به، إلى دور sysadmin، أو استخدم حساب مستخدم آخر. بالنسبة لإصدار SQL Server 2005 والإصدارات الأسبق، تكون مجموعة "Built-in\Administrators" عضوًا في دور sysadmin افتراضيًًا. عند تشغيل هذا البرنامج النصي على نظام التشغيل Windows Vista أو نظام التشغيل Windows Server 2008، تأكد من تشغيله من موجه أوامر "غير مقيد".

المشكلة الثانية

في حالة تشغيل هذا البرنامج النصي في SQL Server 2005، تظهر رسالة الخطأ التالية:
خطأ: تعذر الارتباط بـ <اسم المثيل>
خطأ: 0x-2147217843 - فشل تسجيل الدخول للمستخدم '<المستخدم>'.
خطأ: تعذر تطبيق الحل البديل على <اسم المثيل>.

السبب الثاني

تظهر رسالة الخطأ هذه إذا لم تتمكن من الارتباط بالمثيل "<اسم المثيل>" حتى في حالة وجود هذا المثيل.


عادةً ما تظهر رسالة الخطأ هذه عند الارتباط بمثيلات Microsoft SQL Server 2000 Desktop Edition ‏(Windows)‏ أو Windows Internal Database. عادةً، لا يتوفر لحسابات المستخدمين تسجيلات دخول لقواعد البيانات هذه.

الحل الثاني

تأكد من أن الحساب الذي تستخدمه لتشغيل البرنامج النصي يتوفر لديه تسجيل دخول على قاعدة البيانات التي تكون عضوًا في دور sysadmin.

لا نوصي بإضافة مستخدمين فرديين إلى قواعد بيانات Microsoft SQL Server 2000 Desktop Edition ‏(Windows) وWindows Internal Database. في حالة القيام بذلك، قد يتداخل المستخدمون الذين تقوم بإضافتهم مع العملية العادية لقواعد البيانات هذه. في هذه الحالة، تأكد من الارتباط من حساب عضو في دور sysadmin. عادةً ما تكون مجموعة "Built-in\Administrators" في Windows عضوًا في دور sysadmin افتراضيًا في SQL Server 2005 وفي الإصدارات الأسبق. عند تشغيل هذا البرنامج النصي على نظام التشغيل Windows Vista أو نظام التشغيل Windows Server 2008، تأكد من تشغيله من موجه أوامر "غير مقيد".

المشكلة الثالثة

قد تلاحظ وجود مثيل من قاعدة بيانات يسمى MICROSOFT##SSEE. على الرغم من ذلك، فإنك لا تقوم بتثبيت قاعدة البيانات هذه.

السبب الثالث

قاعدة البيانات هذه هي Windows Internal Database، تُعرف أيضًا باسم "SQL Server Embedded Edition"، أو أحيانًا تُعرف باسم "Windows Internal Database" أو "Microsoft SQL Server 2000 Desktop Edition ‏(Windows)‏". يتم تثبيتها مع بعض المنتجات من Microsoft، بما في ذلك SharePoint Services.

الحل الثالث

يتم تصميم البرنامج النصي للحل البديل للعمل مع Windows Internal Database. لا يتطلب اتخاذ أي إجراء من جانبك.

بعض التطبيقات لا تعمل على إزالة Windows Internal Database عند إزالة تثبيتها.
لمزيد من المعلومات حول كيفية إزالة Windows Internal Database، انقر فوق رقم المقالة التالي لعرضها في "قاعدة معارف Microsoft" (قد تحتوي هذه المقالة على ارتباطات إلى محتوى باللغة الإنجليزية (محتوى لم تتم ترجمته بعد)):
920277 عدم سرد Windows Internal Database في أداة "إضافة أو إزالة البرامج" وعدم إزالتها عند إزالة Windows SharePoint Services 3.0 من جهاز الكمبيوتر

المشكلة الرابعة

عند تشغيل البرنامج النصي، تظهر رسالة الخطأ التالية:

خطأ: تعذر الارتباط بـ .\<اسم المثيل>
خطأ: 0x-2147467259 - [DBNETLIB][ConnectionOpen (Connect()).]SQL Server غير موجود أو تم رفض الوصول

السبب الرابع

تظهر لك رسالة الخطأ هذه في حالة تحقق الشرطين التاليين:
  • تثبيت إصدار ٣٢ بت من SQL Server 2000 على نظام تشغيل يستند إلى x64 بت.
  • تثبيت إصدار ٦٤ بت من SQL Server 2005 أو SQL Server 2008 على جهاز الكمبيوتر.

تظهر رسالة الخطأ هذه عند استخدام البرنامج النصي لإصدار ٦٤ بت من ملف dbmslpcn.dll. لا يمكن لهذا الإصدار الاتصال بمثيلات WoW من SQL Server 2000.

الحل الرابع

استخدم إصدار ٣٢ بت من الملف cscript.exe من المجلد ‎%WINDOWS%\SysWOW64 لبدء تشغيل البرنامج النصي. يؤدي ذلك إلى تحميل إصدار ٣٢ بت من الملف dbmslpcn.dll الذي يمكنه الكشف عن مثيلات WoW.

مراجع


لمزيد من المعلومات حول كيفية تحديد إصدار SQL Server ونسخته، انقر فوق رقم المقالة التالي لعرضها في "قاعدة معارف Microsoft" (قد تحتوي هذه المقالة على ارتباطات إلى محتوى باللغة الإنجليزية (محتوى لم تتم ترجمته بعد)):
321185 كيفية تحديد إصدار SQL Server ونسخته

معلومات أخرى


يسرد الجدول التالي مراجعات تقنية هامة لهذه المقالة. قد يشير رقم المراجعة وتاريخ آخر مراجعة في هذه المقالة إلى مراجعات بنائية أو مراجعات تحريرية ثانوية غير متضمنة في الجدول لهذه المقالة.
التاريخالمراجعات
٣١ ديسمبر ٢٠٠٨تتضمن برنامجًا نصيًا محدثًا يكشف عن مثيلات تجاوز الفشل للمجموعات لـ SQL Server.
٣٠ ديسمبر ٢٠٠٨تتضمن برنامجًا نصيًا محدثًا يكشف عن إصدارات ٣٢ بت من SQL Server تعمل على إصدارات ٦٤ من أنظمة تشغيل Windows.