Nighthawk 0.3.3 – Evanesco

Introduction

Nighthawk 0.3.3; Evanesco, unveils our latest research. “Evanesco” is a Latin term that means “I disappear” or “I vanish.” Potterheads may also recognise it as the spell used to make objects disappear. This is exactly what the Nighthawk 0.3.3 release does; disappears from memory under a cloak of invisibility.

This is also the first release since we welcomed @s4ntiago_p to the @MDSecLabs team; his positive contributions to Nighthawk are already coming to fruition in this release and we’re excited about what he brings to the team going forwards.

As a minor release, we’ve focussed on delivering a small number of innovative new features, while also making a significant number of quality of life and bug fixes. Let’s look at some of the improvements.

Memory Masking

In-memory tradecraft is now essential for operating in modern, mature Windows environments. Memory scanning, whether periodic or event-driven, poses an unavoidable challenge when facing Endpoint Detection & Response solutions and teams of threat hunters.

One of the biggest challenges for in-memory tradecraft is avoiding signatures. The most common approach is implementing sleep encryption, a technique that keeps the beacon fully encrypted during its sleep cycle. A public example is @C5pider’s EKKO, an implementation influenced by Nighthawk, which encrypts and decrypts copies of the reflective DLL while avoiding executable memory through NTContinue gadgets and the CreateTimerQueueTimer API.

While this approach reduces the exposure of the beacon while no active tasking is occurring, there are of course significant periods while the beacon will be in plaintext in memory, including during initial execution to bootstrap the beacon, during any post-exploitation tasks that may block sleep obfuscation or while in interactive mode such was if performing SOCKs or reverse port forwarding where responsive c2 communications is required. Outside from this, a number of EDR solutions will use event driven memory scanning, where a memory scan is automatically initiated following some form of suspicious actions, which may occur from post-exploitation tooling. This can also tie in to captures of allocation routines, which inevitably leads to the exposure of plaintext memory.

Some of these signatures can be side stepped using simple modifications to either the beacon source code or the reflective DLL, it does require intelligence on the signature. This is less of a problem where the vendor has open sourced their rules, but can be more problematic when up against other less open solutions. In most commercial C2 products, such modifications would also typically need to be done by the vendor and can therefore create a lead time before the signature can be bypassed.

Nighthawk 0.3.3 introduces a groundbreaking memory hiding feature that masks all inactive pages of the beacon, even during interactive execution. This means only a tiny fraction—approximately 2%—of the codebase is ever exposed in plaintext memory at any given moment. We’ve also extended this feature to our execute-exe harness, ensuring that PEs executed inline remain fully encrypted during execution.

The memory hiding feature offers several configurable modes, allowing pages to be optionally encrypted and/or distributed across memory using driploading, for enhanced security.

The following example demonstrates how a Yara rule detects a static signature within a Nighthawk artifact, both on disk and in memory, for a beacon in interactive mode. However, when Nighthawk’s memory hiding feature is enabled, these indicators become invisible—even with a sleep interval of 0:

As mentioned above, this doesn’t just apply to the beacon itself but the memory hiding is extended to the execute-exe inline PE harness, and in the following video you can see we’re able to run mimikatz.exe in the thread of our beacon process on a sleep 0 and still evade any Yara signatures:

Of course, a small stub of code used for decryption and memory hiding must remain exposed. You might think this simply shifts the problem elsewhere. However, we anticipated this and built a code mutator that randomly obfuscates and mutates the decrypt stubs on every artifact generation. This approach significantly reduces the risks posed by signatures.

Python API

In our 0.3 release, we announced the introduction of a new JSON RPC web service API to automate the beacon. With that release, we provided a .NET API, with a selection of c# helper implementations. In this release we’ve mirrored the c# API in an installable Python library.

The Python API allows Nighthawk users to build tooling around the API to interact and automate the beacon. Alongside this, we’ve provided a number of helpful example scripts to illustrate various basic tasks such as notify on new beacons, execute BOFs, monitor for processes on a host and much more.

In the example below, we see two scripts; the first which lists all the agents currently connected, and the second which simply just runs the pwd command on all agents:

Python Module Support

One feature that we were lacking inside Nighthawk was support for client side scripting, similar to how other frameworks such as Cobalt Strike leverage CNA (Sleep 🤮). In this release, we introduced client side scripting support in Python, using PythonNet.

Using the Nighthawk Python API, its now possible to execute and process output from BOFs, Exes and .NET assemblies, facilitating much greater automation within the client.

The Nighthawk Python API introduces the following new methods in the nighthawk Python class:

  • register_command: adds a new command to the Nighthawk client (including help and autocomplete support),
  • execute_exe: instructs the agent to run an exe using the beacons execute-exe command,
  • execute_bof : instructs the agent to run a BOF using the beacons execute-bof harness,
  • inproc_execute_assembly: instructs the agent to run a .NET assembly using the beacons inproc-execute-assembly command,
  • get_agents : retrieve a list of all beacon agents (dead or alive),
  • get_agent_info: gets basic information about an agent,
  • console_write : prints text to the console.

In addition to this, the API also includes a Packer class which can be used to pack BOF arguments.

External tooling can then be trivially integrated in to Nighthawk, and if required parsed and processed in Python code. For example, to add a new calcs command to Nighthawk, using the Situational Awareness BOF suite, you might create a python script similar to the following:

# python function that will be called when the 'cacls' command is entered
def cacls_function(params, info):
	# make sure the parameters are ok
    if len(params) > 1:
        nighthawk.console_write(CONSOLE_ERROR, "No enough params")
        return False
    elif len(params) > 1:
        nighthawk.console_write(CONSOLE_ERROR, "Too many params")
        return False

    # the only parameter is the file path
    path = params[0]

    # get an instance of the packer class
    packer = Packer()
    # add the path as a wide string
    packer.addwstr(path)
    # get the packed arguments
    packed_params = packer.getbuffer()
    
    # schedule the execution of the BOF
    message_id = nighthawk.execute_bof(f"bin/cacls.{info.Agent.ProcessArch}.o", "go", packed_params, True, False, 0)

    # simply return the message_id returned by 'execute_bof'
    return message_id

# register the new 'cacls' command
nighthawk.register_command(cacls_function, "cacls", "List user permissions for the specified file, wildcards supported", "Lists file permissions", """cacls <file path>

    Key:
        F: Full access
        R: Read & Execute access
        C: Read, Write, Execute, Delete
        W: Write access""","cacls C:\\windows\\system32\\cmd.exe" )

The scripts can then be imported to Nighthawk using the Python Modules feature. We’ve provided a thorough example using Fortra’s Nanodump:

When the script is imported, it then introduces the nanodump command to Nighthawk:

We hope this feature will allow users to better extend and instrument Nighthawk from the client.

CET Support

Control-flow Enforcement Technology (CET) is a feature found in modern processors that offers protection against control flow hijacking attacks. To implement this, a secondary stack or “shadow stack” is allocated from memory that is not directly tamperable. CET is significantly growing in prevalence and where hardware support is available, is enabled by default in modern Windows 10 and 11.

Shadow stack has implications for several popular OpSec features used by implants, but almost certainly breaks most if not all publicly known sleep obfuscation techniques. The consequence of which means you may be limited in the processes where you can beacon from, popular targets such as msedge.exe or chrome.exe being protected by CET mitigations.

In this release of Nighthawk, we rearchitected several features of the beacon to ensure they did not trigger a CET exceptions, bypassing shadow stack protection and making beaconing great again.

Miscellaneous Improvements

In addition to the above, we made a number of miscellaneous improvements to Nighthawk and the other supporting tools, including:

  • Added a new, unpublished and private technique for cross process injection using function pointer hijacking,
  • Improvements to Hidden Desktop to incorporate Windows 11 changes,
  • Improvements to Hidden Desktop to evade common detection points,
  • Added support for Cobalt Strike BOF key/value API,
  • A large rewrite of our execute-exe harness, improving OpSec and increasing support for other varieties of PE, including Rust binaries,
  • A number of improvements to NHLoader and the Nighthawk injectors to add support for disabling CET in spawned processes,
  • A complete rewrite of the PE infection mode for NHLoader to implement a more effective method PE infection for exe and DLL PE files,
  • Addition of alternative more evasive options for patching AMSI and ETW.
updated_at 29-11-2024