6. Reverse Port Forward
How does Reverse Port Forward work within Mythic
What is Reverse Port Forward?
Reverse port forwards provide a way to tunnel incoming connections on one port out to another IP:Port somewhere else. It normally provides a way to expose an internal service to a network that would otherwise not be able to directly access it.
What do RPFWD messages look like?
Agents transmit dictionary messages that look like the following:
These messages contain three components:
exit
- boolean True or False. This indicates to either Mythic or your Agent that the connection has been terminated from one end and should be closed on the other end (after sendingdata
). Because Mythic and 2 HTTP connections sit between the actual tool you're trying to proxy and the agent that makes those requests on your tool's behalf, we need this sort of flag to indicate that a TCP connection has closed on one side.server_id
- unsigned int32. This number is how Mythic and the agent can track individual connections. Every new connection will generate a newserver_id
. Unlike SOCKS where Mythic is getting the initial connection, the agent is getting the initial connection in a reverse port forward. In this case, the agent needs to generate this random uint32 value to track connections.data
- base64 string. This is the actual bytes that the proxied tool is trying to send.port
- an optional uint32 value that specifies the port you're listening on within your agent. If your agent allows for multiple rpfwd commands within a single callback, then you need to specify thisport
so that Mythic knows which rpfwd command this data is associated with and can redirect it out to the appropriate remote IP:Port combination. Thisport
value is specifically the local port your agent is listening on, not the port for the remote connection.
In Python translation containers, if exit
is True, then data
can be None
How does this fit into Agent Messages?
These RPFWD messages are passed around as an array of dictionaries in get_tasking
and post_response
messages via a (added if needed) rpfwd
key:
or in the post_response
messages:
Notice that they're at the same level of "action" in these dictionaries - that's because they're not tied to any specific task, the same goes for delegate messages.
This means that if you send a get_tasking
request OR a post_response
request, you could get back rpfwd
data. The same goes for socks
, interactive
, and delegates
.
How does an agent handle RPFWD?
For the most part, the message processing is pretty straight forward:
Agent opens port X on the target host where it's running
ServerA makes a connection to PortX
Agent accepts the connection, generates a new uint32 server_id, and sends any data received to Mythic via
rpfwd
key. If the agent is tracking multiple ports, then it should also send the port the connection was received on with the message.Mythic looks up the
server_id
(and optionally port) for that Callback if Mythic has seen this server_id, then it can pass it off to the appropriate thread or channel to continue processing. If we've never seen the server_id before, then it's likely a new connection that opened up, so we need to handle that appropriately. Mythic makes a new connection out to the RemoteIP:RemotePort specified when starting therpfwd
session. Mythic forwards the data along and waits for data back. Any data received is sent back via therpfwd
key the next time the agent checks in.For existing connections, the agent looks at if
exit
is True or not. Ifexit
is True, then the agent should close its corresponding TCP connection and clean up those resources. If it's not exit, then the agent should base64 decode thedata
field and forward those bytes through the existing TCP connection.The agent should also be streaming data back from its open TCP connections to Mythic in its
get_tasking
andpost_response
messages.
That's it really. The hard part is making sure that you don't exhaust all of the system resources by creating too many threads, running into deadlocks, or any number of other potential issues.
While not perfect, the poseidon agent have a generally working implementation for Mythic: https://github.com/MythicAgents/poseidon/blob/master/Payload_Type/poseidon/poseidon/agent_code/rpfwd/rpfwd.go