Files
AetherForge/agent/vulnprobe/probe_windows.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 }