Scripting Payloads
What does this hook into?
Scripting tasking involves the following RESTful endpoints on an instance of Mythic
. This means you need to create a new Mythic
instance (i.e. mythic = Mythic(username="blah" ...
) and then call these functions like mythic.get_payloads()
:
This section also hooks into the following async callback functions:
Creating Payloads
Getting information for all existing payloads is one thing, and using that info to get detailed information on each payload is another thing, but being able to task Mythic to create new payloads is extremely useful. If you've created payloads in the browser UI though, you know there's a lot of different things that go into creating payloads. Scripting should be a time saver though, so you shouldn't have to be extremely verbose. So, when creating payloads, simply describe what you'd like your payload to look like. Anything not explicitly set is interpreted by Mythic as a default value:
In the above example we create our ideal payload. We set the payload_type
to be the name of the payload type or it can be a PayloadType object. C2 profiles is a little trickier, but it's a dictionary of C2 profile name and an array of all the parameters. In this case, we're just using one C2 profile in the poseidon agent, the HTTP
profile, and we're only setting the callback_host
parameter. Since we're not specifying any of the other parameters, we're leaving them all to their default values. We specify the tag
to give to our payload (this is what autopopulates the description for a callback), and we specify the filename
for the payload.
If the payload takes build parameters (the extra, non-c2 profile pieces you see when you select an agent for creation in the UI), then you specify those in the build_parameters
field. It's a list of dictionaries of name
and value
fields. In the above example, we set the mode
build parameter for poseidon
to default
.
When selecting all_commands=True
in the create_payload
function, you will still be limited by the operating system you select. Additionally, if you want to select all commands except for a few, you can supply a list of command names in the exclude_commands
parameter to the create_payload
call.
How do you find the "name" fields?
In the above snippet, you might be wondering where the name callback_host
or os
came from.
For C2 profiles, you can see these specific value names if you change your view_mode
for your operation from operator
to developer
. You'll see some additional features added on both the Payload Types
page and the C2 Profiles
page. On the C2 Profiles page, click the dropdown for Configure
and select to View Parameters
:
The far left-hand side where it has the Name
column. That's the name you'll be referencing here. On the 3rd row you can see the callback_host
name that we modified. From here, you can also see more of the "developer" based information such as which parameters are marked as required
and which ones have specific regex requirements for what's a valid
value.
For the build parameters on a payload type, go to the Payload Types
page in the UI and select the dropdown for the corresponding payload type, then select View Parameters
:
Again, we can see the name
field on the far left is what we're interested in. In our example, we only set one build parameter - os
. That means we used the default value for the other, mode
, which in the case of a ChooseOne
parameter type, will always be the first parameter listed.
Specifying Commands for the Payload
You might have noticed so far that we didn't actually list any commands to be included in the agent. You can do one of two things:
specify a
commands=["cmd1", "cmd2", "cmd3"...]
argument to the Payload to specify which commands you want included in your agentspecify
all_commands=True
when actually tasking the creation, which will in turn cause the Scripting to pull information about the payload type you're trying to create, get the most recent listing of all the available commands, and automatically include those as part of your request. This is what we do for the poseidon example above.
Submitting the Creation Request
Ok, so far you've just described the payload you want. Now you need to actually tell Mythic to create the thing you just described. This is pretty straight forward:
The interesting piece here is the wait_for_build
flag. So, everything in Mythic is separated out to different Docker containers, including the actual build pieces for agents. This means when you simply hit the RESTful endpoint to start a build, it returns pretty quickly, but that doesn't mean that the corresponding docker container is actually done building the agent. When you specify wait_for_build=True
, after submitting the RESTful request, the script opens up a websocket connection to Mythic for information on the payload. It will then return when there's some sort of completion state for the payload (success or error). Your resp
in this case will have the information about the payload in resp.response
.
Downloading Payloads
So, you built a payload successfully or you see another payload that was previously built that you want to pull down for some purpose. That's pretty easy:
The async def download_payload(self, payload: Union[Payload, Dict])
command allows you to specify the payload you want to download and returns to you the raw bytes of the payload.
Unlike other commands, this does NOT return a MythicResponse object with the payload in the .response
component. It simply returns the raw bytes to you.
Last updated