Porting Shellcode Runner to PowerShell
The concept of translating our shellcode runner technique
from VBA to PowerShell is not that complicated.
We can do this by reusing the theory from our VBA shellcode runner.
We already know the three steps to perform.
First, we allocate executable memory with VirtualAlloc.
Next, we copy our shellcode to the newly allocated memory region.
and finally, we execute it with CreateThread.
In the VBA code, we used RtlMoveMemory to copy the shellcode,
but in PowerShell we can use the .NET Copy method from the
System.Runtime.InteropServices.Marshal namespace.
This allows data to be copied from a managed array
to an unmanaged memory pointer.
We’ll use P/Invoke to translate the arguments of VirtualAlloc
and CreateThread, creating the Add-Type statement.
Note that we used Here-Strings to assign a block of text to the $Kernel32 variable.
We also create the import statements in the public Kernel32 class
so we can reference it and compile it later.
Next we must supply the required shellcode which we’ll again generate with msfvenom.
This time, we’ll use the ps1 output format.
Now that the shellcode has been generated,
we can copy the $buf variable and add it to our code.
We’ll also start setting the API arguments.
We invoke the imported VirtualAlloc call with the same arguments as before.
These include a “0” to let the API choose the allocation address,
the detected size of the shellcode,
and the hexadecimal numbers 0x3000, and 0x40
to set up memory allocation and protections correctly.
We use the .NET Copy method to copy the shellcode,
supplying the managed shellcode array,
an offset of 0 indicating the start of the buffer,
the unmanaged buffer address,
and the shellcode size.
Finally, we call CreateThread, supplying the starting address.
As always, we start our lister before running the code.
If we run this code from PowerShell ISE,
we get a reverse shell.
Nice.
Now we need to trigger this from a Word macro.
However, we won’t simply embed the PowerShell code in VBA.
Instead, we’ll create a cradle
that will download our code into memory and execute it.
First, we declared a string variable
containing the PowerShell invocation of the download cradle
through the Net.WebClient class.
Once the PowerShell script has been downloaded into memory as a string,
it then executes using Invoke-Expression, or IEX.
This entire code execution is triggered with the Shell command.
Notice that the download cradle references run.ps1
in the web root of our Kali machine.
To execute our code, we first copy our PowerShell shellcode runner,
into the run.ps1 file on our Kali Apache web server.
We make sure our listener is running,
and we execute our macro.
However, we don’t catch a shell in our multi/handler.
Let’s try to troubleshoot.
First, we know the macro is executing because our Kali machine’s Apache logs
reveal the GET request for the shellcode runner.
On the Windows side, if we use Process Explorer,
and we are quick,
we might notice that a PowerShell process is being created
but then quickly terminates.
Our previous VBA shellcode runner
continued executing because we never terminated word, its parent process.
However, in this version,
our shell dies as soon as the parent PowerShell process terminates.
Our shell is essentially being terminated before it even starts.
To solve this, we must instruct PowerShell to delay termination
until our shell fully executes.
We’ll use the Win32 WaitForSingleObject API
to pause the script and allow Meterpreter to finish.
We’ll update our shellcode runner PowerShell script to import
WaitForSingleObject using P/Invoke and Add-Type to invoke it.
Let’s discuss this addition.
When CreateThread is called, it returns a handle to the newly created thread.
We provided this handle to WaitForSingleObject
along with the time to wait for that thread to finish.
In this case, we instruct the program to wait forever or until we exit our shell.
Notice that we have explicitly performed a type cast on this value
to an unsigned integer because PowerShell only uses signed integers.
This addition should halt the premature termination of PowerShell.
We can now save the updated PowerShell shellcode runner
hosted on our Kali Linux web server,
and rerun the VBA code.
This should result in a reverse Meterpreter shell.
Very nice.
We can also observe the PowerShell process running as a child process of Word.
In this section we created a shellcode runner in PowerShell.
We used the VBA code in our Word macro
to download and execute this script from our Kali web server.
This effectively moved our payload from the Word document
and it appears that the code is running completely in memory,
which should help evade detection.