Sometimes, IV in CBC mode can be leaked, if a plaintext contains many zeroes or padding bytes.
Let's see.
This is a real case from my practice. I knew the AES key, but I had no idea that CBC is used and I tried to decrypt using ECB mode.
#!/usr/bin/env python3 import os, sys, binascii from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes key = b"some highly secret key 123456789" IV=b"init vector here" def encr(msg): cipher = Cipher(algorithms.AES(key), modes.CBC(IV)) encryptor = cipher.encryptor() return encryptor.update(msg) + encryptor.finalize() def decr_with_IV(msg): cipher = Cipher(algorithms.AES(key), modes.CBC(IV)) decryptor = cipher.decryptor() return decryptor.update(msg) + decryptor.finalize() def decr_without_IV(msg): cipher = Cipher(algorithms.AES(key), modes.ECB()) decryptor = cipher.decryptor() return decryptor.update(msg) + decryptor.finalize() msg=b"Saturday" padded_msg=msg+b"\0"*(16-len(msg)) encrypted=encr(padded_msg) print (decr_with_IV(encrypted)) print (decr_without_IV(encrypted))
b'Saturday\x00\x00\x00\x00\x00\x00\x00\x00' b':\x0f\x1d\x01R\x12\x04\x1ator here'
I saw only the ending of IV, but I quickly deduced its full value.
(See my another blog post about PKCS#7 padding.)
5-byte input file:
test
Encrypt with CBC mode:
# % echo "hello, world." | xxd -g 1 # 00000000: 68 65 6c 6c 6f 2c 20 77 6f 72 6c 64 2e 0a hello, world.. openssl enc -aes-128-cbc -nosalt -e \ -in input -out output \ -K '112233445566778899aabbccddeeff00' -iv '68656c6c6f2c20776f726c642e2e2e2e'
Would be decrypted back:
openssl enc -aes-128-cbc -nosalt -d \ -in output -out output2 \ -K '112233445566778899aabbccddeeff00' -iv '68656c6c6f2c20776f726c642e2e2e2e'
But not in ECB mode, because of padding present:
openssl enc -aes-128-ecb -nosalt -d \ -in output -out output2 \ -K '112233445566778899aabbccddeeff00'
bad decrypt 40471EA6F7720000:error:1C800064:Provider routines:ossl_cipher_unpadblock:bad decrypt:../providers/implementations/ciphers/ciphercommon_block.c:124:
A Python code to decrypt it, also, to cancel padding bytes:
#!/usr/bin/env python3 import os, sys, binascii from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes key=b"\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x00" IV=b"\x68\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72\x6c\x64\x2e\x2e\x2e\x2e" def decr_with_IV(msg): cipher = Cipher(algorithms.AES(key), modes.CBC(IV)) decryptor = cipher.decryptor() plaintext = decryptor.update(msg) + decryptor.finalize() return plaintext def decr_without_IV(msg): cipher = Cipher(algorithms.AES(key), modes.ECB()) decryptor = cipher.decryptor() plaintext = decryptor.update(msg) + decryptor.finalize() return plaintext f=open("output", "rb") x=decr_with_IV(f.read()) print (x) f=open("output", "rb") x=decr_without_IV(f.read()) print (list(map(lambda i: chr(i^0xb), x)))
In first case you see correct decryption and padding bytes. In second -- IV is leaked.
b'test\n\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b' ['\x17', '\x0b', '\x14', '\x13', 'n', ',', ' ', 'w', 'o', 'r', 'l', 'd', '.', '.', '.', '.']
Without knowing padding length, it can be enumerated/bruteforced easily.
Some time ago (before 24-Mar-2025) there was Disqus JS script for comments. I dropped it --- it was so motley, distracting, animated, with too much ads. I never liked it. Also, comments didn"t appeared correctly (Disqus was buggy). Also, my blog is too chamberlike --- not many people write comments here. So I decided to switch to the model I once had at least in 2020 --- send me your comments by email (don"t forget to include URL to this blog post) and I"ll copy&paste it here manually.
Let"s party like it"s ~1993-1996, in this ultimate, radical and uncompromisingly primitive pre-web1.0-style blog and website.