Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
This section talks about the different components for creating the server side docker container for a new C2 profile. This piece accepts messages from the agent, decodes them, forwards them off to the Mythic server, and replies back with the response. Specifically, this goes into the following components:
Docker containers
Configuration Files
Server
All C2 profiles are backed by a Docker container or intermediary layer of some sort.
What do the C2 docker containers do? Why are things broken out this way? In order to make things more modular within Mythic, most services are broken out into their own containers. When it comes to C2 profiles, they simply serve as an intermediary layer that translates between your special sauce C2 mechanism and the normal RESTful interface that Mythic uses. This allows you to create any number of completely disjoint C2 types without having to modify anything in the main Mythic codebase.
C2 Profile Contianers, like Payload Type and Translation Containers, all use the same mythic_base_container
Docker image. From there you can leverage the github.com/MythicMeta/MythicContainer
GoLang package or the mythic_container
PyPi package depending on if you want to write the meta information about your C2 profile in GoLang or Python. The actual code that binds to ports and accepts messages can be written in any language.
There are a few things needed to make a C2 container. For reference on general code structure options, check out 1. Payload Type Development. The general structure is the same for Payload Types, C2 Profiles, and Translation containers.
Instead of declaring a new class of Payload Type though, you declare a new class of type C2Profile. For example, in Python you can do:
The key here for a C2 Profile though is the server_binary_path
- this indicates what actually gets executed to start listening for agent messages. This can be whatever you want, in any language you want, but you just need to make sure it's executable and identified here. If you want this to pick up something from the environment (and it's a script), be sure to put it as a #!
at the top. For example, the default containers leverage python3, so they have #! /usr/bin/env python3
at the top. This file is always executed via bash, so as a sub-process like ./server
Your server
code *MUST* send an HTTP header of Mythic: ProfileNameHere
when connecting to Mythic. This allows Mythic to know which profile is connecting
Within the server_folder_path
should be a file called config.json
, this is what the operator is able to edit through the UI and should contain all of the configuration components. The one piece that doesn't need to be here are if the operator needs to add additional files (like SSL certs).
This is a small class that just defines the metadata aspects of the C2 profile. A big piece here is the definition of the parameters
array. Each element here is a C2ProfileParameter
class instance with a few possible arguments:
There are a few main values you can supply for parameter_type
when defining the parameters of your c2 profile:
String
- This is simply a text box where you can input a string value
ChooseOne
- this is a dropdown choice where the operator makes a choice from a pre-defined list of options
Array
- This allows an operator to input an array of values rather than a single string value. When this is processed on the back-end, it becomes a proper array value like ["val1", "val2"]
.
Date
- This is a date in the YYYY-MM-DD format. However, when specifying a default value for this, you simply supply an offset of the number of days from the current day. For example, to have a default value for the user be one year from the current date, the default_value would be 365
. Similarly, you can supply negative values and it'll be days in the past. This manifests as a date-picker option for the user when generating a payload.
Dictionary
- This one is a bit more complicated, but allows you to specify an arbitrary dictionary for the user to generate and allows you to set some restrictions on the data. Let's take a look at this one more closely:
This is saying that we have a Dictionary called "headers" with a few pre-set options.
Check Agent's Configuration Before Generation
Configuration checks are optional checks implemented by a C2 profile to alert the operator if they're generating an agent with a C2 configuration that doesn't match the current C2 docker services.
This check occurs every time an agent is generated, and this output is added to the payload's build_message
. Thus, an operator sees it when generating a payload, but it's always viewable again from the created payloads page.
The function is part of your C2 Profile's class definition, so it has access to your local config.json
file as well as the instance configuration from the agent.
This is a function operators can manually invoke for a payload to ask the payload's C2 profiles to generate a set of redirection rules for that payload. Nothing in Mythic knows more about a specific C2 profile than the C2 profile itself, so it makes sense that a C2 profile should be able to generate its own redirection rules for a given payload.
These redirection rules are up to the C2 Profile creators, but can include things like Apache mod_rewrite rules, Nginx configurations, and more.
Operationally, users can invoke this function from the created payloads page with a dropdown menu for the payload they're interested in. Functionally, this code lives in the class definition of your C2 Profile.
This function gets passed the same sort of information that the opsec check and configuration check functions get; namely, information about all of the payload's supplied c2 profile parameter values. This function can also access the C2 Profile's current configuration.
The format of the function is as follows:
There are times you want to host a file or payload through your C2 channel at a custom endpoint. However, every C2 profile works a bit differently; so, there needs to be a way to universally tell the C2 profile to host a file in its own way at a certain endpoint.
Below is the function definition to include with your C2 profile to host a custom file.
In the Mythic UI, you'll see blue globe icons that open prompts to host those files via any C2. For example, looking at the Payload's table and clicking the information icon you'll see something like the following:
Here you can see the blue globe icon. Click that and supply the endpoint you want to use, let's say /bob
along with the c2 profile you want to use. For our example, let's say http
, but it could be any egress profile that has this method implemented. The C2 profile should automatically stop and start to ingest the change, but if it doesn't, you might need to toggle the c2 profile off and on again from the payload types and c2 profiles page to make sure the change is picked up by the internal server. From there, you can hit the /bob
endpoint at that C2 profile to fetch the file.
Every C2 Profile should have a config.json
file located in their defined server_folder_path
which contains all the fields an operator would need to configure.
This is the main way for an operator to do configurations of the C2 profile without having to connect to the server where Mythic is running. This can be any format in reality, the name just has to stay the same.
Push C2 is a way to do egress C2 channels without requiring the agent to beacon periodically for tasking. Instead, a connection is held open and Mythic "Pushes" tasks and data down to the agent. This is very similar to how many Peer-to-peer (P2P) agents handle connections, just extended to the egress side of things.
For this to work, there needs to be a held open connection between the C2 Profile docker container and the Mythic server itself. This is done via gRPC. As part of this held open connection, the C2 profile identifies itself and forwards along messages.
In the mythic UI, the last checkin time will change to 1970-01-01 and appear as Streaming Now
. The moment the held open gRPC connection disconnects, that time will update to the current UTC time. This makes it very easy to know that a connection is currently held open even if no traffic is going through it.
Below are some simplified examples of working with this gRPC. An example of this Push style C2 as part of websockets is available with the websocket
C2 profile.
How is an agent supposed to work with a Push-style C2 profile? It's the same as working with a Peer-to-peer (P2P) profile:
If a payload is executed (it's not a callback yet), then reach out to the C2 profile to make a connection. Once a connection is established, start your normal encrypted key exchange or checkin process
If an existing callback loses connection for some reason, then reach out to the C2 profile to make a connection. Once a connection is established, send your checkin message again to inform Mythic of your existence
At this point, just wait for messages to come to you (no need to do a get_tasking poll) and as you get any data (socks, edges, alerts, responses, etc) just send them out through your c2 connection.
There are two types of Push C2. They both function largely the same, but within the C2 server they function differently.
The first one is probably the most common, one-to-one. This means that for every agent that connects to the C2 server (typically with a held-open connection like a WebSocket), the C2 server also opens one gRPC connection to Mythic. The following is a diagram what this means:
In this case, the C2 server connects via:
The first C2 server is just acting like a proxy in this case and forwards the agent's messages along.
The above snippet shows an example of sending a message from an agent - we specify the c2 profile name, the remote IP, and in this case, we're passing along the direct base64 blob from the agent. If you wanted to, depending on how your C2 functions, you could pass along a base64 decoded version in the Message
field instead.
Once Mythic is done processing a message, we can send the response back to the agent:
The other type of Push C2 is one-to-many. This functions largely the same as one-to-one, except that the C2 server only ever opens up one gRPC connection to Mythic with many agents that go through it. Below is a diagram showing this flow:
Because of this, we need to make sure that the user understands that all agents using this C2 profile are sharing a connection with Mythic. We start this connection by telling Mythic that our C2 profile wants to open a oneToMany stream:
Notice how the function call is StartPushC2StreamingOneToMany()
and not StartPushC2Streaming
like in the one-to-one example. Before we even get any agent connections, we send a message to the gRPC stream with just the c2 profile name. This let's Mythic know that we have a new one-to-many c2 profile running and the name of that connection.
At this point, everything works the same as before with the one-to-one profile. As agents send the profile messages, the c2 profile should forward them off to Mythic via the stream and read from the stream to send messages back. You're probably wondering though - how is the multiplexing happening between the one connection to Mythic and the many agents on the other side?
This stream utilizes a TrackingID
that's supplied by the C2 profile to track these individual streams:
This TrackingID
is something generated by the C2 profile when sending messages and is echoed back as part of the responses that Mythic sends. This allows a C2 Profile to do the proper correlation with messages it gets back and which agent to send it to. This data is saved and tracked by Mythic so that it can be used even when Mythic is the one sending the initial piece of data (like a new task). Let's look at a slightly more complete example to see how that works:
Here we have a version of the websocket profile - this version uses the one-to-many format. On initial WebSocket connection, we generate a UUID and save the UUID + connection information off into a global variable. We then send that data off into the gRPC stream.
When we get data back from the gRPC stream, we use the same TrackingID
that's echoed back to look up the corresponding WebSocket stream and send data that way.
There's one additional piece here that hasn't been covered yet though - AgentDisconnected
. Since all agents on the other side of this gRPC connection are sharing the one connection to Mythic, if that gRPC connection exits, then Mythic detects that and marks ALL agents that use that C2 profile has having lost that connection. However, what if just one agent on the other end disconnects? The main gRPC connection is still there, so we need a way to inform Mythic that one remote connection is gone. This is where the AgentDisconnected
piece comes into play:
By sending this message with the same TrackingID that the agent was using, Mythic can look up the corresponding Callback and mark it as no longer "Now Streaming...". If at any point the agent re-connects, then you can use a new TrackingID or even the same TrackingID and everything will connect right back up (assuming the agent sends a message like the checkin message) and the callback's last checkin will update back to "Now Streaming...".
Detecting an agent as gone is easy when we have a held open connection like a WebSocket. Sometimes though, it's not nearly as easy. This is up to you, the C2 developer. You could always leave the agents as "Now Streaming...", but that might be a little confusing for operators. Instead, you could have a timeout where if you haven't heard from a particular agent in a certain amount of time, mark them as gone. If they send a message again later, great, if not though, then at least the user has an idea that the agent might not be there anymore.
It's often useful to test your C2 redirector setup before your final deployment. It's also tough to know if there's an issue, if it could be with the agent, with a redirector, or with the C2 profile itself. Because of this, it can be very helpful for a C2 profile to generate a "sample message" that should fit all of the criteria based on an agent's configuration that you can either test configurations or even include in a report about how the C2 configuration works.
On the created payloads page, there's an actions dropdown button next to each payload. That dropdown will contain an option to generate a sample message. This request takes that agent's configuration and forwards it along to the C2 profile.
When creating payloads, Mythic will send a C2 Profile's parameters to the associated C2 Profile container for an "opsec check". This is a function that you can choose to write (or not) to look over the C2-specific parameter values that an operator selected to see if they pass your risk tolerance. This function is part of your C2 Profile's class definition:
In the end, the function is returning success or error for if the OPSEC check passed or not.
OPSEC checks for C2 profiles are executed every time a Payload is created. This means when an operator does it through the UI, when somebody scripts it out, and when a payload is automatically generated as part of tasking (such as for lateral movement or spawning new callbacks).
Since C2 profiles can vary pretty wildly, it's not always easy to know what potential indicators of compromise exist for any given c2, especially when you consider how it's modified for a very specific agent. The thing that would know best what kinds of IOCs exist for a given agent configuration for a C2 profile would be the C2 profile itse.f
The dropdown actions button for any payload will have an option to generate IOCs from the corresponding built-in C2 profiles.