|
| 1 | +## Cryptography - Easy CBC |
| 2 | + |
| 3 | +In this challenge, we were given an image that has been encrypted. |
| 4 | + |
| 5 | + |
| 6 | + |
| 7 | +This image has been encrypted with the following [Python script](assets/challenge.py) that implemented AES-CBC encryption. |
| 8 | + |
| 9 | +> AES-CBC (Advanced Encryption Standard - Cipher Block Chaining) is a method of encrypting data to keep it secure. It works by dividing the data into blocks, and then encrypting each block with a secret key using the AES algorithm. The blocks are linked together in a chain, with each block XORed with the previous block before being encrypted. This helps to make it harder for patterns in the plaintext to be seen in the ciphertext. |
| 10 | +
|
| 11 | +The code snippet below. |
| 12 | + |
| 13 | +```python |
| 14 | +# !pip install certifi==2021.10.8 |
| 15 | +# !pip install cffi==1.15.0 |
| 16 | +# !pip install cryptography==36.0.2 |
| 17 | +# !pip install Pillow==9.0.1 |
| 18 | +# !pip install wincertstore==0.2 |
| 19 | +import os |
| 20 | +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes |
| 21 | +from cryptography.hazmat.backends import default_backend |
| 22 | +import PIL.Image as Image |
| 23 | + |
| 24 | +class CBCEncryption: |
| 25 | + def __init__(self, key, iv): |
| 26 | + self.cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) |
| 27 | + self.encryptor = self.cipher.encryptor() |
| 28 | + |
| 29 | + def encrypt(self, image): |
| 30 | + return self.encryptor.update(image) |
| 31 | + |
| 32 | + def finalize_encrypt(self): |
| 33 | + return self.encryptor.finalize() |
| 34 | + |
| 35 | +def EncryptImage(encryption, image, output): |
| 36 | + output = output + '.bmp' |
| 37 | + image = Image.open(image) |
| 38 | + image.save('temp.bmp') |
| 39 | + with open('temp.bmp', 'rb') as reader: |
| 40 | + with open(output, 'wb') as writer: |
| 41 | + image_data = reader.read() |
| 42 | + header, body = image_data[:54], image_data[54:] |
| 43 | + body += b'\x35' * (16 -(len(body) % 16)) |
| 44 | + body = encryption.encrypt(body) + encryption.finalize_encrypt() |
| 45 | + writer.write(header + body) |
| 46 | + writer.close() |
| 47 | + reader.close() |
| 48 | + os.remove('temp.bmp') |
| 49 | + |
| 50 | +def main(): |
| 51 | + key = b'JOINTSCTF2023' |
| 52 | + key = key.ljust(32, b'\x35') |
| 53 | + |
| 54 | + iv = key[:16] |
| 55 | + iv = bytearray(iv) |
| 56 | + for i in range(16): |
| 57 | + iv[i] = iv[i] ^ 0x35 |
| 58 | + iv = bytes(iv) |
| 59 | + |
| 60 | + AesCbc = CBCEncryption(key, iv) |
| 61 | + EncryptImage(encryption=AesCbc, image='flag.jpg', output='out') |
| 62 | + |
| 63 | +if __name__ == '__main__': |
| 64 | + main() |
| 65 | +``` |
| 66 | + |
| 67 | +It defines a class called `CBCEncryption` to handle encryption using the provided `key` and initialization `vector (IV)`, and a function called `EncryptImage` to encrypt the image file. The `key` and `iv` values are manipulated to ensure they are of the correct length. The main function initializes the encryption class with a `key` and `IV`, and then calls the `EncryptImage` function to encrypt an image file called "flag.jpg" and save the output as "out.bmp". |
| 68 | + |
| 69 | +The `EncryptImage` function opens the input image file and saves a temporary copy of it as a BMP file. It then reads the image data from the temporary file, splits the data into a header section and a body section, and pads the body section with additional data to make it a multiple of 16 bytes long. It then uses the provided `CBCEncryption` object to encrypt the padded body section, and saves the encrypted data along with the original header section as the output BMP file. |
| 70 | + |
| 71 | +To decrypt the image, we should do the reverse engineering using the same `key` and `IV` that were used to encrypt the data, and perform the decryption process in the reverse order of the encryption process. Beside that, it also important to unpad the decrypted data to remove any additional bytes that were added during the padding process. The decryptor script can be seen in [here](decrypt.py). |
| 72 | + |
| 73 | +The code snippet below. |
| 74 | + |
| 75 | +```python |
| 76 | +import os |
| 77 | +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes |
| 78 | +from cryptography.hazmat.backends import default_backend |
| 79 | +import PIL.Image as Image |
| 80 | + |
| 81 | +# define a class for CBC decryption |
| 82 | +class CBCDecryption: |
| 83 | + # initialization function to set up the cipher and decryptor |
| 84 | + def __init__(self, key, iv): |
| 85 | + self.cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) |
| 86 | + self.decryptor = self.cipher.decryptor() |
| 87 | + |
| 88 | + # function to decrypt ciphertext |
| 89 | + def decrypt(self, ciphertext): |
| 90 | + return self.decryptor.update(ciphertext) |
| 91 | + |
| 92 | +# function to decrypt an image using a CBC decryption object |
| 93 | +def DecryptImage(decryption, image, output): |
| 94 | + # open the input image file and create the output file |
| 95 | + with open(image, 'rb') as reader: |
| 96 | + with open(output, 'wb') as writer: |
| 97 | + # read the image data and separate the header and body |
| 98 | + image_data = reader.read() |
| 99 | + header, body = image_data[:54], image_data[54:] |
| 100 | + # decrypt the body of the image |
| 101 | + body = decryption.decrypt(body) |
| 102 | + # write the decrypted image data to the output file |
| 103 | + writer.write(header + body) |
| 104 | + writer.close() |
| 105 | + reader.close() |
| 106 | + |
| 107 | +# main function to set up decryption object and decrypt image |
| 108 | +def main(): |
| 109 | + # set up the key and IV for decryption |
| 110 | + key = b'JOINTSCTF2023' |
| 111 | + key = key.ljust(32, b'\x35') |
| 112 | + iv = key[:16] |
| 113 | + iv = bytearray(iv) |
| 114 | + for i in range(16): |
| 115 | + iv[i] = iv[i] ^ 0x35 |
| 116 | + iv = bytes(iv) |
| 117 | + |
| 118 | + # create a CBC decryption object with the key and IV |
| 119 | + AesCbc = CBCDecryption(key, iv) |
| 120 | + # decrypt the image using the decryption object |
| 121 | + DecryptImage(decryption=AesCbc, image='out.bmp', output='flag_decrypted.jpg') |
| 122 | + |
| 123 | +if __name__ == '__main__': |
| 124 | + main() |
| 125 | +``` |
| 126 | + |
| 127 | +Run it, and we got the flag. |
| 128 | + |
| 129 | + |
| 130 | + |
| 131 | +</br> |
| 132 | + |
| 133 | +So, this is the flag. |
| 134 | + |
| 135 | +``` |
| 136 | +JCTF2023{n4rim0_in9_pAndum} |
| 137 | +``` |
0 commit comments