Jan 31 2022 | Cody Thomas

Mythic 2.3 — An Interface Reborn

Share

Mythic 2.3 — An Interface Reborn

New Mythic Search

Mythic started off as a proof of concept, open source project in 2018 with a small follow-on blog series for the process and the rationale of the various technologies and libraries used. However, there are a lot of different components involved in a project as complicated as a command and control (C2) framework, and requirements for projects change all the time. Since Mythic’s debut as a single Python3 application called Apfell to support macOS Red Teaming assessments, Mythic’s architecture evolved to a series of Docker services with a plug-n-play approach to new agents and communication profiles.

Not only has Mythic’s architecture evolved over time, but the use case for Mythic expanded too. Mythic is now used for Red Team operations, training classes, collegiate cyber defense competitions (CCDC), research, and more. As part of this, Mythic can’t just supply an interface for operators to use, but needs to provide the right user experience to allow operators to achieve their objectives more easily. This brings a new challenge to Mythic that wasn’t a big factor for the initial concept — scale.

Scaling

Scale for a command and control (C2) platform is interesting because it varies so greatly. One day might have a single new agent callback with only a handful of tasking, but another might have hundreds of new callbacks. Similarly, when you’re issuing tasking to an agent, one task might return just a few bytes (ex: shell whoami), but another might return megabytes worth of data (ex: ls C:WindowsSystem32). Even the resources given to Mythic can vary wildly depending on if it’s deployed in a large cloud node or in a small isolated virtual machine.

The scaling problem is easily solved for some C2 activities like agent file transfers by having agents chunk files for uploads and downloads. Downloading a 50B, 100MB, or 20GB file is no issue if the agent already chunks it into small pieces in each message. The harder problem is what to do for normal output to the user. What makes things even more complicated is when agents can return output multiple times for a single task (i.e. streaming output).

Some C2 platforms take a terminal-like approach to this issue; there’s some number of buffered lines that can be displayed and anything more than that gets rotated out and discarded. This has a nice benefit for performance because the total amount of text that needs to be rendered has a fixed maximum value. The downside to this comes with usability; specifically, when you run a task that outputs too much data, you now have to go somewhere else to find all of your output, such as to a log file.

Ok, so maybe you don’t want your operators to have to use your user interface and also search around in log files for the output. Mythic’s original solution to this was a simple one — aggregate output together with the task and make it collapsable. That way you can see all of the output grouped together, and when you collapse the task, all of the output is removed from the browser’s memory to help with performance.

Grouped Output With Tasking

What if the task still has a lot of data to display to the user? Once the task is expanded, the interface is flooded with data. The user interface can still get pretty slow trying to render all of that information. Mythic 2.3 takes this one step further and adds in pagination for the aggregated responses with tasks.

Paginated Output With Tasking

The paginated output helps when an agent sends back a lot of data (46 pages and 457 total responses in the above image), but what about if an agent sends back just one massive response? The tables Mythic uses for displaying agent responses aren’t normal tables — they’re virtualized. That means that the Mythic user interface doesn’t render all of the entries. Instead, it only renders the entries that are currently visible, and the other entries are completely removed from the document object model (DOM) of the web page. This allows Mythic to show and track large amounts of data as if it was just a small amount of data. Listing out Window’s System32 folder in the old user interface would freeze the entire interface for about 30 seconds and then render the data, but with the new features, this takes about 3 seconds.

New UI

When Mythic was originally created, there wasn’t a great separation of duties among the various Mythic components. As a result, the main back-end Mythic server was responsible for serving up the web interface, processing agent responses, authentication, websocket streaming, and providing the REST API endpoints for everything. On top of this, the user interface tried to leverage this lack of separation and embed data into web pages rather than fetch it later as needed. To accomplish this, the old interface used a Sanic Webserver, Jinja2 templating, Vue.js and JQuery for real time updates in the web interface. Not only is this clunky, but it makes it hard for others to help out.

Mythic is slowly spreading out all of its separate responsibilities into discrete Docker containers that all just perform one action. To help with this, the new user interface for Mythic 2.3 uses React and GraphQL to fetch data. This GraphQL service is provided by Hasura, a separate container that sits between the new user interface and the Postgres database to provide a GraphQL interface for fetching/updating/deleting data. This removes the need for the REST endpoints in the Mythic server and provides much greater flexibility for the data fetched. As shown in the screenshot below, GraphQL allows you to fetch information from the database in an SQL-like fashion while only getting the pieces of data that you need. Additionally, by moving to React, the new user interface is hosted by a separate Nginx container as a single page application (SPA).

GraphQL example query

Having the new user interface as a React-based SPA means that the various ‘web pages’ are all routed within the operator’s browser rather than by the Mythic server on the back-end. Additionally, by consolidating the various technologies used into just React means that the user interface is easier to understand and more easily updated by multiple people. Now the Mythic server’s responsibilities are just authentication and processing of agents messages. If you want to browse the new interface or contribute, the code is available in the MythicMeta organization on GitHub in the MythicReactUI repository.

New Browser Scripting

One of the things Mythic pushes for with agent development is to use structured input and structured output as much as possible. Structured data is much easier for computers to read than humans, but we can take that structured data and make something better. In Mythic’s case, this means things like download links, links to other parts of the Mythic interface, tables, buttons, and more. If you ever used Mythic and got back a table of data from a tasking, it’s because the structured output was transformed into something more easily readable. While the concept is staying the same, the technical details are changing in a more meaningful way in the new interface.

Instead of just transforming dictionaries and arrays into tables, the new interface and browser scripting allows you to go one step further and actually issue additional tasking like the following example for the list_entitlements command in poseidon.

BrowserScript Output

The browser script for this command looks through the entitlements returned for the currently running processes and searches for potentially interesting values. We can then disable or enable buttons based on if anything of note is found and provide additional follow-on actions like downloading the associated file or listing the contents of the folder.

All of these additional actions are determined by the agent developers and specified via their supported_ui_features attributes on commands. This allows developers to suggest additional commands related to the output (like stealing tokens of processes or setting new sacrificial job processes) and provide the necessary command parameters for them. Not only does this greatly increase the operational flow, but this is highly customizable to the agent you’re using.

The new browser script functionality is also greatly simplified from prior iterations in Mythic. No longer do you need to generate your own HTML and JavaScript that gets rendered. Instead, you simply return a dictionary describing what you want and Mythic will generate everything for you. Agent developers spend a lot of time working on their agents, so it’s important that everything in an agent’s operational flow feels as unique and customizable as possible.

All of this is only possible if two things happen though — the task has to return structured output in some form that is known ahead of time, and there has to be a browser script created to parse it. There are lots of cases though where you want some form of data parsing without knowing exact formats ahead of time. A common example of this might be interacting with another service’s web endpoint through Mythic that might return JSON output. It’s helpful to have this data collapsable and syntax highlightable. Mythic now provides this as the default view for plaintext data as seen below.

Collapsing JSON data

New Tasking Interface

There’s a tough balance when developing agents between keeping similar functionality grouped together in a command and keeping the available parameters under control. It’s easy to develop a command with lots of functionality that then has a bunch of conditional parameters based on what you’re trying to do. In the old Mythic, there was no way to deal with this. All of the parameters would be presented to the user in a popup modal and you needed descriptions that called out things like “only used if parameter X is true”. That’s pretty clunky, so the new user interface and command definitions for the agents provide parameter grouping.

Parameter grouping in Mythic is pulled from PowerShell’s implementation of parameter sets. Individual parameters are grouped into named sets and each set identifies if it’s a required parameter or not. Then, when typing out the tasking, Mythic can identify which parameter group you’re using and only display the appropriate parameters. This sounds confusing, so let’s take an example:

The new upload command for the apfell agent allows the following two scenarios:

  1. You select a file from your local computer and specify a remote path on the target. The file is upload to the Mythic server, then fetched down by the apfell agent in chunks and written to the specified location on disk.
  2. You specify a file already uploaded to Mythic and specify a remote path on the target. The agent fetches the already uploaded file in chunks and writes it to the specified location on disk.

So, we’d have three different parameters here: the remote path, the file to upload, or the name of the file that’s already been uploaded. It wouldn’t make sense to display all three parameters to the user at once because two of them are mutually exclusive. So, we create two parameter groups: a Default group that is what we expect most people to do (specify a remote path and select a file from their local computer) and a secondary group where people specify the remote path and a filename.

Upload `Default` Parameter Group

If a user types on the command line upload -filename bob and tries to submit it, Mythic automatically knows that this must belong to the second parameter set since the filename parameter was specified. Similarly, if the user typed upload -path /tmp/test then Mythic wouldn’t be able to identify the parameter group and prompts for more information or uses the Default group.

To further facilitate this, Mythic now offers tab-completion for parameters. This means you can type upload [tab] and mythic will automatically cycle through available parameters. This tab completion is also limited to the currently matched parameter groups, so if you type upload -filename bob [tab] then Mythic will only offer the path parameter since the other parameter doesn’t fit in the specified group. This means that Mythic’s command line tasking is now more in-line with a PowerShell feel that many operators are used to while still being customizable by the agent developers.

Overall

This release of Mythic marks the beginning of a transition period with a focus on user experience, developer experience, and performance. Mythic proved that the idea to provide a standard interface and framework where agent developers can focus on what they do best while enjoying a featured interface can work. However, there’s a difference between simply providing an interface with which operators can leverage the community agents and providing a good user experience in doing so. Similarly, Mythic is geared towards operators as much as it is towards agent developers, and as such, is improving the overall tasking and developing workflow.

Going Forward

Like many projects, Mythic continuously looks back at its current code base to see what can be refactored or updated. With the addition of the GraphQL container for the new user interface, the Mythic scripting PyPi package will update to use GraphQL instead of the old REST endpoints.

After the scripting is updated, the Mythic server will update. While Python3 has come a long way for asynchronous processing, Mythic still has some core libraries that weren’t built with that intention which leads to some instabilities and inconsistencies.

Throughout all of this, Mythic is going to focus on better development practices and community interaction. So, these scripting and server updates will come with unit tests, better documentation, and examples on how to contribute to the project.

As always, if you have comments, questions, concerns, or just want to chat about Mythic, please reach out either via Twitter at @its_a_feature_ or in the #Mythic channel in the BloodHound Slack channel.

Acknowledgements

I wanted to give callouts for Lee Christensen, Dwight Hohnstein, Russel Van Tuyl, Eli Miller, and everybody in the #Mythic channel in the BloodHound slack that have provided lots of feedback, testing, and suggestions for Mythic. Mythic wouldn’t be where it is today without the feedback and support from all of you.


Mythic 2.3 — An Interface Reborn was originally published in Posts By SpecterOps Team Members on Medium, where people are continuing the conversation by highlighting and responding to this story.