Login Activity Triage

Answers who logged on, from where, to which host, how, and what ran remotely from offline Windows event logs (EVTX) or a machine's live logs. Built for incident response and forensic triage.

Read-only against evidenceRDP · PsExec · WinRM · WMI · DCOM · tasksMITRE ATT&CK mappedAll times UTC

The tool has two parts:

The engine can also be used on its own from a command prompt or a script (see Engine command line).

All screenshots in this manual show a fictional incident at "CONTOSO" (an internet-exposed RDP server brute-forced, followed by persistence and lateral movement). Every host name, account and address in them is invented; 203.0.113.0/24 is a documentation range.

Install & requirements

Get both files (or the bundle zip, which includes this manual) from the latest release, or build the engine from source:

dotnet test LoginActivityTriage.sln
dotnet publish src\LoginActivityTriage.Cli -c Release -o hta\bin

Quick start

  1. Double-click LoginActivityTriage.hta. The header shows the app and engine versions; engine vX in green means it is ready.
  2. Choose what to read: directory (recursive) for a collection folder (several hosts at once is fine), file for one .evtx, or This machine for the live logs.
  3. Set the target hostname (or a case label such as CASE-ALL-HOSTS for a multi-host collection). It names the output folder _Processed\<host>\LoginActivityTriage next to the app.
  4. Leave after-hours zone on auto (each host's own UTC offset, read from its System event 6013) and optionally set an engine window of dates.
  5. Click Process → analyze. A console window shows the engine's progress; results load automatically when it finishes.
  6. Start with the Overview and the Findings tab, then pivot into sessions and the timeline.

To reopen earlier results, click Load results… and pick any file in a results folder (one containing summary.json).

The main window

The main window: header, control panel, overview statistics, charts and the start of the Findings tab
The main window after processing a three-host collection: control panel, overview and the top of the Findings tab.

Control panel

ControlPurpose
Event logs to processdirectory (recursive), file or This machine (live logs). Browse… picks the folder or file.
Target hostnameRequired. Names the output folder; guessed from Velociraptor / KAPE folder names where possible.
Output directoryDefaults to _Processed\<host>\LoginActivityTriage next to the app. Open folder opens it in Explorer.
After-hours zone & hoursBusiness hours (default 7-19) for the after-hours rule. auto uses each host's own zone; you can force a Windows or IANA zone or a fixed offset.
Weekends = after hoursSaturday and Sunday count as outside business hours.
Keep machine/system-account noiseOff by default: routine logons of computer accounts, SYSTEM, service, DWM/UMFD and anonymous accounts are dropped. Remote-execution evidence and failures are never dropped.
Engine window (UTC)Optional from / to dates. Events outside are not imported. This is the same date window as the from / to filter above the tables: changing either updates the other.
Process → analyzeRuns the engine. Results load only when the run succeeds; if it fails, the status line says so and the previous results stay untouched and unloaded.
Check engine · Show log · HTML reports · Open in Timeline ExplorerEngine version check, the engine's console / run log, the self-contained HTML reports, and events.csv opened in Eric Zimmerman's Timeline Explorer when it is installed.
Help · A− / A+Built-in help, and text size for the whole window (pop-ups follow it). Help also has Create desktop shortcut: a shortcut to this copy of the app with the app icon (it needs LoginActivityTriage.ico next to the .hta; the running window itself keeps the standard mshta icon).

Overview

Overview statistics, findings by rule, sessions by technique, most suspicious accounts and sources, and the Findings table
Overview and Findings. Red rows are Critical, orange High, yellow Medium. IP addresses carry the names defined in Known hosts.

Filters, sorting & export

The tabs

Findings

Rule hits ranked by severity, each with a MITRE ATT&CK id, a count, the related event ids and a Reasoning field explaining why it fired and what to check. The severity buttons (All / Critical / High / Medium / Low) filter the list. See the Detection reference.

Remote sessions

Remote sessions table with RDP, PsExec and PowerShell Remoting sessions, and the detail card of an RDP session
Remote sessions. The detail card shows the attacker's RDP session: source, logon id, reconnects, admin rights and every command run inside it.

Each row is one stitched session: an RDP session (from authentication through reconnects to logoff), a PsExec run, a PowerShell Remoting or WinRS shell, WMI / DCOM execution, a remotely created scheduled task or service, an executable written to an admin share, or an outbound connection this host made. Columns include the user, source address and workstation, logon id and type, credentials used, the commands observed, and a Confidence (see How sessions are stitched).

Timeline

Timeline of the intrusion night with red failed-logon rows, RDP pre-authentication connections and the successful RDP logon
Timeline filtered to the night of the intrusion: failed network logons (red) interleaved with RDP pre-authentication connections, then the successful RDP logon.

Every normalised event in time order. By default it loads timeline.csv, a triage subset that leaves out routine successful network / domain-controller authentication and WinRM client errors outside a session; tick full events.csv to load everything (large sets open better in Timeline Explorer).

Users & Source IPs

Per-account and per-source-address roll-ups: first / last seen, hosts, successes, failures, inbound RDP, remote execution, remote-access tools, privileged logons, group changes and the suspicion score.

Local hosts & Remote hosts

Local hosts are the systems whose logs were collected: logons, failures, inbound and outbound RDP, remote execution, remote-access tools, service installs, privileged logons, log clears and the time zone read from event 6013.

Remote hosts table listing destinations with connections, techniques, source hosts, users and credentials used
Remote hosts: one row per destination, an address and its resolved name merged.

Remote hosts are the systems the collected hosts connected to (outbound sessions and explicit-credential targets): connections by technique, which local hosts connected, the accounts and credentials used, and Collected when that system's own logs are in the evidence too.

Import log

What the engine read: per-file SHA-256 and size (the chain-of-custody record of exactly which bytes were analysed — quote them in the case notes), records read, events kept, duplicates, filtered and unreadable records, and errors. A file that is not a readable event log (corrupt, truncated, not EVTX) is listed as failed with the reason; the overview line warns when any source failed. Hover a column heading for its meaning. The temporary copy that Open in Event Viewer makes of a long-path log is checked against the recorded hash and refused if it differs.

IOCs & Known hosts

IOCs

IOCs (n)… opens a list of host names, IPs, user names, service or file names, one per line. Matching is case-insensitive across every column; matching rows get a red left edge and IOC hits only filters to them. A toolkit-wide IOC.txt next to the app is merged in at launch.

Known hosts

Known hosts dialog with IP to name mappings and names learned from the logs
Known hosts: your own IP → name list, plus names the logs themselves resolved (accept with add).

Known hosts (n)… maps IP addresses to host names. Type rows, Paste list… (CSV, tab-separated or hosts-file lines) or Load file…; Learned from these logs offers names the evidence resolved (a 4648 target server for an address, or the workstation name a client reported at logon — client-supplied, so treat with care). Each address appears once with how often and when it was seen; one seen with different names is shown as a conflict to label by hand, a name equal to the logging host itself is never offered, and add all leaves out conflicts and changes to your own labels. The scanned host's own address is added automatically after every Process (and when results without a knownhosts.csv are loaded) when its logs show it authenticating to itself. Case list: results in the standard _Processed\<host>\LoginActivityTriage layout also share _Processed\knownhosts.csv, so a label added on one host applies to every host processed into that folder (removing a row removes it for all of them; each save re-reads the list and applies only that window’s changes). Untick Share with every host in this case for results that must keep their own list — e.g. when one _Processed folder holds more than one customer — which leaves a knownhosts.private marker. When four or more addresses reported several names, the dialog folds those rows into one show line.

Detection reference

Thresholds use a 30-minute window unless stated. Severities can drop one level when the evidence for a session is weak.

RuleSeverityFires whenMITRE
Event log clearedCriticalSystem 104 or Security 1102.T1070.001
New account added to privileged groupCriticalAn account created (4720) is added to an admin / remote-access group within a day (same SID). Otherwise Member added to privileged group (High).T1136, T1098
Privileged group membership added then removedHighA member removed (4729 / 4733 / 4757) from an admin / remote-access group within a day of being added to it: the add-use-remove cleanup pattern. A removal with no recent add is Member removed from privileged group (Medium).T1098
PsExec-style remote service executionHighPSEXESVC / PAExec / RemCom / Impacket psexec-smbexec service, pipe or binary pattern on this host.T1569.002, T1021.002
Service created remotelyHighA service installed from a network logon or right after remote SCM / admin-share access.T1543.003
Remote WMI / DCOM executionHighA shell spawned by WmiPrvSE or a DCOM object; Impacket wmiexec / dcomexec output files.T1047, T1021.003
Scheduled task created remotelyHighA task registered from a network logon (or Impacket atexec).T1053.005
Write access to executable on admin shareHighA 5145 access check granted WriteData / AppendData to an executable or script on ADMIN$ / C$ (read-only access does not count; denied access is context only).T1021.002, T1570
Remote access software installed / executedHigh / MediumAbout 60 remote access / RMM products by service name or binary path (7045 / 4697 installs, 4688 / Sysmon 1 process starts): AnyDesk, TeamViewer, Splashtop, ScreenConnect, Atera, RustDesk, ToDesk, Sunlogin, UltraViewer, AnyViewer, ISL Online, DWService, Parsec, NoMachine, VNC and more. Renamed binaries, unlisted products and portable tools without a service when process auditing is off are missed. High from a user-writable path or during a remote session.T1219
Service with command-line ImagePathHighA service whose image is a shell, PowerShell or script host.T1543.003
Password sprayHighOne source fails for 8+ different accounts.T1110.003
Failed logons followed by successMedium / High3+ failures then success for the same account (domain-aware) from one source; High from 10.T1110
RDP logon from external addressHighRDP logon from a public (non-RFC1918) address.T1133, T1021.001
RDP authentication from external addressHighRDP network-level authentication (RCM 1149) from a public address with no session logon in the collected logs (Security log missing, or the session never completed).T1133, T1021.001
Network logon from external addressHighA successful network logon (type 3 / 8: SMB, WinRM, RPC...) from a public address.T1133, T1021
RDP reachable from the internetMediumRDP TCP connections or bad-credential attempts (RdpCoreTS 131 / 140) from public addresses, one finding per host with the addresses listed.T1133, T1110
RDP connection flood (pre-auth)High / Medium30+ RDP TCP connections (RdpCoreTS 131) from one address; High when public.T1110, T1133
Possible KerberoastingHighRC4 service tickets for 3+ user service accounts within 10 minutes.T1558.003
PowerShell Remoting session / WinRS remote shellMediumA WinRM shell was opened on this host.T1021.006
Alternate credentials used against a remote hostMedium4648: an account used another account's credentials for a remote server. Locally (runas) it is Low.T1078
NewCredentials logonMediumLogon type 9 via seclogo (runas /netonly, pass-the-hash tooling).T1550.002
Local account used for remote logonMediumA local SAM account logged on over the network with NTLM.T1078.003, T1550.002
Authentication failure burstMedium20+ failures from one source.T1110.001
Source IP / User logging on to many hostsMediumOne source or account logs on to 3+ hosts.T1021, T1078
Service tickets for many hosts (DC view)MediumOne client requests tickets for 15+ computer accounts.T1021, T1018
Outbound PsExec / WMI / task / service controlMediumThis host launched remote execution against other systems.T1569.002, T1047, T1053.005
Outbound RDP / PowerShell Remoting / SMB from this hostLowGrouped per destination with attempt counts, the accounts and credentials used. SMB = explicit credentials for file / admin shares (4648 with a cifs/ SPN).T1021.001, T1021.006, T1021.002
Failed console logons followed by successLow / Medium3+ bad passwords at the keyboard (types 2 / 7 / 11) then success; notes cached credentials and remote-access software on the host. Medium from 10.T1110
After-hours interactive / RDP logonLowInteractive or RDP logon outside business hours in the host's zone; names the weekday and why.T1078
Privileged remote logonLowA network / RDP logon that received special privileges (4672).T1078.002
Account locked outLow / Medium4740, with the caller computer; Medium from 3 lockouts.T1110

How sessions are stitched

Event sources

LogEvent ids
Security4624, 4625, 4634, 4647, 4648, 4672, 4688, 4697, 4698 / 4699 / 4702, 4720-4726, 4728 / 4732 / 4756 and 4729 / 4733 / 4757, 4781, 4740, 4768 / 4769 / 4771 / 4776, 4778 / 4779, 5140 / 5145, 1102, 1100, 4608 / 4609
System7045, 7036, 7040, 104, 6013 (time zone), 6005 / 6006 / 6008 (boot, shutdown, unexpected shutdown), 1074 (restart / shutdown initiator), Kernel-Power 41
TerminalServicesLocalSessionManager 21-25, 39, 40, 41; RemoteConnectionManager 1149; RdpCoreTS 131, 140; RDPClient 1024, 1102
WinRM6, 91, 142, 161, 169
PowerShellWindows PowerShell 400 / 403; PowerShell/Operational 4103, 32850
Sysmon1, 17, 18

Process creation (4688 / Sysmon 1) is kept only when it matches a remote-execution pattern or ran inside a remote logon session.

Engine command line

LoginActivityTriageCli -d <folder>     -o <outDir> [options]   recurse a folder of .evtx (KAPE / Velociraptor trees)
LoginActivityTriageCli -f <file.evtx>  -o <outDir> [options]   one EVTX file
LoginActivityTriageCli --live          -o <outDir> [options]   this machine's live logs (run elevated)

  --tz <id>        after-hours zone: auto (default, per host from event 6013), a Windows or IANA id,
                   a fixed offset such as UTC+10:00, local or utc
  --hours 7-19     business hours (22-6 = a shift across midnight)    --no-weekend   weekends are not after hours
  --keep-noise     keep machine / system-account logon noise
  --from <utc>     --to <utc>           ISO-8601 import window
  --no-hash        do not record the SHA-256 and size of each source log in files.csv
  --no-html        skip the HTML reports  -q              no per-file progress   -v   version
Exit codeMeaning
0Success; results published.
1Usage error.
2No .evtx files found at the input.
3Fatal error (including a results file held open by another program).
4None of the inputs could be read (not EVTX, corrupt, access denied).

Results are written to a staging folder and published only when the run succeeds, summary.json last. After any failure the output folder still holds the previous run's files; run.log always describes the latest attempt.

The HTA also accepts a command line: mshta LoginActivityTriage.hta "<evtxDir | file.evtx | live | resultsDir>" ["<outDir>"] [/auto] [/tz:<id>] [/from:yyyy-MM-dd] [/to:yyyy-MM-dd] [/offline]. A results folder opens in the viewer; an EVTX source is prefilled and, with /auto, processed. /from /to set the date window. /offline (or the environment variable LAT_OFFLINE=1) keeps the app off the network: no GitHub update check and no downloads — for an analysis machine without internet access. Otherwise Update and Update engine verify each download against the release's SHA256SUMS.txt and discard a mismatch.

Output files

FileContent
events.csvEvery kept event: time, host, log, event id and type, activity, technique, session ref, result, accounts (target and subject), logon ids, source address and port, workstation, logon type, authentication package, process and command line, service, task, share, group, ticket and failure details, source file.
timeline.csvThe triage subset the HTA loads by default.
sessions.csvStitched remote sessions with evidence and confidence.
findings.csvRule hits with severity, MITRE ids, description and reasoning.
users.csv sourceips.csv hosts.csv remotehosts.csvPivots with heuristic scores.
ipnames.csvIP address → name pairs from the logs: the workstation name a client reported at logon (4624 / 4625 / 4778 / 4779, client-supplied; never the logging host's own name) and the host's own address (a network logon to itself or a 4648 to itself). Feeds Known hosts.
files.csv summary.json run.logWhat was read — with the SHA-256 and size of every source log as it was analysed (chain of custody; --no-hash skips it) — run settings and provenance, and the engine log.
report-findings.html report-sessions.htmlSelf-contained reports for attaching to case notes.
runinfo.json knownhosts.csv exportsWritten by the HTA: run provenance (read by the DFIR Artifact Finder), your Known hosts list, and Export view files.

CSV files are UTF-8 with BOM (Excel and Timeline Explorer detect them), RFC-4180 quoted, timestamps ISO-8601 UTC with a trailing Z. Values a spreadsheet would execute as a formula are prefixed with an apostrophe; the HTA hides it.

Forensic notes & limits

Troubleshooting

SymptomWhat to do
engine: not foundPut LoginActivityTriageCli.exe next to the HTA or in .\bin, or use Download engine (latest GitHub release).
engine v… · older than appThe engine next to the app is from an older release, so its results miss that release's fixes and columns. Use Update engine (latest GitHub release) or copy the matching LoginActivityTriageCli.exe next to the HTA, then Process again.
engine v… · newer than appThe engine is from a newer release than this .hta; update the app (the Update button appears when a release is available) so the two match.
engine: will not startInstall the .NET 8 (or newer) runtime; the log shows the error.
"This is a results folder at a path of … characters (over 259)"Results can be produced at any path, but the app cannot load them from a path over 259 characters. Copy the results folder somewhere shorter and use Load results….
"The input path contains a quote or a control character"A Windows path cannot contain " or line breaks; the pasted text is not a path. Fix it and Process again.
"SHA-256 mismatch" on Update / Update engineThe downloaded file does not match the release's SHA256SUMS.txt and was discarded. Retry; if it persists, download the asset from the GitHub release page by hand and compare its hash.
"the copy's SHA-256 … does not match files.csv"The source log differs from the one the engine analysed (the evidence was modified or replaced since the run). Re-acquire the evidence or Process again, and note it in the case record.
"The engine exited 0 but did not publish a new summary.json for this run"The console closed or was killed before the engine published its results; the folder still holds the previous run's files, which were not loaded. Run again and keep the console open.
"None of the input logs could be read"The files are not EVTX, are corrupt, or access was denied; see the Import log and run.log.
"…is open in another program"Close the results CSV in Timeline Explorer or Excel and process again; the previous results were left intact.
Live run misses logonsThe Security log needs elevation: use Relaunch elevated.
Odd characters / file errors from a network shareCopy the app folder to a local disk; zone policy restricts the HTA's file access on shares.
The timeline is slowNarrow the date window, or open events.csv in Timeline Explorer.
Log note "…characters (over MAX_PATH)"Informational. Deep Velociraptor / KAPE paths (260+ characters) are checked and read through their \\?\ extended-length form. If the note says the path could not be verified, the engine reports it when it is really missing. Event Viewer cannot open such a path at all, so Open in Event Viewer uses a temporary copy (see Timeline).
"Source log not found" when opening an event in Event ViewerThe evidence was moved or the results were made on another machine: the SourceFile column holds the path at processing time. Put the evidence back there, or process again from its new location.
Event Viewer opens on a live log with no eventsThe live log has rolled over since processing and that record is gone. Collect the logs to files and process those.