Lab 3.3: Baby’s Second Detection (EQL)¶
Est. Time: 45 minutes
Optional
Goals:
- Create an EQL detection for persistence encoded within the registry
- Trigger your alert (with an Atomic)
Instructions¶
- Often your detection authoring will be driven by threat intelligence, write ups, or other open source intelligence your team comes across. It’s good practice to be able to digest a threat report and determine what content within would make a good detection in your environment. The detection we’re building next is based on real threat research from the DFIR Report used by the threat actors, Qakbot. The full write up can be found here: https://thedfirreport.com/2022/02/21/qbot-and-zerologon-lead-to-full-domain-compromise/
- The technique we are addressing with our detection is a method of persistence that was observed by Qakbot where two things occurred:
- A registry key was created that contained a Base64 encoded value
- Once decoded, the Base64 in question is used for beaconing/additional persistence
- A scheduled task was created to run every thirty minutes, at which point it would retrieve the value of that registry key, decode it, and run the encoded command(s).
- In terms of MITRE, this falls under T1053.005 Scheduled Task/Job: https://attack.mitre.org/techniques/T1053/005/
- A registry key was created that contained a Base64 encoded value
- Theoretically you could look for this within Elastic using KQL. But I’ll tell you that would be significantly more time consuming and less accurate than just writing this detection in EQL.
- I was going to provide a KQL example, but to be honest, it might take me more time to write this detection in KQL than it would be worth to illustrate the point that it would take a while. KQL just isn’t built for this sort of detection. KQL is built to match a certain event with certain characteristics; EQL on the other hand is build to match sequences, which is exactly what we need to look for here.
-
Start by running the Atomic of this detection on your VM in an administrator PowerShell window:
-
The Atomic you’re looking for in this case is T1053.005, specifically test 7.
- If you run into permissions issues, make sure you are running your PS window as admin, and you may also need to run
powershell -exec bypassagain.- If you go to both your Registry Editor and your Scheduled Tasks you should now see the artifacts of this Atomic:
Scheduled task named “ATOMIC-T1053.005” which triggers every day, running a PowerShell command.
A registry value of “test” has been created under the key “ATOMIC-T1053.005”. The data of test is in Base64.
- If you run into permissions issues, make sure you are running your PS window as admin, and you may also need to run
-
The Base64 decodes to
ping 127.0.0.1in case you’re wondering. If this were a real attack, the attacker could put whatever they want, but likely whatever command or script they use for maintaining their presence on a host. - As long as the schedule task remains and runs periodically, the attacker’s persistence will continue. Even if you were to catch their scheduled task, the registry is a busy place so you may not notice a random key within; which would never be named something so obvious as “ATOMIC-MITRE TTP”.
-
Once the test is done running, it’s good hygiene to clean up the things we created with the Atomic. This can be done with another simple command:
-
-
Head over to Elastic and I now challenge you to find the activity that just occurred. Not just one of the commands; write a KQL query that catches both events, the scheduled task creation and the registry key creation.
-
Spoiler, here is how I did it:
-
Considering the low load of our environment this works just fine, and there you have it, you can see both attack commands and also the cleanup command.
-
Once you find this activity, you could actually switch up your query to look at the
parent.process.pidof that command line session, and then you’ll see what else ART is doing as it executes the test. -
Note that if you ran these commands from something that isn’t powershell.exe; for example, PowerShell ISE; you will need to adjust accordingly to look for the executable running the commands.
- The struggle here with KQL is being able to find something similar between the two commands run to perform this attack: the registry key creation and the scheduled task creation. We shouldn’t make a detection on something easily changed like the task names or registry values- that detection will not be long lived.
- The problem with KQL query above is twofold:
- We are looking for a command that has “reg” or “schtasks” as arguments. That is not necessarily malicious, and in a busier environment that is likely to pick up a lot of false positives.
- It’s possible that the attacker may create the task and the registry key with two different commands, at which point the command arguments would not contain both of the arguments we are looking for.
- What if the attacker runs these commands without PowerShell as the parent?
- The point I’m trying to illustrate here is that this detection is very fragile, and easily bypassed, even by accident.
- One of the hardest parts of a detection engineer’s job is writing a detection that can withstand some variance. This is where EQL comes in.
- Keeping your Discover tab open as a reference in case you need to look at those events again, go back over to
Security > Rules > Detection rules (SIEM)and create a new rule. - This rule should be of the type “Event Correlation (EQL)” and you can leave the index patterns as is again.
- With EQL and sequencing, we can write a detection to look for multiple commands executed in order, within a short range of one another.
- Right now, my theory is that we should look for a scheduled task and a registry key being created within a few seconds of one another.
- For me, that looks something like this:
- Keeping your Discover tab open as a reference in case you need to look at those events again, go back over to
-
-
With that, I already am seeing only the events we need. So technically this alert could be functional now:
In this screenshot are two executions of the Atomic (highlighted yellow and next to each other). The white-highlighted row before each is the full command triggered by the atomic.
-
-
This isn’t quite enough though; this sort of thing may happen all the time in Windows, and we don’t want to overload our SOC with false positives. So we need to refine the query further.
-
How much more noise could this create?
I ran this EQL query in the BHIS SOC over a 4 hour period, and it returned 200 events.
-
Consider what else in the alerting events you were looking at in Discover that could be used to further filter your results. Just remember not to filter too far.
-
Spoiler, this is what I came up with:
sequence with maxspan=10s [ process where process.name: "reg.exe"] [ process where process.name: "schtasks.exe" and stringContains(process.command_line, "FromBase64String") ]You’ll see I stuck with the base query, but I added an additional restriction to the scheduled task portion requiring that the command being run decodes a Base64 value, which I would consider to be abnormal.
Running that EQL query in the BHIS SOC over the same time range as the example before generated 0 results, as opposed to 200:
-
-
At this point, your rule should look something like this:
-
Once you have your query and it matches your events, click Continue and fill in the About rule section.
-
Fill in the values as you feel appropriate. Below is what I put for mine, with a few notes on those values:
- For now I set the Severity value to “High”, considering over a long period in my Elastic instance I have not seen this occurring when it isn’t ART. Your environment may be different, so the severity is contextual as usual.
- Under the advanced section, make sure to include any reference URLS and the MITRE tactics in question (T1053.005). I specifically included both Persistence and Execution since that made sense to me.
- For Investigation guide I copied the sort of template we came up with for the Net User Discovery detection and just replaced portions of it. I also included the Atomic commands needed to trigger this alert in the case of a test. If this alert didn’t have an atomic, I could just include the commands themselves.
-
Hit Continue.
- For scheduling the rule we’re also going to leave this at the defaults.
-
Under Rule actions, we again need to add that Jira connector. Here is where we’re going to make use of the connector values we wrote up for Net User. Just use the same ones since they’re dynamic values.
-
Here they are again in text form:
{{rule.name}} {{context.rule.description}} - -- Hits: {{state.signals_count}} --- {{#context.alerts}} Timestamp: {{kibana.alert.original_time}} Host: {{host.name}} User: {{user.name}} Process Id: {{process.pid}} Process: {{process.executable}} Command Line: {{process.command_line}} Parent Process Id: {{process.parent.pid}} Parent Process: {{process.parent.executable}} Parent Command Line: {{process.parent.command_line}} {{/context.alerts}}
-
-
Once your rule is ready hit Create and enable rule.
-
Your rule should now look something like this:
-
-
The last step here is to test your rule and create a Jira ticket.
-
Back in the VM, run your atomic command again and then let the waiting begin (remember to run the clean up the command when you’re done).
-
Once your rule runs (and the logs have been ingested) you should see an Elastic alert and a Jira ticket like so:
-
One thing you may notice with this EQL alert output is with how we have it set up, each of the matching events gets added to the ticket; so we see all of the commands associated with our sequencing event, rather than one particular one like with our KQL detection.
- You have now written a KQL sequence detection! For more information on EQL, check out the additional references at the bottom of this lab document.
- Before this lab is over, in Elastic go over to
Rules > MITRE ATT&CK coverage. You now cover more techniques, and your coverage matrix is filling in slowly.
-
Hardmode¶
Did you nail this lab right away? If so, here’s an additional challenge for you:
- Write your own custom Atomic script to trigger this alert
- I encourage you to share that code with the class after the lab via Discord













