This is a continuation of The Tale of the Lost, but not Forgotten, Undocumented NetSync (part 1) and in this section, we will look to answer:
- What are Some Early Indicators to Detect NetSync at the Host-based Level?
- What are Some Possible Controls to Deter NetSync?
In an accompanying blog post, Wes Lambert (@therealwlambert) steps through a packet capture (pcap) file to highlight what is happening on the “wire” during the execution of a NetSync attack. Through his network-based analysis, Wes extracts the Indicators of Compromise (IOCs) and crafts the according detections.
2.1 What are Some Early Indicators to Detect NetSync at the Host-based Level?
2.1.1 Host-based Detection
126.96.36.199 Windows Event Logging – What is Needed?
Two (2) of the main Windows Event IDs (EVTX) needed to help detect this attack are 4624 (An Account Was Successfully Logged On) and 5145 (A Network Share Object Was Checked To See Whether Client Can be Granted Desired Access). Both logs need to be configured properly and are generated on the targeted Domain Controller (DC). In order to capture the necessary 4624, the audit policy on the DC has to be set to capture the Success for Logon/Logoff. Additionally, the audit policy on the DC has to be set to capture the Success for Detailed File Share to capture the 5145.
We can use the fantastic Detection Lab project by Chris Long (@Centurion) to quickly spin up a test environment. However, Detection Lab uses the Palantir Windows Event Forwarding (WEF) Framework, and the Detailed File Share (to log the 5145) is only set to capture failures in the Domain Controller Enhanced Auditing Policy.
With a quick
auditpol /set /category:"Detailed File Share" /success:enable /failure:enable
we can adjust the audit policy on the DC to capture the 5145.
188.8.131.52 The Lack of Data Standardization Problem for Logon IDs
With Windows Event ID logs, one of main ways to establish a correlation between two different IDs is via Logon ID. As such, and as Teymur Kheirhabarov notes in his deck Hunting for Credentials in Windows Environment, this can be done for NetSync and other credential dumping techniques too. A Logon ID is a hexadecimal value that is assigned to a logon session and can be very useful to map adversary activity/action. In short, if we can make a proof-of-concept (POC) query to match the Logon IDs for both events, it may be possible to craft a viable and high-fidelity detection query. However, the Logon ID value for a 4624 is under “New Logon,” and for 5145 it is under “Subject.” Matching these values in a Security Information and Event Management (SIEM) can be difficult and cumbersome. One of the most challenging things detection engineers encounter with SIEM solutions is the need by vendors to reformat and transform data. Case in point are the Logon ID values for 4624 and 5145.
Unfortunately, all Logon IDs for 4624s in Splunk get shoved into the same field instead of two (2) distinct fields that differentiate between a New Logon ID and a Subject Logon ID.
If we filter out Logon IDs with a value of 0x0, important data events may get dropped. Since there are no unique New Logon ID or Subject Logon ID fields, detection engineers are forced to first standardize the data, then model and manipulate it into logic to suit their needs. Jonathan Johnson (@Jsecurity101) touched on this concept in his blog Engineering Process Injection Detections — Part 2: Data Modeling.
184.108.40.206 Building Splunk Queries With the Community
Finding this issue extremely cumbersome, I connected first with Olaf Hartong (@OlafHartong) via the BloodHound Slack group. After several back and forth conversations, Hartong suggested using Splunk’s eval command to replace the Logon_ID field, because of the duplicitous nature when parsing a multivalve field. This is evident in the 4624, as shown in the below image, as the Logon_ID field contains multiple values.
However, as evident in the below image, a 5145 only has one value in its Logon_ID field.
Using Hartong’s suggestion and the eval command, we can assign a position value to each Logon_ID. We can then use the 4624 Logon ID in Position 1 and the 5145 Logon_ID in Position 0. The eval command can then be used to extract the values in the respective positions and outputted to a matched destination value that we can use to correlate. The following diagram provides a visual representation of the intended data flow.
This will then serve us with the basis to finally build out two (2) similar but different when executed NetSync detection queries. As a fun thought, if Kevin Beaumont (@GossiTheDog) can give nomenclature names to CVEs, defenders can give fun names to detection queries. As such, I have aptly named the following queries.
220.127.116.11.1 The Olaf ‘Warm Hugs’ Query
As a result of our many conversations, Hartong stated that a multisearch could be used to run two (2) searches simultaneously for the data that is exclusively specified by field value. Each search can then be sent to eval (as noted above). We can then use the Logon_ID Data Reformatting Flow noted above to send to the eval command and match on the destination field with mvindex. Then we can use a transaction to correlate on two (2) events with the same Logon ID value that is populated in different places in the needed EVTXs.
Again, eval is used to pull out the account name in a multivalve field position that we want. Finally, all the data is sent, populated, and formatted in a table showing the fields specified for analyst interpretation via a pipe. Using this Olaf Query, we can finally correlate two (2) events on the Logon_ID value in Splunk to make a viable and high-fidelity detection:
| multisearch [ search index=wineventlog EventCode=4624 Logon_Type=3 Impersonation_Level=Delegation | eval logon_id=mvindex(Logon_ID,1)] [ search index=wineventlog EventCode=5145 Share_Name="\*IPC$" Relative_Target_Name=NETLOGON Access_Mask=0x12019F | eval logon_id=mvindex(Logon_ID,0)] | transaction maxspan=5s maxpause=5s fields=logon_id | eval account_name=mvindex(Account_Name,1) | sort _time | table _time Source_Address,host,account_name,ComputerName,Share_Name,Relative_Target_Name,logon_id,EventCode | eval eventcodes=mvcount(EventCode) | where eventcodes >1
Figure 9 – Splunk Query Courtesy of Olaf ‘Magic’
18.104.22.168.2 The Greg ‘I Want to Go Fast’ Query
In contrast to the Olaf Query, which runs two (2) searches concurrently, this query starts with a single search on the fields for 4624 and 5145. The resulting data is then sent to the eval command, just like above, to normalize the meaningful Logon_ID position, which varies from event code (e.g., New Logon ID, Subject Logon ID, Target Logon ID).
index=wineventlog (EventCode=4624 Logon_Type=3 Impersonation_Level=Delegation) OR (EventCode=5145 Share_Name="\IPC$" Relative_Target_Name=NETLOGON Access_Mask=0x12019F) | eval logon_id=if(EventCode=4624,mvindex(Logon_ID,1),mvindex(Logon_ID,0)) | eventstats values(Share_Name) values(Relative_Target_Name) values(Access_Mask) values(EventCode) values(Source_Address) by logon_id | rename values(*) as * | eval account_name=mvindex(Account_Name,1) | search EventCode=4624 Share_Name=* account_name=* | sort _time | table _time Source_Address,host,account_name,ComputerName,Share_Name,Relative_Target_Name,logon_id,EventCode
The Logon_ID field needed to correlate between the two (2) event IDs is extracted based on its position within the respective event ID. Once the two (2) Logon_IDs are normalized, they are sent to the eventstats command. Eventstats is a stats command that takes the aggregate values and stores them in another field. A rename is then performed to remove the field names that contains values() and presents the data in an easier and artistic fashion without either parentheses or the word values (e.g., values(Share_Name) gets reformatted and transformed into Share_Name). From here, we can send the data to a table for output.
22.214.171.124.3 Comparing the Queries
After running both Splunk queries, we can use the Search Job Inspector to get an indication of job speed completion.
The purpose of presenting both queries is to give the detection engineer or the SOC analyst/hunter/responder multiple pathways for detecting a NetSync attack. Both queries have their advantages, disadvantages, and their respective use cases. For example, the Olaf Query can be used in a lab and small dataset to quickly engineer a detection query, whereas the Greg Query might provide more flexibility with a larger dataset and notable monitoring.
The Olaf Query takes all field values from both event types and combines them via a transaction. The main advantage to this approach is that every single field between both event types will be outputted, it is a resource-costly search, and it is a subsearch, which has a 60 second execution time cap. The transaction will stop merging the events after a minute (this is also the same for a join).
The Greg Query is faster because it does not use multiple sub-searches to query all relevant data, and with the eventstats, it only pulls in values of the 5145 that are specified. Regarding uses cases, the Olaf Query could be more applicable in an Incident Response setting, and the Greg Query can be used for “systematic, notable-based detection.”
One thing that is missing from both queries is Windows Event ID 4688 (process command creation), which provides command line logging. 4688 and command line auditing (enabled separately) are critical in a response setting, but they maybe need to be sacrificed for a query running every hour, given the noise and being resource-intensive when added as a third event to search. Given its immensely high value 4688 (with command line auditing enabled) should still be logged. One possibility is to take the more resource-expensive 4688 query and use them for drill down searches, which are not enterprise wide.
Thank you to Jonathan Johnson (@jsecurity101) for providing the Logon_Type=3.
2.2 What are Some Possible Controls to Deter NetSync?
At TrustedSec we evaluate the effectiveness of the Information Security program by focusing on detection, deflection, and deterrence. This allows an organization to measure the ability to defend against a specified attack. As such TrustedSec recommends the following controls to help deter NetSync:
- At BlackHat 2015, Sean Metcalf provided a recommendation in his Red vs. Blue: Modern Active Directory Attacks, Detection, & Protection presentation to change the computer account password change attribute to 1 day from the default of 30 and “…include computer account password changes as part of domain-wide password change scenario.” This could immensely help in mitigating the compromised credential theft of machine accounts and possibly contain an adversary. Many of these type of deterrence controls go a long way in “tripping” an adversary so they can get caught by other detection and deflection controls.
Additionally, Windows Event ID 5827 was generated twice (for each attempt) on the target Domain Controller.
However, upon altering the security descriptor in the Domain controller: Allow vulnerable Netlogon secure channel connections GPO, and making our domain controller (DC$) to allow for a ‘vulnerable connection,’ the NetSync attack was successfully executed:
In the case of NetSync, in addition to the detections outlined in this blog, TrustedSec also recommends the following controls:
- Set the Domain member: Maximum machine account password age GPO to 1, as first recommended by Sean Metcalf. However, as a consequence of doing so this may substantially increase network replication traffic and as a precaution this should be tested in a phased approach before being broadly deployed enterprise wide.
- Apply the patch for CVE-2020-1472 and follow the Microsoft recommend guidelines.
Although NetSync is not new, as it was first committed back in 2016, the purpose of this blog was to show a ‘recycling’ of an older technique over an older protocol/service for offensive operators, and also to provide defensive operators with awareness, particularly with machine accounts and detection capabilities.
This blog would also not be possible without the immense help of:
Alberto Solino (@agsolino)
Benjamin Delpy (@gentilkiwi)
Carlos Perez (@DarkOperator)
Chris Long (@centurion)
Jason Ashton (@ninewires)
Jonathan Johnson (@jsecurity101)
Lee Christensen (@tifkin_)
Matt Nelson (@enigma0x3)
Mike Spitzer (@__spitzer__)
Ned Pyle (@NerdPyle)
Oddvar Moe (@Oddvarmoe)
Olaf Hartong (@olafhartong)
Sean Metcalf (@Pyrotek3)
Teymur Kheirhabarov (@HeirhabarovT)
Tim McGuffin (@NotMedic)
Wes Lambert (@therealwlambert)
Will Schroeder (@harmj0y)
Vincent Le Toux (@mysmartlogon)