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
The encoded and compressed data is actually the payload of the meterpreter, which is the script text run by Powershell:
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
Found reference to Microsoft.Win32.UnsafeNativeMethods and realized why these methods are being sought and also indirectly (using System.dll). Reference: http://www.exploit-monday.com/2012_05_13_archive.html
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 moduleAfter 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:
AF_INET
2
|
The Internet
Protocol version 4 (IPv4) address family.
|
SOCK_STREAM
1
|
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(
IntPtr ptr,
Type t
)
Parameters
ptr (yoVlPdzFK kernel32.dll VirtualAlloc)
Type: System.IntPtr
The unmanaged
function pointer to be converted.
t (jqzrRwlxR @([IntPtr], [UInt32], [UInt32],
[UInt32]) ([IntPtr]))
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.
Reference: http://www.exploit-monday.com/2012/05/accessing-native-windows-api-in.html
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])
and
([IntPtr])
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)
(New-Object
System.Reflection.AssemblyName('ReflectedDelegate'))
[System.Reflection.Emit.AssemblyBuilderAccess]::Run
DefineDynamicModule('InMemoryModule', $false)
DefineType('MyDelegateType', 'Class, Public, Sealed,
AnsiClass, AutoClass', [System.MulticastDelegate])
[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.
Really nice!
ReplyDelete