In this blog post I’m going to cover my first impressions of MWR Labs C3 project, through installation and setup to usage in a basic scenario.
First, we need to cover some background information on what “External C2” is - if you’re already familiar, skip ahead.
External C2 was introduced back in Cobalt Strike 3.6, and it provides an interface for 3rd party applications to act as a communication layer between Cobalt Strike and Beacon. That is to say, provide communication methods that are not natively supported by the toolset. In the specific case of Cobalt Strike - Beacon has the capability to (at the time of writing) egress C2 traffic over HTTP/S and DNS; and link to other Beacons over SMB named pipes and TCP sockets.
The External C2 specification allows operators to expand how Beacon can communicate (either back to the Cobalt Strike Team Server, or to other Beacons) to practically any means desirable. I’ll wait a moment for that to sink in…
By “any means desirable”, I mean really awesome ways like C2 over Office 365; C2 over fileshares and C2 over Active Directory!
This is good for all sorts of reasons.
Egress C2 over a legitimate service such as O365, Google Drive, Dropbox, Slack, Mattermost or whatever; is going to do such a good job of blending in - particularly if your target uses those services as part of their everyday business practices. In some cases you could even transmit C2 over their own tenants, e.g C2 over their own OneDrive.
Internal C2 over fileshares, AD or anything that can be written to/read from are also going to be difficult to detect; but may also help get around different types of firewall restrictions and perhaps even air-gapping. C2 wrapped to look like RDP, WinRM or WMI traffic are all exciting ideas.
Implementing External C2 is no trivial task (much more so than slapping a Malleable C2 Profile together).
The following diagram is the External C2 architecture taken from the External C2 Spec.
The boxes in yellow (Team Server & SMB Beacon) are the native Cobalt Strike components, whilst those in green (Third-Party Controller & Third-Party Client) are the “3rd party applications” that an operator must develop to leverage External C2. As you can see, they act as a “man-in-the-middle” between a Beacon and Team Server, effectively relaying traffic between them over the custom C2 channel.
So a Client must be capable of staging and talking to Beacon over a named pipe, and communicate with the Controller over whatever means you’re trying to implement. Similarly, the Controller must receive traffic from the Client and send that information to the Team Server in a way that it understands.
It doesn’t take much to realise that there are a lot of re-usable components here, and (attempted?) frameworks (here & here) have been around for some time. MWR Labs C3 is the latest attempt at providing operaters with a stable and feature-rich framework for prototyping and integrating custom C2 channels.
C3 already provides some custom C2 channels that we can use straight out of the box - so I decided to try the Slack and Fileshare channels. I built a contrived lab to demonstrate.
Client 1 and Client 2 are workstations on the same domain, but they cannot communicate with each other (so no SMB/TCP Beacons). Client 1 will run the Slack egress C2 and Client 2 will run the Fileshare C2. Channels can be linked - so the Client 2 Fileshare C2 will link to the Client 1 Slack C2.
For my Team Server, I’m using an Ubuntu 18.04 VM setup as per the standard Cobalt Strike intallation instructions; and my C3 Gateway server is simply a Windows Server 2016 VM with the .NET Core 2.2 runtime installed.
To build the WebController
:
D:\Tools\C3>cd Src\WebController\Backend
D:\Tools\C3\Src\WebController\Backend>dotnet publish -c Release
Microsoft (R) Build Engine version 16.2.32702+c4012a063 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Restore completed in 57.31 ms for D:\Tools\C3\Src\WebController\Backend\WebController.csproj.
WebController -> D:\Tools\C3\Bin\WebController\Release\netcoreapp2.2\C3WebController.dll
WebController -> D:\Tools\C3\Bin\WebController\Release\netcoreapp2.2\publish\
Open Visual Studio and build the GatewayConsoleExe
, NodeRelayConsoleExe
and NodeRelayDll
projects, by simply right-clicking and selecting Build
. These should also be written to D:\Tools\C3\Bin
. Now copy the whole Bin
directory to C:\C3
on the C3 Server.
Your folder structure should look like this:
Go to C:\C3\Bin\WebController\Release\netcoreapp2.2\publish
and open appsettings.json
. Change payloadTemplateDir
to ../../../..
(which drops it in to C:\C3\Bin
).
The WebController
can now be started.
Note:
192.168.1.147
is the IP address of my Gateway server.
C:\Users\Administrator>cd C:\C3\Bin\WebController\Release\netcoreapp2.2\publish
C:\C3\Bin\WebController\Release\netcoreapp2.2\publish>dotnet C3WebController.dll --urls http://192.168.1.147:8080
Gateway API bridge starts listening on 127.0.0.1:2323
Hosting environment: Production
Content root path: C:\C3\Bin\WebController\Release\netcoreapp2.2\publish
Now listening on: http://192.168.1.147:8080
Application started. Press Ctrl+C to shut down.
Browsing to that IP:Port should show the C3 Dashboard.
I had to click Edit Config
and input http://192.168.1.147
and 8080
in the respective boxes. Clicking Save Config
doesn’t seem to update/refresh the page. So spam-click Save
a few times and close the window. Then click Create and Download Gateway
and a ZIP file should download.
Copy this to the Gateway server, unzip and run the binary. All being well the GatewayConsole should connect to the WebController API and and C3 Dashboard should refresh.
This is a very easy step :)
External C2 is started on the Team Server via an Aggressor script. So just save the following as externalc2.cna
and load it into CS via the Script Manager.
Note:
192.168.1.140
is the IP of my Team Server.
externalc2_start("192.168.1.140", 2222);
This will bind port 2222 on the external interface of the Team Server.
[email protected] ~# ss -lt
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 0.0.0.0:ssh 0.0.0.0:*
LISTEN 0 128 [::]:ssh [::]:*
LISTEN 0 32 *:50050 *:*
LISTEN 0 128 [::ffff:192.168.1.140]:2222 *:*
LISTEN 0 50 *:http *:*
Double-click on the Gateway icon on the C3 Dashboard and click Command Center
. From the Select Command
list, choose TurnOnConnectorTeamServer
and provide the IP (192.168.1.140) and port (2222).
Before you can configure the Gateway to use Slack, you need a Slack App in your Workspace.
I tried to be smart/lazy at first and just made it a workspace admin, which didn’t actually work. invictus suggested the following permissions, which worked great for me:
files:read
files:write:user
chat:write:bot
chat:write:user
channels:history
channels:read
channels:write
Once the App is added to the Workspace you can grab the OAuth Access Token (beginning with xoxp
).
Back in the C3 Dashboard, double-click the Gateway > Command Center > AddChannelSlack
and paste the OAuth token. I was happy to leave the channel name as it’s default random name. Then click Send Command
.
You should see the new interface appear in the C3 Dashboard. Any error messges will display themselves in the Gateway Console running on the C3 Server - which is a good place to look for troubleshooting issues.
Next we need to create a Node Relay
for this C2 channel - this is that “3rd party client” that we need to run on a compromised host.
Double-click on the Slack Channel Interface, hover over Interface Options
and select New Relay
. All the relevant information for the Slack channel is pre-populated. We can opt to download the Relay as an exe
or dll
- which one you want will entirely depend (in the real world) on how you want to get this running on a compromised host.
From what I’ve seen: the exe can be run as a normal executable or as a Windows Service; and the DLL with rundll32 Relay.dll,StartNodeRelay
.
Once the Relay has been downloaded and run on the target machine, a new Relay will appear in the C3 Dashboard.
Double-click it to see some metadata about the machine it’s running on.
To get this Relay to stage a Beacon payload, click Command Center > AddPeripheralBeacon > Send Command
.
If you have Slack notifications enabled, you will see messages being written and removed from the channel, both during the staging process and every time you issue a command in CS. The C3 Dashboard will also update to show a new Beacon node.
Now add the Fileshare C2 channel to our existing relay. Double-click the Slack relay > Command Center > AddNegotiationChannelUncShareFile
. The file share I’ve setup is at \\dc1\demo
. For this to work correctly, the fileshare relay will drop files into the share; and the slack relay will read those files and “relay” (d’uh) the data to the Gateway. However, the slack relay also needs to be able to delete the files been written by the fileshare relay. On a share with default inheritance, this can’t work since User A
, doesn’t have permission to remove files created by User B
.
This was fixed in an update, by making the fileshare relay explicity set the DACL on the files its creating so other users’ can remove them.
Now just download the Relay for this new channel in the same way (Double-click > Interface Options > New Relay
, and finally stage a new beacon as well.
Even though I’ve barely scrated the surface of C3, I find it to be a very exciting project.
The UI is well thought-out and easy to use. I think the visualisation layout should add the default labels to icons (like it does for the gateway). A few other tweaks may be required such as the Edit Config
boxes not updating its content. I wish the UI also provided more feedback at times - e.g. if you provide an incorrect IP/port for your Cobalt Strike Team Server, it doesn’t seem to tell you. And being able to rename relays would be a nice feature also.
The Wiki has some basic installation steps that could be fleshed out (but that’s also something that will come with time).
There are also some gotcha’s with the Cobalt Strike interface when dealing with Beacons this way. Particularly closing the Beacon and updating its jitter - these tasks are better done in C3. I also found that switching to the graph view disconects you from the CS client with the following error on the TS: [!] Trapped java.io.EOFException during client (192.168.1.117) read [Manage: rasta]: null
.
I’m not sure if I’ll have a stab at creating a custom channel, since C/C++ is really not my forte. But if this framework and External C2 in general is something you’re interested in, I would highly recommend checking this project out!
I want to keep playing with C3, so hopefully this is not the last post on the matter.