Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
This describes how to report back p2p connection information to the server
This message type allows agents to report back new or removed connections between themselves or elsewhere within a p2p mesh. Mythic uses these messages to construct a graph of connectivity that's displayed to the user and for handling routing for messages through the mesh.
The agent message to Mythic has the following form:
Just like other post_response messages, this message has the same UUID and encryption requirements found in Agent Message Format. Some things to note about the fields:
edges
is an array of JSON objects describing the state of the connections that the agent is adding/removing. Each edge in this array has the following fields:
source
this is one end of the p2p connection (more often than not, this is the agent that's reporting this information)
destination
this is the other end of the p2p connection
metadata
is additional information about the connection that the agent wants to report. For example, when dealing with SMB bind pipes, this could contain information about the specific pipe name instances that are being used if they're being programmatically generated.
action
this indicates if the connection described above is to be added or removed from Mythic.
c2_profile
this indicates which c2 profile is used for the connection
After getting a message like this, Mythic responds with a message of the following form:
This is very similar to most other response messages from the Mythic server.
When an agent sends a message to Mythic with a delegate
component, Mythic will automatically add a route between the delegate and the agent that sent the message.
For example: If agentA is an egress agent sending messages to a C2 Docker container and it links to agentB, a p2p agent. There are now a few options:
If the p2p protocol involves sending messages back-and-forth between the two agents, then agentA can determine if agentB is a new payload, trying to stage, or a complete callback. When agentB is a complete callback, agentA can announce a new route to agentB.
When agentA sends a message to Mythic with a delegate message from agentB, Mythic will automatically create a route between the two agents.
This distinction is important due to how the p2p protocol might work. It could be the case that agentB never sends a get_tasking
request and simply waits for messages to it. In this case, agentA would have to do some sort of p2p comms with agentB to determine who it is so it can announce the route or start the staging process for agentB.
Actions are special messages that don't adhere to the normal message types that you see for the rest of the features in this section. There are only a handful of these messages:
Action: checkin - Initial checkin messages and key exchanges
Action: get_tasking - Getting tasking
Action: post_response - Sending tasking responses
Inside of this is where the features listed throughout this section appear
Agents can report back credentials they discover
The agent can report back multiple credentials in a single response. The credential_type
field represents the kind of credential and must be one of the following:
plaintext
certificate
hash
key
ticket
cookie
The other fields are pretty straightforward, but they must all be provided for each credential. There is one optional field that can be specified here: comment
. You can do this manually on the credentials page, but you can also add comments to every credential to provide a bit more context about it.
Download a file from the target to the Mythic server
This section is specifically for downloading a file from the agent to the Mythic server. Because these can be very large files that you task to download, a bit of processing needs to happen: the file needs to be chunked and routed through the agents.
In general, there's a few steps that happen for this process (visually this can be found on the Message Flow page):
The operator issues a task to download a file, such as download file.txt
This gets sent down to the agent in tasking, the agent locates the file, and determines it has the ability to read it. Now it needs to send the data back
The agent first gets the full path of the file so that it can return that data. That's a quality of life requirement so that operators don't need to supply the full path when specifying files, but so that Mythic can still properly track all files, especially ones that have the same name.
The agent then sends an initial message to the Mythic server indicating that it has a file to send. The agent specifies how many chunks it'll take, and which task this is for. If the agent specifies that there are -1
total chunks, then Mythic will expect at some point for the agent to return a total chunk count so that Mythic knows the transfer is over. This can be helpful when the agent isn't able to seek the file length ahead of time.
The Mythic server then registers that file in the database so it can be tracked. This results in a file UUID. The Mythic server sends back this UUID so that the agent and Mythic can make sure they're talking about the same file for the actual transfer.
The agent then starts chunking up the data and sending it chunk by chunk. Each message will have chunk_size amount of data base64 encoded, the file UUID from step 5, and which chunk number is being sent. Chunk numbers start at 1.
The Mythic server responds back with a successful acknowledgement that it received each chunk
It's not an extremely complex process, but it does require a bit more back-and-forth than a fire-and-forget style. This process allows Mythic to track how many chunks it'll take to download a file and how many have been downloaded so far. The rest of this page will walk through those steps with more concrete code examples.
When an agent is ready to transfer a file from agent to Mythic, it first needs to get the full_path
of the file and determine how many chunks it'll take to transfer the file. It then creates the following structure:
The host
field allows us to track if you're downloading files on the current host or remotely. If you leave this out or leave it blank (""
), then it'll automatically be populated with the callback's hostname. Because you can use this same process for downloading files and downloading screenshots from the remote endpoint in a chunked fashion, the is_screenshot
flag allows this distinction. This helps the UI track whether something should be shown in the screenshot pages or in the files pages. If this information is omitted, then the Mythic server assumes it's a file (i.e. is_screenshot
is assumed to be false
). This message is what's sent as an Action: post_response message.
What if you don't know the total number of chunks ahead of time? No worries - register as normal but for the total_chunks
field put a negative value. Later on, when you're sending chunks, you can add in your total_chunks
and Mythic will simply update it on the fly.
The full_path
can be reported in any of the chunks and is an optional value. For example, if you collected a screenshot into memory and want to "download" it to Mythic, then there is no full_path
to report back. In cases like this, you can specify a filename
value that might make more sense (ex: screenshot 1
, monitor 2
, lsass memory dump
, etc).
Mythic will respond with a file_id:
The agent sends back each chunk sequentially and calls out the file_id its referring to along with the actual chunked data.
The chunk_num
field is 1-based. So, the first chunk you send is "chunk_num": 1
.
"Why on earth is chunk_num
1 based", you might be wondering. It's a legacy situation from Mythic 1.0 where everything was written in Python without proper struct tracking. This meant Mythic was having to do a lot of guess work for if keys weren't there or if agents were simply supplying "empty" or null fields that they weren't using as part of a message. This made it tricky to determine if an agent was referring to chunk 0
or if they were simply setting that value to 0
because it wasn't being used (especially if a user tried to download a file that was 0 bytes in size). Starting real chunk data at 1
made it much easier to determine the scenario.
Since then, Mythic was rewritten in Golang with stronger type checking, structs, and a slightly modified struct structure to help with all of this. Now it's a legacy thing so that everybody's agents don't have a breaking change.
If your agent language is strongly typed or you need to supply all of the fields in every request, then for these additional file transfer messages, make sure the total_chunks
field is set to null
, otherwise Mythic will think you're trying to transfer another file.
For each chunk, Mythic will respond with a success message if all went well:
Once all of the chunks have arrived, the file will be downloadable from the Mythic server. Mythic can handle chunks out of order, but needs to know the chunk_size
first. The chunk_size
allows Mythic to seek the right spot in the file on disk before writing that chunk's data. chunk_size
is not the size of the current chunk, Mythic can determine that much, but rather the size of every chunk the agent will try to read at a time. The last chunk is most likely not going to be the same size as the other chunks; because of this, Mythic needs to know the size of chunks in general in case it gets the last chunk first.
Upload a file from Mythic to the target
This section is specifically for uploading a file from the Mythic server to an agent. Because these messages aren't just to an agent directly (they can be routed through a p2p mesh), they can't just be as simple as a GET request to download a file. The file needs to be chunked and routed through the agents. This isn't specific to the upload
command, this is for any command that wants to leverage a file from Mythic.
In general, there's a few steps that happen for this process (this can be seen visually on the Message Flow page):
The operator issues some sort of tasking that has a parameter type of "File". You'll notice this in the Mythic user interface because you'll always see a popup for you to supply a file from your file system.
Once you select a file and hit submit, Mythic loops through all of the files selected and registers them. This process sends each one down to Mythic, saves it off to disk, and assigns it a UUID. These UUIDs are what's stored in place of the raw bytes when you submit your task. So, if you had an upload command that takes a file and a path, your arguments would end up looking like {"file":"uuid", "path": "/some/path"}
rather than {"file": raw bytes of file, "path": "/some/path"}
.
In the Payload Type's corresponding command python file there is a function called create_tasking
that handles the processing of tasks from users before handing them off to the database to be fetched by an agent. If your agent supports chunking and transferring files that way, then you don't need to do anything else, but if your agent requires that you send down the entire file's contents as part of your parameters, you need to get the associated file.
To get the file with Mythic, there's an RPC call we can do:
Lines 2-5 is where we do an RPC call with Mythic to search for files, specifically, we want ones where the file_id matches the one that was passed down as part of our parameters. This should only return one result, but the result, for consistency, will always come back as an Array. So, file_resp.response
is an array of file information, we'll take the filename
from the first entry. We can use this to get the original filename back out from Mythic from the user's upload command. If there's something we want to modify about the file (such as adding a comment automatically or indicating that the file should be deleted from disk after the agent fetches it) we can use the update_file
RPC function to do so.
At this point, if you wanted to use the raw bytes of a file instead of the UUID as part of your tasking, your get_file
query should indicate get_contents=True
, then you can access the raw bytes via file_resp.response[0]["contents"]
. You can then swap out the contents of the parameter with task.args.add_arg("arg name", "base64 of file contents here")
.
If you want to register a NEW file with Mythic from the payload container that the user didn't first upload, you need to use the create_file
RPC call.
4. The agent gets the tasking and sees that there's a file UUID it needs to pull. It sends an initial message to Mythic saying that it will be downloading the file in chunks of a certain size and requests the first chunk. If the agent is going to be writing the file to disk (versus just pulling down the file into memory), then the agent should also send full_path
. This allows Mythic to track a new entry in the database with an associated task for uploads. The full_path
key lives at the same level as user_output
, total_chunks
, etc in the post_response
.
5. The Mythic server gets the request for the file, makes sure the file exists and belongs to this operation, then gets the first chunk of the file as specified by the agent's chunk_size
and also reports to the agent how many chunks there are.
6. The Agent can now use this information to request the rest of the chunks of the file.
The agent reporting back full_path
is what allows Mythic to track the file in the Files search page as a file that has been written to disk. If you don't report back a full_path
or have full_path
as an empty string, then Mythic thinks that the file transfer only lived in memory and didn't touch disk. This is separate from reporting that a file was written to disk as part of artifact tracking on the Reporting Artifacts page.
It's not an extremely complex process, but it does require a bit more back-and-forth than a fire-and-forget style. The rest of this page will walk through those steps with more concrete code examples.
There is no expectation when doing uploads or downloads that the operator must type the absolute path to a file, that's a bit of a strict requirement. Instead, Mythic allows operators to specify relative paths and has an option in the upload action to specify the actual full path (this option also exists for downloading files so that the absolute path can be returned for better tracking). This allows Mythic to properly track absolute file system paths that might have the same resulting file name without an extra burden on the operator.
Files can (optionally) be pulled down multiple times (if you set delete_after_fetch
as True
, then the file won't exist on disk after the first fetch and thus can't be re-used). This is to prevent bloating up the server with unnecessary files.
An agent pulling down a file to the target is similar to downloading a file from the target to Mythic. The agent makes the following request to Mythic:
The chunk_num
field is 1-based. So, the first chunk you request is "chunk_num": 1
.
The full_path
parameter is helpful for accurate tracking. This allows an operator to be in the /Temp
directory and simply call the upload function to the current directory, but allows Mythic to track the full path for easier reporting and deconfliction.
The full_path
parameter is only needed if the agent plans to write the file to disk. If the agent is pulling down a file to load into memory, then there's no need to report back a full_path
.
The agent gets back a message like:
This process repeats as many as times as is necessary to pull down all of the contents of the file.
If there is an error pulling down a file, the server will respond with as much information as possible and blank out the rest (i.e.: {'action': 'post_response', 'responses': [ {'total_chunks': 0, 'chunk_num': 0, 'chunk_data': '', 'file_id': '', 'task_id': '', 'status': 'error', 'error': 'some error message'} ] }
)
If the task_id was there in the request, but there was an error with some other piece, then the task_id will be present in the response with the right value.
There's a reason that files aren't base64 encoded and placed inside the initial tasking blobs. Keeping the files tracked by the main Mythic system and used in a separate call allows the initial messages to stay small and allows for the agent and C2 mechanism to potentially cache or limit the size of the transfers as desired.
Consider the case of using DNS as the C2 mechanism. If the file mentioned in this section was sent through this channel, then the traffic would potentially explode. However, having the option for the agent to pull the file via HTTP or some other mechanism gives greater flexibility and granular control over how the C2 communications flow.
Agent reports new artifacts created on the system or network
Any command is able to reply with its own artifacts that are created along the way. The following response can be returned as a separate C2 message or as part of the command's normal output.
The following response is part of the normal agent response. So, it is base64 encoded and put in the normal response format
Agents can report back their own artifacts they create at any time. They just include an artifacts
keyword with an array of the artifacts. There are two components to this:
base_artifact
is the type of base artifact being reported. If this base_artifact type isn't already captured in the "Global Configurations" -> "Artifact Types" page, then this base_artifact
value will be created.
artifact
is the actual artifact being created. This is a free-form field.
Artifacts created this way will be tracked in Artifacts page (click the fingerprint icon at the top)
Unified process listing across multiple callbacks for a single host
The supported ui features flag on the command that does this tasking needs to have the following set:supported_ui_features = ["process_browser:list"]
if you want to be able to issue a process listing from the UI process listing table. If you don't care about that, then you don't need that feature set for your command.
There are many instances where you might have multiple agents running on a single host and you run common tasking like process lists over and over and over again. You often do this because the tasking has scrolled out of view, maybe it's just stale information, or maybe you couldn't quite remember which callback actually had that task. This is where the unified process listing comes into play.
With a special format for process listing, Mythic can track all the different process lists together for a single host. It doesn't matter which host you ran the task on, as long as you pull up the process_list view for that host, all of the tasks will be available and viewable.
Naturally, this has a special format for us to make it the most useful. Like almost everything else in Mythic, this requires structured output for each process we want the following:
All that's needed is an array of all the processes with the above information in the processes
field of your post_response
action. That allows Mythic to create a process hierarchy (if you supply both process_id
and parent_process_id
) and a sortable/filterable table of processes. The above example shows a post_response
with one response in it. That one response has a processes
field with an array of processes it wants to report.
Any field that ends with _time
expects the value to be an int64 of unix epoch time in milliseconds. You're welcome to supply any additional field you want about a process - it all gets aggregated together and provided as part of the "metadata" for the process that you can view in the UI in a nice table listing.
For example, a macOS agent might report back signing flags and entitlements and a windows agent might report back integrity level and user session id.
All additional buttons through the Process Browser UI (such as task inject, task kill, etc) have their own supported ui features: process_browser:inject
, process_browser:kill
, process_browser:list_tokens
, process_browser:steal_token
. All of these will get three parameters passed to them for tasking:
host
process_id
architecture
For example, {"host": "ABC.COM", "process_id": 1234, "architecture": "x64"}
. Your commands that support these features will need to expect and process these arguments.
For the file browser, there are a few capabilities that need to be present and implemented correctly if you want to allow users to list, remove, download, or upload from the file browser. Specifically:
File Listing - there needs to be a command marked as supported_ui_features = ["file_browser:list"]
with your payload type that sends information in the proper format.
File Removal - there needs to be a command marked as supported_ui_features = ["file_browser:remove"]
with your payload type that reports back success properly
File Download - there needs to be a command marked as supported_ui_features = ["file_browser:download"]
with your payload type
File Upload - there needs to be a command marked as supported_ui_features = ["file_browser:upload"]
with your payload type
These components together allow an operator to browse the file system, request listings of new directories, track downloaded files, upload new files, and even remove files. Let's go into each of the components and see what they need to do specifically.
There are two components to file listing that need to be handled - what the file browser sends as initial tasking to the command marked as supported_ui_features = ["file_browser:list"]
and what data is sent back to Mythic for processing.
When doing a file listing via the file browser, the command_line for tasking will always be the following as as JSON string (this is what gets sent as the self.command_line
argument in your command's parse_arguments
function):
This might be different than the normal parameters you take for the command marked as supported_ui_features = ["file_browser:list"]
. Since the payload type's command handles the processing of arguments itself, we can handle this case and transform the parameters as needed. For example, the apfell
payload takes a single parameter path
as an argument for file listing, but that doesn't match up with what the file browser sends. So, we can modify it within the async def parse_arguments
function:
In the above example we check if we are given a JSON string or not by checking that the self.command_line
length is greater than 0 and that the first character is a {
. We can then parse it into a Python dictionary and check for the two cases. If we're given something with host
in it, then it must come from the file browser instead of the operator normally, so we take the supplied parameters and add them to what the command normally needs. In this case, since we only have the one argument path
, we take the path
and file
variables from the file browser dictionary and combine them for our path variable.
Now that we know how to translate file browsing file listing tasking to whatever our command needs, what kind of output do we need to send back?
We have another component to the post_response
for agents.
As a shortcut, if the file you're removing is on the same host as your callback, then you can omit the host
field or set it to ""
and Mythic will automatically add in your callback's host information instead.
If you're listing out the top level folder (/
on linux/macOS or a drive like C:\
on Windows, then the parent path should be "" or null.
Most of this is pretty self-explanatory, but there are some nuances. Only list out the inner files for the initial folder/file listed (i.e. don't recursively do this listing). For the files
array, you don't need to include host
or parent_path
because those are both inferred based on the info outside the files
array, and the success
flag won't be included since you haven't tried to actually list out the contents of any sub-folders. The permissions JSON blob allows you to include any additional information you want to show the user in the file browser. For example, with the apfell
agent, this blob includes information about extended attributes, posix file permissions, and user/group information. Because this is heavily OS specific, there's no requirement here other than it being a JSON blob (not a string).
By having this information in another component within the responses array, you can display any information to the user that you want without being forced to also display this listing each time to the user. You can if you want, but it's not required. If you wanted to do that, you could simply turn all of the file_browser
data into a JSON string and put it in the user_output
field. In the above example, the user output is a simple message stating why the tasking was issued, but it could be anything (even left blank).
Mythic doesn't currently support .
, ..
, or ~
paths. Any information about .
should be part of the main file_browser
JSON data (not part of the files
array). ~
should be fixed to an absolute path.
There's a special key in there that doesn't really match the rest of the normal "file" data in that file_browser response - update_deleted
. If you include this key as True
and your success
flag is True
, then Mythic will use the data presented here to update which files are deleted.
By default, if you list the contents of ~/Downloads
twice, then the view you see in the UI is a merge of all the data from those two instance of listing that folder. However, that might not always be what you want. For instance, if a file was deleted between the first and second listing, that deletion won't be reflected in the UI because the data is simply merged together. If you want that delete to be automatically picked up and reported as a deleted file, use the update_deleted
flag to say to Mythic "hey, this should be everything that's in the folder, if you have something else that used to be there but I'm not reporting back right now, assume it's deleted".
You might be wondering why this isn't just the default behavior for listing files. There are two main other scenarios that we want to support that are counter to this idea - paginated results (only return 20 files at a time) and filtered results (only return files in the folder that end in .txt). In these cases, we don't want the rest of the data to be automatically marked as deleted because we're clearly not returning the full picture of what's in a folder. That's why it's an optional flag to say to performing the automatic updates. If you want to be explicit with things though (for example, if you delete a file and want to report it back without having to re-list the entire contents of the directory), you can use the next section - FIle Removal.
There are two components to file listing that need to be handled - what the file browser sends as initial tasking to the command marked as supported_ui_features = ["file_browser:remove"]
and what data is sent back to Mythic for processing.
This is the exact same as the supported_ui_features = ["file_browser:list"]
and the File Browser section above.
Ok, so we listed files and tasked one for removal. Now, how is that removed file tracked back to the file browsing to mark it as removed? Nothing too crazy, there's another field in the post_response
:
This removed_files
section simply returns an array of dictionaries that spell out the host and paths of the files that were deleted. On the back-end, Mythic takes these two pieces of information and searches the file browsing data to see if there's a matching path
for the specified host
in the current operation that it knows about. If there is, it gets marked as deleted
and in the UI you'll see a small trashcan next to it along with a strikethrough.
This response isn't ONLY for when a file is removed through the file browser though. You can return this from your normal removal commands as well and if there happens to be a matching file in the browser, it'll get marked as removed. This allows you to simply type things like rm /path/to/file
on the command-line and still have this information tracked in the file browser without requiring you to remove it through the file browser specifically.
There are two components to file listing that need to be handled - what the file browser sends as initial tasking to the command marked as supported_ui_features = ["file_browser:download"]
and what data is sent back to Mythic for processing.
This is the exact same as the supported_ui_features = ["file_browser:list"]
and the File Browser section above.
There's nothing special here outside of normal file download processes described in the Download section. When a new file is tracked within Mythic, there's a "host" field in addition to the full_path
that is reported. This information is used to look up if there's any matching browser objects and if so, they're linked together.
Having a command marked as supported_ui_features = ["file_browser:upload"]
will cause that command's parameters to pop-up and allow the operator to supply in the normal file upload information. To see this information reflected in the file browser, the user will then need to issue a new file listing.
This page discusses how to register that commands are loaded/unloaded in a callback
It's a common feature for agents to be able to load new functionality. Even within Mythic, you can create agents that only start off with a base set of commands and more are loaded in later. Using the commands
keyword, agents can report back that commands are added ("action": "add"
) or removed ("action": "remove"
).
This is easily visible when interacting with an agent. When you start typing a command, you'll see an autocomplete list appear above the command prompt with a list of commands that include what you've typed so far. When you load a new command and register it back with mythic in this way, that new command will also appear in that autocomplete list.
If you're curious how SOCKS works within Mythic and how you can hook into it with an agent, check out the SOCKS section for coding.
All of the following features describe information that can be included in responses. These sections describe some additional JSON formats and data that can be used to have your responses be tracked within Mythic or cause the creation of additional elements within Mythic (such as files, credentials, artifacts, etc).
You can hook multiple features in a single response because they're all unique. For example, to display something to the user, it should be in the user_output
field, such as:
When we talk about Hooking Features
in the Action: post_response message of an agent, we're really talking about a specific set of Dictionary key value pairs that have special meaning. All responses from the agent to the Mythic server already have to be in a structured format. Each of the following sections goes into what their reserved keywords mean, but some simpler ones are:
task_id - string - UUID associated with tasks
user_output - string - used with any command to display information back to the user
completed - boolean - used with any command to indicate that the task is done (switches to the green completed icon)
status - string - used to indicate that a command is not only done, but has encountered an error or some other status to return to the user
process_response - this is passed to your command's python file for processing in the process_response
function.
As you're developing an agent to hook into these features, it's helpful to know where to look if you have questions. All of the Task, Command, and Parameter definitions/functions available to you are defined in the mythic_container
PyPi package, which is hosted on the MythicMeta Organization on GitHub. Information about the Payload Type itself (BuildResponse, SupportedOS, BuildParameters, PayloadType, etc) can be found in the PayloadBuilder.py file in the same PyPi repo.
Throughout this section, the payload type development section, and the c2 message format sections, you'll see a lot of information about message structure. Here is a quick "cheat sheet" reference guide with links to the appropriate sections for more information. The following is an example of a get_tasking
request to Mythic with almost every possible field added:
RemovedFiles
Sometimes you want your agent to be able to bring something to the operator's attention. Your task might return an error, but if it's a long-running task, then who knows if the operator is actively looking at the task.
Using the alerts
keyword, agents can sent alert messages to Mythic's operation event log. There are two ways you can do this:
As part of a task, you can use another keyword, alerts
to send the following structure:
You can send multiple alerts at once since it's an array.
The source
field doesn't get displayed to the user, but it is used to collapse like-messages within the UI. If the user has an alert that's not resolved with a source of "bob", then the next message with a source of "bob" will NOT be displayed, but will instead simply increase the count of the current message. If the user resolves the message with a source of "bob" and a new one comes in, then that message WILL be displayed. This is to help prevent users from getting flooded with messages.
The "squishing" of alert messages only happens in the UI - if you have a webhook container and are listening for alerts, you will get all of the messages. The basic_webhook
container has code in it to throttle alert messages of the same source though that they must be a minute apart (again, to help prevent spam to users)
Sometimes you have other aspects to your agent that might be monitoring or running tasks and you want to report that data back to the user, but you don't have a specific task to associate it with. In that case, you can do the exact same alerts
structure, but at a higher level in the structure:
The base format is an alert
to display in the UI and the source
of the alert. This can be extended though to provide more customization:
This extended format has a few more fields:
send_webhook
- normally, if the level
is not provided or is warning
then an alert
webhook is sent. However, if you set the level to something like info
or debug
, then you can optionally still force a webhook with this flag
level
- this identifies how alert
is presented to the user. warning
is the default and will show a warning error as well as put a warning notification in the event log (increasing the warning count in the UI by 1). info
is similar - it displays an informative message in the UI to the user and adds a message to the event log, but it isn't a warning message that needs to be addressed. debug
will allow you to send a message to the event log, but it will not display a toast notification to the user.
alert
- your normal string message that's displayed to the user and put in the event log
webhook_alert
- optional dictionary data that doesn't get displayed to the user or put in the operation event log, but is instead sent along as custom data to the custom_webhook
webhook for additional processing. Specifically, the data sent to the custom_webhook
is as follows:
You probably noticed as you used Mythic that there's a status associated with your task. This status goes through a variety of words/colors depending on where things are in the pipeline and what the agent has done with the task. This provides a way for the operator to know what's happening behind the scenes.
By default, a task goes through the following stages with the following statuses:
preprocessing
- The task is being sent to the Payload Type container for processing (parsing arguments, confirming values, doing RPC functionality to register files, etc)
submitted
- The task is now ready for an agent to pick it up
processing
- The task has been picked up by an agent, but there hasn't been any response back yet
processed
- The task has at least one response, but the agent hasn't marked it as done yet.
completed
- The task is marked as completed by the agent and there wasn't an error in execution
error:*
- The agent report back a status of "error"
The agent can set the status of the task to anything it wants as part of its normal post_response
information. Similarly, in a task's create_tasking
function, you're free to set the task.status
value. Anything of the error:*
format will show up as red in the Mythic UI as an error for the user.
If you're curious how reverse port forwards work within Mythic and how you can hook into them with an agent, check out the section on RPFWD development.
Keystrokes are sent back from the agent to the Mythic server
Agents can report back keystrokes at any time. There are three components to a keystroke report:
user
- the user that is being keylogged
window_title
- the title of the window to which the keystrokes belong
keystrokes
- the actual recorded keystrokes
Having the information broken out into these separate pieces allows Mythic to do grouping based on the user and window_title for easier readability.
If the agent doesn't know the user or the window_title fields, they should still be included, but can be empty strings. If empty strings are reported for either of these two fields, they will be replaced with "UNKNOWN" in Mythic.
What happens if you need to send keystrokes for multiple users/windows?
Token awareness and Token tasking
Mythic supports Windows tokens in two ways: tracking which tokens are viewable and tracking which tokens are usable by the callback. The difference here, besides how they show up in the Mythic interface, is what the agent can do with the tokens. The idea is that you can list out all tokens on a computer, but that doesn't mean that the agent has a handle to the token for use with tasking.
As part of the normal responses sent back from the agent, there's an additional key you can supply, tokens
that is a list of token objects that can be stored in the Mythic dat