Learning from our Myths

It’s been almost a year since I published A Change of Mythic Proportions where Apfell was rebranded to Mythic. Since then, a lot has changed within Mythic, and with the upcoming Mythic 2.2 release, I wanted to take a moment to talk about some of the new features coming out and the direction Mythic is going.

For a full list of changes check out the change log and accompanying update process from Mythic 2.1 to 2.2.

It’s important to remember that Mythic tries to be an agent-agnostic platform geared towards making red team operations more user friendly, more OPSEC aware, and highly customizable. To achieve this, there are two main goals that we will address in this post: providing an easy way for custom agents to leverage a common user interface, and processing the data an agent sends back in a meaningful way. That second goal is particularly important — getting data back from your agent is a basic requirement for a command and control framework, but processing what the agent sends back to adapt and adjust future tasking automatically is really listening to your agents.


What does this mean if I’m an operator that uses Mythic?

With Mythic 2.2, the immediate thing to know is that the Mythic repository on GitHub no longer has any C2 Profile or Payload Type code. This is done for a variety of reasons called out later in this post, but you will need to run mythic-cli install github <url>to pull in agents from the MythicAgents organization and C2 profiles from the MythicC2Profiles organization. All of the Bash scripts that Mythic leveraged are now replaced with a single Golang binary called mythic-cli.Otherwise, your normal work-flow will be the same.

What does this mean if I develop agents for Mythic?

With Mythic 2.2, you have a lot more control over debugging, you can leverage your own message format instead of Mythic’s, you can control how tasking is displayed to users, and there’s a solidified peer-to-peer specification. You also have the ability to script custom operational security (OPSEC) controls that query Mythic’s database to make decisions on a per-task basis. You should check out the developer updates to see all the new features available to you.

Mythic User Interface Updates

For the first goal, there are already multiple agents developed by the community in a wide range of languages that leverage the current Mythic user interface. However, the current code for the user interface isn’t designed for easy developer collaboration, which means that people can’t easily add new features. To help with this and to bring Mythic’s user interface more to the modern era, we are slowly starting to port everything over to React with a GraphQL query schema. This is very much in alpha stages, but in Mythic 2.2 you can browse to /new/login and check out the changes. From this new interface, operators can generally issue tasks, but due to React, the current method of browser scripting won’t work.

Mythic leverages quite a few services now to support all the features it offers, and many of them open up ports on the host to provide communication between them. However, this doesn’t mean that we have to expose all of these ports externally. The following diagram shows a slightly different layout to Mythic’s normal operating model — specifically, Mythic now leverages an Nginx reverse proxy that you connect to which then forwards traffic to the appropriate back-end service (web server, documentation container, GraphQL queries, etc).

Mythic 2.2 Communication Flow

The new user interface isn’t simply a rewrite the old interface in React, but incorporates user feedback and tries to provide more quality of life features such as egress indicators and grouping of agents. The normal callbacks table now indicates if agents are egress (wifi symbol), if they’re p2p (link symbol), and if Mythic knows of a route from that agent to Mythic (green vs red). This gives a very quick glance to see if your agents can still get tasking. Similarly, if you try to task an agent that doesn’t have a route out, Mythic will give a warning notification that you should establish connectivity again first. If you click that C2 symbol, then you’ll get a popup dialog with an isolated graph just showing the callbacks associated with that egress path so that you don’t have to hunt down that agent in a larger graph view.

Active Callbacks TablePopup Dialog for Callback 35’s Broken Chain

Additionally, the grouping of agents by host allows an easy overview when there are a lot of callbacks to track. With this new grouping view, we can also toggle what we want to see — all callbacks, only active ones, all edges, only active ones, etc. We can even save the graph as a PNG for easy inclusion in reports. You’ll notice in the above screenshot that callback 35 (the top one) has a red link icon, and if we look at the graph view below, we can see that callback 35 is linked to callback 34 which no longer has that http connectivity to Mythic.

Graph View with Host Grouping

Operator updates

While passwords are salted and hashed within Mythic’s database, nothing was preventing somebody from trying to brute force a user’s password. This is a big issue, especially if you expose your web UI port to the internet without locking it down (you should always lock down your internet accessible ports). Not everybody takes the proper precautions though, so Mythic now has some safe guards about this:

  • All passwords must be at least 12 characters long
  • 10 incorrect password attempts in a row will lock an account and only a global Mythic admin can unlock it. When an account is locked, a warning message is sent to all operations.
  • Attempting to log in with an unknown username sends a warning message to all operations.
  • The initial account created with Mythic cannot be locked out in this way because otherwise you might not have a way to get back in. Instead, once this account gets 10 incorrect passwords attempts in a row, login attempts are throttled to one a minute and a warning message is sent to all operations about it.
  • You can no longer create accounts from the login screen. You have to be already logged in and do it through the Settings menu or via Mythic’s scripting capabilities.
Login Error Message Types

Event Notifications

Mythic has an event feed that it uses to send notifications and warnings to an operation about what’s going on. Operators can use this same event feed to send messages or keep operation-wide notes about what they’re finding. The most common warning notifications that operators or developers sees is about Mythic getting a message it doesn’t understand (typically from a port that’s too open and getting scanned) or Mythic getting a UUID that it doesn’t know (typically from an agent that’s still checking in after you wipe the database). In some cases, there can be a massive amount of these notifications and they can be in a very short time period (scanning is typically pretty fast and a low sleep time agent does a lot of checkins). Since Mythic alerts the user when this happens, we typically get flooded with messages that makes the user interface really hard to use. So, to help with this, Mythic now collapses like-events and simply increments a counter.

Incremented Count For Error Messages

Mythic Tasking Improvements

When it comes to tasking, there’s always a bit of a dichotomy — what’s easy for an operator to see and understand is hard for a computer to interpret and what’s easy for a computer to understand is hard for an operator to read. Mythic’s language of choice is JSON, which is normally easy for agents to understand and process, but is hard for operators to view. To help with this, Mythic allows commands to optionally support free-form text tasking (easy for an operator to issue and read), but then they typically have to parse this on the back-end into something that’s easier for the agent to pick up and use. If a command doesn’t support this sort of free-form text though, then you’re often left with a bunch of long JSON text strings in the user interface which clutters things and makes it hard to see what’s going on.

To help with this, Mythic now supports commands setting their own display_params. This has no bearing on agents, but provides a lot more useful information to operators. Let’s take an example of apfell’s spawn_drop_and_execute command which allows you to select a previously generated apfell payload, dynamically generate a new version, and then use that to kick off a new callback on your host. Normally, people see this in their tasking as spawn_drop_and_execute {"template": "uuid here"} which doesn’t give a lot of useful information since the back-end and the agent don’t need to know anything other than the original UUID. We can modify this now to be more expressive:

Display Param Modification

We can always go back through and view the history of parameters — what the operator initially sent, what is getting sent down to the agent, and what modified display view is presented to the operator. Take a tasking of sleep 10 3 for example:

All Parameters for a Task

Sub-tasking and completion hooks

Operators tend to do two things during operations: issue tasking and analyze responses. We can help with both of these situations via Mythic’s new sub-tasking and completion hook capabilities.

Tasks can issue subtasks dynamically based on the state of the operational environment (such as executing additional OPSEC tasks before doing something dangerous). The point of issuing a subtask though is to make a decision based on the output of that task. To help with this, tasks can issue completion hooks for subtasks, groups of subtasks, or themselves. From these completion hooks you’re able to use MythicRPC to query the database for information about what your task or your subtasks returned, files registered, files created, credential harvested, etc and act on it asynchronously.

Let’s take an example of doing a common PSExec command. Instead of baking everything needed for PSExec into a single command within your agent, you might want to split this out into the different components (copying files over and remote service creation). As an operator, you also probably do some additional checks during this process such as making sure that the remote port is accessible and that your context has the right permissions to both copy files and create services.

When a task issues sub-tasks, it enters a “delegating” state to indicate that it has child tasks waiting to complete. To help bring this scripting and dynamic tasking capability into the user interface, you can designate a command as “script_only” — these commands can’t get baked into an agent and never get sent down to an agent for processing, but instead act as server-side scripts to kick off sub-tasks, process responses, and coordinate with other systems.

Sub-taskingNew UI Sub-tasking

Understanding Agent Data

For the second goal of actually listening to what our agents tell us, we have to step back a bit. Historically, C2 frameworks were mainly a way for operators to run shell commands and get raw output back. While that’s useful in some scenarios, we can do better. We have more and more tooling that allows us to perform operational security (OPSEC) checks on a target before we take certain actions, and we spend a lot of time manually combing through verbose tooling output for situational awareness before deciding what to do next. While this is a step in the right direction, it’s still error prone and heavily dependent on the operator. How often does an operator really know the fine implementation details behind a command? Assuming that after all of that analysis an operator decides that command X in a tool should be fine to do without throwing up alarm bells, how often does that command update over time without the operator knowing? Or, to think about this another way, why do we expect the operator to investigate the command when the command can can just tell us about itself?

In the latest Mythic, we’ve expanded the data model and processing capabilities to be more expressive and provide dynamic OPSEC checks. Detailed information on how this is implemented can be found on the Mythic documentation website, but for this we’ll just go through an example.

In the apfell agent, when we run the shell command, we’re actually spawning /bin/sh -c <command>. Just like in the Windows world in many C2 frameworks, running the shell command spawns something like %comspec% /C <command>. While you should know that this is happening under the hood for your tooling, the command itself absolutely knows this. Knowing is one thing, but applying is another. When might this affect what you’re doing? One easy example might be that there are certain endpoint detection and response (EDR) products that flag on this sort of child process spawning with known flags from unsafe processes. If one of those products is running, what do you do? What if you’re on a team and you don’t notice your colleague running this command in a different callback that then leads to the team getting caught? In Mythic, we can block specific commands in agents from being tasked by certain operators, but that’s overly broad and hard to apply in a variety of situations.

This is where Mythic’s OPSEC scripting comes into play. For every command, you can implement an OPSEC class and functions that are executed before/after your command processes its tasking (but before your agent picks up the task). These functions know exactly how the command is implemented, so they know what might make a dangerous situation. To help make this determination, these functions can query Mythic’s database and either block your tasking or allow you through. You can even block a command from happening where only the lead of the operation can bypass the block and accept the risk. To help make this a bit clearer, the following video clip illustrates this scenario:

  1. We get a new callback from a host and want to run a shell command.
  2. The shell command knows that if Windows Defender is a running process, then your command will get logged and potentially generate alerts, so it queries Mythic’s database to see if we have process information for that host. Since this is a new callback on a new host, we don’t have that data. The OPSEC check blocks the action and says that the operator should instead run the list_apps command to gather that information first before trying again.
  3. The shell command leverages the new sub-task capability to automatically queue up the list_apps command to get some process data back on that host. The next steps are purposefully manual in the video so that you can see what’s going on, but the shell command could automatically process the results of the list_apps command and take action on that too.
  4. Since this block was only meant for operators, the current operator can submit the bypass request and continue the tasking life cycle — processing the free-form text input into JSON for the agent to more easily parse and passing control to another OPSEC check.
  5. Again, this OPSEC check queries the database to check for processes, which now exist. The check looks through those processes for the Windows Defender process, finds it, and blocks the command again. This “POST” check happens after the command is processed, but before the agent picks it up. This is a useful hook to allow OPSEC checks performed against payloads generated, files uploaded, and more.
  6. This time though, the block requires the operational lead to bypass it because we know for sure that the EDR product is running. The operation lead submits the bypass request on behalf of the user, and the agent is finally able to pick up and process the tasking.
<a href="https://medium.com/media/2e63734ad659ad80823b7e6e4d2bfc2f/href">https://medium.com/media/2e63734ad659ad80823b7e6e4d2bfc2f/href</a>

Your C2 framework should enable your operations, but to do that means that you need to listen to, process, and leverage the data that your agents send back.

Agent Message Freedom

On the topic of listening to your agents, it’s not necessarily easy for every agent to support a single C2 frameworks’ communication style. For example, Mythic uses JSON to send tasking down to agents and to process results back. Because JSON is a very well-defined format, it’s easy for Mythic to generate and for multiple agents to know how to process it. However, JSON isn’t available by default in all languages, such as C or C++. What do you do then? Similarly, since Mythic wants you to be able to develop your own agent in your own way, what if the message format you decide to use doesn’t quite match up with the format that Mythic uses? What if you want to use a different kind of encryption, key exchange, or staging mechanism than what Mythic has built-in?

To support this, Mythic 2.2 now has the idea of a Translation Container. These containers are very minimal, are tied to your agent, and do exactly what the name implies — translate. Their whole goal is to translate between Mythic’s JSON format and whatever format your agent uses. There are still some pieces of information that must exist even through the translation (such as the task’s identifier so that you can report back results), but the way that you handle it is completely up to you. Even if you don’t want to completely change the messaging schema, you can use this to make modifications to what actually gets sent down to your agent (such as swapping the command name with an enum index or adding/removing custom parameters).

Similarly, you might be fine with JSON and Mythic’s format in general, but you don’t want to deal with Mythic’s format for hooking into various components (keystrokes, credentials, file browser, process listing, etc). You still want Mythic to register and track this information, but maybe another format is easier for your agent to store that data. To support this, Mythic 2.2 has a special key for you to use in your normal response data called process_response. More details are in the linked documentation, but your tasking files can implement the process_response function and any data in that key from your agents gets passed to that function for processing. You can then use RPC calls back to Mythic to register that data, manipulate the data, send messages to operators, etc. This gives even more flexibility to agents.

Separation of Duties

Mythic is working towards complete separation of duties — each container and project is responsible for exactly one thing. To help facilitate this, the Mythic project no longer contains any Payload Types or C2 Profiles by default.

Because Mythic uses Docker containers for all of its services, the total sum of disk space required for everything can potentially get quite large. The more Docker layers that each image shares, the less space overall is taken, but this is still something to take into account, especially if all of your containers are on a single host. Over time, we found that by default people clone down Mythic and then simply start it. This means that every agent and c2 profile has its container built and started. That can be a large amount of space if you’re not expecting it (the poseidon container alone is 3.5GB so it can cross compile CGO with Linux and macOS). This is an unnecessary burden if the whole reason you are using Mythic is to use a single specific agent. Agents and C2 Profiles can be installed on demand via the mythic-cli install github <url> command. Let’s look at how all of the Mythic-based organizations and repositories are now set up:

  • Mythic — This is the main repository for the base Mythic install. This includes all of the services you need to leverage Mythic (the sever, database, RabbitMQ, etc).
  • MythicAgents — This organization houses the repositories for all of the Mythic agents that are publicly known. If you have your own Mythic agent that you’d like added to this organization, let us know. These repositories can be easily updated outside of the update cycle for the main Mythic repository. This makes it easier for developers to make incremental updates and feature additions as needed without it all getting lost in Git branches on the main Mythic repository.
  • MythicC2Profiles — This organization houses the repositories for all of the C2 Profiles that are publicly known and that are not tied to one specific agent. Similar to the MythicAgents organization, this separation makes it easier to update and keep track of available C2 Profiles. If you develop your own C2 profile that you’d like to make available for other agents to leverage, let us known and we can get it added here as well.
  • MythicMeta — This organization is for all of the things that are related to Mythic, but don’t need to be included in the main repository. This organization includes things like: the source code for all of the PyPi packages mythic leverages, the Dockerfiles used to create all of the Docker Images used by Mythic, agents, c2 profiles, and translation containers, and the code for the mythic-cli Golang binary used to control Mythic.

Having all of the components split out into their respective organizations and repositories makes it easier to maintain, update, and coordinate. This should also make it easier for developers to access source code for internal pieces and give operators a firmer grasp on what all they’re installing and why.


There are even more updates than what’s called out in this post, so please check out the updates page, the change-log, and a guide for updating your agents from Mythic 2.1 to Mythic 2.2.

Be sure to join the #mythic channel in the Bloodhound Slack. If you have questions/comments/concerns/requests/etc please put them in that slack channel, DM me, reach out over Twitter, or open an issue on GitHub. If you develop Mythic agents or C2 profiles that you’d like included in the Mythic* organizations, reach out and we can get that added too.

With Mythic’s UI moving towards React and GraphQL, we’ll hopefully be able to allow operators to create their own UI components, pages, and features that can be incorporated back into the main Mythic repository. While Mythic currently allows operators to create new UI components based on task responses, this will give operators control over the UI as a whole. As part of this, we’re trying to give operators/developers even more control over what their agents can do, how operators interact with them, and what that process looks like. We will also keep expanding the data model within Mythic to make it easier to track and analyze what agents report back so that you can make the most informed operational decisions possible.

Learning from our Myths was originally published in Posts By SpecterOps Team Members on Medium, where people are continuing the conversation by highlighting and responding to this story.

Article Link: https://posts.specterops.io/learning-from-our-myths-45a19ad4d077?source=rss----f05f8696e3cc---4