A while back, Zerologon came along and helped everyone look really hard at the effectiveness of their operating system (OS) patching strategy.
If you’re looking for more information on the exploit, there’s an excellent write-up by Lares Labs on the nature of Zerologon you can check out.
If your organization doesn’t yet have Sysmon logging or the luxury of running packet captures inline in front of your domain controllers (DCs), this post will attempt to outline a more robust detection scheme within Splunk Enterprise Security (ES) using typical Windows logs. This assumes you have the relevant traditional Windows OS logs being ingested into Splunk, all your data is CIM (Splunk’s Common Information Model) compliant, and your Enterprise Security implementation is following common best practices. If that’s not the case, the search techniques outlined below won’t work on your data, so you’ll have to customize it as necessary to fit your logging formats. However, the basic architecture should at least get you started in the right direction.
Details of the basic search to find insecure Netlogon events
When the exploit first appeared, the Hurricane Labs SOC team worked up a basic search to look for the insecure Netlogon events:
Unfortunately when viewed as an alert, the above search can be rather false positive prone and often too noisy for live SOC alerting. It only shows us that the insecure Netlogon protocol is in use, not necessarily that it’s being attacked. Due to how the exploit is run, I attempted to get around this shortcoming by first checking for a combination of 4624+4742 logged close together, which–as noted in the Lares post–will be logged when the exploit triggers; only then do we look for whether there are relevant 58** series events for the same src+dest within a similar timeframe.
To build this out, we first look for any src/dest combinations that log both 4624+4742 in short order. This is a multipart process due to how most Windows logging implementations store the various components involved here, which I’ll explain as we go.
Building stronger context through data models
The initial step is to find your 4624 events in the Authentication data model. Running against data models whenever possible, especially in a multipart search like this, is preferred as it will run much faster versus doing the same operations against raw logs.
One detail to note here is that the above snippet relies on your Splunk ES Search Head being able to resolve DNS names for the src hosts of the 4624 authentications. This is because the Authentication events log “src” as IP, and the logs that contain the 4742 Change events, whether viewed in the Change data model or the raw sourcetype, log “src” as the hostname.
So we need to get a common point–the lowercase hostname–on which to pivot from one data source to the other. In order to do this, we pull the DNS entry and trim out the hostname. This allows us to then search the Change data model or directly in our Windows logs for those same hosts logging 4724 events. We also make sure to narrow this down to incidents where there was a successful computer account password change.
Note the various field renames in the above code blocks–Event IDs for both sets of events are shown as “signature_id” and “result_id”, event timestamps are “last_auth_time” and “change_time”, etc. Renaming fields in this way can lend a bit more readability to a large table of data wherein several similar fields represent different parts of an event series.
Now that we see which hosts logged a 4624 and 4742 within 60 minutes (a wide margin just to ensure we catch any), we check for any matches on any of the 58** series codes.
If you’re lucky, there are no results when you combine these three stages.
The two-part result
What we then end up with are two components:
- The full combined search can be set up as an alert to your SOC so that if it actually trips you’ll want to investigate and remediate as necessary.
- The basic search we started with can be spun into a report of instances of the 58** series event codes to track usage of the insecure netlogon protocol.
The full combined search code is:
Copy to Clipboard
| join type=left src [
| search sourcetype=wineventlog EventCode=4742 Password_Last_Set!=”-” earliest=-60m
| stats count by dest, user, EventCode, Password_Last_Set, _time
| rex field=user “(?.+)\$”
| rename computer_user AS src, _time AS change_time
| rex field=dest “(?[^.]*)”
| rename trimmed_dest AS dest, EventCode AS result_id,
| eval dest=lower(dest)
| eval src=lower(src) ]| where isnotnull(result_id)
| convert ctime(*_time)
| join type=inner src
[| search earliest=-24h index=windows source=WinEventLog OR sourcetype=WinEventLog
EventCode IN (5827,5828,5829,5830,5831,5805) (“5827” OR “5828” OR “5830” OR “5829” OR “5831” OR “5805”)
| bucket _time span=5m
| convert ctime(_time) as orig_time
| stats count
first(orig_time) as first_time
last(orig_time) AS last_time
values(src_nt_domain) as src_nt_domain
values(dest_ip) as dest_ip
values(EventCode) as EventCode
values(body) as body
values(status) as status
values(Machine_SamAccountName) AS src
| fields – src_asset_id src_category src_city src_country src_dns src_is_expected src_lat src_long src_mac src_nt_domain src_nt_host src_pci_domain src_requires_av src_should_timesync src_should_update tag
| rex field=dest “(?[^.]*)”
| rename trimmed_dest AS dest
| eval dest=lower(dest)
| eval src=lower(src)
| rename EventCode AS 58_series_event, first_time AS 58_event_first_time, last_time AS 58_event_last_time ]
Hopefully you find this useful in your team’s defense strategy. Thanks!