rust_padbuster/
lib.rs

1use base64::{Engine, engine::general_purpose};
2use log::{error, info};
3
4/// Errors which can occured in encrypt / decrypt
5#[derive(Debug)]
6pub enum PadbusterError {
7    /// Occured when parameters are invalid.
8    ValidationError(&'static str),
9    /// Occured when oracle is unable to find a specific byte.
10    UnableFindByteError(u8, Box<PadbusterError>),
11    /// Occured when oracle is unable to decryt specific byte.
12    UnableDecryptByteError(Vec<u8>, u8),
13    /// Must occured in oracle function.
14    BadPaddingError(Vec<u8>, String),
15    /// Occured When error is not specified.
16    Unspecified(&'static str),
17}
18
19impl std::fmt::Display for PadbusterError {
20    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
21        match *self {
22            PadbusterError::ValidationError(ref s) => write!(f, "{s}"),
23            PadbusterError::UnableFindByteError(ref byte, ref origin) => {
24                write!(f, "Unable to find {byte} byte: {origin}")
25            }
26            PadbusterError::UnableDecryptByteError(ref block, ref max_retries) => write!(
27                f,
28                "Could not decrypt byte in {block:?} within maximum allowed retries ({max_retries})"
29            ),
30            PadbusterError::BadPaddingError(ref ciphertext, ref origin) => {
31                write!(f, "Bad Padding for ciphertext {ciphertext:?}: {origin}")
32            }
33            PadbusterError::Unspecified(ref s) => write!(f, "Unexpected error occured: {s}"),
34        }
35    }
36}
37
38/// `PaddingOracle` represent an instance of padding oracle
39pub struct PaddingOracle {
40    oracle: fn(Vec<u8>) -> Result<(), PadbusterError>,
41    block_size: u8,
42    max_retries: u8,
43}
44
45impl PaddingOracle {
46    /// Return a PaddingOracle with the given function
47    ///
48    /// # Arguments
49    ///
50    /// * `block_size` - Size of CBC block (8, 16, 32).
51    /// * `max_retries` - Maximal number of retries if it had an error.
52    /// * `oracle` - Oracle function must return `PadbusterError::BadPaddingError` in case of padding error.
53    ///
54    pub fn new(
55        block_size: u8,
56        max_retries: u8,
57        oracle: fn(Vec<u8>) -> Result<(), PadbusterError>,
58    ) -> Self {
59        PaddingOracle {
60            oracle,
61            block_size,
62            max_retries,
63        }
64    }
65
66    /// Encrypt the given plaintext & iv using oracle
67    ///
68    /// # Arguments
69    ///
70    /// * `plaintext` - Plaintext to use.
71    /// * `iv` - IV to use if known empty Vec<u8> if not.
72    ///
73    pub fn encrypt(&self, plaintext: &[u8], iv: &[u8]) -> Result<Vec<u8>, PadbusterError> {
74        info!("Starting Encrypt Mode");
75
76        if plaintext.is_empty() {
77            return Err(PadbusterError::ValidationError(
78                "Cannot encrypt empty plaintext",
79            ));
80        }
81        let mut pad = ((self.block_size as usize) - (plaintext.len() % (self.block_size as usize)))
82            % (self.block_size as usize);
83        if pad == 0 {
84            pad = self.block_size as usize;
85        }
86        let mut used_iv = iv.to_owned();
87        let mut ptext = plaintext.to_owned();
88        let mut padding = vec![pad as u8; pad];
89        ptext.append(&mut padding);
90
91        info!("Attempting to encrypt {ptext:?} bytes");
92        if used_iv.is_empty() {
93            used_iv = vec![0u8; self.block_size as usize];
94        }
95        let mut encrypted = used_iv;
96        let mut block = encrypted.clone();
97
98        let mut n = ptext.len();
99        while n > 0 {
100            info!("*** Starting Block {} ***", n / (self.block_size as usize));
101            let intermediate_bytes = self.bust(&mut block)?;
102            let current_intermediate_bytes = intermediate_bytes.clone();
103
104            block = xor_data(
105                intermediate_bytes,
106                ptext[(n - (self.block_size as usize))..n].to_vec(),
107            );
108            let current_block = block.clone();
109
110            let mut current_encrypted = encrypted.clone();
111            encrypted.clone_from(&block);
112            encrypted.append(&mut current_encrypted);
113
114            info!(
115                "Block {} Results:
116            [+] New Cipher Text (HEX): {}
117            [+] Intermediate Bytes (HEX): {}",
118                n / (self.block_size as usize),
119                hex::encode(current_block),
120                hex::encode(current_intermediate_bytes)
121            );
122
123            n -= self.block_size as usize;
124        }
125
126        info!(
127            "*** Finished ***
128        [+] Encrypted value is: {}",
129            general_purpose::STANDARD.encode(&encrypted)
130        );
131
132        Ok(encrypted)
133    }
134
135    /// Decrypt the given ciphertext & iv using oracle
136    ///
137    /// # Arguments
138    ///
139    /// * `ciphertext` - Ciphertext to use.
140    /// * `iv` - IV to use if known empty Vec<u8> if not.
141    ///
142    pub fn decrypt(&self, ciphertext: &[u8], iv: &[u8]) -> Result<Vec<u8>, PadbusterError> {
143        info!("Starting Decrypt Mode");
144        info!("Attempting to decrypt {ciphertext:?} bytes");
145
146        if !ciphertext.len().is_multiple_of(self.block_size as usize) {
147            return Err(PadbusterError::ValidationError(
148                "Ciphertext not of block size",
149            ));
150        }
151
152        if iv.is_empty() && ciphertext.len() < (self.block_size as usize) * 2 {
153            return Err(PadbusterError::ValidationError(
154                "Ciphertext not at least 2 * block size",
155            ));
156        }
157
158        let mut used_iv = iv.to_owned();
159        let mut ctext = ciphertext.to_owned();
160        if iv.is_empty() {
161            used_iv = ciphertext[..(self.block_size as usize)].to_vec();
162            ctext = ciphertext[(self.block_size as usize)..].to_vec();
163        }
164
165        let mut decrypted = vec![0u8; ctext.len()];
166
167        let mut n = 0;
168        while !ctext.is_empty() {
169            info!("*** Starting Block {} ***\n", n / self.block_size);
170            let mut block = ctext[0..(self.block_size as usize)].to_vec();
171            ctext = ctext[(self.block_size as usize)..].to_vec();
172            let next_iv = block.clone();
173
174            let current_block = block.clone();
175
176            let intermediate_bytes = self.bust(&mut block)?;
177            let current_intermediate_bytes = intermediate_bytes.clone();
178
179            let mut decrypted_block = xor_data(intermediate_bytes, used_iv);
180            let current_decrypted_block = decrypted_block.clone();
181            decrypted.append(&mut decrypted_block);
182
183            used_iv = next_iv;
184            n += self.block_size;
185
186            info!(
187                "Block {} Results:
188            [+] New Cipher Text (HEX): {}
189            [+] Intermediate Bytes (HEX): {}
190            [+] Plain Text: {}",
191                n / self.block_size,
192                hex::encode(current_block),
193                hex::encode(current_intermediate_bytes),
194                std::str::from_utf8(&current_decrypted_block).unwrap()
195            );
196        }
197
198        info!(
199            "*** Finished ***
200        [+] Decrypted value (ASCII): {}
201        [+] Decrypted value (HEX): {}
202        [+] Decrypted value (Base64): {}",
203            std::str::from_utf8(&decrypted).unwrap(),
204            hex::encode(&decrypted),
205            general_purpose::STANDARD.encode(&decrypted)
206        );
207
208        Ok(decrypted)
209    }
210
211    fn try_bust(&self, block: &mut Vec<u8>) -> Result<Vec<u8>, PadbusterError> {
212        let mut intermediate_bytes = vec![0u8; self.block_size as usize];
213        let mut test_bytes = vec![0u8; self.block_size as usize];
214        test_bytes.append(block);
215
216        let mut byte_num = self.block_size;
217        while byte_num > 0 {
218            let mut try_number = 0;
219            let mut r = 255u8;
220            let mut i = r as i16;
221            while i >= 0 {
222                // Fuzz the test byte
223                test_bytes[(byte_num - 1) as usize] = r;
224                // Decrement r for the next loop
225                r -= 1;
226
227                // If a padding oracle could not be identified from the
228                // response, this indicates the padding bytes we sent
229                // were correct.
230                let oracle_test_bytes = test_bytes.clone();
231                try_number += 1;
232                if let Err(e) = (self.oracle)(oracle_test_bytes) {
233                    if r == 0 {
234                        return Err(PadbusterError::UnableFindByteError(byte_num, Box::new(e)));
235                    }
236                    if let PadbusterError::BadPaddingError(_, _) = e {
237                        i -= 1;
238                        continue;
239                    }
240                    error!("{e}");
241                    return Err(e);
242                }
243
244                let current_pad_byte = self.block_size - (byte_num - 1);
245                let next_pad_byte = self.block_size - (byte_num - 1) + 1;
246                let decrypted_byte = test_bytes[(byte_num - 1) as usize] ^ current_pad_byte;
247
248                intermediate_bytes[(byte_num - 1) as usize] = decrypted_byte;
249
250                let mut k = byte_num - 1;
251                while k < self.block_size {
252                    // XOR the current test byte with the padding value
253                    // for this round to recover the decrypted byte
254                    test_bytes[k as usize] ^= current_pad_byte;
255                    // XOR it again with the padding byte for the
256                    // next round
257                    test_bytes[k as usize] ^= next_pad_byte;
258
259                    k += 1;
260                }
261                info!("[+] Success: ({try_number}/256) [Byte {byte_num}]");
262                break;
263            }
264
265            byte_num -= 1;
266        }
267
268        Ok(intermediate_bytes)
269    }
270
271    fn bust(&self, block: &mut Vec<u8>) -> Result<Vec<u8>, PadbusterError> {
272        info!("Processing block {block:?}");
273
274        for retry in 0..self.max_retries {
275            match self.try_bust(block) {
276                Ok(r) => return Ok(r),
277                Err(e) => {
278                    error!(
279                        "[+] Retrying {}/{} unable to bust block {:?} {}",
280                        retry, self.max_retries, block, e
281                    );
282                }
283            };
284        }
285        Err(PadbusterError::UnableDecryptByteError(
286            block.to_vec(),
287            self.max_retries,
288        ))
289    }
290}
291
292/// `xor_data` xor data with key
293fn xor_data(data: Vec<u8>, key: Vec<u8>) -> Vec<u8> {
294    let mut res = vec![0u8; data.len()];
295    for (index, b) in data.iter().enumerate() {
296        res[index] = b ^ key[index % key.len()];
297    }
298    res
299}
300
301#[cfg(test)]
302mod tests {
303    use super::*;
304
305    use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit, block_padding::Pkcs7};
306    use hex_literal::hex;
307
308    extern crate simple_logger;
309
310    type Aes128CbcEnc = cbc::Encryptor<aes::Aes128>;
311    type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;
312
313    fn unpad(b: Vec<u8>, block_size: usize) -> Result<Vec<u8>, &'static str> {
314        if block_size == 0 {
315            return Err("Invalid block size");
316        }
317        if b.is_empty() {
318            return Err("Invalid PKCS7 data");
319        }
320        if b.len() % block_size != 0 {
321            return Err("Invalid PKCS7 data");
322        }
323        let c = b[b.len() - 1];
324        if c == 0 || c > (b.len() as u8) {
325            return Err("Invalid PKCS7 padding");
326        }
327        for i in 0..(c as usize) {
328            if b[b.len() - (c as usize) + i] != c {
329                return Err("Invalid PKCS7 padding");
330            }
331        }
332        Ok(b[..(b.len() - (c as usize))].to_vec())
333    }
334
335    fn oracle_fn(data: Vec<u8>) -> Result<(), PadbusterError> {
336        // re-create cipher mode instance
337        let key = hex!("000102030405060708090a0b0c0d0e0f");
338        let iv = &data[..16];
339        let ciphertext = &data[16..];
340        let mut buf = ciphertext.to_vec();
341        match Aes128CbcDec::new(&key.into(), iv.into()).decrypt_padded_mut::<Pkcs7>(&mut buf) {
342            Ok(_) => Ok(()),
343            Err(e) => Err(PadbusterError::BadPaddingError(
344                ciphertext.to_vec(),
345                format!("{}", e),
346            )),
347        }
348    }
349
350    #[test]
351    fn test_encrypt_decrypt() {
352        let _ = simple_logger::SimpleLogger::new()
353            .with_level(log::Level::Info.to_level_filter())
354            .init();
355
356        let plaintext = b"Hello world!Hello world!";
357        let current_plaintext = *plaintext;
358        let oracle = PaddingOracle::new(16, 1, oracle_fn);
359
360        let iv: Vec<u8> = [].to_vec();
361
362        let ciphertext = oracle.encrypt(&plaintext.to_vec(), &iv).unwrap();
363        let plaintext = oracle.decrypt(&ciphertext, &iv).unwrap();
364        assert_eq!(
365            current_plaintext.to_vec(),
366            unpad(plaintext[32..].to_vec(), 16).unwrap()
367        );
368    }
369
370    #[test]
371    fn test_decrypt() {
372        let _ = simple_logger::SimpleLogger::new()
373            .with_level(log::Level::Info.to_level_filter())
374            .init();
375
376        let key = hex!("000102030405060708090a0b0c0d0e0f");
377        let iv = hex!("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
378        let plaintext = b"Hello world!Hello world!";
379        // buffer must have enough space for message+padding
380        let mut buffer = vec![0u8; plaintext.len() + (16 - plaintext.len() % 16) % 16];
381        // copy message to the buffer
382        let pos = plaintext.len();
383        buffer[..pos].copy_from_slice(plaintext);
384        let ciphertext = Aes128CbcEnc::new(&key.into(), &iv.into())
385            .encrypt_padded_mut::<Pkcs7>(&mut buffer, pos)
386            .unwrap();
387
388        let mut to_decrypt = iv.to_vec();
389        to_decrypt.append(&mut ciphertext.to_vec());
390
391        let iv: Vec<u8> = [].to_vec();
392        let oracle = PaddingOracle::new(16, 1, oracle_fn);
393        let decrypted = oracle.decrypt(&to_decrypt, &iv).unwrap();
394
395        assert_eq!(unpad(decrypted[32..].to_vec(), 16).unwrap(), plaintext);
396    }
397
398    #[test]
399    fn test_xor_data() {
400        let v1: Vec<u8> = vec![1, 0, 0, 1];
401        let v2: Vec<u8> = vec![1, 1];
402        let expected: Vec<u8> = vec![0, 1, 1, 0];
403
404        assert_eq!(xor_data(v1, v2), expected);
405    }
406
407    #[test]
408    fn test_aes_crypt() {
409        let key = hex!("000102030405060708090a0b0c0d0e0f");
410        let iv = hex!("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
411        let plaintext = b"Hello world!";
412        // buffer must have enough space for message+padding
413        let mut buffer = vec![0u8; plaintext.len() + (16 - plaintext.len() % 16) % 16];
414        // copy message to the buffer
415        let pos = plaintext.len();
416        buffer[..pos].copy_from_slice(plaintext);
417        let ciphertext = Aes128CbcEnc::new(&key.into(), &iv.into())
418            .encrypt_padded_mut::<Pkcs7>(&mut buffer, pos)
419            .unwrap();
420
421        assert_eq!(ciphertext, hex!("1b7a4c403124ae2fb52bedc534d82fa8"));
422
423        // re-create cipher mode instance
424        let mut buf = ciphertext.to_vec();
425        let decrypted_ciphertext = Aes128CbcDec::new(&key.into(), &iv.into())
426            .decrypt_padded_mut::<Pkcs7>(&mut buf)
427            .unwrap();
428
429        assert_eq!(decrypted_ciphertext, plaintext);
430    }
431}