Reversing File-less attack - Meterpreter through Powershell
After selecting exploit and payload we exploit using msfconsole of Metasploit framework. In this case we used psexec module:
We get the meterpreter shell below
Checking the Windows Event Logs on the target, there are several events which are created. One of them shows some powershell script running some base64 encoded text which is compressed
Following is the full text of the Event. $s is the processstartinfo which is saying to start a process whereby the file system location points to “powershell.exe”, the arguments are comprised of the compressed and encoded text, windowstyle is hidden, createnowindow value is set to true. $p is the process object which starts up $s.
To find out what the encoded and compressed text is, I copied it to linux and used base64 decode and hexdump to decode and see the hashdump of the result. From the header I can see it is a gz format file. Notice the x8bx1f bytes.
Now I dump the raw binary output to a bin file which is actually the .gz file
I change the extension to gz and gunzip the compressed file
I moved the ps1 script to windows and opened it in Windows PowerShell ISE for debugging:
The script looks for UnsafeNativeMethods in the System.dll assembly
Reveresing the probable shellcode - Disassembling
We see the following shellcode, which seemed to be passed on to a Copy function where it is most probably being copied to the memory to be executed:
We see the size of the shellcode is 360 bytes:
The shellcode is base64 encoded, so I decoded it and piped it to xxd –p to get hexadecimal and then I used sed (stream editor) to manipulate the stream and padded all bytes with /x:
Next I placed the resulting shellcode in hex into a skeletal C file:
I had to compile this c code into an object file:
Now I am ready to disassemble the C code with the shellcode, into opcode to try to reverse the shellcode:
I used radar2 for this purpose:
Initially I used the pd@sym command to diassemble the code, whereby it will disassemble a chunk of code equal to a default block size:
I then use the pdf command to disassemble the full code in its entirety.
We can see that 0x0804844b, that the pointer to the shellcode byte array is being passed on to the EAX register and then EAX is being called.
Now obviously we need to disassemble the shellcode sym.shellcode we see up and we need to specify the size of the shellcode in bytes. We remember from the debugging session of the Powershell script code that the shellcode size is 360 bytes. So we disassemble the shellcode by specifying the no of bytes. And voila! we have the assembly for the shellcode:
We know that fs0x30 is the pointer to PEB. The shellcode is trying to get pointer to LDR
Reveresing the probable shellcode - Debugging
Creating windows exe from the c code using Visual Studio Developer:
We see the shellcode in the “data” section 00ED8000
Can you see the byte stream 81C454F2 above in OllyDB DUMP section and below in the Radar2 disassembly of the shellcode?
We also see “Length: %d” string in the debug session
ADD ESP, -0DAC lands EIP somewhere in the ntdll:
Using shellcode2exe.py instead of PE skeleton and Visual Studio Developer to convert the shellcode into a windows binary
We see the bytes for ASCII rep of “ws2_32” pushed to the stack and then ESP is pushed which is pointing to this string.
We also see that 0x0726774C is also passed. Looking up this address on the internet we see that this is pointing to a well known hash of LoadLibraryA method of kernel32.dll module for runtime loading of APIs (obfuscation):
PUSHAD is used to push all registers to the stack and then we see FS[0x30], which points to PEB is acquired into EDX 0x7FFDE000
When we add 0x0C to the PEB we reach to ntdll.771F8440. Furthermore adding 0x14 to that points to 0x00601898, which is the default heap of the process. Adding 0x28 to that leads to a pointer to 0x0060154E which is “shellc.exe” the name of the binary.
The following logic takes the name of the binary and runs some logic including SUB ROR and ADD operations to derive a final value in EDI register: 0x30B18AFE
Further a pointer to the binary header is acquired and 0x3c bytes are added to the location to find a hex value of 0x0100. Then we see 0x100 + 0x78 is added to the binary header to reach to 0x00400178 and a value is checked whether it is 0x00000000 or not. It is and a jump takes place to 0x00401088
The loop continues and this time pointer to ntdll.dll header is acquired:
This time the ECX register is not 0x00 so the logic continues:Next some other offsets are acquired and checked and we see another loop structure which is trying to get pointers to functions in the ntdll module
After going through many functions it finds what it is looking for:
We know that ws2_32 is used for sockets and therefore binding ports to sockets etc. The WSAStartup function initiates use of the Winsock DLL by a process.
Next we see opening a socket object. Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ms742212(v=vs.85).aspx with following parameters:
The Internet Protocol version 4 (IPv4) address family.
A socket type that provides sequenced, reliable, two-way, connection-based byte streams with an OOB data transmission mechanism. This socket type uses the Transmission Control Protocol (TCP) for the Internet address family (AF_INET or AF_INET6).
If a value of 0 is specified for protocol, the caller does not wish to specify a protocol and the service provider will choose the protocol to use.
At this stage obviously the connection will fail because the stack does not contain the details of the sockaddr structure at 0x0013F040
Now that we know what the shellcode is doing. Let us return to the powershell debugging. We were here:
After decoding the base64 shellcode which is creating a socket and connecting to the given ip and port (of the attacker as this is reverse tcp). The next lines of code to be reversed is from line # 24:
Let us look at:
[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((yoVlPdzFK kernel32.dll VirtualAlloc), (jqzrRwlxR @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr])))
In .NET a Marshal Class provides a collection of methods for allocating unmanaged memory, copying unmanaged memory blocks, and converting managed to unmanaged types, as well as other miscellaneous methods used when interacting with unmanaged code.
C# delegates are similar to pointers to functions, in C or C++. A delegate is a reference type variable that holds the reference to a method. The reference can be changed at runtime.
Delegates are especially used for implementing events and the call-back methods
public static Delegate GetDelegateForFunctionPointer(
ptr (yoVlPdzFK kernel32.dll VirtualAlloc)
The unmanaged function pointer to be converted.
t (jqzrRwlxR @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr]))
Type: System.Type The type of the delegate to be returned.
We have reversed the above shellcode before where it loads the ws2_32 library and create a socket object and set it to connect back (reverse tcp) to the attacker's ip (source).
The two parameters of the GetDelegateForFunctionPointer function are actually two more function calls "yoVlPdzFK" and "jqzrRwlxR". Let us see the first one:
We see that the UnsafeNAtiveMethods from System.dll are acquired in the variable $mP916i:
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.
In this list of assemblies we select the following UnsafeNativeMethods: GetProcAddress and GetMoudleHandle
Basically we are invoking GetModuleHandle to get handle to the module kernel32.dll, which is passed as a parameter as seen above and then a HandleRef Constructor is called to create a new object whereby (New-Object InPtr) is the wrapper parameter and the second parameter is the handle to the kernel32.dll which is acquired using GetModuleHandle.
This HandleRef object along with the hardcoded "VirtualAlloc" string are passed on as parameters to the UnSafeNativeMethod "GetProcAddress", the return value of which is then returned as a function pointer (first parameter to the "GetDelegateforFunctionPointer" method.
Let us discuss the second parameter for the "GetDelegateforFunctionPointer" method which is highlighted below:
Here we see a function call to a function jgzrRwlxR and two parameters are passed on it which are:
([IntPtr], [UInt32], [UInt32], [UInt32])
This is the function:
Now resuming debugging we see that the first parameter is an array of type "Type" and the second parameter is a pointer to a Type.
Lets break down the next few lines of code:
Let us look up some function definitions on MSDN:
DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run)
DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
We see above that an assembly of type System.MulticastDelegate is being dynamically created on the fly. We see that the name of this assembly is given as "ReflectedDelegate" and it is set to "Run", which means that the dynamic assembly can be executed but not saved. So that nothing is written on the disk. Next we see that once this assembly is dynamically created, a module of this assembly is dynamically defined as "DefineDynamicModule('InMemoryModule', $false)". The name of this module is "InMemoryModule" and the symbol information is not to be emitted. Next we see that a type is to be defined for this assembly which is defined as "DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])". The type name is "MyDelegateType" and its attributes says that it is a public class which cannot be extended (sealed). Moreover the last parameter to DefineType says that the parent type (Class) of this class is MulticastDelegate, which itself is a subclass of System.Delegate. Ref: https://msdn.microsoft.com/en-us/library/system.reflection.typeattributes(v=vs.110).aspx
Now lets us look further at next lines of code:
We see that come static functions of the object which is created above, which is a dyamically created assembly, are being called. A constructor is being defined as " DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $o2tqMg0ahtJe)"
Here we see that the attributes of the constructors are RTSpecialName, HideBySig, and Public. The calling convention of this constructor is set to "Standard" and the types of parameters to be passed are $o2tqMg0ahtJe. Remember this is an array which was passed on to this function "([IntPtr], [UInt32], [UInt32], [UInt32])". So in order to instantiate an object of this runtime assembly (class) we need to send these parameters to the constructor method.
Next we see that the constructor function Implementation Flags are set as "Runtime" and "Managed". Similarly a static function DefineMethod of the object $fHJrG1SCsVS8 is called to define one of the methods of the class.
$fHJrG1SCsVS8.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $lzJ23R, $o2tqMg0ahtJe).SetImplementationFlags('Runtime, Managed')
Following is the MSDN definition for this constructor.
The runtime method created for this runtime class is called "Invoke" and the return type is $lzJ23R and the parameters which can be passed to it is an array of Type $o2tqMg0ahtJe. The method attributes tells us that it is a virtual method. The Implementation Flags set for this method are also Runtime and Managed.
Once this is done, the type is created by CreateType() method.
Now This type is returned in place of the second parameter of the GetDelegateForFunctionPointer method. Remember the first parameter was the address in memory of the VirtualAlloc method of the Kernel32.dll module:
The first argument "ptr" is basically the address (pointer) to Kernel32.Virtualalloc() and argument "t" is the Type we created by dynamically creating an assembly. This type is called "MyDelegateType" which extends MulticastDelegate, which in turns extend Delegate class. A delegated instance (function pointer) for kernel32.virtualalloc is the return value. This is done to call Windows API within memory by using .NET native method wrappers (mainly System.Reflection namespace). Ref: http://www.exploit-monday.com/2012/05/accessing-native-windows-api-in.html
Let us look at the highlighted code now:
This is showing that the function "Invoke" of the delegate instance for Kernel32.VirtualAlloc is being called. Previously we have seen this Method being created on the fly in the memory. Recall: $fHJrG1SCsVS8.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $lzJ23R, $o2tqMg0ahtJe).SetImplementationFlags('Runtime, Managed')
We see that the arguments to pass to this function called "Invoke" (same name as Powershell Invoke function so as to not create suspicion), is $o2tqMg0ahtJe which is "([IntPtr], [UInt32], [UInt32], [UInt32])". Hence we see that the parameters map as following:
This maps to the VirtualAlloc function syntax:
[IntPtr] = [IntPtr]::Zero The starting address of the region to allocate. If NULL then system will determine this
[UInt32] = $f7539EUN16f.Length (length of the shellcode to be injected)
[UInt32] = 0x3000 Type of memory allocation. 0x3000 indicates MEM_RESERVE and MEM_COMMIT
[UInt32] = 0x40 This indicated memory protection. 0x40 indicates PAGE_EXECUTE_READ_WRITE access to the committed region of pages
Now Let us look at the next line of code:
Here the Marshal Copy method is basically used to copy the byte array of the shellcode [Byte]$f7539EUN16f to the memory location allocated by virtualalloc and pointed to by the pointer $xjoiBREpOG_ and the number of bytes to copy is the length of the shellcode (360 bytes).
Next let us examine once the code is injected into the memory. How exactly it is executed as a new thread of the existing process:
Exactly how kernel32.virtuallalloc was called, we see above that delegate instances for kernel32.CreateThread and kernel32.WaitForSingleObject are created using GetDelegateForFunctionPointer, yoVlPdzFK and jqzrRwlxR functions.
We can see that while calling Invoke function of the delegate for CreateThread we are passing $xjoiBREpOG_ as the pointer to the application-defined function to be executed by the thread.
This is memory injection happening as a post-exploitation step which is an example of a fileless attack.