What does it mean to upload a file?
This section is specifically for uploading a file from the Mythic server to an agent. Because these messages aren’t just to an agent directly (they can be routed through a p2p mesh), they can’t just be as simple as a GET request to download a file. The file needs to be chunked and routed through the agents. This isn’t specific to theupload
command, this is for any command that wants to leverage a file from Mythic.
In general, there’s a few steps that happen for this process (this can be seen visually on the Message Flow page):
- The operator issues some sort of tasking that has a parameter type of “File”. You’ll notice this in the Mythic user interface because you’ll always see a popup for you to supply a file from your file system.
- Once you select a file and hit submit, Mythic loops through all of the files selected and registers them. This process sends each one down to Mythic, saves it off to disk, and assigns it a UUID. These UUIDs are what’s stored in place of the raw bytes when you submit your task. So, if you had an upload command that takes a file and a path, your arguments would end up looking like
{"file":"uuid", "path": "/some/path"}
rather than{"file": raw bytes of file, "path": "/some/path"}
. - In the Payload Type’s corresponding command python file there is a function called
create_go_tasking
that handles the processing of tasks from users before handing them off to the database to be fetched by an agent. If your agent supports chunking and transferring files that way, then you don’t need to do anything else, but if your agent requires that you send down the entire file’s contents as part of your parameters, you need to get the associated file. - To get the file with Mythic, there’s an RPC calls we can do:
Get File Metadata
example). If there’s something we want to modify about the file (such as adding a comment automatically or indicating that the file should be deleted from disk after the agent fetches it) we can use the SendMythicRPCFileUpdate
RPC function to do so.
At this point, if you wanted to use the raw bytes of a file instead of the UUID as part of your tasking, you should use the
Get File Contents
example above to fetch the actual contents. Then you can then swap out the contents of the parameter with taskData.args.add_arg("arg name", "base64 of file contents here")
.If you want to register a NEW file with Mythic from the payload container that the user didn’t first upload, you need to use the
SendMythicRPCFileCreate
RPC call.full_path
. This allows Mythic to track a new entry in the database with an associated task for uploads.
5. The Mythic server gets the request for the file, makes sure the file exists and belongs to this operation, then gets the first chunk of the file as specified by the agent’s chunk_size
and also reports to the agent how many chunks there are.
6. The Agent can now use this information to request the rest of the chunks of the file.
The agent reporting back
full_path
is what allows Mythic to track the file in the Files search page as a file that has been written to disk. If you don’t report back a full_path
or have full_path
as an empty string, then Mythic thinks that the file transfer only lived in memory and didn’t touch disk. This is separate from reporting that a file was written to disk as part of artifact tracking on the Reporting Artifacts page.Example (agent pull down):
Files can (optionally) be pulled down multiple times (if you setdelete_after_fetch
as True
, then the file won’t exist on disk after the first fetch and thus can’t be re-used). This is to prevent bloating up the server with unnecessary files.
An agent pulling down a file to the target is similar to downloading a file from the target to Mythic. The agent makes the following request to Mythic:
The
chunk_num
field is 1-based. So, the first chunk you request is "chunk_num": 1
.full_path
parameter is helpful for accurate tracking. This allows an operator to be in the /Temp
directory and simply call the upload function to the current directory, but allows Mythic to track the full path for easier reporting and deconfliction.
The
full_path
parameter is only needed if the agent plans to write the file to disk. If the agent is pulling down a file to load into memory, then there’s no need to report back a full_path
.If there is an error pulling down a file, the server will respond with as much information as possible and blank out the rest (i.e.:
{'action': 'post_response', 'responses': [ {'total_chunks': 0, 'chunk_num': 0, 'chunk_data': '', 'file_id': '', 'task_id': '', 'status': 'error', 'error': 'some error message'} ] }
) If the task_id was there in the request, but there was an error with some other piece, then the task_id will be present in the response with the right value.