Detecting Mimikatz with Busylight

In 2015 Raphael Mudge released an article [1] that detailed that versions of mimikatz released after 8th of October, 2015 had a new module that was utilising certain types of external USB devices to flash lights in different colours if mimikatz was executed. The technique presented in the article required certain kind of busylights that are mainly used by developers to signal their availability to other employees in offices.

The reason why this module was merged into mimikatz is not clear, but it meant that unmodified versions of mimikatz could be physically detected if a device like this was plugged into the computer that was being attacked. Obviously, this kind of detection mechanism is not really feasible in enterprise environments for multiple reasons.

NCC Group had an idea that was put into research to improve on the basic idea, and a way was found to detect mimikatz activity reliably without significant deployment or development costs. Although the result of the research works perfectly and 100% reliable, it can only detect version of mimikatz with the busylight module compiled. Five out of eight variants were detected. More on the results at the end of this article.

The Idea

The idea was to detect the busylight interaction without an external USB device. Taking a look on the busylight devices, it quickly turned out that they do not require any special drivers, they are simple HID devices. Fortunately Windows has the capability to emulate any kind of devices including USB HID devices, and there are also open-source driver examples on Github that can be used for development reasons, so we were up for a promising start.

The Busylight Module

Mimikatz commited the busylight module into the Github source on the 8th of October, 2015. Every release since has the module compiled module that in a nutshell does the following things:

  • Exposes the module to the user, which can be interacted with:
Figure 1 – busylight model invoked
  • It also sends an initialisation sequence to the busylight in a separate thread when the tool gets executed
  • Sends a static keep-alive sequence every 5 seconds
  • Upon exit it sends a final sequence as well

Looking through mimikatz’s code, by default it only supports 6 different type of busylights. The PID and VID numbers are hardcoded and their capabilities as well, so the code can recognize a specific device and send commands accordingly:

Figure 2 – Supported Busylight devices

The Solution

Putting the pieces together, if we can create an emulated HID device with one of the PID/VID values above and listen for the sequences that are sent by mimikatz, we can log those events. Possibly the most secure and portable way to do this would be to use a user-mode driver with low privileges to emulate the device and capture the sequences sent by mimikatz, and when an event happened (start, keep-alive or stop) we would invoke a function from a DLL.

There are multiple ways to do this, but the most user- and coder-friendly version was to use the HID Minidriver Sample from Microsoft’s Github [2], which was based on UMDF 2 (User Mode Driver Framework). Older UMDF versions could be used as well to implement the detection too, but for simplicity we stick to UMDF 2. KMDF (Kernel Mode Driver Framework) is also a possibility, but that would grant higher privilege level for our driver, since it would be in Kernel-space, which we do not require for this, neither want to increase the attack surface of the kernel by adding 3rd party modules.

Implementing changes seemed to be straightforward at this point, but as always it came with a few complications. In general the following things were changed in the sample source code:

  • Vendor and Product ID to match one of the mimikatz supported ones
  • HID Report Descriptor to match the device capabilities
  • The WriteReport() function to check the byte sequences that mimikatz sent and call a function from an external DLL that implements the required functionality

Offloading the functionality to an external DLL made sense, since we do not want to change the driver’s functionality all the time and redeploy it to the machine again and again. Also requirement from different clients could differ, by changing the DLL only would provide greater flexibility.

The Implementation & Usage

The implementation of the Proof-of-Concept driver and sample DLL can be found here: [3].

The Sample DLL shipped with this project is just a Proof-of-Concept that shows how the driver works. In case any of the three events triggered, one of the following functions will be called (ulPid is the Process ID of the process that triggered the event):

  • VOID start(ULONG ulPid)
  • VOID keepalive(ULONG ulPid)
  • VOID stop(ULONG ulPid)

The DLL is capable to log the event into the event log, to the debugger attached to WUDFhost.exe or send the log to a remote syslog server. In case a different event handling is required, that can be easily added to the DLL or it can be replaced easily.

In case the driver was signed with a trusted certificate, the installation is quite straightforward. The DLL needs to be copied into the system32 folder, so it cannot be modified by low-privileged users and the driver can be installed by Microsoft’s Device Console utility (devcon.exe).

After successful installation the following two devices will show up in Device Manager:

Figure 3 – Two devices added

Upon execution of mimikatz, no difference can be seen, and by listing the busylight devices one shows up:

Figure 4 – One compatible Busylight shown in the list

More importantly in the event log, the exact time of execution and termination can be found with keep-alive messages in every 5 seconds. The message also consist the Process ID of mimikatz for forensics purposes.

Figure 5 – Warnings in event log

Since the driver is implemented as a user-mode driver, it is running as NT AUTHORITY\LocalService, therefore with very limited privileges, therefore cannot be used to enumerate process related information. It is recommended to integrate this tool with EDR/SIEM related products to enhance its capability.

It would be also possible to use the driver as a kernel-mode driver to get more privileges, but as explained that would increase the attack surface of the OS.

The detection and limitations

As detailed, the PoC driver was implemented as UMDF 2 [4], which means it could be only used on Windows 8.1 or newer. Support for older operating systems could be done by porting the driver to UMDF 1 for example.

The detection of this PoC was tested against several publicly available mimikatz versions. (Un)fortunately Metasploit’s and Cobalts Strike’s mimikatz binaries were not compiled with the busylight module, therefore detection this way was not possible.

Tested variants:

  • Original version of Mimikatz since 8th of October 2015 (Detected)
  • Original compiled into DLL (Detected)
  • Original compiled into PowerShell (Invoke-Mimikatz) (Detected)
  • PowerSploit – Invoke-Mimikatz (Detected)
  • CrackMapExec – Invoke-Mimikatz (Detected)
  • Metasploit kiwi module (NOT Detected)
  • Cobalt Strike (NOT Detected)
  • Pypykatz (NOT Detected)

Presentation

The busylight related method was the phase one for a longer research on alternative detection techniques against mimikatz. The full research (phase one and two) was presented on the following conferences:

Since the talk covered phase two as well, which was a research on sniffing ConDrv related IOCTLs and detecting mimikatz based on console communication, the code for both phases was open-sourced and can be found below:

Write-up for phase two is coming up soon.

References

[1] Revolutionary Device Detects Mimikatz Use – https://www.cobaltstrike.com/blog/revolutionary-device-detects-mimikatz-use/

[2] HID Minidriver Sample (UMDF version 2) https://github.com/microsoft/Windows-driver-samples/tree/master/hid/vhidmini2

[3] https://github.com/nccgroup/mimikatz-detector-busylight

[4] https://docs.microsoft.com/en-us/windows-hardware/drivers/wdf/umdf-version-history