pip3 install mythic
. The current mythic package is version 0.0.23
and 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_rest
because 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.Mythic
server. This includes the credentials we use to log in and information about the server itself. The global_timeout
is an optional parameter to globally provide timeouts if no others are provided. If the global_timeout
is 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 settings
page 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 mythic
instance.mythic
object with the following:login
or set_or_create_apitoken
.listen
will return a MythicResponse
class object. This allows you to properly inspect the .status
of the query ( success
or error
) as well as the .response_code
(the web response code from the query). The actual response object(s) is in the .response
component. For example:json_print
function 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 .to_json()
function.listen_for
. These functions all have the same general format:callback_function
isn'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_whoami
function defined on lines 14-25 is executed. These callback functions always have 2 parameters - the mythic instance associated with the data and a STRING
representation 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 callback
being a Callback
object 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 MythicResponse
instead 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.