163 lines
5.4 KiB
Go
163 lines
5.4 KiB
Go
//go:build windows
|
|
|
|
package vulnprobe
|
|
|
|
import (
|
|
"encoding/json"
|
|
"runtime"
|
|
"strings"
|
|
)
|
|
|
|
const windowsProbeScript = `
|
|
$ErrorActionPreference = 'SilentlyContinue'
|
|
$out = [ordered]@{}
|
|
|
|
# Hotfix + QuickFixEngineering (read-only patch inventory)
|
|
$kbs = @()
|
|
try {
|
|
$hf = Get-HotFix | Sort-Object InstalledOn -Descending
|
|
if ($hf) {
|
|
$latest = $hf | Select-Object -First 1
|
|
if ($latest.InstalledOn) {
|
|
$d = [datetime]$latest.InstalledOn
|
|
$out.last_patch = $d.ToString('yyyy-MM-dd')
|
|
$out.last_patch_days = [int]((Get-Date) - $d).TotalDays
|
|
}
|
|
$kbs += @($hf | ForEach-Object { $_.HotFixID })
|
|
}
|
|
} catch {}
|
|
try {
|
|
$qfe = Get-CimInstance Win32_QuickFixEngineering -ErrorAction SilentlyContinue
|
|
if ($qfe) { $kbs += @($qfe | ForEach-Object { $_.HotFixID }) }
|
|
} catch {
|
|
try {
|
|
$qfe = Get-WmiObject Win32_QuickFixEngineering -ErrorAction SilentlyContinue
|
|
if ($qfe) { $kbs += @($qfe | ForEach-Object { $_.HotFixID }) }
|
|
} catch {}
|
|
}
|
|
$out.installed_kbs = @($kbs | Where-Object { $_ } | Select-Object -Unique)
|
|
|
|
# Service surface (Get-Service)
|
|
$exSvc = @(Get-Service -ErrorAction SilentlyContinue | Where-Object { $_.Name -like 'MSExchange*' -or $_.DisplayName -like '*Exchange*' })
|
|
$out.exchange_installed = ($exSvc.Count -gt 0 -or (Test-Path 'HKLM:\SOFTWARE\Microsoft\ExchangeServer'))
|
|
|
|
try {
|
|
$dc = (Get-CimInstance Win32_ComputerSystem).DomainRole -in 4,5
|
|
} catch { $dc = $false }
|
|
$out.is_domain_controller = $dc
|
|
|
|
$pulse = @(Get-Service -ErrorAction SilentlyContinue | Where-Object {
|
|
$_.DisplayName -match 'Pulse|Ivanti|Juniper Pulse' -or $_.Name -match 'Pulse'
|
|
})
|
|
$out.pulse_present = ($pulse.Count -gt 0)
|
|
|
|
$citrix = @(
|
|
(Test-Path 'C:\Program Files\Citrix'),
|
|
(Test-Path 'C:\Program Files (x86)\Citrix')
|
|
) | Where-Object { $_ }
|
|
$out.citrix_present = ($citrix.Count -gt 0)
|
|
|
|
$f5 = @(Get-Process -ErrorAction SilentlyContinue | Where-Object { $_.Name -match 'bigip|f5' })
|
|
$out.f5_process = ($f5.Count -gt 0)
|
|
|
|
$conf = @(Get-Process -ErrorAction SilentlyContinue | Where-Object {
|
|
$_.Path -match 'atlassian|confluence|tomcat' -or $_.ProcessName -match 'confluence|tomcat'
|
|
})
|
|
$out.confluence_like = ($conf.Count -gt 0)
|
|
|
|
$me = @(
|
|
(Test-Path 'C:\Program Files\ManageEngine'),
|
|
(Test-Path 'C:\ManageEngine')
|
|
) | Where-Object { $_ }
|
|
$out.manageengine_present = ($me.Count -gt 0)
|
|
|
|
$forti = @(Get-Process -ErrorAction SilentlyContinue | Where-Object { $_.Name -match 'forti' })
|
|
$out.forticlient = ($forti.Count -gt 0)
|
|
|
|
$vmw = @(Get-Service -ErrorAction SilentlyContinue | Where-Object { $_.Name -match 'vpxd|VMware' })
|
|
$out.vmware_serverish = ($vmw.Count -gt 0)
|
|
|
|
try {
|
|
$sp = Get-Service Spooler
|
|
$out.spooler_running = ($sp.Status -eq 'Running')
|
|
} catch { $out.spooler_running = $false }
|
|
|
|
try {
|
|
$sshd = Get-Service -Name sshd -ErrorAction SilentlyContinue
|
|
$out.ssh_listening = ($sshd.Status -eq 'Running')
|
|
} catch { $out.ssh_listening = $false }
|
|
|
|
$log4j = @()
|
|
$roots = @($env:ProgramFiles, ${env:ProgramFiles(x86)}, 'C:\ProgramData') | Where-Object { $_ -and (Test-Path $_) }
|
|
foreach ($root in $roots) {
|
|
$log4j += Get-ChildItem -Path $root -Filter 'log4j-core*.jar' -Recurse -Depth 3 -ErrorAction SilentlyContinue |
|
|
Select-Object -First 5 -ExpandProperty FullName
|
|
}
|
|
$out.log4j_jars = @($log4j | Select-Object -Unique)
|
|
|
|
$out | ConvertTo-Json -Compress -Depth 4
|
|
`
|
|
|
|
type windowsProbeResult struct {
|
|
LastPatch string `json:"last_patch"`
|
|
LastPatchDays int `json:"last_patch_days"`
|
|
InstalledKBs []string `json:"installed_kbs"`
|
|
ExchangeInstalled bool `json:"exchange_installed"`
|
|
IsDomainController bool `json:"is_domain_controller"`
|
|
PulsePresent bool `json:"pulse_present"`
|
|
CitrixPresent bool `json:"citrix_present"`
|
|
F5Process bool `json:"f5_process"`
|
|
ConfluenceLike bool `json:"confluence_like"`
|
|
ManageEnginePresent bool `json:"manageengine_present"`
|
|
FortiClient bool `json:"forticlient"`
|
|
VMwareServerish bool `json:"vmware_serverish"`
|
|
SpoolerRunning bool `json:"spooler_running"`
|
|
SSHListening bool `json:"ssh_listening"`
|
|
Log4jJars []string `json:"log4j_jars"`
|
|
}
|
|
|
|
// ProbeHost gathers Windows LOTL recon inputs (Get-HotFix, Get-Service, WMI QFE).
|
|
func ProbeHost(listeningPorts map[int]bool, osVersion string) HostContext {
|
|
ctx := HostContext{
|
|
Platform: runtime.GOOS,
|
|
OSVersion: osVersion,
|
|
LastPatchDays: -1,
|
|
ListeningPorts: listeningPorts,
|
|
}
|
|
out, err := HiddenExec(
|
|
"powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command",
|
|
windowsProbeScript,
|
|
)
|
|
if err != nil {
|
|
ctx.ProbeError = err.Error()
|
|
return ctx
|
|
}
|
|
raw := strings.TrimSpace(string(out))
|
|
if idx := strings.LastIndex(raw, "{"); idx > 0 {
|
|
raw = raw[idx:]
|
|
}
|
|
var p windowsProbeResult
|
|
if err := json.Unmarshal([]byte(raw), &p); err != nil {
|
|
ctx.ProbeError = err.Error()
|
|
return ctx
|
|
}
|
|
ctx.LastPatch = p.LastPatch
|
|
ctx.LastPatchDays = p.LastPatchDays
|
|
ctx.InstalledKBs = p.InstalledKBs
|
|
ctx.ExchangeInstalled = p.ExchangeInstalled
|
|
ctx.IsDomainController = p.IsDomainController
|
|
ctx.PulsePresent = p.PulsePresent
|
|
ctx.CitrixPresent = p.CitrixPresent
|
|
ctx.F5Process = p.F5Process
|
|
ctx.ConfluenceLike = p.ConfluenceLike
|
|
ctx.ManageEnginePresent = p.ManageEnginePresent
|
|
ctx.FortiClient = p.FortiClient
|
|
ctx.VMwareServerish = p.VMwareServerish
|
|
ctx.SpoolerRunning = p.SpoolerRunning
|
|
ctx.SSHListening = p.SSHListening
|
|
ctx.Log4jJars = p.Log4jJars
|
|
return ctx
|
|
}
|
|
|
|
func linuxPackageFindings(_ HostContext) []VulnFinding { return nil }
|