covert_common/
lib.rs

1#![warn(missing_docs)]
2#![allow(incomplete_features)]
3#![feature(slice_as_chunks)]
4#![feature(generic_const_exprs)]
5//! Helper utilities for creating [external c2][1] systems for [cobaltstrike][2].
6//!
7//! ![C2](https://i.ibb.co/Cszd81H/externalc2.png)
8//!
9//!
10//!
11//![1]: https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics/listener-infrastructue_external-c2.htm
12//! [2]: https://www.cobaltstrike.com/
13
14use aes::Aes256;
15use bincode::{
16    config::{RejectTrailing, VarintEncoding, WithOtherIntEncoding, WithOtherTrailing},
17    DefaultOptions, Options,
18};
19use cipher::{
20    block_padding::Pkcs7, generic_array::GenericArray, BlockDecrypt, BlockEncrypt,
21    KeyInit,
22};
23use rand::random;
24use serde::{de::DeserializeOwned, Deserialize, Serialize};
25use std::error::Error;
26use std::{
27    collections::{HashMap, VecDeque},
28    fmt::Display,
29};
30
31/// Error type for the things that can go wrong when you put packets in the channel
32///
33
34#[derive(Debug)]
35pub enum CovertError {
36    /// Theres an issue with decrypting the packet
37    DecryptionError,
38    /// There is an issue with the hash after decryption (Could be a tampered packet)
39    InvalidHash,
40    /// The contained message or packet could not be deserialized to a rust struct
41    DeserializeError,
42}
43
44impl Display for CovertError {
45    #[cfg(not(debug_assertions))]
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        write!(f, "")
48    }
49
50    #[cfg(debug_assertions)]
51    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52        match self {
53            CovertError::DecryptionError => write!(f, "Decryption error"),
54            CovertError::InvalidHash => write!(f, "Invalid hash error"),
55            CovertError::DeserializeError => write!(f, "Deserialization error"),
56        }
57    }
58}
59
60impl Error for CovertError {
61    fn source(&self) -> Option<&(dyn Error + 'static)> {
62        None
63    }
64
65    fn description(&self) -> &str {
66        ""
67    }
68
69    fn cause(&self) -> Option<&dyn Error> {
70        self.source()
71    }
72}
73
74#[derive(Deserialize, Serialize, Debug)]
75struct CovertPacket {
76    hash: u8,
77    stream: u16,
78    syn: u32,
79    want: u32,
80    last: bool,
81    payload: Vec<u8>,
82    pad: Vec<u8>,
83} //17 bytes max
84
85impl CovertPacket {
86    fn new(stream: u16, syn: u32, last: bool, payload: &[u8], blocks: usize) -> Self {
87        let buf_size = (blocks * 16) - 18;
88        let pad = vec![0u8; buf_size - payload.len()];
89        let pad: Vec<u8> = pad.iter().map(|_| random()).collect();
90
91        Self {
92            hash: 0,
93            stream,
94            syn,
95            want: 0,
96            last,
97            payload: payload.to_vec(),
98            pad,
99        }
100    }
101}
102
103struct CovertStream<T> {
104    out_bound_packet_cache: VecDeque<CovertPacket>,
105    in_bound_packet_cache: VecDeque<CovertPacket>,
106    message_cache: VecDeque<T>,
107    out_count: u32,
108    out_syn: u32,
109    in_syn: u32,
110}
111
112/// Establishes a covert channel handler.  this struct enables sending series of messages
113/// that are broken into small encrypted packets.  messages are put into the channel with
114/// put_message and then sent with get_packet.  In the reverse direction packets are
115/// read into the channel with put_packets and converted to messages with get_message.
116/// packets need to be sent in both directions to "syn/ack" to confirm messages are
117/// transmitted in full.
118
119pub struct CovertChannel<T, const BLOCKS: usize>
120where
121    T: DeserializeOwned + Serialize,
122{
123    engine: Aes256,
124    encoder: WithOtherTrailing<
125        WithOtherIntEncoding<DefaultOptions, VarintEncoding>,
126        RejectTrailing,
127    >,
128    streams: HashMap<u16, CovertStream<T>>,
129}
130
131impl<T, const BLOCKS: usize> CovertChannel<T, BLOCKS>
132where
133    T: DeserializeOwned + Serialize,
134{
135    /// Create a new covert channel with the provided 32 byte aes key.
136    pub fn new(key: [u8; 32]) -> CovertChannel<T, BLOCKS> {
137        let encoder = bincode::DefaultOptions::new()
138            .with_varint_encoding()
139            .reject_trailing_bytes();
140        // let cryptor = with_key(create_key(key).expect("Bad key"));
141        CovertChannel {
142            encoder,
143            engine: Aes256::new(&GenericArray::from(key)),
144            streams: HashMap::new(),
145        }
146    }
147
148    /// get a complete message T from this channel.  if there are no messages sent completely
149    /// yet then the Option is None.
150    pub fn get_message(&mut self, stream_id: u16) -> Option<T> {
151        let stream = self.streams.get_mut(&stream_id)?;
152        stream.message_cache.pop_front()
153    }
154
155    /// send a message through this channel.  
156    pub fn put_message(&mut self, msg: T, stream_id: u16) -> ()
157    where
158        [(); (BLOCKS * 16) - 18]:,
159    {
160        let encode = self.encoder;
161        let stream = self.get_stream_by_id(stream_id);
162        let res = encode.serialize(&msg).unwrap();
163        let (parts, end) = res.as_chunks::<{ (BLOCKS * 16) - 18 }>();
164        for part in parts {
165            let new_packet = CovertPacket::new(
166                stream_id,
167                stream.out_count,
168                false,
169                part.as_slice(),
170                BLOCKS,
171            );
172            stream.out_count += 1;
173            stream.out_bound_packet_cache.push_back(new_packet);
174        }
175        if end.len() != 0 {
176            let last_packet =
177                CovertPacket::new(stream_id, stream.out_count, true, end, BLOCKS);
178            stream.out_bound_packet_cache.push_back(last_packet);
179            stream.out_count += 1;
180        } else {
181            stream.out_bound_packet_cache.back_mut().unwrap().last = true;
182        }
183    }
184
185    /// get the next packet that needs to be sent for this channel.  Even if there are
186    /// no messages to be sent a call to get_packet with return successfully.  empty packets
187    /// still contain synchronizing information and should be sent regularly.  additionally
188    /// the receiving end of the channel accounts for packets that contain no message data.
189    pub fn get_packet(&mut self, stream_id: u16) -> Vec<u8> {
190        let encode = self.encoder;
191        let stream = self.get_stream_by_id(stream_id);
192        if stream.out_bound_packet_cache.len() == 0 {
193            stream.out_bound_packet_cache.push_front(CovertPacket::new(
194                stream_id,
195                stream.out_count,
196                false,
197                &[],
198                BLOCKS,
199            ));
200            stream.out_count += 1;
201        }
202        let out = stream.out_bound_packet_cache.front_mut().unwrap();
203        out.want = stream.in_syn;
204        let mut tmp = encode.serialize(out).unwrap();
205        let hash = crc32fast::hash(&tmp).to_le_bytes()[0];
206        tmp[0] = hash;
207        let done = self.engine.encrypt_padded_vec::<Pkcs7>(&tmp);
208        return done;
209    }
210
211    /// Put packets into this channel to be decoded.  When a complete packet is ready to
212    /// be read from the channel put packet will contain true in the Ok result.
213    pub fn put_packet(&mut self, pkt: &[u8]) -> Result<(u16, bool), CovertError> {
214        let encode = self.encoder;
215        let mut tmp = self
216            .engine
217            .decrypt_padded_vec::<Pkcs7>(pkt)
218            .or(Err(CovertError::DecryptionError))?;
219        let hash = tmp[0];
220        tmp[0] = 0;
221        let actual = crc32fast::hash(&tmp).to_le_bytes()[0];
222        if hash != actual {
223            return Err(CovertError::InvalidHash);
224        };
225        let in_packet = encode
226            .deserialize::<CovertPacket>(&tmp)
227            .or(Err(CovertError::DeserializeError))?;
228
229        let stream_id = in_packet.stream;
230        let mut stream = self.get_stream_by_id(stream_id);
231
232        // Clear packets in the out_cache that have been confirmed
233        stream.out_syn = Ord::max(stream.out_syn, in_packet.want);
234        stream
235            .out_bound_packet_cache
236            .retain(|item| item.syn >= stream.out_syn);
237        // Append to the in_cache if this is the next packet needed else drop it
238        if in_packet.syn == stream.in_syn {
239            stream.in_syn += 1;
240            let is_last = in_packet.last;
241            stream.in_bound_packet_cache.push_back(in_packet);
242            if is_last {
243                let mut payload: Vec<u8> = stream
244                    .in_bound_packet_cache
245                    .drain(..)
246                    .map(|i| i.payload)
247                    .flatten()
248                    .collect();
249                let in_message = encode
250                    .deserialize::<T>(&mut payload)
251                    .or(Err(CovertError::DeserializeError))?;
252                stream.message_cache.push_back(in_message);
253                return Ok((stream_id, true));
254            }
255        };
256        return Ok((stream_id, false));
257    }
258
259    /// Get the number of packets in the inbound and outbound queue (in,out)
260    pub fn packets_in_queue(&self, stream_id: u16) -> (usize, usize) {
261        if let Some(stream) = self.streams.get(&stream_id) {
262            return (
263                stream.in_bound_packet_cache.len(),
264                stream.out_bound_packet_cache.len(),
265            );
266        }
267        return (0, 0);
268    }
269
270    fn get_stream_by_id(&mut self, stream_id: u16) -> &mut CovertStream<T> {
271        if !self.streams.contains_key(&stream_id) {
272            let new_stream = CovertStream {
273                out_bound_packet_cache: VecDeque::new(),
274                in_bound_packet_cache: VecDeque::new(),
275                message_cache: VecDeque::new(),
276                out_count: 0,
277                out_syn: 0,
278                in_syn: 0,
279            };
280            self.streams.insert(stream_id, new_stream);
281        }
282        self.streams.get_mut(&stream_id).unwrap()
283    }
284}
285
286#[cfg(test)]
287mod test;