What is it?
Push C2 is a way to do egress C2 channels without requiring the agent to beacon periodically for tasking. Instead, a connection is held open and Mythic “Pushes” tasks and data down to the agent. This is very similar to how many Peer-to-peer (P2P) agents handle connections, just extended to the egress side of things.How does it work?
For this to work, there needs to be a held open connection between the C2 Profile docker container and the Mythic server itself. This is done via gRPC. As part of this held open connection, the C2 profile identifies itself and forwards along messages.What does it look like?
In the mythic UI, the last checkin time will change to 1970-01-01 and appear asStreaming Now
. The moment the held open gRPC connection disconnects, that time will update to the current UTC time. This makes it very easy to know that a connection is currently held open even if no traffic is going through it.
Below are some simplified examples of working with this gRPC. An example of this Push style C2 as part of websockets is available with the websocket
C2 profile.
Agent Expectations
How is an agent supposed to work with a Push-style C2 profile? It’s the same as working with a Peer-to-peer (P2P) profile:- If a payload is executed (it’s not a callback yet), then reach out to the C2 profile to make a connection. Once a connection is established, start your normal encrypted key exchange or checkin process
- If an existing callback loses connection for some reason, then reach out to the C2 profile to make a connection. Once a connection is established, send your checkin message again to inform Mythic of your existence
- At this point, just wait for messages to come to you (no need to do a get_tasking poll) and as you get any data (socks, edges, alerts, responses, etc) just send them out through your c2 connection.
Types of Push C2
There are two types of Push C2. They both function largely the same, but within the C2 server they function differently.One-to-One
The first one is probably the most common, one-to-one. This means that for every agent that connects to the C2 server (typically with a held-open connection like a WebSocket), the C2 server also opens one gRPC connection to Mythic. The following is a diagram what this means:
Message
field instead.
Once Mythic is done processing a message, we can send the response back to the agent:
One-to-Many
The other type of Push C2 is one-to-many. This functions largely the same as one-to-one, except that the C2 server only ever opens up one gRPC connection to Mythic with many agents that go through it. Below is a diagram showing this flow:
StartPushC2StreamingOneToMany()
and not StartPushC2Streaming
like in the one-to-one example. Before we even get any agent connections, we send a message to the gRPC stream with just the c2 profile name. This let’s Mythic know that we have a new one-to-many c2 profile running and the name of that connection.
At this point, everything works the same as before with the one-to-one profile. As agents send the profile messages, the c2 profile should forward them off to Mythic via the stream and read from the stream to send messages back. You’re probably wondering though - how is the multiplexing happening between the one connection to Mythic and the many agents on the other side?
This stream utilizes a TrackingID
that’s supplied by the C2 profile to track these individual streams:
TrackingID
is something generated by the C2 profile when sending messages and is echoed back as part of the responses that Mythic sends. This allows a C2 Profile to do the proper correlation with messages it gets back and which agent to send it to. This data is saved and tracked by Mythic so that it can be used even when Mythic is the one sending the initial piece of data (like a new task). Let’s look at a slightly more complete example to see how that works:
TrackingID
that’s echoed back to look up the corresponding WebSocket stream and send data that way.
There’s one additional piece here that hasn’t been covered yet though - AgentDisconnected
. Since all agents on the other side of this gRPC connection are sharing the one connection to Mythic, if that gRPC connection exits, then Mythic detects that and marks ALL agents that use that C2 profile has having lost that connection. However, what if just one agent on the other end disconnects? The main gRPC connection is still there, so we need a way to inform Mythic that one remote connection is gone. This is where the AgentDisconnected
piece comes into play: