feat: T1016 dns_config probe + server-side drift detection + Crucible DNS DRIFT badge
This commit is contained in:
68
agent/client/dns_windows.go
Normal file
68
agent/client/dns_windows.go
Normal file
@@ -0,0 +1,68 @@
|
||||
//go:build windows
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// probeDNS returns the DNS servers and search domains currently active on
|
||||
// this machine's non-loopback network interfaces (Windows).
|
||||
//
|
||||
// Uses Get-DnsClientServerAddress (fast, built into Windows 8+/2012+).
|
||||
// Falls back to ipconfig /all parsing if the CIM call fails (older OSes).
|
||||
func probeDNS() *DNSConfig {
|
||||
cfg := &DNSConfig{}
|
||||
|
||||
// Primary: CIM-based — deduped, IPv4+IPv6, excludes loopback adapters
|
||||
const script = `
|
||||
$addrs = Get-DnsClientServerAddress -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.InterfaceAlias -notmatch 'Loopback|Npcap|VirtualBox|VMware' } |
|
||||
Select-Object -ExpandProperty ServerAddresses |
|
||||
Where-Object { $_ -ne '' -and $_ -ne '::1' -and $_ -ne '127.0.0.1' } |
|
||||
Sort-Object -Unique
|
||||
$search = (Get-DnsClient -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.ConnectionSpecificSuffix -ne '' } |
|
||||
Select-Object -ExpandProperty ConnectionSpecificSuffix |
|
||||
Sort-Object -Unique) -join ','
|
||||
[PSCustomObject]@{ servers = ($addrs -join ','); search = $search } | ConvertTo-Json -Compress
|
||||
`
|
||||
if out, err := exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script).Output(); err == nil {
|
||||
raw := strings.TrimSpace(string(out))
|
||||
if idx := strings.LastIndex(raw, "{"); idx >= 0 {
|
||||
raw = raw[idx:]
|
||||
}
|
||||
cfg = parseDNSJSON(raw)
|
||||
}
|
||||
|
||||
// Fallback: ipconfig /all if the CIM call returned nothing
|
||||
if len(cfg.Servers) == 0 {
|
||||
cfg = parseDNSIpconfig()
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
// parseDNSIpconfig extracts DNS servers from ipconfig /all output.
|
||||
func parseDNSIpconfig() *DNSConfig {
|
||||
cfg := &DNSConfig{}
|
||||
out, err := exec.Command("ipconfig", "/all").Output()
|
||||
if err != nil {
|
||||
return cfg
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if strings.HasPrefix(strings.ToLower(line), "dns servers") {
|
||||
parts := strings.SplitN(line, ":", 2)
|
||||
if len(parts) == 2 {
|
||||
addr := strings.TrimSpace(parts[1])
|
||||
if addr != "" && addr != "127.0.0.1" && addr != "::1" && !seen[addr] {
|
||||
seen[addr] = true
|
||||
cfg.Servers = append(cfg.Servers, addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
Reference in New Issue
Block a user