SCCM SQL queries for KBs related to WannaCrypt.

The following is shared by CSS Support Escalation Engineer Vinay Pamnani, to help give the IT Pro some sample queries that may assist them in their security update compliance reporting as it relates to WannaCrypt. It is provided as a sample and NOT to be taken as a definitive compliance posture information source. As with all Software Update compliance information, the queries below rely on current and accurate scan result information in the ConfigMgr database. The sample queries below have had limited testing against ConfigMgr version 1702 and SQL Server 2016.

The simplest and most generally recommended approach is to deploy the latest CU to Windows 10 or Server 2016 systems, and to deploy the latest Monthly Rollup to pre-Windows 10 machines, and use the built-in ConfigMgr Compliance reports to determine overall compliance.

Official Customer Guidance for WannaCrypt attacks: https://blogs.technet.microsoft.com/msrc/2017/05/12/customer-guidance-for-wannacrypt-attacks/

General information on ransomware: https://www.microsoft.com/en-us/security/portal/mmpc/shared/ransomware.aspx

Microsoft Malware Protection Center blog: https://blogs.technet.microsoft.com/mmpc/2017/05/12/wannacrypt-ransomware-worm-targets-out-of-date-systems/

MS17-010 Security Update: https://technet.microsoft.com/en-us/library/security/ms17-010.aspx

However, the following queries can also enable admins to report on MS17-010 compliance.

What do these queries do?

Pre-Windows 10 machines:

Windows 8.1 and Server 2012 R2 machines that do not report KB2919355 as installed will be returned by the query. This is because KB2919355 is required for the later KBs to be reported as applicable. So, these systems can be considered at risk and require further investigation.

For Windows Vista, Windows 7, Windows 8.1, Windows Server 2008 R2 SP1, Windows Server 2008 SP2, Windows Server 2012, and Windows Server 2012 R2 query below, the systems returned will be those that do not have either the March, April, or May monthly rollups installed -AND- are reporting the following specific ‘Security Only’ updates as ‘Required’:

Windows Vista and Server 2008 SP2: KB4012598
Windows 7 and Server 2008 R2 SP1: KB4012212
Windows Server 2012: KB4012214
Windows Server 2012 R2 and Windows 8.1: KB4012213

-- For Windows 7, Server 2008 R2 SP1, Windows Server 2012, Server 2012 R2 and Windows 8.1, Windows Vista and Server 2008 SP2
-- This query lists machines that are reporting any of the 'Security Only' updates as 'Required'.
-- If any machine has either March, April or May Monthly Rollup installed, then they wouldn't report March 'Security Only' update as 'Required', but look for the Monthly updates anyway.
-- Also include any Windows 8.1 and Server 2012 R2 machines which do not report ‘KB2919355’ as Installed.

 DECLARE @MarchSecurityOnly TABLE (ArticleID NVARCHAR(20))
INSERT INTO @MarchSecurityOnly VALUES ('4012212')
INSERT INTO @MarchSecurityOnly VALUES ('4012213')
INSERT INTO @MarchSecurityOnly VALUES ('4012214')
INSERT INTO @MarchSecurityOnly VALUES ('4012598')

DECLARE @MarchMonthly TABLE (ArticleID NVARCHAR(20))
INSERT INTO @MarchMonthly VALUES ('4012215')
INSERT INTO @MarchMonthly VALUES ('4015549')
INSERT INTO @MarchMonthly VALUES ('4019264')
INSERT INTO @MarchMonthly VALUES ('4012216')
INSERT INTO @MarchMonthly VALUES ('4015550')
INSERT INTO @MarchMonthly VALUES ('4019215')
INSERT INTO @MarchMonthly VALUES ('4012217')
INSERT INTO @MarchMonthly VALUES ('4015551')
INSERT INTO @MarchMonthly VALUES ('4019216')

DECLARE @KB2919355 NVARCHAR(10) = '2919355'-- Pre-req

SELECT 
       RS.Name0, 
       UI.ArticleID as ArticleID, 
       UI.BulletinID as BulletinID, 
       UI.Title as Title, 
       SN.StateDescription AS State,
       UCS.LastStatusCheckTime AS LastStateReceived,
       UCS.LastStatusChangeTime AS LastStateChanged,
       UI.CI_UniqueID AS UniqueUpdateID
FROM v_Update_ComplianceStatusReported UCS
JOIN v_UpdateInfo UI ON UCS.CI_ID = UI.CI_ID
JOIN v_R_System RS ON RS.ResourceType=5 AND RS.ResourceID = UCS.ResourceID
JOIN v_StateNames SN ON SN.TopicType=500 AND SN.StateID=2 AND SN.StateID = UCS.Status
WHERE UI.ArticleID IN (SELECT ArticleID FROM @MarchSecurityOnly) 
AND RS.Name0 NOT IN (
       -- Monthly is installed
       SELECT distinct RS.Name0 
       FROM v_Update_ComplianceStatusReported UCS
       JOIN v_UpdateInfo UI ON UCS.CI_ID = UI.CI_ID
       JOIN v_R_System RS ON RS.ResourceType=5 AND RS.ResourceID = UCS.ResourceID
       JOIN v_StateNames SN ON SN.TopicType=500 AND SN.StateID=3 AND SN.StateID = UCS.Status
       WHERE UI.ArticleID IN (SELECT ArticleID FROM @MarchMonthly) 
)
UNION 
-- Windows 8.1 and Server 2012 R2 machines that do not report KB2919355 as Installed.
SELECT 
       distinct RS.Name0, 
       UI.ArticleID as ArticleID, 
       UI.BulletinID as BulletinID, 
       'KB2919355' as Title,      
       'Update is not Installed' AS State,
       NULL AS LastStateReceived,
       NULL AS LastStateChanged,
       'KB2919355' AS UniqueUpdateID
FROM v_Update_ComplianceStatusReported UCS
JOIN v_UpdateInfo UI ON UCS.CI_ID = UI.CI_ID
JOIN v_R_System RS ON RS.ResourceType=5 AND RS.ResourceID = UCS.ResourceID
JOIN v_StateNames SN ON SN.TopicType=500 AND SN.StateID = UCS.Status AND SN.StateID <> 3
JOIN v_GS_OPERATING_SYSTEM OS ON RS.ResourceID = OS.ResourceID AND OS.BuildNumber0 = '9600' -- Windows 8.1 and Server 2012 R2
WHERE UI.ArticleID = @KB2919355

Windows 10 and Server 2016

For the Windows 10 and Server 2016 queries, there are 2 scenarios that may apply depending on an environment’s configuration on the expiry of superseded updates in ConfigMgr. For more information on this, see the Supersedence rules section on TechNet and this.

Scenario 1: Customers with Supersedence rule NOT set to ‘Immediately expire’:

If the superseded updates are not expired and therefore still available in ConfigMgr, you can use the following query to help identify Windows 10 and Windows Server 2016 systems that do not have the March CU or a subsequent CU installed. Please note that for the March CU data to be evaluated, the months to wait before an update is expired value in ConfigMgr must be set to a high enough value such that the March update was not expired. The same consideration applies to the subsequent updates. If this does not apply to your environment, the information in Scenario 2: Customers with Supersedence rule set to ‘Immediately expire’ (or not long enough) can be tried.

For the following Windows 10 and Server 2016, the query below returns systems that do not have any of the following monthly CUs, released in March or later (through the date of this post), installed:

Win10  RTM: KB4012606, KB4019474, KB4015221, KB4016637
Win10 1511: KB4013198, KB4015219, KB4016636, KB4019473
Win10 1607/Server 2016: KB4013429, KB4015217, KB4015438, KB4016635, KB4019472

-- Windows 10 machines that do not have the March (or any of the superseding updates) installed, and could be 'at risk'.
-- These queries are OS dependent, since we are querying individual KB's, and need to compare those KB's against proper builds to prevent getting inaccurate results.

-- Windows 10 RTM

DECLARE @BuildNumberRTM INT = '10240'

DECLARE @MarchWin10 TABLE (ArticleID NVARCHAR(20))
INSERT INTO @MarchWin10 VALUES ('4012606') -- March Cumulative
INSERT INTO @MarchWin10 VALUES ('4019474')
INSERT INTO @MarchWin10 VALUES ('4015221')
INSERT INTO @MarchWin10 VALUES ('4016637')

SELECT RS.Name0, OS.BuildNumber0 FROM v_R_System RS
JOIN v_GS_OPERATING_SYSTEM OS ON RS.ResourceID = OS.ResourceID AND OS.BuildNumber0 = @BuildNumberRTM
WHERE RS.Name0 NOT IN (
       SELECT RS.Name0
       FROM v_Update_ComplianceStatusReported UCS
       JOIN v_UpdateInfo UI ON UCS.CI_ID = UI.CI_ID
       JOIN v_R_System RS ON RS.ResourceType=5 AND RS.ResourceID = UCS.ResourceID
       JOIN v_StateNames SN ON SN.TopicType=500 AND SN.StateID=3 AND SN.StateID = UCS.Status
       JOIN v_GS_OPERATING_SYSTEM OS ON OS.ResourceID = RS.ResourceID AND OS.BuildNumber0 = @BuildNumberRTM
       WHERE UI.ArticleID IN (SELECT ArticleID FROM @MarchWin10)
)

-- Windows 10 1511

DECLARE @BuildNumber1511 INT = '10586'

DECLARE @MarchWin101511 TABLE (ArticleID NVARCHAR(20))
INSERT INTO @MarchWin101511 VALUES ('4013198') -- March Cumulative
INSERT INTO @MarchWin101511 VALUES ('4015219')
INSERT INTO @MarchWin101511 VALUES ('4016636')
INSERT INTO @MarchWin101511 VALUES ('4019473')

SELECT RS.Name0, OS.BuildNumber0 FROM v_R_System RS
JOIN v_GS_OPERATING_SYSTEM OS ON RS.ResourceID = OS.ResourceID AND OS.BuildNumber0 = @BuildNumber1511
WHERE RS.Name0 NOT IN (
       SELECT RS.Name0
       FROM v_Update_ComplianceStatusReported UCS
       JOIN v_UpdateInfo UI ON UCS.CI_ID = UI.CI_ID
       JOIN v_R_System RS ON RS.ResourceType=5 AND RS.ResourceID = UCS.ResourceID
       JOIN v_StateNames SN ON SN.TopicType=500 AND SN.StateID=3 AND SN.StateID = UCS.Status
       JOIN v_GS_OPERATING_SYSTEM OS ON OS.ResourceID = RS.ResourceID AND OS.BuildNumber0 = @BuildNumber1511
       WHERE UI.ArticleID IN (SELECT ArticleID FROM @MarchWin101511)
)

-- Windows 10 1607

DECLARE @BuildNumber1607 INT = '14393'

DECLARE @MarchWin101607 TABLE (ArticleID NVARCHAR(20))
INSERT INTO @MarchWin101607 VALUES ('4013429') -- March Cumulative
INSERT INTO @MarchWin101607 VALUES ('4015217')
INSERT INTO @MarchWin101607 VALUES ('4015438')
INSERT INTO @MarchWin101607 VALUES ('4016635')
INSERT INTO @MarchWin101607 VALUES ('4019472')

SELECT RS.Name0, OS.BuildNumber0 FROM v_R_System RS
JOIN v_GS_OPERATING_SYSTEM OS ON RS.ResourceID = OS.ResourceID AND OS.BuildNumber0 = @BuildNumber1607
WHERE RS.Name0 NOT IN (
       SELECT RS.Name0
       FROM v_Update_ComplianceStatusReported UCS
       JOIN v_UpdateInfo UI ON UCS.CI_ID = UI.CI_ID
       JOIN v_R_System RS ON RS.ResourceType=5 AND RS.ResourceID = UCS.ResourceID
       JOIN v_StateNames SN ON SN.TopicType=500 AND SN.StateID=3 AND SN.StateID = UCS.Status
       JOIN v_GS_OPERATING_SYSTEM OS ON OS.ResourceID = RS.ResourceID AND OS.BuildNumber0 = @BuildNumber1607
       WHERE UI.ArticleID IN (SELECT ArticleID FROM @MarchWin101607)
)

Scenario 2: Customers with Supersedence rule set to ‘Immediately expire’ (or not long enough):

Since CUs are superseded each month, and expired due to the ConfigMgr Supersedence Rules option being set to ‘Immediately Expire’, compliance data is not available on the expired update – in this scenario, you will, however, have compliance data on the newest CU available, so the simplest path forward would be to deploy the latest CU and report against it.

Alternate Options (for Windows 10 and Server 2016):

Alternative options to the above, that may help determine ‘at risk’ machines, by reporting on the expired CU, are as follows:

A. Extend Hardware Inventory to include Win32_QuickFixEngineering, and use this data to identify ‘at risk’ machines. If any machine has neither March, April or May CU installed, they’re ‘at risk’. NOTE that if you do not have this already enabled and enable it now, you would need to wait for all the clients to report Hardware Inventory.

-- Customers with Win32_QuickFixEngineering class enabled for HINV can use these queries.
-- Windows 10 machines that do not have the March (or any of the superseding updates) installed and could be 'at risk'.
-- These queries are OS dependent, since we are querying individual KB's, and need to compare those KB's against proper builds to prevent getting inaccurate results.
-- Query limits results for machines that have at least one row in v_GS_Quick_Fix_Engineering class to ensure there is some HINV data for the machine for this class.

-- Windows 10 RTM

DECLARE @BuildNumberRTM INT = '10240'
DECLARE @MarchWin10 TABLE (ArticleID NVARCHAR(20))
INSERT INTO @MarchWin10 VALUES ('4012606') -- March Cumulative
INSERT INTO @MarchWin10 VALUES ('4019474')
INSERT INTO @MarchWin10 VALUES ('4015221')
INSERT INTO @MarchWin10 VALUES ('4016637')

SELECT RS.Name0, OS.BuildNumber0, QFE.HotFixID0, COUNT(QFEALL.HotFixID0) AS TotalHotfixes FROM v_R_System RS 
JOIN v_GS_OPERATING_SYSTEM OS ON OS.ResourceID = RS.ResourceID AND OS.BuildNumber0 = @BuildNumberRTM
JOIN v_GS_QUICK_FIX_ENGINEERING QFEALL ON QFEALL.ResourceID = RS.ResourceID
LEFT JOIN v_GS_QUICK_FIX_ENGINEERING QFE ON QFE.ResourceID = RS.ResourceID AND QFE.HotFixID0 IN (SELECT 'KB' + ArticleID FROM @MarchWin10)
WHERE QFE.HotFixID0 IS NULL 
GROUP BY RS.Name0, OS.BuildNumber0, QFE.HotFixID0
HAVING COUNT(QFEALL.HotFixID0) > 0

-- Windows 10 1511
DECLARE @BuildNumber1511 INT = '10586'
DECLARE @MarchWin101511 TABLE (ArticleID NVARCHAR(20))
INSERT INTO @MarchWin101511 VALUES ('4013198') -- March Cumulative
INSERT INTO @MarchWin101511 VALUES ('4015219')
INSERT INTO @MarchWin101511 VALUES ('4016636')
INSERT INTO @MarchWin101511 VALUES ('4019473')
SELECT RS.Name0, OS.BuildNumber0, QFE.HotFixID0, COUNT(QFEALL.HotFixID0) AS TotalHotfixes FROM v_R_System RS 
JOIN v_GS_OPERATING_SYSTEM OS ON OS.ResourceID = RS.ResourceID AND OS.BuildNumber0 = @BuildNumber1511
JOIN v_GS_QUICK_FIX_ENGINEERING QFEALL ON QFEALL.ResourceID = RS.ResourceID
LEFT JOIN v_GS_QUICK_FIX_ENGINEERING QFE ON QFE.ResourceID = RS.ResourceID AND QFE.HotFixID0 IN (SELECT 'KB' + ArticleID FROM @MarchWin101511)
WHERE QFE.HotFixID0 IS NULL 
GROUP BY RS.Name0, OS.BuildNumber0, QFE.HotFixID0
HAVING COUNT(QFEALL.HotFixID0) > 0
-- Windows 10 1607
DECLARE @BuildNumber1607 INT = '14393'
DECLARE @MarchWin101607 TABLE (ArticleID NVARCHAR(20))
INSERT INTO @MarchWin101607 VALUES ('4013429') -- March Cumulative
INSERT INTO @MarchWin101607 VALUES ('4015217')
INSERT INTO @MarchWin101607 VALUES ('4015438')
INSERT INTO @MarchWin101607 VALUES ('4016635')
INSERT INTO @MarchWin101607 VALUES ('4019472')
SELECT RS.Name0, OS.BuildNumber0, QFE.HotFixID0, COUNT(QFEALL.HotFixID0) AS TotalHotfixes FROM v_R_System RS 
JOIN v_GS_OPERATING_SYSTEM OS ON OS.ResourceID = RS.ResourceID AND OS.BuildNumber0 = @BuildNumber1607
JOIN v_GS_QUICK_FIX_ENGINEERING QFEALL ON QFEALL.ResourceID = RS.ResourceID
LEFT JOIN v_GS_QUICK_FIX_ENGINEERING QFE ON QFE.ResourceID = RS.ResourceID AND QFE.HotFixID0 IN (SELECT 'KB' + ArticleID FROM @MarchWin101607)
WHERE QFE.HotFixID0 IS NULL 
GROUP BY RS.Name0, OS.BuildNumber0, QFE.HotFixID0
HAVING COUNT(QFEALL.HotFixID0) > 0

B. Create a Configuration Item and Baseline which queries the March, April and May CU’s from Win32_QuickFixEngineering and reports Compliance. Here’s a sample PowerShell script written by Umair Khan that can be used in a DCM Baseline:

$InstalledKBList = Get-Wmiobject -class Win32_QuickFixEngineering -namespace "root\cimv2" | select-object -Property HotFixID | Out-String
[array]$WannaCryList = 
"KB4012598",
"KB4012212",
"KB4012215",
"KB4012213",
"KB4012216",
"KB4012214",
"KB4012217",
"KB4012606",
"KB4013198",
"KB4013429",
"KB4015219",
"KB4015221",
"KB4016636",
"KB4015438",
"KB4015550",
"KB4015551",
"KB4016637",
"KB4019473",
"KB4016635",
"KB4018466",
"KB4015552",
"KB4019215",
"KB4019216",
"KB4019474",
"KB4019472",
"KB4019264"
  
$Compliant = 0;
foreach ($elem in $WannaCryList) 
{   
    if ($InstalledKBList -match $elem) 
    { 
    #Write-Output "$elem Found"
    $Compliant = 1 
    break
    } 
}

$Compliant