danielbohannon / Invoke-Obfuscation

PowerShell Obfuscator
Apache License 2.0
3.62k stars 763 forks source link

Out-ObfuscatedCommandTokenLevel2 adds an invoke operator even if one already exists #3

Closed cobbr closed 7 years ago

cobbr commented 7 years ago

Problem

The Out-ObfuscatedCommandTokenLevel2 function adds an invoke operator even if one already exists, without wrapping the script in parentheses.

I discovered this issue while attempting to obfuscate modules in the Empire project.

This error specifically was found while attempting to obfuscate Invoke-WinEnum.ps1

Steps to reproduce

PS> git clone https://github.com/danielbohannon/Invoke-Obfuscation.git
PS> wget https://github.com/adaptivethreat/Empire/raw/master/data/module_source/situational_awareness/host/Invoke-WinEnum.ps1
PS> Import-Module .\Invoke-Obfuscation\Invoke-Obfuscation.psm1

[*] Validating necessary commands are loaded into current PowerShell session.

[*] Function Loaded :: Out-ObfuscatedTokenCommand
[*] Function Loaded :: Out-ObfuscatedStringCommand
[*] Function Loaded :: Out-EncodedAsciiCommand
[*] Function Loaded :: Out-EncodedHexCommand
[*] Function Loaded :: Out-EncodedOctalCommand
[*] Function Loaded :: Out-EncodedBinaryCommand
[*] Function Loaded :: Out-SecureStringCommand
[*] Function Loaded :: Out-EncodedBXORCommand
[*] Function Loaded :: Out-PowerShellLauncher
[*] Function Loaded :: Invoke-Obfuscation

[*] All modules loaded and ready to run Invoke-Obfuscation

PS> Out-ObfuscatedTokenCommand -Path .\Invoke-WinEnum.ps1 | Out-File out
Obfuscating Invoke-WinEnum.ps1

[*] Obfuscating 25 Comment tokens.

[*] Obfuscating 155 String tokens.

[*] Obfuscating 66 Argument tokens.

[*] Obfuscating 90 Command tokens.
Exception calling "Create" with "1" argument(s): "At line:116 char:15
+             & &("{0}{1}{2}"-f 'powers','h','ell') -Sta -Command $cmd
+               ~
The ampersand (&) character is not allowed. The & operator is reserved for future use; wrap an ampersand in double quotation marks ("&") to pass it as part of a string."
At <PATH>\Invoke-Obfuscation\Out-ObfuscatedTokenCommand.ps1:137 char:13
+             $ScriptString = Out-ObfuscatedTokenCommand ([ScriptBlock] ...
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ParseException
(errors continue)

The error message makes it pretty clear that there is a problem with an invoke operator (ampersand).

Reducing the Invoke-WinEnum.ps1 file to this minified version still reproduces the error.

& powershell

Solution

It seems as if simply wrapping parentheses around the existing invoke operator fixes the problem.

Adding code so that the following:

# Randomly choose between the & and . Invoke Operators.
# In certain large scripts where more than one parameter are being passed into a custom function 
# (like Add-SignedIntAsUnsigned in Invoke-Mimikatz.ps1) then using . will cause errors but & will not.
# For now we will default to only & if $ScriptString.Length -gt 10000
If($ScriptString.Length -gt 10000) {$RandomInvokeOperator = '&'}
Else {$RandomInvokeOperator = Get-Random -InputObject @('&','.')}

# Add invoke operator (and potentially whitespace) to complete splatting command.
$ObfuscatedToken = $RandomInvokeOperator + $ObfuscatedToken

# Add the obfuscated token back to $ScriptString.
$ScriptString = $ScriptString.SubString(0,$Token.Start) + $ObfuscatedToken + $ScriptString.SubString($Token.Start+$Token.Length)

Return $ScriptString

becomes:

# Randomly choose between the & and . Invoke Operators.
# In certain large scripts where more than one parameter are being passed into a custom function 
# (like Add-SignedIntAsUnsigned in Invoke-Mimikatz.ps1) then using . will cause errors but & will not.
# For now we will default to only & if $ScriptString.Length -gt 10000
If($ScriptString.Length -gt 10000) {$RandomInvokeOperator = '&'}
Else {$RandomInvokeOperator = Get-Random -InputObject @('&','.')}

# Add invoke operator (and potentially whitespace) to complete splatting command.
$ObfuscatedToken = $RandomInvokeOperator + $ObfuscatedToken

# If an invoke operator is already in use, then we need to wrap this one in parentheses
If($ScriptString.SubString(0,$Token.Start).Trim().EndsWith('&') -or $ScriptString.SubString(0,$Token.Start).Trim().EndsWith('.')) {
    $ObfuscatedToken = '(' + $ObfuscatedToken + ')'
}

# Add the obfuscated token back to $ScriptString.
$ScriptString = $ScriptString.SubString(0,$Token.Start) + $ObfuscatedToken + $ScriptString.SubString($Token.Start+$Token.Length)

Return $ScriptString
danielbohannon commented 7 years ago

Issue fixed in d419d0b4a0592c0cd48d7a22d462477f4b976970 release.