Payload Type Info
Payload Type information must be set and pulled from python within the associated /Mythic/Payload_Types/[agent name]/mythic/agent_functions
folder. The filename can be anything, but the contents should look like:
There are a couple key pieces of information here:
Line 1 imports all of basic classes needed for creating an agent
line 4 defines the new class (our agent). This can be called whatever you want, but the important piece is that it extends the
PayloadType
class as shown with the()
.Lines 6-24 defines the parameters for the payload type that you'd see throughout the UI.
the name is the name of the payload type
supported_os is an array of supported OS versions
supports_dynamic_loading indicates if the agent allows you to select only a subset of commands when creating an agent or not
build_parameters is a dictionary describing all build parameters when creating an agent
the "key" here and the "name" in the BuildParameter() class must match.
c2_profiles is an array of c2 profile names that the agent supports
support_browser_scripts is a list of browser script names and authors that are callable from all other scripts in your payload type
if the script is called "create_table", then in this payload type's browser_scripts folder, there needs to be a
create_table.js
file with the code for that browser script. This script and information will then be available in the UI.
The last piece is the function that's called to build the agent based on all of the information the user provides from the web UI.
The PayloadType
base class is in the PayloadBuilder.py
file. This is an abstract class, so your instance needs to provide values for all these fields.
Build Parameters
Build parameters define the components shown to the user when creating a payload.
The BuildParameter
class has a couple of pieces of information that you can use to customize and validate the parameters supplied to your build:
name is the name of the parameter, if you don't provide a longer description, then this is what's presented to the user when building your payload
parameter_type describes what is presented to the user - a String or a ChooseOne dropdown list
required indicates if there must be a value supplied. If no value is supplied by the user and no default value supplied here, then an exception is thrown before execution gets to the
build
function.verifier_regex is a regex the web UI can use to provide some information to the user about if they're providing a valid value or not
default_value is the default value used for building if the user doesn't supply anything
choices is where you can supply an array of options for the user to pick from if the parameter_type is ChooseOne
value is the component you access when building your payload - this is the final value (either the default value or the value the user supplied)
verifier_func is a function you can provide for additional checks on the value the user supplies to make sure it's what you want. This function should either return nothing or raise an exception if something isn't right
As a recap, where does this come into play? In the first section, we showed a section like:
Just make sure the key (i.e. "version" or "arch" here) matches the name in the BuildParameter
Building
You have to implement the build
function and return an instance of the BuildResponse
class. This response has three fields:
status - an instance of BuildStatus (Success or Error)
Specifically,
BuildStatus.Success
orBuildStatus.Error
payload - the raw bytes of the finished payload (if you failed to build, set this to
None
or empty bytes likeb''
in Python.message - any build message you want the user to see. This can be helpful execution tips or error messages
The most basic version of the build function would be:
Once the build
function is called, all of your BuildParameters
will already be verified (all parameters marked as required
will have a value
of some form (user supplied or default_value) and all of the verifier functions will be called if they exist). This allows you to know that by the time your build
function is called that all of your parameters are valid.
Your build function gets a few pieces of information to help you build the agent (other than the build parameters):
From within your build function, you'll have access to the following pieces of information:
self.uuid
- the UUID associated with your payloadThis is how your payload identifies itself to Mythic before getting a new Staging and final Callback UUID
self.commands
- a wrapper class around the names of all the commands the user selected.Access this list via
self.commands.get_commands()
self.agent_code_path
- apathlib.Path
object pointing to the path of theagent_code
directory that holds all the code for your payload. To be more explicit, this is the path/Mythic/agent_code
in the normal docker containers.To access "test.js" in that "agent_code" folder, simply do:
f = open(self.agent_code_path / "test.js", 'r')
.With
pathlib.Path
objects, the/
operator allows you to concatenate paths in an OS agnostic manner. This is the recommended way to access files so that your code can work anywhere.
self.get_parameter("parameter name here")
The build parameters that are validated from the user. If you have a build_parameter with a name of "version", you can access the user supplied or default value with
self.get_parameter("version")
self.c2info
- this holds a list of dictionaries of the c2 parameters and c2 class information supplied by the user. This is a list because the user can select multiple c2 profiles (maybe they want HTTP and SMB in the payload for example). For each element in self.c2info, you can access the information about the c2 profile withget_c2profile()
and access to the parameters viaget_parameters_dict()
. Both of these return a dictionary of key-value pairs.the dictionary returned by
self.c2info[0].get_c2profile()
contains the following:name
- name of the c2 profiledescription
- description of the profileis_p2p
- boolean of if the profile is marked as a p2p profile or notis_server_routed
- boolean of if the profile is server routed or notsampleServer
- sample server side information for the c2 profile provided by the creator of the profilesampleClient
- sample agent side information for the c2 profile provided by the creator of the profile
the dictionary returned by
self.c2info[0].get_parameters_dict()
contains the following:key
- valuewhere each
key
is thekey
value defined for the c2 profile's parameters andvalue
is what the user supplied. You might be wondering where to get these keys? Well, it's not too crazy and you can view them right in the UI - Name Fields.
One way to leverage this could be:
This should be all the information you need to build your agent.
When building your payload, if you have to modify files on disk, then it's helpful to do this in a "copy" of the files. You can make a temporary copy of your code and operate there with the following sample:
Execution flow
So, what's the actual, end-to-end execution flow that goes on?
PayloadType container is started, it connects to Mythic and sends over its data (by parsing all these python files)
An operator wants to create a payload from it, so they go to "Create Components" -> "Create Payload"
The operator selects an OS type that the agent supports (ex. Linux, macOS, Windows)
The operator selects all c2 profiles they want included
and for each c2 selected, provides any c2 required parameters
The operator selects this payload type
The operator fills out/selects all of the payload type's build parameters
The operator selects all commands they want included in the payload
Mythic takes all of this information and sends it to the payload type container
The mythic_service in the Payload Type container does the following:
Specifically, it takes the information that the user selected for the c2 profiles and gets them organized into a list (lines 1-4). It makes a list of all of the selected commands (line 5). It looks for a class that subclasses the PayloadType class and instantiates it (lines 6-11). It takes the user supplied parameters, sets the associated build parameter values, and verifies that all build parameters marked as "required" have values (line 13). Lastly, it calls the PayloadType's build
function.
After this section, the container sends the BuildResponse
message back to the Mythic server.
Last updated