Skip to content

nccgroup/async-pico-hub

Repository files navigation

Async PICO HUB

Async PICOs is a framework for running long-lived, event-driven Beacon Object Files inside a Cobalt Strike Beacon process. It provides asynchronous execution, task tracking, graceful shutdown, and safe asynchronous output by combining Crystal Palace PICOs with a Beacon-side execution model.

Unlike Cobalt Strike's native asynchronous BOFs, Async PICOs execute in Beacon's process and can wake Beacon to surface output when events occur.

Why Async PICOs?

Cobalt Strike native asynchronous BOFs solve a different problem. Async PICOs are intended for long-running, event-driven tasks that execute inside Beacon and can safely communicate results immediately back to the operator.

For implementation details and design rationale, see the accompanying blog post:

https://www.nccgroup.com/research/async-picos-and-custom-beacon-wakeups-in-cobalt-strike/

Building

Requirements

Before building, the following components are required:

  • A local checkout of this repository

  • A built version of Crystal Palace

  • Visual Studio with MSVC and CMake support

  • Tradecraft Garden

  • Clone the repository

Clone or copy the repository locally:

git clone <repo-url>
cd async-pico-hub

Install Crystal Palace

Async PICOs rely on Crystal Palace for PICO generation.

Download the latest compressed release of Crystal Palace and build it according to its instructions. Once built, place the Crystal Palace binaries inside:

pico-tools/crystal-palace/

The expected layout should look similar to:

pico-tools/
└── crystal-palace/
    ├── src/
    ├── lib/
    └── ...

Download the latest release of Tradecraft Garden and place it into

pico-tools/tradecraftgarden

The expected layout should look similar to:

pico-tools/
└── tradecraftgarden/
    ├── libtcg/
    ├── simple_pic/
    └── ...

Make sure to build libtcg and the simple_pic example to be able to build Async PICOs.

Open the project in Visual Studio

The project uses CMake to simplify building with MSVC.

From the repository root, right click the folder and select:

Open with Visual Studio

This loads the CMake project and exposes the available build configurations.

Build configurations

The following build configurations are available:

x64 Debug

Builds local executable versions of PICOs and BOFs for debugging.

Use this configuration when debugging behavior locally or stepping through code in Visual Studio.

x64 Release

Builds optimized local executable versions of PICOs and BOFs.

Use this configuration to test release behavior outside Beacon.

x64 Release Objects

Builds deployment artifacts:

  • Position-independent PICOs using Crystal Palace
  • Standard BOFs such as AsyncPICOMgr

This is the configuration used to produce objects for Cobalt Strike.

Build output

Once the build completes, generated artifacts can be found in:

build/x64-ReleaseObject/obj/

This directory contains the compiled PICOs and BOFs ready for use.

Quick start

Configure a custom sleepmask

Async PICOs require Beacon to use a custom sleepmask.

In your malleable profile, enable support for a custom sleepmask before attempting to use Async PICOs.

The picos-cna/sleepmask.cna script loads async-sleepmask, a minimal reference implementation used to support asynchronous output and Beacon wake coordination.

This sleepmask is intentionally simple and comes with the limitations described in the Limitations section. It is provided to demonstrate the framework and simplify testing, not as a finished production-ready component.

If you already have a custom sleepmask with OPSEC techniques, such as stack manipulation or others, see Modifying your existing sleepmask to be an Async sleepmask to integrate Async PICO support into your existing implementation.

Load the Aggressor script

After building the project with the x64 Release Objects configuration, load the picos.cna and sleepmask.cna Aggressor script into Cobalt Strike:

Script Manager → Load → picos-cna/picos.cna
Script Manager → Load → picos-cna/sleepmask.cna

Once loaded, Async PICOs can be managed through the picos command.

Start an Async PICO

To start a PICO:

picos start [path to pico] [arguments]

For example:

picos start C:\temp\MonitorTGT.pico

Or with arguments:

picos start C:\temp\MonitorTGT.pico DOMAIN\serviceaccount

List running PICOs

To view running Async PICOs:

picos

This displays the currently running tasks and their identifiers.

Stop a running PICO

To stop an Async PICO:

picos stop [pico id]

For example:

picos stop 3

The PICO receives a stop signal and exits gracefully after performing cleanup.

Help

Additional usage information is available directly inside Cobalt Strike through the built-in help menus for the picos commands.

Writing a custom Async PICO

Check docs/writing_custom_async_pico.md for details.

Modifying your existing sleepmask to be an Async Sleepmask

Check docs/modifying_existing_sleepmask.md for details.

Included examples

  • tgt-monitor-pico: async pico that listens to logins and prints kerberos tickets. Based on https://github.com/jakobfriedl/tgt-monitor-bof
  • example-pico: A really simple PICO that shows async capabilities, like opening a window without hanging Beacon's main thread, and sending output.

OPSEC Considerations

The public implementation is intentionally kept simple, without advanced evasive techniques. It is intended as a foundation to adapt rather than something to deploy unchanged.

Thread creation

Async PICOs are launched using CreateThread. This keeps the execution model simple and easy to reason about, but it also introduces a detection surface. In the public implementation, the thread begins execution from memory that is not backed by a module image, which may be detectable by products or heuristics that inspect thread start addresses.

Users should evaluate whether alternative thread creation or execution strategies are more appropriate for their environment.

Sleepmask integration

The framework relies on a modified sleepmask to relay asynchronous output back to Beacon and coordinate wake events. The implementation included here is intentionally minimal and should be reviewed before operational use.

The sleepmask is part of the execution model, not merely a convenience layer. Any changes to how output is queued, drained, or signaled should be evaluated carefully to avoid introducing concurrency issues or unsupported interactions with Beacon internals.

Limitations

The public implementation comes with several practical limitations that should be understood before its usage.

Shared global variable storage

Crystal Palace makes it possible for PICOs to use global variables, but the public implementation uses a simple shared storage model for storing global state. As a result, global variables are not isolated per thread.

In practice, this means running multiple Async PICOs concurrently can require additional care when they depend on globals.

Sleepmask dependency

Async PICOs depend on a modified sleepmask for asynchronous output and Beacon wake coordination. The framework is therefore not fully self-contained and cannot be treated as a drop-in BOF.

Not intended as a fully finished deployment artifact

The repository is designed to demonstrate a framework and implementation approach rather than provide a finished production-ready component. The included examples are intended to be extended, modified, and adapted to individual use cases.

About

Async PICO Hub is a work-in-progress framework to extend Cobalt Strike with custom event monitoring and in-process Asynchronous BOFs

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors