Vulnerability/Find-EOLSoftware.ps1
|
function Find-EOLSoftware { <# .SYNOPSIS Detects end-of-life (EOL) and unsupported software installations .DESCRIPTION Scans installed software and identifies applications that have reached end-of-life, are no longer supported, or are critically outdated. Provides security risk assessment and upgrade recommendations. .PARAMETER IncludeExpiringSoon Include software expiring within specified months (default: 6) .PARAMETER ExpiringMonths Number of months to check for expiring support (default: 6) .PARAMETER OutputPath Path to save EOL software report .PARAMETER Format Report format: HTML, JSON, CSV, or All .EXAMPLE Find-EOLSoftware Find-EOLSoftware -IncludeExpiringSoon -ExpiringMonths 12 -OutputPath "C:\Reports" .OUTPUTS PSCustomObject with EOL software findings #> [CmdletBinding()] param( [Parameter()] [switch]$IncludeExpiringSoon, [Parameter()] [int]$ExpiringMonths = 6, [Parameter()] [string]$OutputPath, [Parameter()] [ValidateSet('HTML', 'JSON', 'CSV', 'All')] [string]$Format = 'HTML' ) begin { Write-Host "=== END-OF-LIFE SOFTWARE DETECTION ===" -ForegroundColor Cyan Write-Host "Start Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Yellow Write-Host "Include Expiring Soon: $(if($IncludeExpiringSoon){'Yes'}else{'No'})" -ForegroundColor Yellow if ($IncludeExpiringSoon) { Write-Host "Expiring Within: $ExpiringMonths months" -ForegroundColor Yellow } $findings = [PSCustomObject]@{ ScanDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" ComputerName = $env:COMPUTERNAME TotalSoftware = 0 EOLSoftware = @() ExpiringSoon = @() Summary = @{ TotalEOL = 0 CriticalRisk = 0 HighRisk = 0 MediumRisk = 0 TotalExpiring = 0 } Recommendations = @() } # Known EOL Software Database $eolDatabase = @{ 'Windows 7' = @{ EOLDate = [DateTime]'2020-01-14' Severity = 'Critical' Replacement = 'Windows 10/11' Notes = 'No security updates since January 2020' } 'Windows 8' = @{ EOLDate = [DateTime]'2016-01-12' Severity = 'Critical' Replacement = 'Windows 10/11' Notes = 'Extended support ended' } 'Windows Server 2008' = @{ EOLDate = [DateTime]'2020-01-14' Severity = 'Critical' Replacement = 'Windows Server 2019/2022' Notes = 'Critical security risk - no patches' } 'Windows Server 2012' = @{ EOLDate = [DateTime]'2023-10-10' Severity = 'Critical' Replacement = 'Windows Server 2019/2022' Notes = 'Extended support ended October 2023' } 'Office 2010' = @{ EOLDate = [DateTime]'2020-10-13' Severity = 'High' Replacement = 'Microsoft 365 / Office 2021' Notes = 'No security updates available' } 'Office 2013' = @{ EOLDate = [DateTime]'2023-04-11' Severity = 'High' Replacement = 'Microsoft 365 / Office 2021' Notes = 'Extended support ended April 2023' } 'Office 2016' = @{ EOLDate = [DateTime]'2025-10-14' Severity = 'Medium' Replacement = 'Microsoft 365 / Office 2021' Notes = 'Approaching end of support' } 'SQL Server 2008' = @{ EOLDate = [DateTime]'2019-07-09' Severity = 'Critical' Replacement = 'SQL Server 2019/2022' Notes = 'Critical database security risk' } 'SQL Server 2012' = @{ EOLDate = [DateTime]'2022-07-12' Severity = 'Critical' Replacement = 'SQL Server 2019/2022' Notes = 'Extended support ended' } 'SQL Server 2014' = @{ EOLDate = [DateTime]'2024-07-09' Severity = 'High' Replacement = 'SQL Server 2019/2022' Notes = 'Extended support ending soon' } 'Internet Explorer' = @{ EOLDate = [DateTime]'2022-06-15' Severity = 'Critical' Replacement = 'Microsoft Edge' Notes = 'Disabled by Microsoft - security risk' } 'Adobe Flash Player' = @{ EOLDate = [DateTime]'2020-12-31' Severity = 'Critical' Replacement = 'Modern web standards (HTML5)' Notes = 'Blocked by all major browsers' } 'Java SE 7' = @{ EOLDate = [DateTime]'2015-04-14' Severity = 'Critical' Replacement = 'Java SE 17 LTS or 21 LTS' Notes = 'Multiple critical vulnerabilities' } 'Java SE 8' = @{ EOLDate = [DateTime]'2030-12-31' Severity = 'Low' Replacement = 'Java SE 17 LTS or 21 LTS' Notes = 'Public updates ended (commercial available)' } 'Python 2' = @{ EOLDate = [DateTime]'2020-01-01' Severity = 'High' Replacement = 'Python 3.9+' Notes = 'No bug fixes or security updates' } 'PHP 7.2' = @{ EOLDate = [DateTime]'2020-11-30' Severity = 'High' Replacement = 'PHP 8.1+' Notes = 'Security support ended' } 'PHP 7.3' = @{ EOLDate = [DateTime]'2021-12-06' Severity = 'High' Replacement = 'PHP 8.1+' Notes = 'Security support ended' } 'PHP 7.4' = @{ EOLDate = [DateTime]'2022-11-28' Severity = 'High' Replacement = 'PHP 8.1+' Notes = 'Security support ended' } 'Node.js 10' = @{ EOLDate = [DateTime]'2021-04-30' Severity = 'High' Replacement = 'Node.js 18 LTS or 20 LTS' Notes = 'No security updates' } 'Node.js 12' = @{ EOLDate = [DateTime]'2022-04-30' Severity = 'High' Replacement = 'Node.js 18 LTS or 20 LTS' Notes = 'No security updates' } '.NET Framework 4.5' = @{ EOLDate = [DateTime]'2016-01-12' Severity = 'High' Replacement = '.NET 6/7/8' Notes = 'Upgrade to supported version' } } } process { try { # 1. SCAN INSTALLED SOFTWARE Write-Host "`n[1/3] Scanning installed software..." -ForegroundColor Cyan $registryPaths = @( 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' ) $installedSoftware = @() foreach ($path in $registryPaths) { $items = Get-ItemProperty $path -ErrorAction SilentlyContinue if ($items) { foreach ($item in $items) { if ($null -ne $item.PSObject.Properties['DisplayName'] -and $null -ne $item.DisplayName -and $item.DisplayName -ne '') { $displayVersion = if ($null -ne $item.PSObject.Properties['DisplayVersion']) { $item.DisplayVersion } else { 'Unknown' } $publisher = if ($null -ne $item.PSObject.Properties['Publisher']) { $item.Publisher } else { 'Unknown' } $installedSoftware += [PSCustomObject]@{ DisplayName = $item.DisplayName DisplayVersion = $displayVersion Publisher = $publisher } } } } } $installedSoftware = @($installedSoftware | Sort-Object DisplayName -Unique) $findings.TotalSoftware = $installedSoftware.Count Write-Host " Found $($installedSoftware.Count) installed applications" -ForegroundColor Gray # 2. CHECK AGAINST EOL DATABASE Write-Host "`n[2/3] Checking against EOL database..." -ForegroundColor Cyan $currentDate = Get-Date $expiringThreshold = $currentDate.AddMonths($ExpiringMonths) foreach ($software in $installedSoftware) { foreach ($eolName in $eolDatabase.Keys) { if ($software.DisplayName -like "*$eolName*") { $eolInfo = $eolDatabase[$eolName] # Check if EOL or expiring soon $isEOL = $currentDate -gt $eolInfo.EOLDate $isExpiringSoon = ($eolInfo.EOLDate -gt $currentDate) -and ($eolInfo.EOLDate -le $expiringThreshold) if ($isEOL) { $daysSinceEOL = [Math]::Round(($currentDate - $eolInfo.EOLDate).TotalDays) $eolSoftware = [PSCustomObject]@{ Name = $software.DisplayName Version = $software.DisplayVersion Publisher = $software.Publisher EOLDate = $eolInfo.EOLDate.ToString('yyyy-MM-dd') DaysSinceEOL = $daysSinceEOL Severity = $eolInfo.Severity Replacement = $eolInfo.Replacement Notes = $eolInfo.Notes Status = 'End-of-Life' } $findings.EOLSoftware += $eolSoftware $findings.Summary.TotalEOL++ # Update risk counters switch ($eolInfo.Severity) { 'Critical' { $findings.Summary.CriticalRisk++ } 'High' { $findings.Summary.HighRisk++ } 'Medium' { $findings.Summary.MediumRisk++ } } $color = switch ($eolInfo.Severity) { 'Critical' { 'Red' } 'High' { 'Yellow' } default { 'Gray' } } Write-Host " [!] EOL: $($software.DisplayName) - $($eolInfo.Severity) risk ($daysSinceEOL days past EOL)" -ForegroundColor $color } elseif ($isExpiringSoon -and $IncludeExpiringSoon) { $daysUntilEOL = [Math]::Round(($eolInfo.EOLDate - $currentDate).TotalDays) $expiringSoftware = [PSCustomObject]@{ Name = $software.DisplayName Version = $software.DisplayVersion Publisher = $software.Publisher EOLDate = $eolInfo.EOLDate.ToString('yyyy-MM-dd') DaysUntilEOL = $daysUntilEOL Severity = $eolInfo.Severity Replacement = $eolInfo.Replacement Notes = $eolInfo.Notes Status = 'Expiring Soon' } $findings.ExpiringSoon += $expiringSoftware $findings.Summary.TotalExpiring++ Write-Host " [*] Expiring: $($software.DisplayName) - EOL in $daysUntilEOL days" -ForegroundColor Yellow } } } } Write-Host " Found $($findings.Summary.TotalEOL) EOL software" -ForegroundColor $( if ($findings.Summary.TotalEOL -gt 0) { 'Red' } else { 'Green' } ) if ($IncludeExpiringSoon) { Write-Host " Found $($findings.Summary.TotalExpiring) expiring soon" -ForegroundColor $( if ($findings.Summary.TotalExpiring -gt 0) { 'Yellow' } else { 'Green' } ) } # 3. GENERATE RECOMMENDATIONS Write-Host "`n[3/3] Generating recommendations..." -ForegroundColor Cyan if ($findings.Summary.CriticalRisk -gt 0) { $findings.Recommendations += "CRITICAL: Remove or upgrade $($findings.Summary.CriticalRisk) critically outdated software immediately" } if ($findings.Summary.HighRisk -gt 0) { $findings.Recommendations += "HIGH: Plan migration for $($findings.Summary.HighRisk) high-risk EOL software within 30 days" } if ($findings.Summary.MediumRisk -gt 0) { $findings.Recommendations += "MEDIUM: Schedule upgrades for $($findings.Summary.MediumRisk) medium-risk software" } if ($findings.Summary.TotalExpiring -gt 0) { $findings.Recommendations += "Plan upgrades for $($findings.Summary.TotalExpiring) software approaching EOL" } if ($findings.Summary.TotalEOL -eq 0 -and $findings.Summary.TotalExpiring -eq 0) { $findings.Recommendations += "No EOL or expiring software detected - maintain current update schedule" } $findings.Recommendations += "Implement software lifecycle management policy" $findings.Recommendations += "Subscribe to vendor EOL announcements" $findings.Recommendations += "Conduct quarterly EOL software audits" $findings.Recommendations += "Block installation of EOL software via Group Policy" # 4. EXPORT REPORTS if ($OutputPath) { Write-Host "`n[*] Exporting EOL software reports..." -ForegroundColor Cyan if (-not (Test-Path $OutputPath)) { New-Item -Path $OutputPath -ItemType Directory -Force | Out-Null } $timestamp = Get-Date -Format "yyyyMMdd_HHmmss" $reportName = "EOLSoftware_$timestamp" # Export JSON if ($Format -in @('JSON', 'All')) { $jsonPath = Join-Path $OutputPath "$reportName.json" $findings | ConvertTo-Json -Depth 10 | Out-File $jsonPath -Encoding UTF8 Write-Host " JSON report saved: $jsonPath" -ForegroundColor Green } # Export CSV if ($Format -in @('CSV', 'All')) { $csvPath = Join-Path $OutputPath "$reportName.csv" $allFindings = $findings.EOLSoftware + $findings.ExpiringSoon $allFindings | Export-Csv $csvPath -NoTypeInformation Write-Host " CSV report saved: $csvPath" -ForegroundColor Green } # Export HTML if ($Format -in @('HTML', 'All')) { $htmlPath = Join-Path $OutputPath "$reportName.html" $html = Generate-EOLSoftwareHTML -Findings $findings $html | Out-File $htmlPath -Encoding UTF8 Write-Host " HTML report saved: $htmlPath" -ForegroundColor Green } } } catch { Write-Error "Error during EOL software detection: $_" throw } } end { Write-Host "`n=== EOL SOFTWARE DETECTION COMPLETE ===" -ForegroundColor $( if ($findings.Summary.CriticalRisk -gt 0) { 'Red' } elseif ($findings.Summary.HighRisk -gt 0) { 'Yellow' } else { 'Green' } ) Write-Host "`nSummary:" -ForegroundColor Cyan Write-Host " Total Software Scanned: $($findings.TotalSoftware)" -ForegroundColor White Write-Host " EOL Software: $($findings.Summary.TotalEOL)" -ForegroundColor $( if ($findings.Summary.TotalEOL -gt 0) { 'Red' } else { 'Green' } ) Write-Host " Critical Risk: $($findings.Summary.CriticalRisk)" -ForegroundColor $( if ($findings.Summary.CriticalRisk -gt 0) { 'Red' } else { 'Gray' } ) Write-Host " High Risk: $($findings.Summary.HighRisk)" -ForegroundColor $( if ($findings.Summary.HighRisk -gt 0) { 'Yellow' } else { 'Gray' } ) Write-Host " Medium Risk: $($findings.Summary.MediumRisk)" -ForegroundColor Gray if ($IncludeExpiringSoon) { Write-Host " Expiring Soon: $($findings.Summary.TotalExpiring)" -ForegroundColor $( if ($findings.Summary.TotalExpiring -gt 0) { 'Yellow' } else { 'Gray' } ) } if ($findings.EOLSoftware.Count -gt 0) { Write-Host "`nEOL Software Detected:" -ForegroundColor Cyan $findings.EOLSoftware | Sort-Object { switch ($_.Severity) { 'Critical' { 0 } 'High' { 1 } 'Medium' { 2 } default { 3 } } } | Select-Object -First 10 | ForEach-Object { $color = switch ($_.Severity) { 'Critical' { 'Red' } 'High' { 'Yellow' } default { 'Gray' } } Write-Host " [$($_.Severity)] $($_.Name) - Replace with: $($_.Replacement)" -ForegroundColor $color } } if ($findings.Recommendations.Count -gt 0) { Write-Host "`nRecommendations:" -ForegroundColor Cyan $findings.Recommendations | ForEach-Object { Write-Host " ! $_" -ForegroundColor Yellow } } return $findings } } # Helper function to generate HTML report function Generate-EOLSoftwareHTML { param($Findings) $riskColor = if($Findings.Summary.CriticalRisk -gt 0){'critical'} elseif($Findings.Summary.HighRisk -gt 0){'high'} else{'safe'} $html = @" <!DOCTYPE html> <html> <head> <title>EOL Software Report - $($Findings.ComputerName)</title> <style> body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; } .container { max-width: 1200px; margin: 0 auto; background: white; padding: 30px; box-shadow: 0 0 10px rgba(0,0,0,0.1); } h1 { color: #2c3e50; border-bottom: 3px solid #e74c3c; padding-bottom: 10px; } h2 { color: #34495e; margin-top: 30px; } .summary { background: #ecf0f1; padding: 20px; border-radius: 5px; margin: 20px 0; } .metric { display: inline-block; margin: 10px 20px 10px 0; } .metric-label { font-weight: bold; color: #7f8c8d; } .metric-value { font-size: 24px; font-weight: bold; } .critical { color: #e74c3c; } .high { color: #e67e22; } .medium { color: #f39c12; } .low { color: #95a5a6; } .safe { color: #27ae60; } table { width: 100%; border-collapse: collapse; margin: 20px 0; } th { background: #e74c3c; color: white; padding: 12px; text-align: left; } td { padding: 10px; border-bottom: 1px solid #ddd; } tr:hover { background: #f8f9fa; } .severity-badge { padding: 4px 8px; border-radius: 3px; color: white; font-weight: bold; } .severity-critical { background: #e74c3c; } .severity-high { background: #e67e22; } .severity-medium { background: #f39c12; } .footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; color: #7f8c8d; font-size: 12px; } .no-eol { color: #27ae60; font-weight: bold; font-size: 16px; padding: 20px; text-align: center; } </style> </head> <body> <div class="container"> <h1>End-of-Life Software Report</h1> <p><strong>Computer:</strong> $($Findings.ComputerName) | <strong>Scan Date:</strong> $($Findings.ScanDate)</p> <div class="summary"> <h2>Summary</h2> <div class="metric"> <div class="metric-label">Total Software</div> <div class="metric-value">$($Findings.TotalSoftware)</div> </div> <div class="metric"> <div class="metric-label">EOL Software</div> <div class="metric-value $(if($Findings.Summary.TotalEOL -gt 0){'critical'}else{'safe'})"> $($Findings.Summary.TotalEOL) </div> </div> <div class="metric"> <div class="metric-label">Critical Risk</div> <div class="metric-value critical">$($Findings.Summary.CriticalRisk)</div> </div> <div class="metric"> <div class="metric-label">High Risk</div> <div class="metric-value high">$($Findings.Summary.HighRisk)</div> </div> <div class="metric"> <div class="metric-label">Expiring Soon</div> <div class="metric-value medium">$($Findings.Summary.TotalExpiring)</div> </div> </div> <h2>End-of-Life Software</h2> "@ if ($Findings.EOLSoftware.Count -gt 0) { $html += @" <table> <tr> <th>Severity</th> <th>Software</th> <th>Version</th> <th>EOL Date</th> <th>Days Since EOL</th> <th>Replacement</th> <th>Notes</th> </tr> "@ foreach ($software in ($Findings.EOLSoftware | Sort-Object { switch ($_.Severity) { 'Critical' { 0 } 'High' { 1 } 'Medium' { 2 } default { 3 } } }, DaysSinceEOL -Descending)) { $severityClass = "severity-$($software.Severity.ToLower())" $name = $software.Name -replace '<', '<' -replace '>', '>' $version = "$($software.Version)" -replace '<', '<' -replace '>', '>' $replacement = $software.Replacement -replace '<', '<' -replace '>', '>' $notes = $software.Notes -replace '<', '<' -replace '>', '>' $html += @" <tr> <td><span class="severity-badge $severityClass">$($software.Severity)</span></td> <td><strong>$name</strong></td> <td>$version</td> <td>$($software.EOLDate)</td> <td>$($software.DaysSinceEOL)</td> <td>$replacement</td> <td>$notes</td> </tr> "@ } $html += " </table>`n" } else { $html += " <p class=`"no-eol`">No end-of-life software detected</p>`n" } if ($Findings.ExpiringSoon.Count -gt 0) { $html += " `n <h2>Software Expiring Soon</h2>`n <table>`n" $html += " <tr><th>Software</th><th>EOL Date</th><th>Days Until EOL</th><th>Replacement</th></tr>`n" foreach ($software in ($Findings.ExpiringSoon | Sort-Object DaysUntilEOL)) { $name = $software.Name -replace '<', '<' -replace '>', '>' $replacement = $software.Replacement -replace '<', '<' -replace '>', '>' $html += " <tr><td>$name</td><td>$($software.EOLDate)</td><td>$($software.DaysUntilEOL)</td><td>$replacement</td></tr>`n" } $html += " </table>`n" } $html += " `n <h2>Recommendations</h2>`n <ul>`n" foreach ($rec in $Findings.Recommendations) { $recEscaped = $rec -replace '<', '<' -replace '>', '>' $html += " <li>$recEscaped</li>`n" } $currentDate = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' $html += @" </ul> <div class="footer"> Generated by WindowsSecurityAudit Module | EOL Software Detection | $currentDate </div> </div> </body> </html> "@ return $html } |