There is no doubt C# is a high-level language and it generates a bytecode on the compilation. Additionally, it also provides features to run unsafe code like pointers but that too is on a high level if you would compile the code with C# Compiler (
csc.exe). Such type of code in windows is called managed code, where all the memory management is handled by the Common Language Runtime.
Also, there is no doubt that C# is a very flexible language and it allows you to call the function defined in the DLLs of the source code which is not written in C#. Such type of code is known as unmanaged code. Unlike managed code, the developer is responsible for cleaning the resources.
This is post is dedicated to running a shellcode crafted from Metasploit directly into the C# code. Since it will be marked malicious by windows defender, it is advised to disable the AVs before proceeding further.
The source code of this post can be found here – https://github.com/tbhaxor/csharp-and-infosec/tree/main/PInvoke%20MSF%20Payload
There are two ways to create shellcode with the Metasploit framework – MSFVenom and Metasploit CLI. I will be using Metasploit CLI in this case as I feel it is more efficient than the other due to autocompletion and one-time loading. The former one takes the same time when you regenerate the payload with it.
For demonstration, I will use message box payload and show
Hello T3r@byt3 text with the title set to
Hacked!. This payload can be found in
payload/windows/x64/messagebox x64 architecture and
payload/windows/messagebox. We are using the C# programming language, so you need to set the format to csharp using the
-f csharp flag.
Now create the payload as shown below
Copy the payload from the above Metasploit output and paste it in the C# project under Program class. You need to make it
static to use the
buf array in the Main method. This is method is called by the CLR and is considered as the entry point of every C# program
Declaring the Unmanaged Function
To run the unmanaged code, you need to first copy the shellcode in the memory and then get the pointer to function so that you can execute it like a function. For this, you need to first allocate the memory in the current process's address space and copy the content from the buffer into that memory location.
Memory allocation can be done by calling the VirtualAlloc function from
kernel32.dll. You can get this signature from PInvoke.com – https://www.pinvoke.net/default.aspx/kernel32.VirtualAlloc#. The
DllImport attribute lets CLR call the function defined in the DLL specified in the first argument. The function should be marked
extern this tells C# that the function is defined in external assembly and DllImport tells CLR where to find that assembly. The name of the symbol should be the same as defined in the DLL otherwise lookup will fail
To get the function pointer, you need to first have a delegate with an
UnmanagedFunctionPointer attribute which will tell the CLR that this delegate is special and will be used to get the function pointer of unmanaged code instead. We will be requiring it during Marshaling. Marshalling is required to convert the memory representation of the shellcode into a format suitable for transmitting data.
Copying Shellcode in Memory
Now it's time to call the VirtualAlloc function and store the address in another IntPtr. Let's count on the system to allocate the memory for us. To do this, pass the
IntPtr.Zero value in the first argument. How many sizes you want to allocate, pass it in the second argument, here it is
buf.Length. Since we need to allocate the memory, the allocation type for such action is
0x1000 which is enumerated as
MEM_COMMIT. As a security feature, you need to provide the mode of the allocated page. The shellcode means to be "executed" + "read/write", you need to set it to
PAGE_EXECUTE_READWRITE whose hex value is
Once you have allocated the space, this will provide you with a starting address to that chunk. Let's call it to handle for now. So now you need to copy shellcode bytes from managed memory to this handle in an unmanaged location. It's one of the simplest things you will see in this post. Call the
Executing the Shellcode From Memory
Once you have copied the shellcode, you need to get the function pointer to execute the code. For C/C++ or C# language, when you add
() after any address, it means to "execute it".
To get a function pointer, you need to call
Mashal.GetDelegateForFunctionPointer the method which will give you the function pointer from unmanaged code to the managed code as a delegate. A delegate is the same as a function pointer but in managed code. All you need to do to execute your shellcode is to call the delegate function as you would call a normal function