Action: upload

Upload a file from Apfell to the target

What does it mean to upload a file?

This section is specifically for uploading a file from the Apfell 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.

In general, there's a few steps that happen for this process:

  1. The operator issues some sort of tasking that results in the agent getting a UUID of a file it needs to retrieve from the Apfell server. If you have a command parameter type of file, then that parameter will be replaced with a file uuid value instead. So, for example, if the parameters are {"file": "this is of type file", "remote_path": "this is of type string"} then the agent will get {"file": "file uuid", "remote_path": "whatever you typed"}.

  2. The agent gets the tasking and sees that there's a file UUID it needs to pull. It sends an initial message to Apfell saying that it will be downloading the file in chunks of a certain size and requests the first chunk.If the agent is going to be writing the file to disk (versus just pulling down the file into memory), then the agent needs to also send full_path and task_id. This allows Apfell to track a new entry in the database with an associated task for uploads.

  3. The Apfell 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.

  4. The Agent can now use this information to request the rest of the chunks of the file.

It's not an extremely complex process, but it does require a bit more back-and-forth than a fire-and-forget style. The rest of this page will walk through those steps with more concrete code examples.

Example (user tasking):

There are two different ways to specify files to upload to a target: uploading a file with the task (i.e. when you type you command you also browse the file system and select a file) or specifying a UUID of a file that's already been uploaded.

Either way, the agent will get some sort of task that specifies a file's UUID. Below, we can see an example of the parameters section of a command supplied for an upload command. In this case, the operator would simply type upload {"remote_path": "/users/itsafeature/Desktop/notmalware.png", "file_id": "UUID here"}. There is no expectation when doing uploads or downloads that the operator must type the absolute path to a file, that's a bit of a strict requirement. Instead, Apfell allows operators to specify relative paths and has an option in the upload action to specify the actual full path (this option also exists for downloading files so that the absolute path can be returned for better tracking). This allows Apfell to properly track absolute file system paths that might have the same resulting file name without an extra burden on the operator.

{
    "remote_path": "/users/itsafeature/Desktop/notmalware.png"
    "file_id": "UUID of file here"
}

Walkthrough:

The user with either specify a file_id (UUID) or actually upload a file when providing the parameters in the UI. To upload a file (not just providing a file_id), you need to just type upload on the command line and hit enter, a GUI will pop up to allow you to select the file to upload.

Otherwise, you can specify the file_id of another file registered in the system by specifying the file_id value of another upload or something specifically hosted via the "Services" page.

Example (agent pull down):

Unlike with the files for loading, these files can be pulled down multiple times. As a reminder, files hosted via the load command only exist for a single request by the agent. 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 Apfell. The agent makes the following request to Apfell:

Base64( CallbackUUID + JSON(
{
	"action": "upload",
	"chunk_size": 512000, //bytes of file per chunk
	"file_id": UUID, //the file specified to pull down to the target
	"chunk_num": #, // which chunk are we currently pulling down
	"full_path": "full path to uploaded file on target",
	"task_id": task_id // the associated task that caused the agent to pull down this file
}
))

As noted by specifying an action field, this is NOT put in the post_response message from the agent. This is a separate message. This message data should still be encrypted and have the callback's UUID prepended to the data.

The 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 Apfell 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.

The agent gets back a message like:

Base64( CallbackUUID + JSON(
{
	"action": "upload",
	"total_chunks": #, // given the previous chunk size, the total num of chunks
	"chunk_num": #, //the current chunk number Apfell is returning
	"chunk_data": "base64_of_data" // the actual file data,
	"file_id": "file id that was requested",
	"task_id": "UUID of task" // task id that was presented in the request for tracking
}
))

This process repeats as many as times as is necessary to pull down all of the contents of the file.

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': 'upload', 'total_chunks': 0, 'chunk_num': 0, 'chunk_data': '', 'file_id': '', 'task_id': ''}) 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.

Uploading files not through "Upload" command

Commands are allowed to specify a parameter as a file type. This means that you will be prompted to select a file to upload through your browser GUI. When this happens, the value for this key will be a file_id for the agent to pull down. For example:

If a command parameter called shell_code is of the file type, then when the operator tasks this to the agent and selects a file, the agent will actually pull down a command like:

{
    "command": "some_command", 
    "params": "{\"shell_code\": \"UUID1\"}", 
    "id": "task_id UUID"
}

Where the agent can then pull down the file specified by that UUID1 through the C2 channel.

Files in the Tasking JSON

There's a reason that files aren't base64 encoded and placed inside the initial tasking blobs. Keeping the files tracked by the main Apfell system and used in a separate call allows the initial messages to stay small and allows for the agent and C2 mechanism to potentially cache or limit the size of the transfers as desired.

Consider the case of using DNS as the C2 mechanism. If the file mentioned in this section was sent through this channel, then the traffic would potentially explode. However, having the option for the agent to pull the file via HTTP or some other mechanism gives greater flexibility and granular control over how the C2 communications flow.

Last updated