It is possible to invoke Windows API function calls via internal .NET native method wrappers in PowerShell without requiring P/Invoke or C# compilation. How is this useful for an attacker? You can call any Windows API function (exported or non-exported) entirely in memory. For those familiar with Metasploit internals, think of this as an analogue to railgun.
This journey started for me upon releasing my PowerShell shellcode execution scripts and noticing that even if I issued C# compiler parameters to compile in memory, csc.exe would still write temporary files to disk. This is unacceptible for any attacker. The venerable Lee Holmes mentioned to me that the only way to achieve true memory-residence in .NET is through the System.Reflection namespace. Reflection is an extremely powerful technique used to generate dynamic code. In .NET, you can achieve fine-tuned control over generated code even to the MSIL bytecode level. Before I get too deep in the weeds, let me tell you a story about rifiling through .NET assemblies.
At one point, I was interested in auditing .NET methods that manipulated data/memory in a possibly unsafe fashion. To find some potential targets in Powershell I enumerated the exported types of every loaded .NET assembly via the following commands:
This search yielded some very interesting methods. The ones that were of the most immediate value to me were those contained within Microsoft.Win32.UnsafeNativeMethods:
Microsoft.Win32.UnsafeNativeMethods is an internal class that cannot be referenced through any direct means. If you try to reference the class, you will get an error stating that its module is not loaded. Microsoft.Win32.UnsafeNativeMethods is implemented within System.dll in the GAC.
Of the methods listed above, GetModuleHandle and GetProcAddress were the most interesting to me since these two methods form the basis for calling any other function in the Windows API. How do you access these methods you ask? I wrote the following PowerShell function to demonstrate:
The function first gets a list of all loaded assemblies in PowerShell. It then gets a reference to System.dll, which contains Microsoft.Win32.UnsafeNativeMethods. I then call the GetMethod method on 'GetModuleHandle' and 'GetProcAddress'. I then invoke GetModuleHandle. Normally, in PowerShell, you would invoke this .NET method like this:
[Microsoft.Win32.UnsafeNativeMethods]::GetModuleHandle('kernel32.dll')
That syntax would generate an error though because it is not a public class. I then get a reference to the returned handle. This is necessary because the managed version of GetProcAddress requires it. Finally, I call GetProcAddress on the function I'm interested in. As an example, the following command will return the address for VirtualAlloc:
Get-ProcAddress kernel32.dll VirtualAlloc
Now, having the address to VirtualAlloc is great and all, but what can be done with it? Conveniently, the System.Runtime.InteropServices.Marshal class contains a very handy method called GetDelegateForFunctionPointer. For those unfamiliar with delegates, think of them as function pointers. GetDelegateForFunctionPointer takes two parameters: a pointer (what was returned from Get-ProcAddress) and a type (a method signature). In C#, defining a method signature is trivial. You just use the delegate keyword. Therefore if you wanted to create a method signature for VirtualAlloc in C#, you could write something like the following:
IntPtr delegate VirtualAllocSig(IntPtr lpAddress, UInt32 dwSize, UInt32 flAllocationType, UInt32 flProtect);
Unfortunately, PowerShell has no equivalent to the 'delegate' keyword. This is where reflection and a handy article circa 2004 come into play. Using reflection, you can do the equivalent of compiling the C# code snippet above. This process is far from straightforward but can be accomplished via the function below:
So to create the equivalent of the C# code that creates a delegate type, you can issue the following command now:
$VirtualAllocDelegate = Get-DelegateType @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr])
Note that if a function passes a parameter by reference, you can still get a proper delegate type by using the MakeByRefType method:
$ByRefDelegate = Get-DelegateType @([String].MakeByRefType()) ([Void])
Coming back full-circle now, we now have everything GetDelegateForFunctionPointer needs to get a 'managed function pointer' to any Windows API function. So lets say you wanted to allocate some RWX memory. This can now be achieved:
Last but not least, it is worth noting that this method will only execute functions that use the StdCall calling convention. Also, if anyone is familiar with Lee Holmes' awesome Invoke-WindowsApi script, you might have noticed that these techniques offer similar functionality to his script. The main difference is that this technique does not use P/Invoke and because you're calling a function from an address, you not just limited to exported functions. You can call non-exported StdCall functions.
Now, you're limited only by your imagination as to what you can achieve in Powershell and all without touching the disk! For example, soon I'll be modifying PowerSyringe to take advantage of these techniques. Also, expect more features to be added to PowerSyringe in the near future...
Interesting read and once you have access to VirtualAlloc, VirtualProtect and WriteProcessMemory you could even write assembly (in byte opcodes) in PowerShell...
ReplyDeleteThe word Native is a little confusing though since the Native API != Win32 API. See http://en.wikipedia.org/wiki/Native_API
Good point regarding the use of 'native.' I was trying to distinguish between managed APIs in .NET and non-managed functions. I definitely can see how some would be confused by my wording.
ReplyDeleteAs far as executing assembly goes...
http://www.exploit-monday.com/2011/10/exploiting-powershells-features-not.html
http://www.exploit-monday.com/2011/11/powersyringe-powershell-based-codedll.html
Good job
ReplyDeleteSince I did some researches on this
http://mail.metasploit.com/pipermail/framework/2008-November.txt
maybe I can contribute on some parts / payloads / features
Happy Hacking!
/JA
Jerome,
DeleteAbsolument! I'm open to any ideas/contributions. If you'd like to contribute to PowerSploit, I've written a style guide for script submissions: https://github.com/mattifestation/PowerSploit/blob/master/README
What sorts of ideas do you have?
Your Get-DelegateType doesn't seem to work for an Action that has no input or output. I'm not sure what to modify to when $parameters.count -eq 0
ReplyDeleteThanks for pointing that out. I updated Get-DelegateType in PowerSploit a while ago but never thought to update it in the blog post. Updated.
DeleteThanks,
Matt
Nice
ReplyDeleteCan you help me? how to calling function from custom DLL, written in C++?
I try to use your method, but haven't success on this way.
for example, proto: WORD InitRndm(PChar *disk, int win) in custom.dll
I assume you're okay with compiling C# inline in your PowerShell code versus using the more stealthy techniques I've blogged about. If that's the case, I would compile a P/Invoke declaration using the Add-Type cmdlet.
DeleteYou'll need to be comfortable with a debugger, calling conventions, and native vs. managed types though. This process can be tricky but always fairly easy to diagnose issues with a debugger.
Here are some good P/Invoke resources:
* https://msdn.microsoft.com/en-us/library/aa984739(v=vs.71).aspx
* http://www.codeproject.com/Articles/403285/P-Invoke-Tutorial-Basics-Part
* https://msdn.microsoft.com/en-us/magazine/cc164123.aspx
Without knowing the implementation of your function, this is how I would define the P/Invoke declaration initially:
[DllImport("custom.dll", CharSet=CharSet.Ansi, ExactSpelling=true)]
public static extern ushort InitRndm(string disk, int win);
This declaration makes a few assumptions:
1) The function is exported in your DLL.
2) It uses the __stdcall calling convention
3) 'disk' is a 'const char *'. I see you have a 'PChar *'. Did you mean for it to be a char pointer pointer?
I hope this helps.
Regards,
Matt
Thanks for answer, Matt!
DeleteIn later, I found some my mistakes, when trying to use your method.
But, I not understand on 100%, when i use you functions from PSReflect (https://github.com/mattifestation/PSReflect) and trying call functions from custom dll it's not work. (or your method only for native libraries)
at first I trying to load library, and after that import function and this don't work too.
Sample don't correctly work, and I can't understand why:
$Verba = New-InMemoryModule -ModuleName Win32
$FunctionDefinitions = @(
(func kernel32 GetProcAddress ([IntPtr]) @([IntPtr], [String]) ),
(func kernel32 LoadLibrary ([IntPtr]) @([String])),
(func kernel32 GetModuleHandle ([Intptr]) @([String])),
(func ntdll RtlGetCurrentPeb ([IntPtr]) @())
)
$Types = $FunctionDefinitions | Add-Win32Type -Module $Verba -Namespace 'Win32'
$Kernel32 = $Types['kernel32']
$Ntdll = $Types['ntdll']
$Kernel32::GetModuleHandle("kernel32.dll") <--work, return correctly value
$Kernel32::GetModuleHandle("kernel32") <--work, return correctly value
$Kernel32::GetProcAddress( $Kernel32::GetModuleHandle("kernel32.dll") , 'VirtualFree' ) <--don't work, return 0
Get-ProcAddress -Module kernel32.dll -Procedure VirtualFree <--work, return correctly value, your function from Mimikatz (https://github.com/clymb3r/PowerShell/blob/master/Invoke-Mimikatz/Invoke-Mimikatz.ps1)
after that, i load custom library:
$Kernel32::LoadLibrary("custom.dll") <--work, return correctly value
trying to import functions, but it's don't work. zero value for functions.
$FunctionDefinitions2 = @(
(func custom InitRndm ([UInt16]) @([string],[Int16]))
)
$Types2 = $FunctionDefinitions2 | Add-Win32Type -Module $Verba -Namespace 'Win32'
$ver = $Types2['custom']
$ver::InitRndm("A:" , 1 ) <-- error, zero value
but, when i try to make this like mimikatz:
1) get $win32function
2) load my custom library
3) import functions, and add member to $Win32Functions, like in mimikatz
4) call functions
it's work.
where i do mistake?
Regarding GetProcAddress not working, it's because you're ambiguous about the character set being used. GetProcAddress only takes ANSI strings so you should explicitly state that in you declaration. The following will work:
Deleteipmo C:\Users\Anonymous\Documents\WindowsPowerShell\Modules\PowerShellArsenal\Lib\PSReflect
$Verba = New-InMemoryModule -ModuleName Win32
$FunctionDefinitions = @(
(func kernel32 GetProcAddress ([IntPtr]) @([IntPtr], [String]) -Charset Ansi ),
(func kernel32 LoadLibrary ([IntPtr]) @([String])),
(func kernel32 GetModuleHandle ([Intptr]) @([String])),
(func ntdll RtlGetCurrentPeb ([IntPtr]) @())
)
$Types = $FunctionDefinitions | Add-Win32Type -Module $Verba -Namespace 'Win32'
$Kernel32 = $Types['kernel32']
$Ntdll = $Types['ntdll']
$Kernel32::GetModuleHandle("kernel32.dll")
$Kernel32::GetModuleHandle("kernel32")
$Kernel32::GetProcAddress( $Kernel32::GetModuleHandle("kernel32.dll") , 'VirtualFree' )
As for custom.dll, I can't tell you why it's not working without the DLL. For example, what's the calling convention, what type of string does the first parameter accept? Also, using PSReflect assumes that the DLL can be resolved via the Windows DLL load order. I've never tested PSReflect on DLLs that aren't in the load path since I only ever intended to support built-in DLLs.
Out of curiosity, do you plan on loading this DLL entirely in memory or did you want to load it from disk? If you're just loading it from disk, since you wouldn't care about stealth at that point, compiling inline C# using Add-Type is the easiest way to interact with your DLL.
Thanks for answer again!
ReplyDeleteYour answers is very helpfull for me on this way.
I trying to combinate your method + add my DLL to AppInit_DLLs key in windows registry, but have not success, after that i trying to modify GAC of current AppDomain instance throw method Load(myDLL), but and that don't work too.
Optimal method for me is use Mimikatz pattern.
But now i have some trouble with translate function parametres, for example
func proto in C:
extern int WINAPI GetCurrID (char * curr_id, char S_or_E)
curr_id — is output parameter, array of 13 chars.
char S_or_E — input parameter, can take 2 value: 'E' or 'S'
on powershell i do that:
$GetCurrIDAddr = Get-ProcAddress custom.dll GetCurrID
$GetCurrIDDelegate = Get-DelegateType @([char[]],[Char]) ([UInt16])
#$GetCurrIDDelegate = Get-DelegateType @([string],[Char]) ([UInt16])
$GetCurrID = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetCurrIDAddr, $GetCurrIDDelegate)
$VerbaFunctions | Add-Member NoteProperty -Name GetCurrID -Value $GetCurrID -Force
[String]$key_str
$VerbaFunctions.GetCurrID.Invoke( $key_str, 'S' ) ← funct return 0 — no_errors, but $key_str not changed
i think, that mistake somewhere in marshalling, but can't understand where.
Maybe somehow to translate $key_str to pointer of char array?