The audit logging guide ends with logs that are correctly generated, correctly sized, and sitting on the server that produced them. That is where most security systems stop, and it is not far enough. A log on the host that was compromised is a log the attacker can clear, and event 1102 will tell you it happened, right up to the moment they clear that too. The logs have to leave the host, and on a Windows estate the free, supported, built-in way to make that happen is Windows Event Forwarding.
This page is the design and the configuration for a security system estate: the video servers, the access control servers, the domain controllers they authenticate against, and the operator workstations. It assumes the audit policy from the earlier guide is in place. Forwarding an event that was never generated is not a thing.
Why WEF and not an agent
Every SIEM vendor sells an agent, and on a general IT estate the agent is often the right call. On a security system estate there are reasons to prefer WEF as the first hop, with the SIEM reading from the collector:
- No third-party software on the video and access control hosts. Vendors qualify their platforms against a Windows build, not against a Windows build plus whatever agent the security team chose. WEF is part of Windows.
- The transport is WinRM over HTTPS with Kerberos, which the hosts already speak.
- Source-initiated subscriptions mean the hosts push to the collector. Nothing on the collector needs credentials to reach into a VMS server, which is the correct direction for trust to flow.
- When the SIEM contract changes, the collector stays and only the last hop changes.
The trade-off is that WEF is Windows-only and it is not real-time in the sense an agent can be; a few seconds to a minute of delay is normal. For a security system that is acceptable. For a trading floor it might not be.
Collector design
The collector is a Windows Server with the Windows Event Collector service running, a large ForwardedEvents log on its own volume, and nothing else on it. It is not the SIEM, it is not a domain controller, and it is not the VMS management server. It is a log target, and it should be hardened like one, because it is where the evidence goes.
Sizing. A VMS or access control host with the recommended audit policy produces on the order of tens of megabytes of security events a day; a domain controller produces far more; a workstation produces less. Add up the estate, multiply by the retention you want on the collector (30 days is a sensible minimum, with the SIEM holding the long tail), and size the ForwardedEvents log and its volume to that with headroom. Put the log on a volume separate from the OS so that a burst of events cannot fill the system drive.
# On the collector: enable the collector service and size the forwarded log
wecutil qc /q
wevtutil sl ForwardedEvents /ms:34359738368
wevtutil sl ForwardedEvents /lfn:"L:\Logs\ForwardedEvents.evtx"
That is a 32 GB ForwardedEvents log on a dedicated volume. Adjust to the estate.
Redundancy. Two collectors, with each source subscribed to both, is the simplest resilient design. WEF has no built-in collector failover, so the redundancy is at the subscription level: the source forwards to both, and the SIEM reads from both and de-duplicates. For a security system that must produce logs for an investigation, two collectors is not excessive.
Source-initiated subscriptions by Group Policy
Source-initiated is the mode to use. The collector defines the subscription; Group Policy tells the sources where the collector is; the sources connect out and ask what to send. Adding a host to the estate is adding it to the right OU.
On the sources, two policy settings under Computer Configuration, Administrative Templates, Windows Components, Event Forwarding:
- Configure target Subscription Manager, with the value
Server=https://collector.example.local:5986/wsman/SubscriptionManager/WEC,Refresh=60. HTTPS on 5986 requires a certificate on the collector; HTTP on 5985 is supported and encrypts with Kerberos, and HTTPS is what an auditor will expect to see. - Configure forwarder resource usage, which caps how much bandwidth a source will spend on forwarding. Leave it default unless a host is on a constrained link.
Also on the sources, the WinRM service must be running and configured to listen, and the Network Service account must be able to read the Security log. The second is the one everyone misses; without it, the subscription connects, reports healthy, and forwards nothing from the Security log.
# On each source (or by GPO Preferences): let Network Service read the Security log
wevtutil gl Security
# Append (A;;0x1;;;NS) to the channelAccess SDDL shown, then:
wevtutil sl Security /ca:"O:BAG:SYD:(A;;0xf0005;;;SY)(A;;0x5;;;BA)(A;;0x1;;;S-1-5-32-573)(A;;0x1;;;NS)"
Applying that string by Group Policy Preferences registry item to HKLM\SYSTEM\CurrentControlSet\Services\EventLog\Security\CustomSD is the maintainable way to do it across the estate.
On the collector, create the subscription. The GUI in Event Viewer works; the XML is what goes in source control. The parts that matter are the target log, the source computer groups, and the query.
<Subscription xmlns="http://schemas.microsoft.com/2006/03/windows/events/subscription">
<SubscriptionId>SecuritySystems-Core</SubscriptionId>
<SubscriptionType>SourceInitiated</SubscriptionType>
<Enabled>true</Enabled>
<ConfigurationMode>Custom</ConfigurationMode>
<Delivery Mode="Push">
<Batching><MaxItems>50</MaxItems><MaxLatencyTime>30000</MaxLatencyTime></Batching>
<PushSettings><Heartbeat Interval="60000"/></PushSettings>
</Delivery>
<Query><![CDATA[
(query list goes here, see below)
]]></Query>
<ReadExistingEvents>false</ReadExistingEvents>
<TransportName>HTTPS</TransportName>
<ContentFormat>RenderedText</ContentFormat>
<Locale Language="en-CA"/>
<LogFile>ForwardedEvents</LogFile>
<AllowedSourceNonDomainComputers/>
<AllowedSourceDomainComputers>O:NSG:NSD:(A;;GA;;;DC)(A;;GA;;;DD)</AllowedSourceDomainComputers>
</Subscription>
The AllowedSourceDomainComputers SDDL above allows Domain Computers and Domain Controllers. Replace it with a dedicated group for the security system hosts once the estate is defined; a subscription that accepts every computer in the domain will also accept the one an attacker joined.
What to forward from a security system host
Forward what an investigation would need and nothing that a video server produces by the thousand for no reason. The set below is the one I deploy on VMS and access control hosts. It is deliberately not everything.
<QueryList>
<!-- Authentication and privilege -->
<Query Id="0" Path="Security">
<Select Path="Security">*[System[(EventID=4624 or EventID=4625 or EventID=4634
or EventID=4648 or EventID=4672 or EventID=4768 or EventID=4769 or EventID=4771 or EventID=4776)]]</Select>
<!-- drop the machine-account noise that a video server generates constantly -->
<Suppress Path="Security">*[EventData[Data[@Name='TargetUserName'] and (Data='ANONYMOUS LOGON' or Data='SYSTEM')]]</Suppress>
</Query>
<!-- Account and group changes -->
<Query Id="1" Path="Security">
<Select Path="Security">*[System[(EventID=4720 or EventID=4722 or EventID=4724 or EventID=4725 or EventID=4726
or EventID=4728 or EventID=4732 or EventID=4756 or EventID=4738 or EventID=4740 or EventID=4767)]]</Select>
</Query>
<!-- Process, service, task, and log tampering -->
<Query Id="2" Path="Security">
<Select Path="Security">*[System[(EventID=4688 or EventID=4697 or EventID=4698 or EventID=4702 or EventID=1102 or EventID=4719)]]</Select>
</Query>
<Query Id="3" Path="System">
<Select Path="System">*[System[(EventID=7045 or EventID=7034 or EventID=7031 or EventID=7036 or EventID=104 or EventID=6005 or EventID=6006 or EventID=1074)]]</Select>
</Query>
<!-- Application crashes: the VMS falling over is a security event on this estate -->
<Query Id="4" Path="Application">
<Select Path="Application">*[System[(EventID=1000 or EventID=1001 or EventID=1026)]]</Select>
</Query>
<!-- Sysmon, where deployed -->
<Query Id="5" Path="Microsoft-Windows-Sysmon/Operational">
<Select Path="Microsoft-Windows-Sysmon/Operational">*</Select>
</Query>
<!-- PowerShell -->
<Query Id="6" Path="Microsoft-Windows-PowerShell/Operational">
<Select Path="Microsoft-Windows-PowerShell/Operational">*[System[(EventID=4103 or EventID=4104)]]</Select>
</Query>
</QueryList>
A few notes on why those and not others. Event 7036, a service changing state, is noisy on general servers and essential here: it is how you know the Archiver service stopped at 02:14. Event 1074, a shutdown with its reason and the account that requested it, matters because an unexpected reboot of a video server is exactly the kind of thing an investigation asks about. Event 4688 with command-line auditing enabled is the single most useful event in the set, and it is also the most voluminous, which is why the audit policy guide told you to enable command-line logging and size the Security log for it.
Run domain controllers on their own subscription with the full directory service and Kerberos set. Run workstations on a lighter one: logons, process creation, and removable storage.
The last hop
The collector is the aggregation point, not the destination. Point the SIEM’s Windows connector at the collector’s ForwardedEvents log, and only that log. One connection instead of forty, and the video servers never see a SIEM credential.
If there is no SIEM, and on a good share of security systems there is not, the collector is still worth having: it is a copy of the logs that a compromised host cannot reach, and it is searchable with Event Viewer and PowerShell. Add a scheduled export of ForwardedEvents to an immutable store, and the estate has met the intent of the retention and integrity controls in 800-171 without buying anything.
Proving it is alive
A subscription that shows every source as active and forwards nothing is the standard failure, and it will pass a casual glance. Check three things, on a schedule.
# On the collector: source status per subscription
wecutil gr SecuritySystems-Core
# Any source whose LastHeartbeatTime is older than the heartbeat interval × 3 has stopped
# Events actually arriving, by source, in the last hour
Get-WinEvent -LogName ForwardedEvents -MaxEvents 5000 |
Where-Object TimeCreated -gt (Get-Date).AddHours(-1) |
Group-Object MachineName | Sort-Object Count | Format-Table Count, Name
Every host in the estate should appear in that table. A video server that is not there has either lost its WinRM listener, lost the Network Service read on the Security log, or been moved out of the OU that carries the policy. On the source side, the Eventlog-ForwardingPlugin operational log says which.
Then alert on absence. A host that stops forwarding is a host whose logs are now only on the host, and a scheduled task on the collector that compares the source list against the last hour’s arrivals and emails the difference is ten lines of PowerShell and closes the gap that most log designs leave open.