Part 3: Doing a Task
Mythic request types (checkin
& get_tasking
& post_response
)
Getting Tasks
Now that we performed an initial check-in to the teamserver, we (the agent) need to continuously check for new tasks to execute. In Mythic this can be done with a get_tasking
request. The format for this style of request looks like this:
Base64( CallbackUUID + JSON(
{
"action": "get_tasking",
"tasking_size": 1, //indicate the maximum number of tasks you want back
//if passing on messages for other agents, include the following
"delegates": [
{"message": agentMessage, "c2_profile": "ProfileName", "uuid": "uuid here"},
{"message": agentMessage, "c2_profile": "ProfileName", "uuid": "uuid here"}
],
"get_delegate_tasks": true, //optional, defaults to true
}
)
)
We don’t need to worry about the delegates section for now since we are just asking for basic tasks.
The data gets parsed by the translation container and sent to the get_tasking_to_mythic_format()
function which returns JSON to the Mythic server. It just contains the number of tasks the agent wants from the server.
def get_tasking_to_mythic_format(data):
"""
Parse get_tasking message from Agent and return JSON in Mythic format.
"""
numTasks = int.from_bytes(data[0:4])
mythic_json = {
"action": "get_tasking",
"tasking_size": numTasks
}
return mythic_json
The response from the Mythic server gets sent again through our translation container and the data forwarded to our get_tasking_to_agent_format()
function. This function packs task data in the following order:
get_tasking_hex_byte + (task_hex_byte + task_uuid + task_arguments)
If there were any tasks submitted from the operator, then the function packs the data and sends the response to the agent.
Post Response
The post_response
request type can be thought of as submitting results from an agent task. This request is formatted as follows.
Base64( CallbackUUID + JSON(
{
"action": "post_response",
"responses": [
{
"task_id": "uuid of task",
... response message (see below)
},
{
"task_id": "uuid of task",
... response message (see below)
}
], //if we were passing messages on behalf of other agents
"delegates": [
{"message": agentMessage, "c2_profile": "ProfileName", "uuid": "uuid here"},
{"message": agentMessage, "c2_profile": "ProfileName", "uuid": "uuid here"}
]
}
)
)
Again, I’m not worried about delegates just yet, that's for more advanced agent functionality like pivots or links.
So the specific task that was executed will either send a package Success or Error message. I decided to indicate the type of response at the end of the package with a single byte (0x99
or 0x95
).
This seems to work okay for now, but I’m totally open to suggestions.
Therefore, the post_response
packet coming from the agent will look like this.
# Successful
callback_uuid + (post_repsonse_hex_byte + task_id + (size_of_task_data + task_data) + success_byte)
# Error
callback_uuid + (post_repsonse_hex_byte + task_id + win_error_code + error_byte)
My translation container function post_response_to_mythic_format()
accepts this binary packet and extracts out the information to assemble the JSON that Mythic expects above. If the Status Byte indicates an error from the agent, then it will lookup the 32 bit integer for the Windows error code against a dictionary and return that output to the operator console.
if status == "error":
error = ERROR_CODES.get(error_code, {"name": "UNKNOWN_ERROR", "description": f"Error code {error_code}"})
user_output += f"[!] {error['name']} : {error['description']}\\n"
In most cases I don’t care about the Mythic response from a post_response
requests. The agent sends the results from a task in a post_response
, the translation container converts it, and Mythic displays the output to the operator.
Finally, using the translation container I added a default user_output
string that is similar to Cobalt Strike because it makes me feel cool.
Here is example output from the pwd
command.

Next Steps
At this point I had built out the foundation to be able to request and execute tasks. One of my main goals when starting this project was to support the Httpx C2 profile to allow for malleable C2 profiles. In the next blog I will discuss the implementing this.