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.
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/
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
git clone <repo-url>
cd async-pico-hub
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.
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.
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.
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.
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.cnascript loadsasync-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.
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.
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
To view running Async PICOs:
picos
This displays the currently running tasks and their identifiers.
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.
Additional usage information is available directly inside Cobalt Strike through the built-in help menus for the picos commands.
Check docs/writing_custom_async_pico.md for details.
Check docs/modifying_existing_sleepmask.md for details.
- 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.
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.
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.
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.
The public implementation comes with several practical limitations that should be understood before its usage.
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.
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.
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.