KQL & LOLBAS Detection Ideas.
Some of our favourite defensive measures revolve around limiting access to, and detecting the use of LOLBAS programs within an environment. We’ve included this in our Defending Enterprises training over the past couple of years and it’s proven to be quite a popular query. We’ve done a little more work on this over the last few months, so we thought we’d share this development process.
Previously much of our detection logic relied on referencing static entries that we hardcoded within a KQL query (saved as a variable, let in KQL) which relied on us ensuring that updates from the project were also included within the query.
We’d then run a query to see if any of the LOLBAS programs were identified to be a parent of another process, Microsoft Word spawning cmd.exe comes to mind 😉
A snippet of this KQL query is shown below (cut for ease of reference)…
let binaries = dynamic([ "Atbroker.exe", "Bash.exe", "Bitsadmin.exe", "Certutil.exe", "Cmdkey.exe", "Cmstp.exe", "Control.exe", "Csc.exe", "Cscript.exe", "Dfsvc.exe", "Diskshadow.exe" ...*****]); ... | where ParentProcessName has_any (binaries)
This is a great query for identifying malicious living off the land techniques (with a little more logic, obviously), but it relies on a list of hardcoded objects, which isn’t ideal.
So overall, this is an effective detection method but not the easiest to manage.
Real Time Data Lookups
Let’s give a warm welcome to externaldata. This operator allows us to turn static data blob references into dynamic/real-time lookups!
Using this operator we’re able to dynamically query the LOLBAS API (available in CSV, JSON or YAML formats), store this data in a local table and then query it to see if any of the LOLBAS binaries are identified to be parent processes within the environment.
In the following example event ID 4688 (a new process has been created) is being used, along with the LOLBAS API CSV endpoint.
Note: We’re only using the filename field in this query and we’re also excluding explorer.exe. Explorer is listed as a LOLBAS technique, but will likely skew our results. That being said if you’re interested in identifying users that natively spawn cmd.exe for example, you may well wish to keep this included! We’re also excluding computer accounts from the results (!endswith “$”).
let binaries = externaldata(filename:string, description:string, author:string, loldate:datetime , command:string, commanddesc:string, commanduse:string, commandcat:string, commandprivs:string, mitre:string, os:string, paths:string, detections:string, resources:string, acknowledge:string, url:string)[@"https://lolbas-project.github.io/api/lolbas.csv"] with (format="csv", ignoreFirstRecord=true); let lolbinexe = binaries | project filename; SecurityEvent | where EventID == 4688 | project TimeGenerated, ParentProcessName , Process, NewProcessName, Computer, Account | where Account !endswith "$" | where ParentProcessName != "C:\\Windows\\explorer.exe" | where ParentProcessName has_any (lolbinexe) | project TimeGenerated, ParentProcessName, ChildProcessName = NewProcessName, Computer, Account | sort by TimeGenerated
Update: 01/02/23 @ 19:07
The following line was originally referencing the incorrect variable binaries. This has now been updated to the correct variable lolbinexe.
| where ParentProcessName has_any (lolbinexe)
The following example shows MSBuild.exe has been used in some capacity, likely deserving further scrutiny!
As always there’s more logic that we can (and likely should) add to this query, but nevertheless it provides a great starting block and shows that LOLBAS is a project that can, and should be used in blue as much as it is in red!