SkillAgentSearch skills...

Pycobalt

Cobalt Strike Python API

Install / Use

/learn @dcsync/Pycobalt
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

PyCobalt is a Python API for Cobalt Strike.

Quick Start

Have Python3+ installed on Linux. PyCobalt probably works on macOS and Windows as well. I only test it on Linux though.

First you're going to install the PyCobalt Python library. To do that run python3 setup.py install. If you need more installation help head over to the Installation section.

Now you're ready to start writing PyCobalt scripts. A Python script for PyCobalt looks like this:

#!/usr/bin/env python3

import pycobalt.engine as engine
import pycobalt.aggressor as aggressor
import pycobalt.aliases as aliases

# register this function as a Beacon Console alias
@aliases.alias('example-alias')
def example_alias(bid):
    aggressor.blog2(bid, 'example alias')

# read commands from cobaltstrike. must be called last
engine.loop()

You need to execute this Python script from an Aggressor script. An Aggressor script for PyCobalt looks like this:

$pycobalt_path = '/root/pycobalt/aggressor';
include($pycobalt_path . '/pycobalt.cna');
python(script_resource('example.py'));

It's necessary to set the $pycobalt_path variable so that PyCobalt can find its dependencies.

Now load this Aggressor script into Cobalt Strike. Open up the Cobalt Strike Script Console and you'll see this:

[pycobalt] Executing script /root/pycobalt/example.py

PyCobalt comes with some Script Console commands:

aggressor> python-list
[pycobalt] Running scripts:
 -  /root/pycobalt/example.py

aggressor> python-stop /root/pycobalt/example.py
[pycobalt] Asking script to stop: /root/pycobalt/example.py
[pycobalt] Script process exited: /root/pycobalt/example.py

aggressor> python /root/pycobalt/example.py
[pycobalt] Executing script /root/pycobalt/example.py

aggressor> python-stop-all
[pycobalt] Asking script to stop: /root/pycobalt/example.py
[pycobalt] Script process exited: /root/pycobalt/example.py

When you reload your Aggressor script you should explicitly stop the Python scripts first. Otherwise they'll run forever doing nothing.

aggressor> python-stop-all
[pycobalt] Asking script to stop: /root/pycobalt/example.py
[pycobalt] Script process exited: /root/pycobalt/example.py

aggressor> reload example.cna
[pycobalt] Executing script /root/pycobalt/example.py

You can restart individual scripts as well:

aggressor> python /root/pycobalt/example.py
[pycobalt] /root/pycobalt/example.py is already running. Restarting.
[pycobalt] Asking script to stop: /root/pycobalt/example.py 
[pycobalt] Script process exited: /root/pycobalt/example.py
[pycobalt] Executing script /root/pycobalt/example.py 

For these commands to work properly you can only call PyCobalt in one Aggressor script. Personally I have a single all.cna file with a bunch of calls to python() and include().

PyCobalt Python Library

PyCobalt includes several Python modules. Here's the full list, with links to usage and examples:

For full pydoc documentation head over to the docs/ directory.

Usage and Examples

Here are some script examples. For more complete examples see the examples directory.

Script Console Messages

To print a message on the Script Console:

import pycobalt.engine as engine

engine.message('test message')

engine.loop()

This shows up in the Script Console as:

[pycobalt example.py] test message

To print an error message on the Script Console:

import pycobalt.engine as engine

engine.error('test error')

engine.loop()

This shows up in the Script Console as:

[pycobalt example.py error] test error

To print debug messages to the Script Console:

import pycobalt.engine as engine

engine.enable_debug()
engine.debug('debug message 1')
engine.debug('debug message 2')
engine.disable_debug()
engine.debug('debug message 3')

engine.loop()

This shows up in the Script Console as:

[pycobalt example.py debug] debug message 1
[pycobalt example.py debug] debug message 2

To print raw stuff to the Script Console you can just call the Aggressor print functions:

import pycobalt.engine as engine
import pycobalt.aggressor as aggressor

aggressor.println('raw message')

engine.loop()

Aggressor

pycobalt.aggressor provides wrappers for all ~300 Aggressor functions and some Sleep functions. Here's how you call an Aggressor function:

import pycobalt.engine as engine
import pycobalt.aggressor as aggressor

for beacon in aggressor.beacons():
    engine.message(beacon['user'])

engine.loop()

To call an Aggressor function with a callback:

import pycobalt.engine as engine
import pycobalt.aggressor as aggressor

def my_callback(bid, results):
    aggressor.blog2(bid, 'ipconfig: ' + results)

for beacon in aggressor.beacons():
    bid = beacon['bid']
    aggressor.bipconfig(bid, my_callback)

engine.loop()

To call an Aggressor function without printing tasking information to the Beacon Console (! operator, only supported by certain functions):

...
aggressor.bshell(bid, 'whoami', silent=True)
...

For information on calling Sleep or Aggressor functions that aren't in pycobalt.aggressor (including your own Aggressor functions) see the Sleep Functions section below.

For notes on using non-primitive objects such as dialog objects see the Non-Primitive Objects section.

Aliases

pycobalt.aliases provides the ability to register Beacon Console aliases.

import pycobalt.engine as engine
import pycobalt.aliases as aliases
import pycobalt.aggressor as aggressor

@aliases.alias('test_alias')
def test_alias(bid, arg1, arg2='test'):
    aggressor.blog2(bid, 'test alias called with args {} {}'.format(arg1, arg2))

engine.loop()

You can register help info with an alias and it will show up when you run Cobalt Strike's help command:

...
@aliases.alias('test_alias', short_help='Tests alias registration')
...

By default the long help will be based on the short help and Python function syntax. For example:

beacon> help test_alias
Tests alias registration

Syntax: test_alias arg1 [arg2=test]

Or you can specify the long help yourself:

...
@aliases.alias('test_alias', 'Tests alias registration', 'Test alias\n\nLong help')
...

Argument Checking

When the alias is called its arguments will be automagically checked against the arguments of the Python function. For example:

beacon> test_alias 1 2 3
[-] Syntax: test_alias arg1 [arg2=test]

To bypass this you can use python's * operator:

import pycobalt.engine as engine
import pycobalt.aliases as aliases
import pycobalt.aggressor as aggressor

@aliases.alias('test_alias', 'Tests alias registration')
def test_alias(bid, *args):
    aggressor.blog2(bid, 'test alias called with args: ' + ', '.join(args))

engine.loop()

This also allows you to use Python's argparse with aliases. For more information about using argparse see the Argparse section below.

Exception Handling

If an unhandled exception occurs in your alias callback PyCobalt will catch it and print the exception information to the Beacon Console. For example, while I was writing the previous example I typed engine.blog2() instead of aggressor.blog2() by accident and got this error:

beacon> test_alias
[-] Caught Python exception while executing alias 'test_alias': module 'pycobalt.engine' has no attribute 'blog2'
    See Script Console for more details.

In the Script Console:

...
[pycobalt script error] exception: module 'pycobalt.engine' has no attribute 'blog2'
[pycobalt script error] traceback: Traceback (most recent call last):
  File "/usr/lib/python3.7/site-packages/pycobalt-1.0.0-py3.7.egg/pycobalt/engine.py", line 122, in loop
    handle_message(name, message)
  File "/usr/lib/python3.7/site-packages/pycobalt-1.0.0-py3.7.egg/pycobalt/engine.py", line 89, in handle_message
    callbacks.call(callback_name, callback_args)
  File "/usr/lib/python3.7/site-packages/pycobalt-1.0.0-py3.7.egg/pycobalt/callbacks.py", line 42, in call
    callback(*args)
  File "/usr/lib/python3.7/site-packages/pycobalt-1.0.0-py3.7.egg/pycobalt/aliases.py", line 36, in alias_callback
    raise e
  File "/usr/lib/python3.7/site-packages/pycobalt-1.0.0-py3.7.egg/pycobalt/aliases.py", line 32, in alias_callback
    callback(*args)
  File "/sandboxed/tools/cobaltstrike/scripts/recon.py", line 170, in test_alias
    engine.blog2(bid, 'test alias called with args: ' + ', '.join(args))
AttributeError: module 'pycobalt.engine' has no attribute 'blog2'

Double Quotes

Cobalt Strike's Beacon an

View on GitHub
GitHub Stars304
CategoryDevelopment
Updated1mo ago
Forks57

Languages

Python

Security Score

80/100

Audited on Feb 13, 2026

No findings