Skip to content

Run login payloads once per desktop sign-in, not on every interactive logon - #22

Merged
rodchristiansen merged 1 commit into
mainfrom
fix/logon-session-filter
Oct 7, 2026
Merged

rodchristiansen merged 1 commit into
mainfrom
fix/logon-session-filter

Conversation

@rodchristiansen

Copy link
Copy Markdown
Collaborator

Problem

The login worker treats every 4624 event with logon type 2, 10 or 11 as a user arriving at the desktop, and runs the whole login batch against the console session for each one. Those logon types are also written by:

  • scheduled tasks and services that log an account on, every time the task runs
  • runas, and elevation prompts that take another account's credentials
  • an administrator's own sign-in, which writes two events (one for the filtered token, one for the elevated token), so the batch ran twice

The session lookup also never worked. It read a SessionId field that event 4624 doesn't have, so it always fell back to the console session.

Change

  • LogonSessions (new, in src/Engine/Native) uses LsaGetLogonSessionData to find which desktop session the event's TargetLogonId belongs to, and WTSQuerySessionInformation to read when that session was signed in to.
  • LoginGate (new, in src/Engine) checks each logon in two steps:
    • Before the desktop wait, it rejects a logon in session 0 and one whose logon session has already closed.
    • After the wait, it runs the batch only if the logon's account is the user signed in to that session, and only once per sign-in. A sign-in is identified by its session, user and sign-in time.
  • LogonEventWorker applies both checks. The start-up catch-up goes through the same claim, so a later event for that sign-in doesn't run the batch again.
  • Skipped logons are logged at Debug with the reason, so a task that runs every minute doesn't flood the log.
  • If LSA can't say which session a logon belongs to, the worker falls back to the console session as before. The once-per-sign-in check still applies.

Tests

dotnet test tests/StartSet.Tests -c Release: 303 passed, 0 failed.

LoginGateTests check that:

  • session 0 and closed logon sessions are rejected
  • the first logon of a sign-in runs the batch, and later ones don't
  • an administrator's two tokens, claimed concurrently, run the batch once
  • a new sign-in to the same session runs it again
  • another account logging on inside the session doesn't run it
  • the catch-up is deduplicated against later events

They also check the native lookups against the test process's own logon session: LSA maps it to the process's own session id, and the session's sign-in time is in the past.

Not covered here

The change has not been run on a VM against real scheduled-task, runas and administrator sign-ins. That needs the Security log read as SYSTEM.

… logon

Event 4624 with logon type 2, 10 or 11 is also written for scheduled tasks,
services calling LogonUser and runas, and twice for an administrator's own
sign-in. Each of those ran the whole login batch against the console session.

The worker now asks LSA which session the logon belongs to and ignores logons
in session 0 or ones that have already closed. After the desktop wait it runs
the batch only when the logon's account is the one signed in to that session,
and only once for each sign-in, identified by session, user and logon time.
@rodchristiansen
rodchristiansen merged commit e598120 into main Oct 7, 2026
1 check passed
@rodchristiansen
rodchristiansen deleted the fix/logon-session-filter branch October 7, 2026 03:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant