MsrcSecurityUpdates.psm1
$msrcApiUrl = 'https://api.msrc.microsoft.com' $msrcApiVersion = 'api-version=2016-08-01' function Get-MsrcSecurityUpdate { <# .Synopsis Get MSRC security updates .DESCRIPTION Calls the CVRF Update API to get a list of security updates .EXAMPLE #Get all the updates Get-MsrcSecurityUpdate -ApiKey 'YOUR API KEY' .EXAMPLE #Get all the updates containing Vulnerability CVE-2017-003 Get-MsrcSecurityUpdate -ApiKey 'YOUR API KEY' -Vulnerability CVE-2017-0003 .EXAMPLE #Get all the updates for the year 2017 Get-MsrcSecurityUpdate -ApiKey 'YOUR API KEY' -Year 2017 .EXAMPLE #Get all the updates for the CVRF document with ID of 2017-Jan Get-MsrcSecurityUpdate -ApiKey 'YOUR API KEY' -Cvrf 2017-Jan .EXAMPLE #Get all the updates before January 1st, 2017 Get-MsrcSecurityUpdate -ApiKey 'YOUR API KEY' -Before 2017-01-01 .EXAMPLE #Get all the updates after January 1st, 2017 Get-MsrcSecurityUpdate -ApiKey 'YOUR API KEY' -After 2017-01-01 .EXAMPLE #Get all the updates before January 1st, 2017 and after October 1st, 2016 Get-MsrcSecurityUpdate -ApiKey 'YOUR API KEY' -Before 2017-01-01 -After 2016-10-01 #> [CmdletBinding(DefaultParametersetName="AllUpdates")] Param ( <# API Key for the MSRC CVRF API To get an API key, visit https://portal.msrc.microsoft.com #> [Parameter(Mandatory=$true,ParameterSetName='AllUpdates')] [Parameter(Mandatory=$true,ParameterSetName='ByDate')] [Parameter(Mandatory=$true,ParameterSetName='ByYear')] [Parameter(Mandatory=$true,ParameterSetName='ByCVRF')] [Parameter(Mandatory=$true,ParameterSetName='ByVulnerability')] [String] $ApiKey, <# Get security updates released after this date #> [Parameter(ParameterSetName='ByDate')] [DateTime] $After, <# Get security updates released before this date #> [Parameter(ParameterSetName='ByDate')] [DateTime] $Before, <# Get security updates for the specified year (ie. 2016) #> [Parameter(Mandatory=$true,ParameterSetName='ByYear')] [ValidateScript({ if ($_ -lt 2016 -or $_ -gt [DateTime]::Now.Year) { throw 'Year must be between 2016 and this year' } else { $true } })] [Int] $Year, <# Get security updates for the specified Vulnerability CVE (ie. CVE-2016-0128) #> [Parameter(Mandatory=$true,ParameterSetName='ByVulnerability')] [String] $Vulnerability, <# Get security update for the specified CVRF ID (ie. 2016-Aug) #> [Parameter(Mandatory=$true,ParameterSetName='ByCVRF')] [String] $Cvrf ) ### Construct the URL based on the parameters provided switch ($PSCmdlet.ParameterSetName) { ByDate { if ($PSBoundParameters.ContainsKey('Before') -and $PSBoundParameters.ContainsKey('After')) { $url = "$msrcApiUrl/Updates?`$filter=CurrentReleaseDate gt {0} and CurrentReleaseDate lt {1}&$msrcApiVersion" -f $After.ToString('yyyy-MM-dd'), $Before.ToString('yyyy-MM-dd') } elseif ($PSBoundParameters.ContainsKey('Before')) { $url = "$msrcApiUrl/Updates?`$filter=CurrentReleaseDate lt {0}&$msrcApiVersion" -f $Before.ToString('yyyy-MM-dd') } elseif ($PSBoundParameters.ContainsKey('AFter')) { $url = "$msrcApiUrl/Updates?`$filter=CurrentReleaseDate gt {0}&$msrcApiVersion" -f $After.ToString('yyyy-MM-dd') } else { throw 'Unexpected parameter set' } } ByYear { $url = "$msrcApiUrl/Updates('$Year')?$msrcApiVersion" } ByVulnerability { $url = "$msrcApiUrl/Updates('$Vulnerability')?$msrcApiVersion" } ByCVRF { $url = "$msrcApiUrl/Updates('$Cvrf')?$msrcApiVersion" } Default { $url = "$msrcApiUrl/Updates?$msrcApiVersion" } } try { Write-Verbose "Calling $url" $webResponse = Invoke-RestMethod -Uri $url -Headers @{ 'Accept' = 'application/json' 'Api-Key' = $ApiKey } if (-not $webResponse) { Write-Warning "No results returned from the /Update API" return } Write-Output $webResponse.Value } catch { Write-Error "HTTP Get failed with status code $($_.Exception.Response.StatusCode): $($_.Exception.Response.StatusDescription)" } } function Get-MsrcCvrfDocument { <# .Synopsis Get a MSRC CVRF document .DESCRIPTION Calls the MSRC CVRF API to get a CVRF document by ID .EXAMPLE #Get the Cvrf document '2016-Aug' (returns an object) Get-MsrcCvrfDocument -ID 2016-Aug -ApiKey 'YOUR API KEY' .EXAMPLE #Get the Cvrf document '2016-Aug' (returns an XML string) Get-MsrcCvrfDocument -ID 2016-Aug -ApiKey 'YOUR API KEY' -AsXml .EXAMPLE #Get the Cvrf document '2016-Aug' (returns a JSON string) Get-MsrcCvrfDocument -ID 2016-Aug -ApiKey 'YOUR API KEY' -AsJson #> [CmdletBinding(DefaultParametersetName="DefaultCvrfParameterSet")] Param ( <# API Key for the MSRC CVRF API To get an API key, visit https://portal.msrc.microsoft.com #> [Parameter(Mandatory=$true,ParameterSetName='DefaultCvrfParameterSet')] [Parameter(Mandatory=$true,ParameterSetName='XmlOutput')] [Parameter(Mandatory=$true,ParameterSetName='JsonOutput')] [String] $ApiKey, <# Get the CVRF document for the specified CVRF ID (ie. 2016-Aug) #> [Parameter(Mandatory=$true,ParameterSetName='DefaultCvrfParameterSet')] [Parameter(Mandatory=$true,ParameterSetName='XmlOutput')] [Parameter(Mandatory=$true,ParameterSetName='JsonOutput')] [String] $ID, <# Output as an XML string #> [Parameter(Mandatory=$true,ParameterSetName='XmlOutput')] [Switch] $AsXml, <# Output as an XML string #> [Parameter(Mandatory=$true,ParameterSetName='JsonOutput')] [Switch] $AsJson ) $url = "$msrcApiUrl/cvrf/{0}?$msrcApiVersion" -f $ID try { Write-Verbose "Calling $url" switch ($PSCmdlet.ParameterSetName) { DefaultCvrfParameterSet { Invoke-RestMethod -Uri $url -Headers @{ 'Api-Key' = $ApiKey 'Accept' = 'application/json' } } XmlOutput { $webResponse = Invoke-WebRequest -Uri $url -Headers @{ 'Api-Key' = $ApiKey 'Accept' = 'application/xml' } Write-Output $webResponse.Content } JsonOutput { $webResponse = Invoke-WebRequest -Uri $url -Headers @{ 'Api-Key' = $ApiKey 'Accept' = 'application/json' } Write-Output $webResponse.Content } } } catch { Write-Error "HTTP Get failed with status code $($_.Exception.Response.StatusCode): $($_.Exception.Response.StatusDescription)" } } function Get-MsrcCvrfProductVulnerability { <# .Synopsis Get product vulnerability details from a CVRF document .DESCRIPTION CVRF documents next products into several places, including: -Vulnerabilities -Threats -Remediations -Product Tree This function gathers the details for each product identified in a CVRF document. It provides a list of Threats, Remediations and CVSS Score Sets for each product. .EXAMPLE #Get product vulnerability details from a CVRF document using the pipeline Get-MsrcCvrfDocument -ID 2016-Nov -ApiKey 'YOUR API KEY' | Get-MsrcCvrfProductVulnerability .EXAMPLE #Get product vulnerability details from a CVRF document using a variable and parameters $cvrfDocument = Get-MsrcCvrfDocument -ID 2016-Nov -ApiKey 'YOUR API KEY' Get-MsrcCvrfProductVulnerability -Vulnerability $cvrfDocument.Vulnerability -ProductTree $cvrfDocument.ProductTree -DocumentTracking $cvrfDocument.DocumentTracking -DocumentTitle $cvrfDocument.DocumentTitle #> Param ( <# API Key for the MSRC CVRF API To get an API key, visit https://portal.msrc.microsoft.com #> [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] $Vulnerability, <# Get the CVRF document for the specified CVRF ID (ie. 2016-Aug) #> [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] $ProductTree, <# Get the CVRF document for the specified CVRF ID (ie. 2016-Aug) #> [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] $DocumentTracking, <# Get the CVRF document for the specified CVRF ID (ie. 2016-Aug) #> [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] $DocumentTitle ) ### Create a list of all the products in the CVRF document $CvrfRelatedProducts = @() foreach ($branch in $ProductTree.Branch.Items) { foreach ($branchItem in $branch.Items) { $CvrfRelatedProducts += [psCustomObject]@{ CvrfAlias = $DocumentTracking.Identification.Alias.Value CvrfTitle = $DocumentTitle.Value BranchName = $branch.Name ProductName = $branchItem.Value ProductID = $branchItem.ProductID } } } ### For each product, get the: ### Threats ### Remediations ### CVSS Score Sets foreach ($CvrfRelatedProduct in $CvrfRelatedProducts) { $Remediations = @() $Threats = @() $CVSSScoreSets = @() foreach ($vuln in $Vulnerability) { foreach ($Remediation in $vuln.Remediations | Where-Object ProductID -Contains $CvrfRelatedProduct.ProductID) { $Remediation | Add-Member -NotePropertyName VulnerabilityCVE -NotePropertyValue $vuln.CVE -Force $Remediation | Add-Member -NotePropertyName VulnerabilityTitle -NotePropertyValue $vuln.Title.Value -Force $Remediations += $Remediation } $CvrfRelatedProduct | Add-Member -NotePropertyName Remediations -NotePropertyValue $Remediations -Force foreach ($Threat in $vuln.Threats | Where-Object ProductID -Contains $CvrfRelatedProduct.ProductID) { $Threat | Add-Member -NotePropertyName VulnerabilityCVE -NotePropertyValue $vuln.CVE -Force $Threat | Add-Member -NotePropertyName VulnerabilityTitle -NotePropertyValue $vuln.Title.Value -Force $Threats += $Threat } $CvrfRelatedProduct | Add-Member -NotePropertyName Threats -NotePropertyValue $Threats -Force foreach ($CVSSScoreSet in $vuln.CVSSScoreSets | Where-Object ProductID -Contains $CvrfRelatedProduct.ProductID) { $CVSSScoreSet | Add-Member -NotePropertyName VulnerabilityCVE -NotePropertyValue $vuln.CVE -Force $CVSSScoreSet | Add-Member -NotePropertyName VulnerabilityTitle -NotePropertyValue $vuln.Title.Value -Force $CVSSScoreSets += $CVSSScoreSet } $CvrfRelatedProduct | Add-Member -NotePropertyName CVSSScoreSets -NotePropertyValue $CVSSScoreSets -Force } #region MaximumSeverity $MaximumSeverity = 'Unknown' $SeverityValues = $Vulnerability.Threats | Where-Object ProductID -Contains $CvrfRelatedProduct.ProductID | Where-Object Type -EQ 3 | Select @{Name='Severity' ;Expression={$_.Description.Value}} -Unique | Select -ExpandProperty Severity if ($SeverityValues -contains 'Critical') { $MaximumSeverity = 'Critical' } elseif ($SeverityValues -contains 'Important') { $MaximumSeverity = 'Important' } elseif ($SeverityValues -contains 'Moderate') { $MaximumSeverity = 'Moderate' } elseif ($SeverityValues -contains 'Low') { $MaximumSeverity = 'Low' } else { Write-Verbose "Could not determine the Maximum Severity from the Threats" } $CvrfRelatedProduct | Add-Member -NotePropertyName MaximumSeverity -NotePropertyValue $MaximumSeverity -Force #endregion #region RestartRequired $RestartRequired = 'Unknown' if ($Vulnerability.Remediations.RestartRequired.Value -contains 'Yes') { $RestartRequired = 'Yes' } elseif ($Vulnerability.Remediations.RestartRequired.Value -contains 'Maybe') { $RestartRequired = 'Maybe' } $CvrfRelatedProduct | Add-Member -NotePropertyName RestartRequired -NotePropertyValue $RestartRequired -Force #endregion $CvrfRelatedProduct } } function Get-MsrcSecurityBulletinHtml { #TODO - refactor the code used for populating the tables into functions Param ( <# API Key for the MSRC CVRF API To get an API key, visit https://portal.msrc.microsoft.com #> [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] $Vulnerability, <# Get the CVRF document for the specified CVRF ID (ie. 2016-Aug) #> [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] $ProductTree, <# Get the CVRF document for the specified CVRF ID (ie. 2016-Aug) #> [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] $DocumentTracking, <# Get the CVRF document for the specified CVRF ID (ie. 2016-Aug) #> [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] $DocumentTitle ) $htmlDocumentTemplate = @' <html> <head> <!-- this is the css from the old bulletin site. Change this to better style your report to your liking --> <link rel="stylesheet" href="https://i-technet.sec.s-msft.com/Combined.css?resources=0:ImageSprite,0:TopicResponsive,0:TopicResponsive.MediaQueries,1:CodeSnippet,1:ProgrammingSelector,1:ExpandableCollapsibleArea,0:CommunityContent,1:TopicNotInScope,1:FeedViewerBasic,1:ImageSprite,2:Header.2,2:HeaderFooterSprite,2:Header.MediaQueries,2:Banner.MediaQueries,3:megabladeMenu.1,3:MegabladeMenu.MediaQueries,3:MegabladeMenuSpriteCluster,0:Breadcrumbs,0:Breadcrumbs.MediaQueries,0:ResponsiveToc,0:ResponsiveToc.MediaQueries,1:NavSidebar,0:LibraryMemberFilter,4:StandardRating,2:Footer.2,5:LinkList,2:Footer.MediaQueries,0:BaseResponsive,6:MsdnResponsive,0:Tables.MediaQueries,7:SkinnyRatingResponsive,7:SkinnyRatingV2;/Areas/Library/Content:0,/Areas/Epx/Content/Css:1,/Areas/Epx/Themes/TechNet/Content:2,/Areas/Epx/Themes/Shared/Content:3,/Areas/Global/Content:4,/Areas/Epx/Themes/Base/Content:5,/Areas/Library/Themes/Msdn/Content:6,/Areas/Library/Themes/TechNet/Content:7&v=9192817066EC5D087D15C766A0430C95"> <!-- this style section changes cell widths in the exec header table so that the affected products at the end are wide enough to read --> <style> #execHeader td:first-child {{ width: 10% ;}} #execHeader td:nth-child(5) {{ width: 37% ;}} </style> <!-- this section defines explicit width for all cells in the affected software tables. This is so the column width is the same across each product --> <style> .affected_software td:first-child {{ width: 20% ; }} .affected_software td:nth-child(2) {{ width: 20% ; }} .affected_software td:nth-child(3) {{ width: 15% ; }} .affected_software td:nth-child(4) {{ width: 22.5% ; }} .affected_software td:nth-child(5) {{ width: 22.5% ; }} </style> </head> <body lang=EN-US link=blue> <div id="documentWrapper" style="width: 90%; margin-left: auto; margin-right: auto;"> <h1>Microsoft Security Bulletin Summary for {0}</h1> <p>This bulletin summary lists security bulletins released for {0}.</p> <p>Microsoft also provides information to help customers prioritize monthly security updates with any non-security, high-priority updates that are being released on the same day as the monthly security updates. Please see the section, <b>Other Information</b>. </p> <p> As a reminder, the <a href="https://portal.msrc.microsoft.com/en-us/security-guidance">Security Updates Guide</a> will be replacing security bulletins. Please see our blog post, <a href="https://blogs.technet.microsoft.com/msrc/2016/11/08/furthering-our-commitment-to-security-updates/">Furthering our commitment to security updates</a>, for more details. </p> <p>To receive automatic notifications whenever Microsoft Security Updates are issued, subscribe to <a href="http://go.microsoft.com/fwlink/?LinkId=21163">Microsoft Technical Security Notifications</a>. </p> <h1>Executive Summaries</h1> <p>The following table summarizes the security bulletins for this month in order of severity. For details on affected software, see the next section, Affected Software. </p> <table id="execHeader" border=1 cellpadding=0 width="99%"> <thead style="background-color: #ededed"> <tr> <td><b>CVE ID</b></td> <td><b>Vulnerability Description</b></td> <td><b>Maximum Severity Rating</b></td> <td><b>Vulnerability Impact</b></td> <td><b>Affected Software</b></td> </tr> </thead> {1} </table> <h1>Exploitability Index</h1> <p>The following table provides an exploitability assessment of each of the vulnerabilities addressed this month. The vulnerabilities are listed in order of bulletin ID then CVE ID. Only vulnerabilities that have a severity rating of Critical or Important in the bulletins are included.</p> <p><b>How do I use this table?</b></p> <p>Use this table to learn about the likelihood of code execution and denial of service exploits within 30 days of security bulletin release, for each of the security updates that you may need to install. Review each of the assessments below, in accordance with your specific configuration, to prioritize your deployment of this month's updates. For more information about what these ratings mean, and how they are determined, please see <a href="http://technet.microsoft.com/security/cc998259">Microsoft Exploitability Index</a>. </p> <p>In the columns below, "Latest Software Release" refers to the subject software, and "Older Software Releases" refers to all older, supported releases of the subject software, as listed in the "Affected Software" and "Non-Affected Software" tables in the bulletin.</p> <table border=1 cellpadding=0 width="99%"> <thead style="background-color: #ededed"> <tr> <td><b>CVE ID</b></td> <td><b>Vulnerability Title</b></td> <td><b>Exploitability Assessment for Latest Software Release</b></td> <td><b>Exploitability Assessment for Older Software Release</b></td> <td><b>Denial of Service Exploitability Assessment</b></td> </tr> </thead> {2} </table> <h1>Affected Software</h1> <p>The following tables list the bulletins in order of major software category and severity.</p> <p>Use these tables to learn about the security updates that you may need to install. You should review each software program or component listed to see whether any security updates pertain to your installation. If a software program or component is listed, then the severity rating of the software update is also listed.</p> <p><b>Note:</b> You may have to install several security updates for a single vulnerability. Review the whole column for each bulletin identifier that is listed to verify the updates that you have to install, based on the programs or components that you have installed on your system.</p> <!-- Affected software tables --> {3} <!-- End Affected software tables --> <h1>Detection and Deployment Tools and Guidance</h1> <p>Several resources are available to help administrators deploy security updates.</p> <ul> <li> Microsoft Baseline Security Analyzer (MBSA) lets administrators scan local and remote systems for missing security updates and common security misconfigurations. </li> <li> Windows Server Update Services (WSUS), Systems Management Server (SMS), and System Center Configuration Manager help administrators distribute security updates. </li> <li> The Update Compatibility Evaluator components included with Application Compatibility Toolkit aid in streamlining the testing and validation of Windows updates against installed applications. </li> </ul> <p>For information about these and other tools that are available, see <a href="http://technet.microsoft.com/security/cc297183">Security Tools for IT Pros</a>. </p> <h1>Other Information</h1> <h2>Microsoft Windows Malicious Software Removal Tool</h2> <p>Microsoft will release an updated version of the Microsoft Windows Malicious Software Removal Tool on Windows Update, Microsoft Update, Windows Server Update Services, and the Download Center.</p> <h2>Microsoft Active Protections Program (MAPP)</h2> <p>To improve security protections for customers, Microsoft provides vulnerability information to major security software providers in advance of each monthly security update release. Security software providers can then use this vulnerability information to provide updated protections to customers via their security software or devices, such as antivirus, network-based intrusion detection systems, or host-based intrusion prevention systems. To determine whether active protections are available from security software providers, please visit the active protections websites provided by program partners, listed in <a href="http://go.microsoft.com/fwlink/?LinkId=215201">Microsoft Active Protections Program (MAPP) Partners</a>. </p> <h2>Security Strategies and Community</h2> <p>Updates for other security issues are available from the following locations:</p> <ul> <li> Non-Window Security updates are available from <a href="http://go.microsoft.com/fwlink/?LinkId=21129">Microsoft Download Center</a>. You can find them most easily by doing a keyword search for "security update". </li> <li> All Updates are available from <a href="http://go.microsoft.com/fwlink/?LinkID=40747">Microsoft Update</a>. </li> </ul> <h2>IT Pro Security Community</h2> <p>Learn to improve security and optimize your IT infrastructure, and participate with other IT Pros on security topics in <a href="http://go.microsoft.com/fwlink/?LinkId=21164">IT Pro Security Community</a>. </p> <h2>Support</h2> <ul> <li> The affected software listed has been tested to determine which versions are affected. Other versions are past their support life cycle. To determine the support life cycle for your software version, visit <a href="http://go.microsoft.com/fwlink/?LinkId=21742">Microsoft Support Lifecycle</a>. </li> <li> Help protect your computer that is running Windows from viruses and malware: <a href="http://support.microsoft.com/contactus/cu_sc_virsec_master">Virus and Security Solution Center</a> </li> </ul> <h2>Disclaimer</h2> <p>The information provided in the Microsoft Knowledge Base is provided "as is" without warranty of any kind. Microsoft disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. In no event shall Microsoft Corporation or its suppliers be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if Microsoft Corporation or its suppliers have been advised of the possibility of such damages. Some states do not allow the exclusion or limitation of liability for consequential or incidental damages so the foregoing limitation may not apply.</p> </div> </body> </html> '@ #region CVE Summary Table $cveSummaryRowTemplate = @' <tr> <td>{0}</td> <td>{1}</td> <td>{2}</td> <td>{3}</td> <td>{4}</td> </tr> '@ $cveSummaryTableHtml = '' foreach($vuln in $Vulnerability) { $SeverityValues = $vuln.Threats | Where-Object Type -EQ 3 | Select @{Name='Severity' ;Expression={$_.Description.Value}} -Unique | Select -ExpandProperty Severity if ($SeverityValues -contains 'Critical') { $maximumSeverity = 'Critical' } elseif ($SeverityValues -contains 'Important') { $maximumSeverity = 'Important' } elseif ($SeverityValues -contains 'Moderate') { $maximumSeverity = 'Moderate' } elseif ($SeverityValues -contains 'Low') { $maximumSeverity = 'Low' } else { Write-Warning "Could not determine the Maximum Severity from the Threats" $maximumSeverity = 'Unknown' } $ImpactValues = $vuln.Threats | Where-Object Type -EQ 0 | ForEach-Object {$_.Description.Value} | Select-Object -Unique $AffectedSoftware = $vuln.ProductStatuses.ProductID | ForEach-Object { $ProductTree.FullProductName | Where ProductID -EQ $PSItem | Select -ExpandProperty Value } | Select -Unique $vulnTableColumn = $vuln.CVE + "<br>" + "<a href=`"http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=$($vuln.CVE)`">MITRE</a>" + "<br>" + "<a href=`"https://web.nvd.nist.gov/view/vuln/detail?vulnId=$($vuln.CVE)`">NVD</a>" $cveSummaryTableHtml += $cveSummaryRowTemplate -f @( $vulnTableColumn #TODO - make this an href $vuln.Notes | Where Title -eq Description | Select -ExpandProperty Value $maximumSeverity $ImpactValues -join ',<br>' $AffectedSoftware -join ',<br>' ) } #endregion #region Exploitability Index Table $exploitabilityRowTemplate = @' <tr> <td>{0}</td> <td>{1}</td> <td>{2}</td> <td>{3}</td> <td>{4}</td> </tr> '@ $exploitabilityIndexTableHtml = '' foreach($vuln in $Vulnerability) { $ExploitStatusLatest = '' $ExploitStatusOlder = '' $ExploitStatusThreat = $vuln.Threats | Where Type -EQ 1 | Select -Last 1 $ExploitStatus = Get-MsrcThreatExploitStatus -ExploitStatusString $ExploitStatusThreat.Description.Value $exploitabilityIndexTableHtml += $exploitabilityRowTemplate -f @( $vuln.CVE #TODO - make this an href $vuln.Title.Value $ExploitStatus.LatestSoftwareRelease $ExploitStatus.OlderSoftwareRelease $ExploitStatus.DenialOfService ) } #endregion #region Affected Software Table $affectedSoftwareNameHeaderTemplate = @' <table class="affected_software" border=1 cellpadding=0 width="99%"> <thead style="background-color: #ededed"> <tr> <td colspan="5"><b>{0}</b></td> </tr> </thead> <tr> <td><b>CVE ID</b></td> <td><b>KB Article</b></td> <td><b>Restart Required</b></td> <td><b>Severity</b></td> <td><b>Impact</b></td> </tr> {1} </table> '@ $affectedSoftwareRowTemplate = @' <tr> <td>{0}</td> <td>{1}</td> <td>{2}</td> <td>{3}</td> <td>{4}</td> </tr> '@ $affectedSoftwareTableHtml = '' $affectedSoftwareDocumentHtml = '' $affectedSoftware = Get-MsrcCvrfAffectedSoftware -Vulnerability $Vulnerability -ProductTree $ProductTree foreach($productName in $($affectedSoftware.FullProductName | Sort-Object | Get-Unique )) { $affectedSoftwareTableHtml = '' foreach($affectedSoftwareItem in $affectedSoftware | Where-Object {$_.FullProductName -eq $productName}) { $affectedSoftwareTableHtml += $affectedSoftwareRowTemplate -f @( $affectedSoftwareItem.CVE $( if (!$affectedSoftwareItem.KBArticle){"None"}else{$affectedSoftwareItem.KBArticle} ) $( if (!$affectedSoftwareItem.RestartRequired){"Unknown"}else{$affectedSoftwareItem.RestartRequired} ) $( if (!$affectedSoftwareItem.Severity){"Unknown"}else{$affectedSoftwareItem.Severity} ) $( if (!$affectedSoftwareItem.Impact){"Unknown"}else{$affectedSoftwareItem.Impact} ) ) } $affectedSoftwareDocumentHtml += $affectedSoftwareNameHeaderTemplate -f @( $ProductName $affectedSoftwareTableHtml ) } #endregion Write-Output ($htmlDocumentTemplate -f @( $DocumentTitle.Value #Title $cveSummaryTableHtml #CVE Summary Rows $exploitabilityIndexTableHtml #Expoitability Rows $affectedSoftwareDocumentHtml #Affected Software Rows )) } function Get-MsrcThreatExploitStatus { <# .EXAMPLE "Publicly Disclosed:No;Exploited:No;Latest Software Release:Exploitation More Likely;Older Software Release:N/A;" | Get-MsrcThreatExploitStatus .EXAMPLE Get-MsrcThreatExploitStatus -ExploitStatusString "Publicly Disclosed:No;Exploited:No;Latest Software Release:Exploitation More Likely;Older Software Release:N/A;" #> Param ( <# The Exploit Status string, which is delimited #> [Parameter(Mandatory=$true, ValueFromPipeline=$true)] $ExploitStatusString ) Process { $ExploitStatus = [PSCustomObject]@{ PubliclyDisclosed = '' Exploited = '' LatestSoftwareRelease = '' OlderSoftwareRelease = '' DenialOfService = 'N/A' } foreach ($exploitStatusItem in $ExploitStatusString -split ';') { $exploitStatusName, $exploitStatusValue = $exploitStatusItem -split ':' if ($exploitStatusName -eq 'Publicly Disclosed') { $ExploitStatus.PubliclyDisclosed = $exploitStatusValue } if ($exploitStatusName -eq 'Exploited') { $ExploitStatus.Exploited = $exploitStatusValue } if ($exploitStatusName -eq 'Latest Software Release') { $ExploitStatus.LatestSoftwareRelease = $exploitStatusValue } if ($exploitStatusName -eq 'Older Software Release') { $ExploitStatus.OlderSoftwareRelease = $exploitStatusValue } } Write-Output $ExploitStatus } } function Get-MsrcCvrfAffectedSoftware { <# .Synopsis Get details of products affected by a CVRF document .DESCRIPTION CVRF documents next products into several places, including: -Vulnerabilities -Threats -Remediations -Product Tree This function gathers the details for each product identified in a CVRF document. .EXAMPLE #Get product details from a CVRF document using the pipeline Get-MsrcCvrfDocument -ID 2016-Nov -ApiKey 'YOUR API KEY' | Get-MsrcCvrfAffectedSoftware .EXAMPLE #Get product details from a CVRF document using a variable and parameters $cvrfDocument = Get-MsrcCvrfDocument -ID 2016-Nov -ApiKey 'YOUR API KEY' Get-MsrcCvrfAffectedSoftware -Vulnerability $cvrfDocument.Vulnerability -ProductTree $cvrfDocument.ProductTree #> Param ( <# API Key for the MSRC CVRF API To get an API key, visit https://portal.msrc.microsoft.com #> [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] $Vulnerability, <# Get the CVRF document for the specified CVRF ID (ie. 2016-Aug) #> [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] $ProductTree ) foreach($vuln in $Vulnerability) { foreach($vulnProductID in $vuln.ProductStatuses.ProductID) { $FullProductName = $ProductTree.FullProductName | Where-Object ProductID -EQ $vulnProductID | Select -ExpandProperty Value if (-not $FullProductName) { $FullProductName = "Could not find FullProductName for $vulnProductID" } $VendorFixes = $vuln.Remediations | Where-Object ProductID -Contains $vulnProductID | Where-Object Type -EQ 2 if ($VendorFixes) { $KBArticle = '' foreach($fix in $VendorFixes) { $KBArticle += '<a href="{0}">{1}</a><br>' -f $fix.URL, $fix.Description.Value } } $SeverityValues = $vuln.Threats | Where Type -EQ 3 | Where-Object ProductID -Contains $vulnProductID if ($SeverityValues) { $Severity = $SeverityValues.Description.Value -join '<br>' } else { $Severity = 'Unknown' } $ImpactValues = $vuln.Threats | Where Type -EQ 0 | Where-Object ProductID -Contains $vulnProductID if ($ImpactValues) { $Impact = $ImpactValues.Description.Value -join '<br>' } else { $Impact = 'Unknown' } $SupercedenceValues = $vuln.Remediations | Where-Object ProductID -Contains $vulnProductID if ($SupercedenceValues) { $Supercedence = $SupercedenceValues.Supercedence -join '<br>' } else { $Supercedence = 'Unknown' } $RestartRequiredValues = $vuln.Remediations | Where-Object ProductID -Contains $vulnProductID | Select -ExpandProperty RestartRequired if ($RestartRequiredValues.Value -contains 'Yes') { $RestartRequired = 'Yes' } else { $RestartRequired = 'Maybe' } [PSCustomObject] @{ FullProductName = $FullProductName KBArticle = $KBArticle CVE = $vuln.CVE Severity = $Severity Impact = $Impact RestartRequired = $RestartRequired Supercedence = $Supercedence } } } } function Get-MsrcVulnerabilityReportHtml { <# .Synopsis Use a CVRF document to create a Vulnerability summary .EXAMPLE #Create a report with all the Vulnerabilities in a CVRF document Get-MsrcCvrfDocument -ID 2016-Aug -ApiKey 'YOUR API KEY' | Get-MsrcVulnerabilityReportHtml | Out-File -FilePath Cvrf-CVE-Summary.html .EXAMPLE #Create a report for each of the Vulnerabilities in a CVRF document $cvrfDocument = Get-MsrcCvrfDocument -ID 2016-Nov -ApiKey 'YOUR API KEY' foreach ($vulnerability in $cvrfDocument.Vulnerability) { $vulnerability.CVE Get-MsrcVulnerabilityReportHtml -Vulnerability $vulnerability -ProductTree $cvrfDocument.ProductTree | Out-File -FilePath "Cvrf-$($vulnerability.CVE)-Summary.html" } .EXAMPLE #Create a report for specific Vulnerabilities in a CVRF document $cvrfDocument = Get-MsrcCvrfDocument -ID 2016-Nov -ApiKey 'YOUR API KEY' Get-MsrcVulnerabilityReportHtml -Vulnerability ($cvrfDocument.Vulnerability | Where-Object CVE -In @('CVE-2016-0026','CVE-2016-7202','CVE-2016-3343')) -ProductTree $cvrfDocument.ProductTree | Out-File -FilePath Cvrf-CVE-Summary.html #> [OutputType([string])] Param ( <# The Vulnerability node of a CVRF document #> [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] $Vulnerability, <# The ProductTree node of a CVRF document #> [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] $ProductTree ) $htmlDocumentTemplate = @' <html> <head> <!-- this is the css from the old bulletin site. Change this to better style your report to your liking --> <link rel="stylesheet" href="https://i-technet.sec.s-msft.com/Combined.css?resources=0:ImageSprite,0:TopicResponsive,0:TopicResponsive.MediaQueries,1:CodeSnippet,1:ProgrammingSelector,1:ExpandableCollapsibleArea,0:CommunityContent,1:TopicNotInScope,1:FeedViewerBasic,1:ImageSprite,2:Header.2,2:HeaderFooterSprite,2:Header.MediaQueries,2:Banner.MediaQueries,3:megabladeMenu.1,3:MegabladeMenu.MediaQueries,3:MegabladeMenuSpriteCluster,0:Breadcrumbs,0:Breadcrumbs.MediaQueries,0:ResponsiveToc,0:ResponsiveToc.MediaQueries,1:NavSidebar,0:LibraryMemberFilter,4:StandardRating,2:Footer.2,5:LinkList,2:Footer.MediaQueries,0:BaseResponsive,6:MsdnResponsive,0:Tables.MediaQueries,7:SkinnyRatingResponsive,7:SkinnyRatingV2;/Areas/Library/Content:0,/Areas/Epx/Content/Css:1,/Areas/Epx/Themes/TechNet/Content:2,/Areas/Epx/Themes/Shared/Content:3,/Areas/Global/Content:4,/Areas/Epx/Themes/Base/Content:5,/Areas/Library/Themes/Msdn/Content:6,/Areas/Library/Themes/TechNet/Content:7&v=9192817066EC5D087D15C766A0430C95"> <!-- this style section changes cell widths in the exec header table so that the affected products at the end are wide enough to read --> <style> #execHeader td:first-child {{ width: 10% ;}} #execHeader td:nth-child(5) {{ width: 37% ;}} </style> <!-- this section defines explicit width for all cells in the affected software tables. This is so the column width is the same across each product --> <style> .affected_software td:first-child {{ width: 20% ; }} .affected_software td:nth-child(2) {{ width: 20% ; }} .affected_software td:nth-child(3) {{ width: 15% ; }} .affected_software td:nth-child(4) {{ width: 22.5% ; }} .affected_software td:nth-child(5) {{ width: 22.5% ; }} </style> </head> <body lang=EN-US link=blue> <div id="documentWrapper" style="width: 90%; margin-left: auto; margin-right: auto;"> <h1 id="top">Microsoft CVE Summary</h1> <p>This report contains detail for the following vulnerabilities:</p> <ul> {0} </ul> {1} </div> <br> </body> </html> '@ $cveListHtml = '' $cveSectionHtml = '' foreach($vuln in $Vulnerability) { if ($vuln.Title.Value) { $cveTitle = $vuln.Title.Value } else { Write-Warning "Missing Title for $($vuln.CVE)" $cveTitle = 'Unknown' } $cveSectionHtml += '<h1 id="{0}">{0} - {1}</h1> (<a href="#top">top</a>)' -f $vuln.CVE, $cveTitle #region CVE Summary List $cveListHtml += '<li><a href="#{0}">{0}</a> - {1}</li>' -f $vuln.CVE, $cveTitle #endregion #region CVE Summary Table $cveSummaryTableHtml = @' <table id="execHeader" border=1 cellpadding=0 width="99%"> <thead style="background-color: #ededed"> <tr> <td><b>CVE ID</b></td> <td><b>Vulnerability Description</b></td> <td><b>Maximum Severity Rating</b></td> <td><b>Vulnerability Impact</b></td> </tr> </thead> <tr> <td>{0}</td> <td>{1}</td> <td>{2}</td> <td>{3}</td> </tr> </table> '@ $SeverityValues = $vuln.Threats | Where-Object Type -EQ 3 | Select @{Name='Severity' ;Expression={$_.Description.Value}} -Unique | Select -ExpandProperty Severity if ($SeverityValues -contains 'Critical') { $maximumSeverity = 'Critical' } elseif ($SeverityValues -contains 'Important') { $maximumSeverity = 'Important' } elseif ($SeverityValues -contains 'Moderate') { $maximumSeverity = 'Moderate' } elseif ($SeverityValues -contains 'Low') { $maximumSeverity = 'Low' } else { Write-Warning "Could not determine the Maximum Severity from the Threats for $($vuln.CVE)" $maximumSeverity = 'Unknown' } $ImpactValues = $vuln.Threats | Where-Object Type -EQ 0 | ForEach-Object {$_.Description.Value} | Select-Object -Unique if ($ImpactValues) { $impactColumn = $ImpactValues -join ',<br>' } else { Write-Warning "Could not determine the Impact from the Threats for $($vuln.CVE)" $impactColumn = 'Unknown' } $vulnTableColumn = $vuln.CVE + "<br>" + "<a href=`"http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=$($vuln.CVE)`">MITRE</a>" + "<br>" + "<a href=`"https://web.nvd.nist.gov/view/vuln/detail?vulnId=$($vuln.CVE)`">NVD</a>" $vulnDescriptionColumnTemplate = @' <b>CVE Title:</b> {0} <br> <b>Description:</b> <br>{1} <br> <b>FAQ:</b><br>{2} <br> <b>Mitigations:</b><br>{3} <br> <b>Workarounds:</b><br>{4} <br> '@ if ($vuln.Notes | Where-Object Title -eq Description) { $cveDescription = $vuln.Notes | Where Title -eq Description | Select -ExpandProperty Value } else { Write-Warning "Missing Description for $($vuln.CVE)" $cveDescription = 'Unknown' } $cveFaqs = $vuln.Notes | Where Title -eq FAQ | Select -ExpandProperty Value if ($cveFaqs) { $cveFaq = $cveFaqs -join '<br>' } else { $cveFaq = "No FAQ for $($vuln.CVE)" } $cveMitigations = $vuln.Remediations | Where-Object Type -EQ 1 if ($cveMitigations) { $cveMitigation = $cveMitigations.URL -join '<br>' } else { $cveMitigation = "No mitigations for $($vuln.CVE)" } $cveWorkarounds = $vuln.Remediations | Where-Object Type -EQ 0 | Select-Object -ExpandProperty Description | Select-Object -ExpandProperty Value if ($cveWorkarounds) { $cveWorkaround = $cveWorkarounds -join '<br>' } else { $cveWorkaround = "No workarounds for $($vuln.CVE)" } $vulnDescriptionColumn = $vulnDescriptionColumnTemplate -f @( $cveTitle $cveDescription $cveFaq $cveMitigation $cveWorkaround ) $cveSectionHtml += $cveSummaryTableHtml -f @( $vulnTableColumn $vulnDescriptionColumn $maximumSeverity $impactColumn ) #endregion #region Exploitability Index Table $exploitabilityIndexTableHtml = @' <h2>Exploitability Index</h2> <p>The following table provides an exploitability assessment of each of the vulnerabilities addressed this month. The vulnerabilities are listed in order of bulletin ID then CVE ID. Only vulnerabilities that have a severity rating of Critical or Important in the bulletins are included.</p> <table border=1 cellpadding=0 width="99%"> <thead style="background-color: #ededed"> <tr> <td><b>CVE ID</b></td> <td><b>Vulnerability Title</b></td> <td><b>Exploitability Assessment for Latest Software Release</b></td> <td><b>Exploitability Assessment for Older Software Release</b></td> <td><b>Denial of Service Exploitability Assessment</b></td> </tr> </thead> <tr> <td>{0}</td> <td>{1}</td> <td>{2}</td> <td>{3}</td> <td>{4}</td> </tr> </table> '@ $ExploitStatusLatest = '' $ExploitStatusOlder = '' $ExploitStatusThreat = $vuln.Threats | Where Type -EQ 1 | Select -Last 1 if ($ExploitStatusThreat.Description.Value) { $ExploitStatus = Get-MsrcThreatExploitStatus -ExploitStatusString $ExploitStatusThreat.Description.Value } else { Write-Warning "Missing ExploitStatus for $($vuln.CVE)" } if ($ExploitStatus.LatestSoftwareRelease) { $LatestSoftwareRelease = $ExploitStatus.LatestSoftwareRelease } else { $LatestSoftwareRelease = 'Not Found' } if ($ExploitStatus.OlderSoftwareRelease) { $OlderSoftwareRelease = $ExploitStatus.OlderSoftwareRelease } else { $OlderSoftwareRelease = 'Not Found' } if ($ExploitStatus.DenialOfService) { $DenialOfService = $ExploitStatus.DenialOfService } else { $DenialOfService = 'Not Found' } $cveSectionHtml += $exploitabilityIndexTableHtml -f @( $vuln.CVE $cveTitle $LatestSoftwareRelease $OlderSoftwareRelease $DenialOfService ) #endregion #region Affected Software Table $affectedSoftwareTableTemplate = @' <table class="affected_software" border=1 cellpadding=0 width="99%"> <thead style="background-color: #ededed"> <tr> <td colspan="6"><b>{0}</b></td> </tr> </thead> <tr> <td><b>CVE</b></td> <td><b>KB Article</b></td> <td><b>Severity</b></td> <td><b>Impact</b></td> <td><b>Supersedence</b></td> <td><b>Restart Required</b></td> </tr> {1} </table> <br> '@ $affectedSoftwareRowTemplate = @' <tr> <td>{0}</td> <td>{1}</td> <td>{2}</td> <td>{3}</td> <td>{4}</td> <td>{5}</td> </tr> '@ $cveSectionHtml += @' <h2>Affected Software</h2> <p>The following tables list the affected software details for the vulnerability.</p> '@ $affectedSoftware = Get-MsrcCvrfAffectedSoftware -Vulnerability $vuln -ProductTree $ProductTree foreach($productName in $($affectedSoftware.FullProductName | Sort-Object | Get-Unique )) { $affectedSoftwareTableHtml = '' foreach($affectedSoftwareItem in $affectedSoftware | Where-Object {$_.FullProductName -eq $productName}) { $affectedSoftwareTableHtml += $affectedSoftwareRowTemplate -f @( $affectedSoftwareItem.CVE $( if (!$affectedSoftwareItem.KBArticle){"None"}else{$affectedSoftwareItem.KBArticle} ) $( if (!$affectedSoftwareItem.Severity){"Unknown"}else{$affectedSoftwareItem.Severity} ) $( if (!$affectedSoftwareItem.Impact){"Unknown"}else{$affectedSoftwareItem.Impact} ) $( if (!$affectedSoftwareItem.Supercedence){"Unknown"}else{$affectedSoftwareItem.Supercedence} ) $( if (!$affectedSoftwareItem.RestartRequired){"Unknown"}else{$affectedSoftwareItem.RestartRequired} ) ) } $cveSectionHtml += $affectedSoftwareTableTemplate -f @( $ProductName $affectedSoftwareTableHtml ) } #endregion #region Acknowledgments Table $acknowledgmentsTableTemplate = @' <h2>Acknowledgements</h2> <table border=1 cellpadding=0 width="99%"> <thead style="background-color: #ededed"> <tr> <td><b>CVE ID</b></td> <td><b>Acknowledgements</b></td> </tr> </thead> <tr> <td>{0}</td> <td>{1}</td> </tr> </table> '@ if ($vuln.Acknowledgments) { $acknowledgmentsValue = '' foreach ($ack in $vuln.Acknowledgments) { if ($ack.Name.Value) { $acknowledgmentsValue += $ack.Name.Value $acknowledgmentsValue += '<br>' } if ($ack.URL) { $acknowledgmentsValue += $ack.URL $acknowledgmentsValue += '<br>' } $acknowledgmentsValue += '<br><br>' } } else { Write-Warning "No Acknowledgments for $($vuln.CVE)" $acknowledgmentsValue = 'No Acknowledgments' } $cveSectionHtml += $acknowledgmentsTableTemplate -f @( $vuln.CVE $acknowledgmentsValue ) } #endregion Write-Output ($htmlDocumentTemplate -f @( $cveListHtml $cveSectionHtml )) } |