pip3 install mythic. The current mythic package is version
0.0.23and reports to Mythic as scripting version "3". The code for it is public - https://github.com/MythicMeta/Mythic_Scripting
mythic_rest, so do the following:
mythic_restbecause there are likely to be other interfaces in the future (such as graphql).
main()- this function calls the scripting function asynchronously (
await scripting()), then loops through all functions sitting on the current event loop until everything is done. This is what allows you to eventually hook into the WebSocket eventing and wait for notifications.
scripting()- this is the initial function where you write your code.
Mythicserver. This includes the credentials we use to log in and information about the server itself. The
global_timeoutis an optional parameter to globally provide timeouts if no others are provided. If the
global_timeoutis set to anything less than 0, then the program will wait indefinitely.
await mythic.login(). This sends the credentials over the connection and gets back the standard JWT access_token and refresh_tokens.
await mythic.set_or_create_apitoken(). Because dealing with JWT access tokens is annoying (they have short timestamps) and trying to make sure you properly deal with refresh tokens in scripting is error prone, there's a helper function to create a user level API token. These API tokens show up in your
settingspage and can be deactivated or deleted. The advantage of these tokens is that while they're active, they don't expire. This is very useful for long running tasks. Doing
set_or_create_apitoken()is a helper function to get and potentially create a user-level API token and set it on your current
mythicobject with the following:
listenwill return a
MythicResponseclass object. This allows you to properly inspect the
.statusof the query (
error) as well as the
.response_code(the web response code from the query). The actual response object(s) is in the
.responsecomponent. For example:
json_printfunction allows you to easily print Mythic class objects as JSON. If you just want to access it as JSON data (i.e. not printing it), then each object has a
listen_for. These functions all have the same general format:
callback_functionisn't supplied, then the resulting data is simply printed to the screen. The timeout can be used for indicating how long you want to listen. Let's see how this can be used:
issue_shell_whoamifunction defined on lines 14-25 is executed. These callback functions always have 2 parameters - the mythic instance associated with the data and a
STRINGrepresentation of the data. To get back to a python dictionary, run
json.loads(data). At this point, you can either deal with data as a dictionary directly (as shown in the above code), or you can cast it back to the right object. In the above example, we could do
callback = Callback(**json.loads(data))which would result in
Callbackobject instead of a python dictionary.
await mythic_rest.json_print(object here). This provides a clean way to see the output that you actually got back. The reason everything returns a
MythicResponseinstead of just the object is so that we can capture HTTP response codes, raw output, and the Object casted types. This allows you to more easily manipulate and see data you get back via scripting.