When writing detections based on threat reports, research blogs or other sources, common sense dictates that we should find a way to alert on the malicious actions as best as possible, ideally using TTPs. This is commonly done by looking at unique strings, and as good as that may be, there’s a better way to approach it. In this post we’ll cover some examples of broader detections, based on pattern recognition.
Recognizing the Patterns
SigmaHQ is among the (if not the) most popular detection repositories out there. Detection engineers and SOC analysts who write rules use it as a source of inspiration. So do big companies such as Splunk, Elastic and more. SigmaHQ is a great source but sometimes the detections lack a deeper investigation of the actual tools in question and focus on a very specific attack as opposed to attempting to create a broader detection for irregular anomalies.
Most LOLBAS binaries, when used in a legitimate manner, use a pattern, as we’ll see below. By researching these patterns, we can attempt to create detections not only for what we have already seen used in the wild, but also for anomalies that could point to potentially malicious activity.

The Many Uses of Rundll32
Rundll32 is a native Windows utility that allows execution of functions exported from dynamic-link libraries (DLLs) via command-line. It is commonly used for legitimate administrative tasks, but it is also used for malicious purposes by a variety of attackers and malware. This is a commonly used technique that was seen used in the wild by various threat actors such as APT 38, APT 28 and FIN 7.
As of the writing of this post, SigmaHQ has over 30 detections related to rundll32. Most of them look at specific parameters in the command line, which makes sense as the usage usually requires the input or a dll file and the function to run:
rundll32 <dll file or path>,<function> <optional parameters><br>Knowing this pattern we could attempt to create a rule to detect unusual combinations of dll files and functions:
DeviceProcessEvents
| where FileName =~ "rundll32.exe"
| where ProcessCommandLine matches regex "\\w+,\\s?\\w+" // match the pattern for dll and function execution
| extend dll_full_path = extract(@"(?i)rundll32(?:\.exe)?""?(?:\s+/\w+)*\s+[""]?([^,\s""]+)", 1, ProcessCommandLine), // parse the dll full path, if present
dll_name = extract(@"([^\\\/:""]+)$", 1, extract(@"(?i)rundll32(?:\.exe)?""?(?:\s+/\w+)*\s+[""]?([^,\s""]+)", 1, ProcessCommandLine)), // parse the dll name
dll_function = extract(@"\s*,\s*([^\s""\\/,]+)", 1, ProcessCommandLine), // parse the function used
dll_arguments = extract(@"\s*,\s*[^\s""\\/,]+\s+(.*)", 1, ProcessCommandLine) // parse any arguments passed to the function
| where not (dll_full_path contains "C:\\Windows\\System32" or dll_full_path contains "C:\\Windows\\SysWOW64\\") // exclude commonly used paths by the system
| extend name_and_function = strcat(dll_name, "," ,dll_function)
| where isnotempty(name_and_function) and InitiatingProcessAccountSid !~ "S-1-5-18" // exclude the user System using its SID
| summarize InitiatingProcessFileName = make_set(InitiatingProcessFileName), InitiatingProcessCommandLine = make_set(InitiatingProcessCommandLine), InitiatingProcessFolderPath = make_set(InitiatingProcessFolderPath), InitiatingProcessParentFileName = make_set(InitiatingProcessParentFileName), InitiatingProcessAccountName = make_set(InitiatingProcessAccountName), ProcessCommandLine = make_set(ProcessCommandLine), dll_full_path = make_set(dll_full_path), dll_function = make_set(dll_function), dll_arguments = make_set(dll_arguments), count() by DeviceName, dll_name
Additionally, we will have to run the query and see what is used in our environment to exclude it using the field “name_and_function”. This is not a catch all rule, but does a great job at alerting on anomalies, either things we know were used by attackers or ones that were not previously seen.
There are some known dll files in System32 that are used by attackers but that can easily be handled by a compensating rule. This can be done by either creating a whitelist of all DLLs and function combinations from the System32 or SysWOW64 folder or by researching known uses of DLLs in these folders, like Raspberry Robin’s use of shell32.dll. It is important to note that because System32 and SysWOW64 are configured as PATH environmental variables, there is no need to specify the path, and attackers usually use the DLL name only if it resides in these folders.
The Legendary Svchost
Svchost.exe (Service Host) is a core Windows process used to host one or more system services that run from DLLs. This process is recognizable by anyone who knows anything about security. It’s a commonly abused service that is often used by malicious actors in different ways such as process injection or masquerading as a legitimate service. Similar to rundll32, svchost also has its share of detections in SigmaHQ and other detection repositories.
When malware uses svchost for malicious purposes, it often has some unusual command line parameters. This leads us to look into the pattern used by the legitimate svchost, which comes down to a simple regular expression that can be implemented into a query:
DeviceProcessEvents
| where FileName =~ "svchost.exe" and isnotempty(ProcessCommandLine)
| where not (ProcessCommandLine matches regex @'-k\s\w+((\s?(-p|-s))|$)' or ProcessCommandLine matches regex @'^(?i)(?:\")svchost\.exe(?:\")$') // Exclude the known good command line pattern and anything that contains only the process name
| project-reorder DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, InitiatingProcessParentFileName, InitiatingProcessAccountName, ProcessCommandLine, TimeGeneratedSimilarly to the rundll32 example, this is not a catch all rule for svchost, but it does provide a wider net, which could potentially help detect use cases we have not seen yet.

Conclusion
As with any other detection engineering method, this is not a magic solution. What this does is provide us with broader detections that will not rely on what a single attacker did and instead use a whitelist approach to find anomalies.
The downside is the research time – finding the correct patterns and getting the regular expressions correctly can take time (and quite a bit of tuning or troubleshooting), even for regex wizards.
By embracing this philosophy, we have a better chance at detecting anomalies. As opposed to creating detections based on known attacks, this would also allow us to alert on previously unseen attacks using the tools we focused on.
About the Author
Liran Ravich is a Security Architect at CardinalOps, specializing in detection engineering and security operations. Prior to this role, he worked at the Israeli National Cyber Directorate (INCD), where he worked within the national CERT and held various cybersecurity roles across government agencies. He is also an active contributor to the MITRE ATT&CK framework since version 12.
