Extreme PowerShell Obfuscation

We recently stumbled upon an old article by Daisuke Mutaguchi explaining an extreme technique for PowerShell obfuscation. The article is in Japanese, so you may have to use Google translate.

Here’s the final example provided by the author of the article:

${;}=+$();${=}=${;};${+}=++${;};${@}=++${;};${.}=++${;};${[}=++${;};
${]}=++${;};${(}=++${;};${)}=++${;};${&}=++${;};${|}=++${;};
${"}="["+"$(@{})"[${)}]+"$(@{})"["${+}${|}"]+"$(@{})"["${@}${=}"]+"$?"[${+}]+"]";
${;}="".("$(@{})"["${+}${[}"]+"$(@{})"["${+}${(}"]+"$(@{})"[${=}]+"$(@{})"[${[}]+"$?"[${+}]+"$(@{})"[${.}]);
${;}="$(@{})"["${+}${[}"]+"$(@{})"[${[}]+"${;}"["${@}${)}"];
"${"}${.}${[}+${"}${)}${@}+${"}${+}${=}${+}+${"}${+}${=}${&}+${"}${+}${=}${&}+${"}${+}${+}${+}+${"}${[}${[}+${"}${.}${@}+${"}${+}${+}${|}+${"}${+}${+}${+}+${"}${+}${+}${[}+${"}${+}${=}${&}+${"}${+}${=}${=}+${"}${.}${.}+${"}${.}${[}|${;}"|&${;};

Yes, this is valid PowerShell.

Although there are limits to static deobfuscation, we decided to see what can be done about this with the new release of our PowerShell Beautifier package.

Before beginning, make sure you have the latest version of the package installed and let’s deobfuscate the code with all parameters set.

Ant this is the result:

$var_13 = "".inSert;
$var_14 = 'ie' + "$var_13"[27];
"[CHar]34+[CHar]72+[CHar]101+[CHar]108+[CHar]108+[CHar]111+[CHar]44+[CHar]32+[CHar]119+[CHar]111+[CHar]114+[CHar]108+[CHar]100+[CHar]33+[CHar]34|$var_14" | & $var_14;

Incredible! It’s already much easier to read!

We can see that this line has not been fully resolved:

$var_14 = 'ie' + "$var_13"[27];

The reason is that the code relies on something which is known only at execution time: namely the signature of the “insert” method. Of course, given what is already present, we can guess the result, but let’s not.

If we try to execute the following lines:

$var_13 = "".inSert;
Write-Host "$var_13"

PowerShell will output the aforementioned method signature:

string Insert(int startIndex, string value)

Let’s print only the index used by the code:

Write-Host "$var_13"[27]

As expected, it prints out the character “x” and thus making the string “iex”.

So let’s replace the unresolved string with the resolved one:

$var_13 = "".inSert;
$var_14 = 'iex';
"[CHar]34+[CHar]72+[CHar]101+[CHar]108+[CHar]108+[CHar]111+[CHar]44+[CHar]32+[CHar]119+[CHar]111+[CHar]114+[CHar]108+[CHar]100+[CHar]33+[CHar]34|$var_14" | & $var_14;

And now we deobfuscate again.

$var_1 = "".inSert;
"[CHar]34+[CHar]72+[CHar]101+[CHar]108+[CHar]108+[CHar]111+[CHar]44+[CHar]32+[CHar]119+[CHar]111+[CHar]114+[CHar]108+[CHar]100+[CHar]33+[CHar]34|iex" | & 'iex';

It is clear that the code uses “iex” (aka Invoke-Expression) to execute the code in the string. If we wish to know what the code in the string contains, we can isolate the contents of the string and execute the deobfuscator only on this portion:

[CHar]34+[CHar]72+[CHar]101+[CHar]108+[CHar]108+[CHar]111+[CHar]44+[CHar]32+[CHar]119+[CHar]111+[CHar]114+[CHar]108+[CHar]100+[CHar]33+[CHar]34|iex

The result:

'"Hello, world!"' | Invoke-Expression

The code prints out the string “Hello, world!”.

牟田口さん、ありがとうございました!

Leave a Reply

Your email address will not be published. Required fields are marked *