C2 Related Development

C2 Structure

Command and Control (C2) profiles are a little different in Mythic than you might be used to. Specifically, C2 profiles live in their own docker containers and act as a translation mechanism between whatever your special sauce C2 protocol/format/encryption is and what the back-end Mythic server understands (HTTP + JSON).

By defining a C2 protocol specification, other payload types can register that they speak that C2 protocol as well and easily hook in without having to do back-end changes. By having C2 protocols divorced from the main Mythic server, you can create entirely new C2 protocols more easily and you can do them in whatever language you want. If you want to do all your work in Golang, C#, or some other language for the C2 protocol, go for it. It's all encapsulated in the C2's Docker container with whatever environment you desire.

Since there's so much variability possible within a C2 Docker container, there's some required structure and python files similar to how Payload Types are structured. This is covered in C2 Profile Code.

C2 / Mythic Comms

Every C2 Docker container has an environment variable, MYTHIC_ADDRESS which points to https://127.0.0.1:7443 by default. This information is pulled from the main /Mythic/mythic-docker/config.json file. So, if you change Mythic's main UI to HTTP on port 7444, then each C2 Docker container's MYTHIC_ADDRESS environment variable will have the value http://127.0.0.1:7444. This allows your code within the docker container to always know where to forward requests so that the main Mythic server can process them.

C2 RPC

C2 Docker containers allow RPC calls to them from other containers. This gives even more flexibility to C2 containers since they can do some of the pieces of the Mythic server for themselves. Specifically, there's two directions of C2 RPC calls: C2 Docker -> Mythic and Payload Type Docker -> C2 Docker.

C2 Docker -> Mythic RPC

The C2 Docker container can make the following requests to the Mythic server:

  • get_tasking - Given a callback's UUID, use that to check if there's any tasking for the callback or any callback that's routable through that callback.

    • parameters are:

      • uuid - the UUID of the callback you're interested in

      • tasking_size - the number of tasks you want at a time. Defaults to 1, but if you specify -1 you'll get all that are in the submitted state.

      • raw_response - Boolean flag if you want mythic to return the raw response for you or try to encrypt/encode it

  • add_route - Add a route from one callback to another callback. This is what illustrates to Mythic and in the UI what P2P connections exist.

    • parameters are:

      • source - The source of the connection as a callback UUID

      • destination - the destination of the connection as a callback UUID

      • direction - the direction of the connection. Defaults to 1 (source to destination), but can also be 2 (destination to source).

      • metadata - string of any additional metadata you might want to store or share about the connection. This data must match when you go to remove the route!

  • remove_route - Remove a route from one callback to another callback.

    • parameters are:

      • source - The source of the connection as a callback UUID

      • destination - the destination of the connection as a callback UUID

      • direction - the direction of the connection.

      • metadata - string of any additional metadata you might want to store or share about the connection. This data must match what was set when the connection was added!

  • get_callback_info - Get all the information related to the callback

    • parameters are:

      • uuid - The callback UUID you're interested in

  • update_callback_info - Update information related to a callback

    • parameters are:

      • uuid - the callback UUID you're interested in

      • info - a dictionary of updated values you want to set for the callback (this can include encryption information as well).

  • add_event_message - Send a message to the Mythic event log

    • parameters are:

      • level - what level of severity is the message ("info" or "warning")

      • message - what is the actual message you want to send

  • get_encryption_data - Get the current encryption data for a callback if a callback UUID is specified, or get all of the C2 profile parameters associated with the payload if a payload UUID is specified

    • parameters are:

      • uuid - the UUID we're interested in (can be callback UUID or payload UUID)

      • profile - the name of your c2 profile. This allows Mythic to look up the c2 profile parameters associated with your profile.

  • encrypt_bytes - The C2 profile gives Mythic some bytes and a callback UUID and asks Mythic to encrypt them

    • parameters are:

      • uuid - the UUID for the callback we're interested in

      • data - the raw bytes that we're wanting to encrypt

      • with_uuid - this indicates if the resulting data should be with_uuid (i.e. base64(uuid+encrypted_blob) or just base64(encrypted_blob).

  • decrypt_bytes - The C2 profile gives Mythic some bytes and a callback UUID and asks Mythic to decrypt them

    • parameters are:

      • uuid - the UUID for the callback we're interested in

      • data - the raw bytes of the data we're wanting to decrypt (if this is a message that already has the UUID prepended to the front, leave it there)

      • with_uuid - this indicates if the data portion has the UUID included or if the C2 profile is simply passing in raw bytes to decrypt. The scenario here is that a C2 profile could get a normal Mythic message, pass it off to get decrypted, then manipulate the data as needed.

These functions are available from within the C2 docker container via Python (see the HTTP C2 server file as an example). These functions can be useful if you want to implement your own C2 protocol instead of JSON, but you will have to translate to/from JSON and Mythic's format at some point to interact with Mythic.

Payload Type Docker -> C2 Docker

This one is a little less intuitive than the C2 Docker container directly reaching out to the Mythic server for functionality. This functionality allows tasking as an operator to directly manipulate a C2 component. This functionality has no "default" functions, it's all based on the C2 profile itself. Here is an example of a simple RPC function from a Payload Type Docker container -> C2 Docker container:

from C2ProfileBase import *
from MythicCallbackRPC import *
# request is a dictionary: {"action": func_name, "message": "the input", "task_id": task id num}
# must return an RPCResponse() object and set .status to an instance of RPCStatus and response to str of message
async def test(request):
response = RPCResponse()
response.status = RPCStatus.Success
response.response = "hello"
return response

Within the C2 profile's mythic/c2_functions folder can be any number of python files that have defined functions. These functions are automatically imported and available for RPC when the container starts. In this case, a remote container can call this C2's test function.