Nighthawk 0.4 – Janus
Introduction
If you’ve been following our trajectory over the past 12 months, you will have noticed some of the significant design and architecture changes we’ve been making. The largest of which was the full rewrite of the backend teamserver and introduction of JSON RPC APIs which we’ve discussed in previous posts. One of the key drivers for these changes was to pre-position the framework for what we’ll be releasing in Nighthawk 0.4, which will be soon released customers over the coming weeks.
Open For Business
Red Team Operations are multi-faceted, and adaptability is a key requirement for ensuring continued success. It is not unusual during an operation to find yourself with execution on a variety of different platforms outside of the traditional Windows environments. There are also from time to time circumstances where you might also find yourself in scenarios where its preferable to have your agent execution in a specific language, for example to be able to blend in to the environment.
The ability to develop your own custom agents, and have them integrated in to a single backend API, is therefore a beneficial model for operators. This concept is supported inside the open source Mythic framework, and it’s an idea we’ve always admired.
Such flexibility promotes staging, a concept that is heavily used in our own red team engagements as you’ll find out in latter sections of this post.
With Nighthawk 0.4, we introduced a new feature we’re labelling “Open Agent”; another Nighthawk first for commercial C2s. Open Agent allows you to develop and integrate your own agents, whether they’re complete agents or stage 1s, in to Nighthawk. Open Agents will communicate with Nighthawk backend API and appear in the UI, where they can be controlled, in the same way as the Nighthawk C2 agent.
To facilitate Open Agent, we’ve provided customers with extensive documentation and samples on Nighthawk’s C2 protocol and tasking commands. This includes the expected format for all of the built-in commands, how they’re serialised, compressed and encrypted. This enables Nighthawk users to not only build their own agents, but add support for existing commands. For example, should a user desire to build a .NET agent, they may wish to implement the CPMT_EXECUTE_ASSEMBLY
command which would allow them to execute inline .NET assemblies in their own agent but taking advantage of the existing inproc-execute-assembly
command from the UI and backend API server.
At minimum, Open Agents must implement the following three tasking commands, while respecting the communication protocol:
CPMT_GET_DETAILED_INFO
: Allows the backend to obtain basic information about the machine in which the OA is running on, needed for populating several UI elements.CPMT_GET_CONFIG
: Allows the backend to know the sleep and fragmentation settings of the OA.CPMT_TERMINATE_PROCESS
: Allows the operator to instruct the OA to terminate its own execution.
In addition to Open Agents implementing any of the built-in tasking commands, we also added support for them to roll their own custom commands. Custom commands are implemented using the CPMT_OPEN_AGENT_CMD
command type. This means that an Open Agent is able to completely implement its own unique C2 commands; for example you could have a macOS JXA agent that implemented it’s own execute-jxa
command. Open Agent custom commands can be integrated in to the Nighthawk UI using Nighthawk’s client-side Python modules.
With this release, we’ve provided two sample Open Agents; a python Nighthawk agent and a .NET based agent.
In the video below we can see both python (pyHawk) and .NET (SharpHawk) Open Agents running on macOS and integrating to the Nighthawk UI and backend. The video also shows how custom commands are added to these agents, in this case basic commands for greet
and sum
:
Taking Center Stage
The concept of staging is one that is used heavily in MDSec’s own red team operations. Staging provides red teamers with a number of benefits, including a tiny initial footprint, compartmentalisation for burnable payloads, reduced exposure of your stage 2 implants and smaller IoCs due to a minimalistic implant.
Complementing our Open Agent feature, in our 0.4 release we’ve introduced a suite of new staging tools we’ve dubbed the Stager Kit. This suite is compromised of NHStager, a Builder, Visual Studio code templates and a new OpSec driven loader.
The Nighthawk framework now provides the optional ability to deploy implants across stages 0 to 2, where NHLoader PE artifacts can be used to load NHStager implants, to triage and assess a host and environment, prior to in-memory loading the stage 2 implants such as Nighthawk. This might look as follows:
The Stager Kit is a plugin based stage 1 framework designed with OpSec and malleability in mind when used in conjunction with our loader (detailed below). The Stager Kit comes with stage 1 agent, NHStager, which are present provides a SMB based server agent.
NHStager supports a minimal set of commands, including:
whoami
, getosversion
, ps
, execute-bof
, inject
, ls
, mv
, cp
, rm
, mkdir
, rmdir
, upload
, download
and shutdown
NHStager comes with a built-in BOF loader, with full support for the Cobalt Strike BOF API. This allows post-exploitation actions to be performed from within the stage 1, using existing public tooling.
Each component of NHStager is extensible through a plugin based architecture, including even the server component, meaning that custom agents can be built for arbitrary protocols. With this release we provide an OpSec implementation of an SMB stage 1, complete with a set of evasive plugins.
Further, we also provide a source code based example of a TCP stage 1 as part of the Stager Kit. This comes in the form of a Visual Studio project built for developing your own NHStager agents and plugins. NHStager agents do not need to beacon by default (however this can be achieved and even integrated as an Open Agent), acting as a server based listener, waiting for communications from the client. An example client is provided in the form of a BOF with a client-side Nighthawk Python module.
To process the plugins and construct the stage 1, we provide a builder tool which will process the list of plugins, configure and build the final stage 1 DLL:
Nighthawk Stager Build Tool
Option '--plugins' is required.
Option '--arch' is required.
Description:
Build a Nighthawk stager
Usage:
Builder [options]
Options:
--plugins <plugins> (REQUIRED) Specify one or more paths to plugins that the stager should use
--config <config> Configuration for the server plugin. This data will be serialized according to the
prefix and passed to the server plugin in the order it was specified. The valid
prefixes are:
f - The contents of a file, e.g. f"Bar.bin"
z - Null-terminated ANSI string, e.g. z"foobar"
Z - Null-terminated unicode string, e.g. Z"foobar"
b - Binary data in hexadecimal form, e.g. b"012345" becomes 01 23 45
i - 4 byte integer in decimal form e.g. i"123,456"
l - 8 byte integer in decimal form e.g. l"5,294,967,295"
--spawnto64 <spawnto64> The x64 SpawnTo value that will be returned by the BOF API [default:
C:\Windows\System32\rundll32.exe]
--spawnto86 <spawnto86> The x86 SpawnTo value that will be returned by the BOF API [default:
C:\Windows\System32\rundll32.exe]
--arch <x64|x86> (REQUIRED) Specify the architecture of the generated stager
--version Show version information
-?, -h, --help Show help and usage information
To streamline interaction with NHStager servers, we’ve also provided a client-side Python module that allows you to execute any of the stage 1 server commands more seamlessly from within Nighthawk.
For example, to run a BOF on your NHStager server running on a remote host, you might do something like the following which shows us executing a BOF:
Building a NHStager Plugin
NHStager is built to be extended such that custom stage 1 implants can be developed by its operators. This is achieved through a plugin-based framework, where plugins are integrated in to final DLL using aforementioned builder. NHStager itself has minimal OpSec, but is specifically designed to be used in conjunction with our loader (detailed below).
NHStager plugins come in the form of DLLs, with a slightly modified definition of DllMain
:
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved,
PSTAGER Stager)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
break;
}
case DLL_PROCESS_DETACH:
{
break;
}
}
return TRUE;
}
The eagled eyed reader will note the additional Stager
parameter. This is a pointer to a structure that is shared by the stager and all of its plugins, and which contains metadata about the stager as well as an array of function pointers for the Stager API.
Via the Stager API, operators are able to hook various features of the stager to allow custom implementations; this includes things like GetProcAddress
, GetModuleHandleA
, GetModuleHandleW
,LoadLibrary
, the process injection routines, sleep masking, the communications methods and the syscall implementations used by the BOF API.
For example, to implement a custom sleep masking, ServerSleep can be hooked like the below within a plugin:
Core = Stager;
Core->Api.ServerSleep = reinterpret_cast<decltype(ServerSleep)*>(StubSleep);
An implementation of a sleep masking stub can then then be implemented. For example, a basic implementation below is shown that will encrypt the stager in memory, alongside all its plugins, marking those sections as PAGE_READWRITE
in the process. We of course wouldn’t recommend this for use in live operations, but it serves as a simple example of how NHStager plugins can be extended to implement custom functionality:
ULONG APIENTRY StubSleep(_In_ BOOLEAN Wait, _In_opt_ ULONG Count, _In_reads_(Count) HANDLE Handles[], _In_opt_ BOOLEAN WaitAll, _In_ BOOLEAN Alertable, _In_opt_ ULONG Timeout)
{
//
// Encrypt the stager and all other plugins beside us
//
ULONG OldProtect{};
for (AUTO Next{ Core->Plugins.Flink }; Next != &Core->Plugins; Next = Next->Flink)
{
AUTO Plugin{ CONTAINING_RECORD(Next, PLUGIN_INFORMATION, ListEntry) };
if (reinterpret_cast<PUCHAR>(StubSleep) >= reinterpret_cast<PUCHAR>(Plugin->BaseAddress)
&&
reinterpret_cast<PUCHAR>(StubSleep) <= reinterpret_cast<PUCHAR>(Plugin->BaseAddress) + Plugin->Length)
{
continue;
}
VirtualProtect(Plugin->BaseAddress, Plugin->Length, PAGE_READWRITE, &OldProtect);
RtlEncryptMemory(Plugin->BaseAddress, Plugin->Length - (Plugin->Length % RTL_ENCRYPT_MEMORY_SIZE), 0);
}
PVOID StagerAddress{ Core->StagerAddress };
SIZE_T StagerSize { Core->StagerSize };
VirtualProtect(StagerAddress, StagerSize, PAGE_READWRITE, &OldProtect);
RtlEncryptMemory(StagerAddress, StagerSize - (StagerSize % RTL_ENCRYPT_MEMORY_SIZE), 0);
//
// Now sleep
//
ULONG Result{};
if (Wait == TRUE)
{
Result = WaitForMultipleObjectsEx(Count, Handles, WaitAll, Timeout, Alertable);
}
else
{
Result = SleepEx(Timeout, Alertable);
}
//
// Decrypt the stager and plugins and fix their memory permissions
//
RtlDecryptMemory(StagerAddress, StagerSize - (StagerSize % RTL_ENCRYPT_MEMORY_SIZE), 0);
FixSectionPermissions(Core->StagerAddress, Core->NumberOfSections, Core->StagerSections);
for (AUTO Next{ Core->Plugins.Flink }; Next != &Core->Plugins; Next = Next->Flink)
{
AUTO Plugin{ CONTAINING_RECORD(Next, PLUGIN_INFORMATION, ListEntry) };
if (reinterpret_cast<PUCHAR>(StubSleep) >= reinterpret_cast<PUCHAR>(Plugin->BaseAddress)
&&
reinterpret_cast<PUCHAR>(StubSleep) <= reinterpret_cast<PUCHAR>(Plugin->BaseAddress) + Plugin->Length)
{
continue;
}
RtlDecryptMemory(Plugin->BaseAddress, Plugin->Length - (Plugin->Length % RTL_ENCRYPT_MEMORY_SIZE), 0);
FixSectionPermissions(Plugin->BaseAddress, Plugin->NumberOfSections, Plugin->SectionHeaders);
}
return Result;
}
Once the custom plugin set has been compiled, they can be included in the final stage 1 output by passing them to the builder using the --plugins
argument.
Bootstrap Your OpSec
To complement NHStager, we developed a new loader that is integrated in to the Nighthawk backend API and UI payload generator. The loader uses a plugin architecture to provide evasive strategies to PE, while converting them to PIC shellcode. The loader is intended to provide configurable evasion for NHStager, any custom PEs the user wishes to run and in the longer term will completely replace Nighthawk’s own reflective loader. In this release, aside from NHStager, the loader replaces Nighthawk’s previous keying and compression implementations. While this facilitates many of the same keying strategies as prior releases, it now provides the capability to stack them. That is, keying can be performed multiple times, using multiple keys. For example, the Nighthawk shellcode could be first encrypted against the current user’s username, then with a key derived from the machine name, then against an environment variable and finally with a key retrieved from a HTTPS request.
The loader provides a multitude of OpSec options that can be made persistent across the loaded PE through IAT hooks; the available plugins include:
- Custom unhooking to remove user mode hooks,
- A call stack spoofing plugin that masks the stack of threads generated by the PE,
- Capability to clear the process instrumentation callback,
- A syscall proxy to allow the PE to perform indirect syscall execution via the threadpool,
- An opsec LoadLibrary implementation to proxy DLL loads,
- A plugin to dynamically resolve and perform indirect syscalls,
- An IAT hook plugin to redirect API calls made by the PE to the loaders OpSec plugin implementations,
If the IAT hook plugin is enabled, the selected OpSec strategies will apply globally across the PE. The loader can be used to convert any PE (DLL or EXE) to PIC; for example, the operator may want to turn other tools such as Chisel or mimikatz to PIC shellcode, which can easily be achieved using the loader.
Additionally, the outputted PIC shellcode is obfuscated using a new mutation engine built to support the loader, helping to evade static signatures that may be crafted for detection purposes.
NHConfigurator
Nighthawk is highly configurable, with operators able to change almost every component of the beacons loading and runtime configuration to alter the beacon’s behaviour. However, with this flexibility comes complexity. In order to help assist operators in rapidly configuring and deploying Nighthawk profiles, we developed NHConfigurator.
NHConfigurator is a simple UI based wizard that allows operators to cherry pick which high-level OpSec configuration options they want, while also automatically creating random beacon network profiles, producing nginx location
rules and optionally deploying the configuration to the backend teamserver.
Alternatively, a simple step profile generation is available that allows the operator to select the recommended profile based on that commonly seen to be successful against a given EDR solution, with EDR presets.
In the video below, we give a quick demonstration of NHConfigurator and its capabilities for profile generation:
As we draw to a close on the beta testing, we anticipate pushing Nighthawk 0.4 out to customers over the coming weeks.