mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 04:32:32 +00:00
fix(rules): drop AutoLogon false-positive on normal Autopilot enrollments
ANALYZE-SEC-002 fired Warning (confidence 90) on every normal user-driven Entra-join enrollment. During ESP, Windows writes AutoAdminLogon=1 + DefaultUserName + AutoLogonSID + no DefaultPassword for the enrolling user — the exact fingerprint the sysinternals_autologon_suspected heuristic targeted (enabled && user && !password). AutoLogon-enabled is registry-indistinguishable from a Sysinternals/kiosk setup during Autopilot, so the heuristic and SEC-002 could never tell a benign enrollment from a real one. Declare AutoLogon only when a plaintext password is actually present: - delete ANALYZE-SEC-002 (broad AutoLogon-active warning) - remove the non-discriminating sysinternals heuristic from AutoLogonAnalyzer (computation + finding label + checks.sysinternals_autologon_suspected) - strip the sysinternals condition+factor from SEC-004 (kiosk allow-list, still a disabled opt-in template) and refresh its now-stale SEC-002 guidance - ANALYZE-SEC-003 (plaintext DefaultPassword -> high) is now the sole default-on AutoLogon grade; the autologon_analysis event still carries all raw facts at Info Tests: assert SEC-002 absent + no rule references the sysinternals signal; add analyzer regression guard for enabled+user+no-password. V2-only, no ConfigVersion bump. Build 0 err, AutoLogon 19/19, BuiltInRulesTests 8/8, web tsc clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,69 +0,0 @@
|
||||
{
|
||||
"$schema": "../schema/analyze-rule.schema.json",
|
||||
"ruleId": "ANALYZE-SEC-002",
|
||||
"title": "AutoLogon enabled on device",
|
||||
"description": "Fires when the AutoLogon analyzer reports an active automatic sign-in (AutoAdminLogon or ForceAutoLogon set in Winlogon). AutoLogon can be a legitimate kiosk or shared-device configuration, but it is also a classic way to automate enrollment or OOBE, so an active AutoLogon during Autopilot is worth surfacing.",
|
||||
"severity": "warning",
|
||||
"category": "security",
|
||||
"version": "1.0.0",
|
||||
"enabled": true,
|
||||
"trigger": "single",
|
||||
"baseConfidence": 80,
|
||||
"conditions": [
|
||||
{
|
||||
"signal": "autologon_enabled",
|
||||
"source": "event_type",
|
||||
"eventType": "autologon_analysis",
|
||||
"dataField": "checks.autologon_enabled",
|
||||
"operator": "equals",
|
||||
"value": "true",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"signal": "sysinternals_suspected",
|
||||
"source": "event_data",
|
||||
"eventType": "autologon_analysis",
|
||||
"dataField": "checks.sysinternals_autologon_suspected",
|
||||
"operator": "equals",
|
||||
"value": "true",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"confidenceFactors": [
|
||||
{ "signal": "sysinternals_suspected", "condition": "exists", "weight": 10 }
|
||||
],
|
||||
"confidenceThreshold": 40,
|
||||
"explanation": "This device has **automatic sign-in (AutoLogon) enabled** in the registry (`HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon`). The `AutoAdminLogon` and/or `ForceAutoLogon` value is set, so Windows will log a user in without prompting for credentials.\n\nThis is legitimate for kiosk and shared-device scenarios, but it is also a common technique to automate enrollment or OOBE. If AutoLogon was not part of your intended deployment, treat it as a possible manipulation: review the configured default user, the source that wrote the keys (provisioning script, Win32 app, or the Sysinternals Autologon tool), and whether the account is more privileged than the kiosk use case requires.\n\n**Sysinternals Autologon:** when the evidence includes `sysinternals_suspected`, the AutoLogon password is stored as an encrypted **LSA secret** instead of a plaintext `DefaultPassword` registry value — the signature of the Sysinternals Autologon tool (AutoLogon enabled, a default user set, and no registry password). That is a deliberate, tool-assisted setup: still legitimate for an approved kiosk, but a stronger indicator of intent than a flag left behind by accident (the rule raises its confidence accordingly).\n\nReview the `autologon_analysis` event in the session timeline for the exact Winlogon values (default user / domain, AutoLogon count, whether a Sysinternals LSA-secret password is suspected).",
|
||||
"remediation": [
|
||||
{
|
||||
"title": "Confirm AutoLogon is intended",
|
||||
"steps": [
|
||||
"Open the autologon_analysis event in the session timeline and review the default user/domain and AutoLogon flags",
|
||||
"Confirm whether AutoLogon is part of an approved kiosk / shared-device deployment",
|
||||
"If unexpected, identify what wrote the Winlogon keys (provisioning script, Win32 app, or Sysinternals Autologon)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Disable AutoLogon if not required",
|
||||
"steps": [
|
||||
"Set HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\AutoAdminLogon to 0",
|
||||
"Remove DefaultPassword (and any Sysinternals LSA secret) if present",
|
||||
"For kiosk scenarios, prefer Assigned Access / kiosk profiles over raw AutoLogon where possible"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Run deliberate kiosks? Use the allow-list instead",
|
||||
"steps": [
|
||||
"If AutoLogon is an approved kiosk configuration, configure the template rule ANALYZE-SEC-004 with your expected kiosk account name(s)",
|
||||
"Then disable this rule (ANALYZE-SEC-002) so approved kiosk AutoLogon stays quiet while AutoLogon for unexpected users still warns"
|
||||
]
|
||||
}
|
||||
],
|
||||
"relatedDocs": [
|
||||
{
|
||||
"title": "Microsoft: Configure Windows to automatically sign in (AutoLogon)",
|
||||
"url": "https://learn.microsoft.com/en-us/troubleshoot/windows-server/user-profiles-and-logon/turn-on-automatic-logon"
|
||||
}
|
||||
],
|
||||
"tags": ["security", "autologon", "winlogon", "kiosk", "credentials"]
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
"$schema": "../schema/analyze-rule.schema.json",
|
||||
"ruleId": "ANALYZE-SEC-004",
|
||||
"title": "AutoLogon enabled for an unexpected user",
|
||||
"description": "Template variant of the AutoLogon warning with a kiosk allow-list. Fires when AutoLogon is enabled for a default user that is NOT on your configured list of approved kiosk / shared-device accounts. Disabled until configured: set your allowed account name(s), then disable the broad ANALYZE-SEC-002 so deliberate kiosk AutoLogon stops raising a warning while unexpected AutoLogon still does.",
|
||||
"description": "Optional kiosk allow-list for AutoLogon. Fires when AutoLogon is enabled for a default user that is NOT on your configured list of approved kiosk / shared-device accounts. Disabled until configured because a normal Autopilot enrollment leaves Windows' own ESP auto-logon for the enrolling user (AutoLogon enabled, a default user, no plaintext password) — so AutoLogon-enabled alone is not graded by default; only a plaintext DefaultPassword on disk (ANALYZE-SEC-003) is. Enable this rule with your expected kiosk account name(s) if you want to surface AutoLogon configured for any other user.",
|
||||
"severity": "warning",
|
||||
"category": "security",
|
||||
"version": "1.0.0",
|
||||
@@ -27,22 +27,10 @@
|
||||
"operator": "not_in",
|
||||
"value": "EXPECTED-KIOSK-USER-1,EXPECTED-KIOSK-USER-2",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"signal": "sysinternals_suspected",
|
||||
"source": "event_data",
|
||||
"eventType": "autologon_analysis",
|
||||
"dataField": "checks.sysinternals_autologon_suspected",
|
||||
"operator": "equals",
|
||||
"value": "true",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"confidenceFactors": [
|
||||
{ "signal": "sysinternals_suspected", "condition": "exists", "weight": 10 }
|
||||
],
|
||||
"confidenceThreshold": 40,
|
||||
"explanation": "AutoLogon is enabled on this device for a user that is **not on your approved kiosk allow-list**. AutoLogon is legitimate for kiosk and shared-device scenarios, so this rule only warns when the configured default user differs from the account(s) you expect — letting you keep deliberate kiosk AutoLogon quiet while still surfacing unexpected automatic sign-in.\n\nTo use this rule: configure the allow-list with your approved kiosk account name(s) (comma-separated, case-insensitive exact match against the Winlogon `DefaultUserName`), then disable the broad **ANALYZE-SEC-002 — AutoLogon enabled on device** rule so it does not also fire on your approved kiosk devices.\n\nNote: an AutoLogon with no explicit default user (e.g. ForceAutoLogon only) is treated as unexpected and will warn.",
|
||||
"explanation": "AutoLogon is enabled on this device for a user that is **not on your approved kiosk allow-list**. AutoLogon is legitimate for kiosk and shared-device scenarios, so this rule only warns when the configured default user differs from the account(s) you expect.\n\nThis rule is **off by default**. A normal user-driven Autopilot enrollment leaves Windows' own ESP auto-logon for the enrolling user (`AutoAdminLogon=1`, a `DefaultUserName`, an `AutoLogonSID`, and no plaintext password) — registry-indistinguishable from a kiosk AutoLogon — so AutoLogon-enabled alone is not graded. The only AutoLogon state graded by default is a plaintext `DefaultPassword` on disk (**ANALYZE-SEC-003**).\n\nTo use this rule: configure the allow-list with your approved kiosk account name(s) (comma-separated, case-insensitive exact match against the Winlogon `DefaultUserName`) and enable it. AutoLogon for any other user will then warn.\n\nNote: an AutoLogon with no explicit default user (e.g. ForceAutoLogon only) is treated as unexpected and will warn.",
|
||||
"remediation": [
|
||||
{
|
||||
"title": "Confirm the AutoLogon user",
|
||||
|
||||
+2
-96
@@ -1481,84 +1481,6 @@
|
||||
"compliance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ruleId": "ANALYZE-SEC-002",
|
||||
"title": "AutoLogon enabled on device",
|
||||
"description": "Fires when the AutoLogon analyzer reports an active automatic sign-in (AutoAdminLogon or ForceAutoLogon set in Winlogon). AutoLogon can be a legitimate kiosk or shared-device configuration, but it is also a classic way to automate enrollment or OOBE, so an active AutoLogon during Autopilot is worth surfacing.",
|
||||
"severity": "warning",
|
||||
"category": "security",
|
||||
"version": "1.0.0",
|
||||
"enabled": true,
|
||||
"trigger": "single",
|
||||
"baseConfidence": 80,
|
||||
"conditions": [
|
||||
{
|
||||
"signal": "autologon_enabled",
|
||||
"source": "event_type",
|
||||
"eventType": "autologon_analysis",
|
||||
"dataField": "checks.autologon_enabled",
|
||||
"operator": "equals",
|
||||
"value": "true",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"signal": "sysinternals_suspected",
|
||||
"source": "event_data",
|
||||
"eventType": "autologon_analysis",
|
||||
"dataField": "checks.sysinternals_autologon_suspected",
|
||||
"operator": "equals",
|
||||
"value": "true",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"confidenceFactors": [
|
||||
{
|
||||
"signal": "sysinternals_suspected",
|
||||
"condition": "exists",
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"confidenceThreshold": 40,
|
||||
"explanation": "This device has **automatic sign-in (AutoLogon) enabled** in the registry (`HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon`). The `AutoAdminLogon` and/or `ForceAutoLogon` value is set, so Windows will log a user in without prompting for credentials.\n\nThis is legitimate for kiosk and shared-device scenarios, but it is also a common technique to automate enrollment or OOBE. If AutoLogon was not part of your intended deployment, treat it as a possible manipulation: review the configured default user, the source that wrote the keys (provisioning script, Win32 app, or the Sysinternals Autologon tool), and whether the account is more privileged than the kiosk use case requires.\n\n**Sysinternals Autologon:** when the evidence includes `sysinternals_suspected`, the AutoLogon password is stored as an encrypted **LSA secret** instead of a plaintext `DefaultPassword` registry value — the signature of the Sysinternals Autologon tool (AutoLogon enabled, a default user set, and no registry password). That is a deliberate, tool-assisted setup: still legitimate for an approved kiosk, but a stronger indicator of intent than a flag left behind by accident (the rule raises its confidence accordingly).\n\nReview the `autologon_analysis` event in the session timeline for the exact Winlogon values (default user / domain, AutoLogon count, whether a Sysinternals LSA-secret password is suspected).",
|
||||
"remediation": [
|
||||
{
|
||||
"title": "Confirm AutoLogon is intended",
|
||||
"steps": [
|
||||
"Open the autologon_analysis event in the session timeline and review the default user/domain and AutoLogon flags",
|
||||
"Confirm whether AutoLogon is part of an approved kiosk / shared-device deployment",
|
||||
"If unexpected, identify what wrote the Winlogon keys (provisioning script, Win32 app, or Sysinternals Autologon)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Disable AutoLogon if not required",
|
||||
"steps": [
|
||||
"Set HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\AutoAdminLogon to 0",
|
||||
"Remove DefaultPassword (and any Sysinternals LSA secret) if present",
|
||||
"For kiosk scenarios, prefer Assigned Access / kiosk profiles over raw AutoLogon where possible"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Run deliberate kiosks? Use the allow-list instead",
|
||||
"steps": [
|
||||
"If AutoLogon is an approved kiosk configuration, configure the template rule ANALYZE-SEC-004 with your expected kiosk account name(s)",
|
||||
"Then disable this rule (ANALYZE-SEC-002) so approved kiosk AutoLogon stays quiet while AutoLogon for unexpected users still warns"
|
||||
]
|
||||
}
|
||||
],
|
||||
"relatedDocs": [
|
||||
{
|
||||
"title": "Microsoft: Configure Windows to automatically sign in (AutoLogon)",
|
||||
"url": "https://learn.microsoft.com/en-us/troubleshoot/windows-server/user-profiles-and-logon/turn-on-automatic-logon"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"security",
|
||||
"autologon",
|
||||
"winlogon",
|
||||
"kiosk",
|
||||
"credentials"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ruleId": "ANALYZE-SEC-003",
|
||||
"title": "AutoLogon plaintext password stored in registry",
|
||||
@@ -1616,7 +1538,7 @@
|
||||
{
|
||||
"ruleId": "ANALYZE-SEC-004",
|
||||
"title": "AutoLogon enabled for an unexpected user",
|
||||
"description": "Template variant of the AutoLogon warning with a kiosk allow-list. Fires when AutoLogon is enabled for a default user that is NOT on your configured list of approved kiosk / shared-device accounts. Disabled until configured: set your allowed account name(s), then disable the broad ANALYZE-SEC-002 so deliberate kiosk AutoLogon stops raising a warning while unexpected AutoLogon still does.",
|
||||
"description": "Optional kiosk allow-list for AutoLogon. Fires when AutoLogon is enabled for a default user that is NOT on your configured list of approved kiosk / shared-device accounts. Disabled until configured because a normal Autopilot enrollment leaves Windows' own ESP auto-logon for the enrolling user (AutoLogon enabled, a default user, no plaintext password) — so AutoLogon-enabled alone is not graded by default; only a plaintext DefaultPassword on disk (ANALYZE-SEC-003) is. Enable this rule with your expected kiosk account name(s) if you want to surface AutoLogon configured for any other user.",
|
||||
"severity": "warning",
|
||||
"category": "security",
|
||||
"version": "1.0.0",
|
||||
@@ -1641,26 +1563,10 @@
|
||||
"operator": "not_in",
|
||||
"value": "EXPECTED-KIOSK-USER-1,EXPECTED-KIOSK-USER-2",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"signal": "sysinternals_suspected",
|
||||
"source": "event_data",
|
||||
"eventType": "autologon_analysis",
|
||||
"dataField": "checks.sysinternals_autologon_suspected",
|
||||
"operator": "equals",
|
||||
"value": "true",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"confidenceFactors": [
|
||||
{
|
||||
"signal": "sysinternals_suspected",
|
||||
"condition": "exists",
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"confidenceThreshold": 40,
|
||||
"explanation": "AutoLogon is enabled on this device for a user that is **not on your approved kiosk allow-list**. AutoLogon is legitimate for kiosk and shared-device scenarios, so this rule only warns when the configured default user differs from the account(s) you expect — letting you keep deliberate kiosk AutoLogon quiet while still surfacing unexpected automatic sign-in.\n\nTo use this rule: configure the allow-list with your approved kiosk account name(s) (comma-separated, case-insensitive exact match against the Winlogon `DefaultUserName`), then disable the broad **ANALYZE-SEC-002 — AutoLogon enabled on device** rule so it does not also fire on your approved kiosk devices.\n\nNote: an AutoLogon with no explicit default user (e.g. ForceAutoLogon only) is treated as unexpected and will warn.",
|
||||
"explanation": "AutoLogon is enabled on this device for a user that is **not on your approved kiosk allow-list**. AutoLogon is legitimate for kiosk and shared-device scenarios, so this rule only warns when the configured default user differs from the account(s) you expect.\n\nThis rule is **off by default**. A normal user-driven Autopilot enrollment leaves Windows' own ESP auto-logon for the enrolling user (`AutoAdminLogon=1`, a `DefaultUserName`, an `AutoLogonSID`, and no plaintext password) — registry-indistinguishable from a kiosk AutoLogon — so AutoLogon-enabled alone is not graded. The only AutoLogon state graded by default is a plaintext `DefaultPassword` on disk (**ANALYZE-SEC-003**).\n\nTo use this rule: configure the allow-list with your approved kiosk account name(s) (comma-separated, case-insensitive exact match against the Winlogon `DefaultUserName`) and enable it. AutoLogon for any other user will then warn.\n\nNote: an AutoLogon with no explicit default user (e.g. ForceAutoLogon only) is treated as unexpected and will warn.",
|
||||
"remediation": [
|
||||
{
|
||||
"title": "Confirm the AutoLogon user",
|
||||
|
||||
+14
-9
@@ -14,8 +14,10 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Monitoring.Analyzers
|
||||
{
|
||||
/// <summary>
|
||||
/// AutoLogonAnalyzer — pure classification (BuildPayload) plus emission contract. The analyzer
|
||||
/// reports raw Winlogon facts at Info severity; the Warning/escalation grading lives in backend
|
||||
/// analyze-rules (ANALYZE-SEC-002/003), so these tests assert facts, not severity judgements.
|
||||
/// reports raw Winlogon facts at Info severity; the escalation grading lives in backend
|
||||
/// analyze-rules (ANALYZE-SEC-003 plaintext password), so these tests assert facts, not severity
|
||||
/// judgements. AutoLogon-enabled alone is deliberately ungraded (it matches Windows' own ESP
|
||||
/// auto-logon on every normal Autopilot enrollment).
|
||||
/// </summary>
|
||||
public sealed class AutoLogonAnalyzerTests
|
||||
{
|
||||
@@ -90,25 +92,28 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Monitoring.Analyzers
|
||||
// Presence-only: the password value must never appear anywhere in the payload.
|
||||
Assert.False(checks.ContainsKey("default_password"));
|
||||
Assert.DoesNotContain("DefaultPassword", checks.Keys);
|
||||
// A plaintext password means it is NOT the Sysinternals (LSA-secret) shape.
|
||||
Assert.False((bool)checks["sysinternals_autologon_suspected"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildPayload_sysinternals_heuristic_when_enabled_user_set_no_reg_password()
|
||||
public void BuildPayload_enabled_user_no_password_does_not_flag_sysinternals()
|
||||
{
|
||||
// Regression guard: "AutoLogon enabled + a default user + no registry password" is the
|
||||
// exact fingerprint of Windows' own ESP auto-logon on a normal Autopilot enrollment.
|
||||
// The old sysinternals heuristic flagged it on every device — it must be gone now: no
|
||||
// sysinternals check, no sysinternals finding, just the factual autologon_active label.
|
||||
var snap = new AutoLogonSnapshot
|
||||
{
|
||||
WinlogonKeyPresent = true,
|
||||
AutoAdminLogon = "1",
|
||||
DefaultUserName = "kioskuser",
|
||||
DefaultUserName = "luke.skywalker@example.net",
|
||||
DefaultPasswordPresent = false,
|
||||
};
|
||||
var data = AutoLogonAnalyzer.BuildPayload(snap, "shutdown");
|
||||
var data = AutoLogonAnalyzer.BuildPayload(snap, "device_setup_complete");
|
||||
|
||||
var checks = Checks(data);
|
||||
Assert.True((bool)checks["sysinternals_autologon_suspected"]);
|
||||
Assert.Contains("sysinternals_autologon_suspected", (List<string>)data["findings"]);
|
||||
Assert.DoesNotContain("sysinternals_autologon_suspected", checks.Keys);
|
||||
Assert.DoesNotContain("sysinternals_autologon_suspected", (List<string>)data["findings"]);
|
||||
Assert.Equal("autologon_active", data["finding"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
+16
-12
@@ -17,16 +17,21 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Telemetry.Analyzers
|
||||
/// <para>
|
||||
/// AutoLogon (<c>HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon</c>) is a classic
|
||||
/// way to automate sign-in. It can be legitimate (kiosk / shared-device deployment) OR a
|
||||
/// manipulation vector to automate enrollment / OOBE. The Sysinternals <c>Autologon</c> tool
|
||||
/// stores the password as an LSA secret rather than the plaintext <c>DefaultPassword</c> value;
|
||||
/// we detect that heuristically (AutoLogon enabled + a default user but no registry password)
|
||||
/// WITHOUT reading the LSA secret.
|
||||
/// manipulation vector to automate enrollment / OOBE.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Note on grading: a normal user-driven Autopilot enrollment leaves <c>AutoAdminLogon=1</c>
|
||||
/// with a <c>DefaultUserName</c>, an <c>AutoLogonSID</c> and NO <c>DefaultPassword</c> — Windows'
|
||||
/// own ESP auto-logon for the enrolling user. That state is registry-indistinguishable from a
|
||||
/// Sysinternals-Autologon / kiosk setup (both leave AutoLogon enabled + a user but no plaintext
|
||||
/// password), so AutoLogon-enabled alone is NOT a reliable signal during Autopilot. The only
|
||||
/// unambiguous risk is a plaintext <c>DefaultPassword</c> on disk — graded by ANALYZE-SEC-003.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This analyzer reports facts ONLY, always at <see cref="EventSeverity.Info"/>. It never reads
|
||||
/// or emits the <c>DefaultPassword</c> value — only its presence. Whether an AutoLogon is a
|
||||
/// problem is judged by backend analyze-rules (ANALYZE-SEC-002 = AutoLogon active → Warning,
|
||||
/// ANALYZE-SEC-003 = plaintext DefaultPassword on disk → escalated).
|
||||
/// problem is judged by backend analyze-rules (ANALYZE-SEC-003 = plaintext DefaultPassword on
|
||||
/// disk → escalated; ANALYZE-SEC-004 = optional kiosk allow-list template).
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>Trigger</b>: NOT at agent start — the Winlogon keys are written later by a provisioning
|
||||
@@ -185,17 +190,17 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Telemetry.Analyzers
|
||||
bool autologonEnabled = autoAdminLogonEnabled || forceAutoLogonEnabled;
|
||||
bool defaultIdentityPresent =
|
||||
!string.IsNullOrEmpty(snap.DefaultUserName) || !string.IsNullOrEmpty(snap.DefaultDomainName);
|
||||
// Sysinternals Autologon stores the password as an LSA secret, leaving AutoLogon enabled
|
||||
// with a default user but NO plaintext DefaultPassword in the registry.
|
||||
bool sysinternalsSuspected =
|
||||
autologonEnabled && !string.IsNullOrEmpty(snap.DefaultUserName) && !snap.DefaultPasswordPresent;
|
||||
|
||||
// Factual indicator labels (no judgement — severity is the rule's job).
|
||||
// Note: there is deliberately NO "sysinternals suspected" label. The heuristic it used
|
||||
// (AutoLogon enabled + a default user + no registry password) is the exact fingerprint
|
||||
// of Windows' own ESP auto-logon during a normal Autopilot enrollment, so it fired on
|
||||
// every device and graded benign enrollments as suspicious. A plaintext DefaultPassword
|
||||
// (below) is the only unambiguous signal we surface.
|
||||
var findings = new List<string>();
|
||||
if (autoAdminLogonEnabled) findings.Add("auto_admin_logon_enabled");
|
||||
if (forceAutoLogonEnabled) findings.Add("force_auto_logon_enabled");
|
||||
if (snap.DefaultPasswordPresent) findings.Add("default_password_present");
|
||||
if (sysinternalsSuspected) findings.Add("sysinternals_autologon_suspected");
|
||||
if (defaultIdentityPresent) findings.Add("default_identity_present");
|
||||
|
||||
// Primary label, most-notable-first (still factual).
|
||||
@@ -220,7 +225,6 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Telemetry.Analyzers
|
||||
{ "auto_logon_sid", snap.AutoLogonSid ?? string.Empty },
|
||||
{ "auto_logon_count", snap.AutoLogonCount.HasValue ? (object)snap.AutoLogonCount.Value : string.Empty },
|
||||
{ "default_password_present", snap.DefaultPasswordPresent },
|
||||
{ "sysinternals_autologon_suspected", sysinternalsSuspected },
|
||||
{ "default_identity_present", defaultIdentityPresent },
|
||||
};
|
||||
|
||||
|
||||
@@ -91,24 +91,19 @@ public class BuiltInRulesTests
|
||||
{
|
||||
var rules = BuiltInAnalyzeRules.GetAll();
|
||||
|
||||
// SEC-002: broad AutoLogon warning, default-on, NOT a template.
|
||||
var sec002 = rules.FirstOrDefault(r => r.RuleId == "ANALYZE-SEC-002");
|
||||
Assert.NotNull(sec002);
|
||||
Assert.True(sec002!.Enabled, "ANALYZE-SEC-002 should be enabled by default");
|
||||
Assert.Equal("warning", sec002.Severity);
|
||||
Assert.True(sec002.TemplateVariables == null || sec002.TemplateVariables.Count == 0);
|
||||
Assert.Contains(sec002.Conditions, c => c.DataField == "checks.autologon_enabled");
|
||||
// SEC-002 was removed: AutoLogon-enabled alone is the exact fingerprint of Windows' own ESP
|
||||
// auto-logon during a normal Autopilot enrollment, so a broad "AutoLogon active" warning was
|
||||
// a guaranteed false positive on every device. Only a plaintext password (SEC-003) is graded.
|
||||
Assert.DoesNotContain(rules, r => r.RuleId == "ANALYZE-SEC-002");
|
||||
|
||||
// The sysinternals confidence factor must be backed by a real (non-required) condition
|
||||
// carrying the same signal — otherwise the `exists` factor can never match (dead factor).
|
||||
var sec002SysFactor = sec002.ConfidenceFactors?.FirstOrDefault(f => f.Signal == "sysinternals_suspected");
|
||||
Assert.NotNull(sec002SysFactor);
|
||||
Assert.Contains(sec002.Conditions, c =>
|
||||
c.Signal == "sysinternals_suspected"
|
||||
&& c.DataField == "checks.sysinternals_autologon_suspected"
|
||||
&& !c.Required);
|
||||
// The non-discriminating sysinternals heuristic was removed everywhere — no surviving rule
|
||||
// may reference it (it fired identically for native ESP auto-logon and a Sysinternals kiosk).
|
||||
Assert.DoesNotContain(rules, r =>
|
||||
r.Conditions != null && r.Conditions.Any(c => c.DataField == "checks.sysinternals_autologon_suspected"));
|
||||
Assert.DoesNotContain(rules, r =>
|
||||
r.ConfidenceFactors != null && r.ConfidenceFactors.Any(f => f.Signal == "sysinternals_suspected"));
|
||||
|
||||
// SEC-003: plaintext password, default-on, escalated.
|
||||
// SEC-003: plaintext password, default-on, escalated. The sole default-on AutoLogon signal.
|
||||
var sec003 = rules.FirstOrDefault(r => r.RuleId == "ANALYZE-SEC-003");
|
||||
Assert.NotNull(sec003);
|
||||
Assert.True(sec003!.Enabled, "ANALYZE-SEC-003 should be enabled by default");
|
||||
|
||||
@@ -418,9 +418,11 @@ namespace AutopilotMonitor.Shared
|
||||
public const string ProvisioningPackageScan = "provisioning_package_scan";
|
||||
// AutoLogon scan — runs at DeviceSetup-phase completion and at final shutdown. Reports
|
||||
// raw facts only (Winlogon registry indicators; DefaultPassword presence-only, never the
|
||||
// value) at Info severity. Backend analyze-rules (ANALYZE-SEC-002/003) judge the facts:
|
||||
// an active AutoLogon is a Warning, a plaintext DefaultPassword on disk is escalated.
|
||||
// AutoLogon may be legitimate (kiosk deployment) OR an enrollment/OOBE manipulation vector.
|
||||
// value) at Info severity. AutoLogon-enabled alone is NOT graded — it is the fingerprint
|
||||
// of Windows' own ESP auto-logon on every normal Autopilot enrollment. Backend analyze-
|
||||
// rules grade only the unambiguous cases: a plaintext DefaultPassword on disk (ANALYZE-
|
||||
// SEC-003, escalated) and an optional kiosk allow-list template (ANALYZE-SEC-004, off by
|
||||
// default). AutoLogon may be legitimate (kiosk) OR an enrollment/OOBE manipulation vector.
|
||||
public const string AutoLogonAnalysis = "autologon_analysis";
|
||||
|
||||
// Termination / diagnostics / server actions
|
||||
|
||||
@@ -117,7 +117,7 @@ export const KNOWN_EVENT_TYPES: EventTypeEntry[] = [
|
||||
{ value: "local_admin_analysis", label: "local_admin_analysis", category: "diagnostics",
|
||||
description: "Local administrator / user-profile audit detecting pre-enrollment admin account creation (an Autopilot bypass technique); emitted at startup and shutdown for delta detection." },
|
||||
{ value: "autologon_analysis", label: "autologon_analysis", category: "diagnostics",
|
||||
description: "Winlogon AutoLogon facts (raw, Info-only): AutoAdminLogon/ForceAutoLogon, default user/domain, AutoLogon count, and presence-only of a plaintext DefaultPassword. Backend analyze-rules (ANALYZE-SEC-002/003/004) grade it; AutoLogon can be a legitimate kiosk or an enrollment/OOBE manipulation vector." },
|
||||
description: "Winlogon AutoLogon facts (raw, Info-only): AutoAdminLogon/ForceAutoLogon, default user/domain, AutoLogon count, and presence-only of a plaintext DefaultPassword. AutoLogon-enabled alone is not graded (Windows' own ESP auto-logon looks identical on every normal enrollment); backend analyze-rules grade only a plaintext DefaultPassword on disk (ANALYZE-SEC-003) plus an optional kiosk allow-list template (ANALYZE-SEC-004). AutoLogon can be a legitimate kiosk or an enrollment/OOBE manipulation vector." },
|
||||
{ value: "gather_result", label: "gather_result", category: "diagnostics",
|
||||
description: "Output of another gather rule (can be chained)." },
|
||||
{ value: "provisioning_package_scan", label: "provisioning_package_scan", category: "diagnostics",
|
||||
|
||||
Reference in New Issue
Block a user