12_byte_ecb_decryption_1.c (4597B)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | // Byte-at-a-time ECB decryption (Simple) #include "aes.h" #include "base64.h" #include "random.h" #include <assert.h> /* * copy your oracle function to a new function that encrypts buffers under ecb * mode using a consistent but unknown key (for instance, assign a single random * key, once, to a global variale). now take that same function and have it * append to the plaintext before encrypting the following string: * Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkgaGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBqdXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg * YnkK. spoiler alert: do not decode this string now. base64 decode the string * before appending it. do note base64 decode the string by hand, make your code * do it. the point is that you don't know its contents. what you have now is a * function that produces 'aes_128_ecb(your_string || unknown_string, * random_key)'. it turns out, you can decrypt 'unknown_string' with repeated * calls to the oracle function. here's roughly how: * 1. feed identical bytes of 'your_string' to the function 1 at a time. start * with 1 byte 'A', then 'AA', then 'AAA' and so on. discover the block size of * the cipher. you know it, but do this step anyway * 2. detect that the function is using ecb. you already know, but do this step * anyway as well * 3. knowing the block sizee, craft an input block that is exactly 1 byte short * (for instance, if the block size is 8 bytes, make 'AAAAAAA'). think about * what the oracle function is going to put in that last byte position * 4. make a dictionary of every possible last byte by feeding different strings * to the oracle. for instance, 'AAAAAAAA', 'AAAAAAAB', 'AAAAAAAC', remembering * the first block of each invocation * 5. match the output of the one byte short input to one ofthe entries in your * dictionary. you've now discovered the first byte of 'unknown_string' * 6. repeat for the next byte * this is the first challenge whose solution will break real crypto. lots of * people know that when you encrypt something in ecb mode, you can see penguins * through it. not so many of them can decrypt the content of those ciphertexts, * and now you can. if our experience is any guideline, this attack will get you * code execution in security tests about once a year. */ const unsigned char *key; char *secret; size_t secret_len; struct bytes append_encrypt_ecb(char *data, size_t data_len) { size_t input_len = data_len + secret_len; char *input = (char *)malloc(input_len * sizeof(char)); memcpy(input, data, data_len); memcpy(input + data_len, secret, secret_len); struct bytes res = aes_encrypt_ecb(input, input_len, key, 16); free(input); return res; } struct bytes probe_encrypt(size_t len, char *suffix, size_t suffix_len) { char *input = (char *)malloc(len * sizeof(char)); memset(input, 'A', len - suffix_len); memcpy(input + len - suffix_len, suffix, suffix_len); struct bytes res = append_encrypt_ecb(input, len); free(input); return res; } int main(void) { srand(time(NULL)); key = random_bytes(16); struct bytes encoded, saved = {NULL, 0}; int block_size, i; char *base64 = "Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkga" "GFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBqdX" "N0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg"; secret_len = base64_decode(base64, strlen(base64), &secret); struct bytes zero_encoded = append_encrypt_ecb("", 0); printf("Ciphertext size: %zu\n", zero_encoded.len); for (i = 1; i < 32; i++) { encoded = probe_encrypt(i, 0, 0); block_size = encoded.len - zero_encoded.len; if (block_size > 0) break; } size_t res_len = zero_encoded.len - i; printf("Block size: %d\n", block_size); printf("Secret size: %zu\n", res_len); char ones[64] = {1}; encoded = append_encrypt_ecb(ones, 64); char *detected_mode = aes_oracle(encoded.data, block_size); printf("%s mode\n\n", detected_mode); char *res = (char *)malloc(res_len * sizeof(char)); for (size_t i = 0; i < res_len; i++) { size_t sz = next_block(i, block_size); saved = probe_encrypt(sz - i - 1, 0, 0); for (unsigned char c = 1; c <= 128; c++) { res[i] = c; res[i + 1] = 0; encoded = probe_encrypt(sz, res, i + 1); if (!memcmp(encoded.data, saved.data, sz)) break; } } printf("%s\n", res); assert(!strcmp(res, secret)); free(res); free(zero_encoded.data); free(encoded.data); free(saved.data); return 0; } |