Command-Line Obfuscation Mechanics: Carets, Token Splitting, and Normalization
By Sharon Ben Moshe · · Updated
Attackers insert escape characters, concatenate variables, and split tokens to break static SIEM string matching. Learn how Windows cmd.exe and PowerShell parse obfuscated commands, how tokenizers normalize them, and how to write resilient detection rules.
Security Information and Event Management (SIEM) rules often rely on literal string matching or simple regular expressions to detect malicious commands. Attackers understand these detection patterns and exploit command interpreter parsing behaviors to alter the appearance of command-line strings while preserving their execution logic. When an analyst queries for "certutil.exe", an obfuscated command like "c^e^r^t^u^t^i^l.exe" fails to match, allowing malicious activity to go unnoticed.
Deobfuscation requires understanding how command shells tokenize strings before execution. Defenders must construct normalization pipelines that strip evasive formatting before running detection heuristics.
Mechanisms of Windows command-line evasion
In the Windows Command Prompt (cmd.exe), several parsing quirks permit obfuscation without altering the resulting execution:
- Caret Insertion (^): The caret serves as an escape character in cmd.exe. When placed between non-special characters, cmd.exe removes the caret and joins the adjacent characters: `c^m^d^.^e^x^e` executes as `cmd.exe`.
- Quote Juggling (""): Quotes delimit strings, but empty quotation pairs within executable names are ignored by the Windows loader in certain contexts: `c""m""d.exe` runs identical to `cmd.exe`.
- Environment Variable Slicing: Windows cmd supports substring expansion. For example, `%COMSPEC:~-3%` extracts the last three characters of the ComSpec path ("exe"), enabling commands like `%COMSPEC:~-3% /c whoami`.
- Variable Concatenation: Setting fragments across separate variables: `set a=cert&& set b=util&& %a%%b% -urlcache ...`.
In PowerShell, backtick (`) escaping and string formatting operators (-f) achieve similar evasion: `&( $env:ComSpec[4,15,25]-Join'')` evaluates characters to dynamically call an executable.
Building a resilient deobfuscation tokenizer
To counter evasion, detection engines must implement a normalization step that processes raw command lines prior to evaluation. The normalization pipeline follows three steps:
- Caret Stripping: Remove isolated carets not preceded by another escape character when processing cmd.exe command lines.
- Variable Resolution: Resolve system environment variables (%TEMP%, %SYSTEMROOT%, %APPDATA%) to their standardized canonical directory paths.
- Quotation Balancing: Tokenize arguments while preserving quoted strings that contain spaces, then strip extraneous interior quotes.
Once normalized, detection logic matches the canonical executable name and flags rather than trying to anticipate every permutation of caret placement.
Entropy and anomalous character density detection
When attackers heavily obfuscate commands, the density of special characters increases noticeably compared to typical administrative scripts. Analyzing character distribution provides a generic detection vector that does not depend on identifying specific tool names.
A high count of carets (^), percentage symbols (%), or double quotes inside a single command line indicates evasion attempts. In Microsoft Sentinel, this can be monitored using calculated ratios:
DeviceProcessEvents
| where strlen(ProcessCommandLine) > 30
| extend CaretCount = countof(ProcessCommandLine, "^")
| extend QuoteCount = countof(ProcessCommandLine, "\"")
| extend Ratio = todouble(CaretCount + QuoteCount) / todouble(strlen(ProcessCommandLine))
| where Ratio > 0.15
| project Timestamp, DeviceName, FileName, ProcessCommandLine, Ratio
Authoring resilient detection signatures
When writing SIEM queries, avoid strict equality checks on executable filenames. Query the original file name from the binary PE header (`ProcessVersionInfoOriginalFileName` in Defender or `OriginalFileName` in Sysmon) because renaming `certutil.exe` to `svchost.exe` does not change its internal header metadata.
In addition, search for parameter roots with wildcards or token lists rather than entire continuous strings. For example, search for `ProcessCommandLine has_all ("urlcache", "split")` rather than `-urlcache -split`.
Test your commands in our interactive Command Analyzer, review Sharon Ben Moshe research methodology on our About page, and browse pre-built queries in our KQL detection catalog and LOLBins Directory.
For foundational research on command-line evasion, review Check Point Research Publications and Daniel Bohannon's work on DOSfuscation documented in FireEye Mandiant Research.
Published on LOLPulse. Browse the LOLBin catalog