challenge.py
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 |
import binascii import hashlib import sys from Crypto.Cipher import AES key = b'*XhN2*8d%8Slp3*v' key_len = len(key) def pad(message): padding = bytes((key_len - len(message) % key_len) * chr(key_len - len(message) % key_len), encoding='utf-8') return message + padding def encrypt(message, key, iv): aes = AES.new(key, AES.MODE_CBC, iv) return aes.encrypt(message) h = hashlib.sha256(key).hexdigest() hidden = binascii.unhexlify(h)[:10] message = b'CBC (Cipher Blocker Chaining) is an advanced form of block cipher encryption' + hidden with open('flag', 'rb') as f: IV = f.read().strip(b'TMUCTF{').strip(b'}') print(binascii.hexlify(encrypt(pad(message), key, IV))) ''' output.txt :9**********b4381646*****01********************8b9***0485******************************0**ab3a*cc5e**********18a********5383e7f**************1b3*******9f43fd66341f3ef3fab2bbfc838b9ef71867c3bcbb ''' |
Summary:
Sebuah plaintext terdiri dari kalimat “CBC (Cipher Blocker Chaining) is an advanced form of block cipher encryption” disambung dengan 10 byte pertama hasil hash SHA256 dari KEY. Plaintext tersebut diencrypt dengan algoritma AES metode CBC (Cipher Block Chaining). Kita diberikan sebagian plaintext (sebagian lagi merupakan hasil hash dari KEY), serta KEY dan ciphertext yang beberapa karakternya sengaja dihilangkan. Flagnya bisa kita dapatkan dengan menemukan IV (Initialization Vector) kodingan tersebut.
Cara kerja AES CBC

- Untuk Encrypt, plaintext akan dibagi terlebih dahulu menjadi beberapa bagian/block (pada soal ini, tiap blocknya terdiri dari 16 bytes Plaintext).
- Block Plaintext pertama akan di-XOR dengan serangkaian bytes yang disebut IV (Initialization Vector).
- Hasil XOR akan di-encrypt dengan sebuah KEY, didapatkan block Cipher pertama.
- Block Plaintext kedua akan di-XOR dengan block Cipher pertama, lalu di-encrypt. Didapatkan block Cipher kedua.
- Block Plaintext ketiga akan di-XOR dengan block Cipher kedua, lalu di-encrypt. Didapatkan block Cipher ketiga. Begitu seterusnya hingga seluruh Plaintext ter-encrypt.
- Sambungkan semua Block Ciphertext dari yang pertama sampai terakhir, didapatkan ciphertext lengkap.
Lalu untuk Decrypt bagaimana? Tinggal kita balik saja prosesnya:
Kita mulai dari Block Cipher terakhir, Block tersebut akan di-decrypt dengan sebuah KEY, hasilnya kita XOR dengan Block Cipher urutan sebelumnya. Didapatkan Block Plaintext urutan terakhir. Ulangi terus hingga sampai ke Block Cipher pertama, kita decrypt, XOR dengan Initialization Vector, dan dapatkan Block Plaintext pertama. Sambungkan semua Block Plaintext dari yang pertama sampai terakhir, didapatkan plaintext lengkap.
Penyelesaian soal
Barusan kita tahu bahwa proses Decrypt menghasilkan Plaintext dari Block-block Ciphertext dan Initialization Vector yang di-XOR. Nah karena kita diberikan Ciphertext, bisakah kita mencari tahu nilai IV serta block-block ciphertext yang hilang? Tentu saja bisa, dengan prinsip XOR (a^b = c maka a^c = b). Untuk KEY karena hanya 3 karakter yang hilang, tinggal kita lakukan bruteforce untuk menemukannya dan secara bersamaan kita juga mem-brute force sebagian Plaintext yang tidak diberitahu.
Pertama-tama kita pecah dulu Plaintext dan Ciphertext per 16 byte.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Plaintext P1 = CBC (Cipher Bloc P2 = ker Chaining) is P3 = an advanced for P4 = m of block ciphe P5 = r encryption**** P6 = ******@@@@@@@@@@ * adalah karakter-karakter hidden yang berasal dari hasil hash KEY @ adalah padding dari fungsi pad Ciphertext C1 = 9**********b4381646*****01****** C2 = **************8b9***0485******** C3 = **********************0**ab3a*cc C4 = 5e**********18a********5383e7f** C5 = ************1b3*******9f43fd6634 C6 = 1f3ef3fab2bbfc838b9ef71867c3bcbb |
Untungnya Block Cipher terakhir (C6) diberikan secara utuh, jadi mempermudah kita dalam melakukan Bruteforce. Block C6 ini akan kita decrypt dengan KEY yang kita Brute Force, di-XOR dengan Block Plaintext terakhir (P6) dan jika potongan “9f43fd6634” dari C5 ada pada hasil XOR, maka KEY bisa kita temukan dan operasinya berada di arah yang benar. Gunakan kodingan berikut:
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 |
def pad(message): padding = bytes((key_len - len(message) % key_len) * chr(key_len - len(message) % key_len), encoding='utf-8') return message + padding def encrypt(message, key, iv): aes = AES.new(key, AES.MODE_CBC, iv) return aes.encrypt(message) def decrypt(message, key, iv): aes = AES.new(key, AES.MODE_CBC, iv) return aes.decrypt(message) Go = True for i in range(33, 127): if Go: for j in range(33, 127): if Go: for k in range(33, 127): key = (chr(i) + 'XhN2' + chr(j)+'8d%8Slp3'+chr(k)+'v').encode() key_len = len(key) h = hashlib.sha256(key).hexdigest() hidden = binascii.unhexlify(h)[:10] message = b'CBC (Cipher Blocker Chaining) is an advanced form of block cipher encryption' + hidden padded = pad(message) ori = binascii.hexlify(padded[80:]) IV = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ciphertext = bytes.fromhex("1f3ef3fab2bbfc838b9ef71867c3bcbb")#C6 (only known ciphertext) decrypted = binascii.hexlify(decrypt(ciphertext, key, IV)) res = hex(int(decrypted, 16)^int(ori, 16))[2:] if "9f43fd6634" in str(res): print(f"HOLLLLDDDD! key: {key} prevcipher:{res}") Go = False break |
*catatan:
- Nilai IV saya isi dengan b”\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00″ agar hasil fungsi decrypt tidak berubah karena di-XOR.
- Ori dimulai dari index 80 ke ujung sebagai index dari Block Plaintext P6
hasilnya:

Yes! ternyata dengan KEY 0XhN2!8d%8Slp3Ov kita menemukan nilai lengkap Block Cipher C5, yaitu 9c1a9d16795c1b334d6ec49f43fd6634. Nah dari sini kita tidak perlu lagi melakukan brute force, cukup lakukan metode:
- Decrypt Block Cipher ke-n dengan KEY yang sudah ketemu
- Hasil Decrypt di-XOR dengan Block Plaintext ke-n
- Didapatkan Block Cipher ke-(n-1).
Lakukan metode tersebut dan didapatkanlah Block-Block Ciphertext fullnya seperti ini:
1 2 3 4 5 6 |
C1 = 9f4ac903118b43816462b35101a7b6fe C2 = 8bc67a00c0e5198b9d0304858953eb83 C3 = 959349e93481869c836d900dcab3a6cc C4 = 5e5969dcdb9b18ac33993785383e7f32 C5 = 9c1a9d16795c1b334d6ec49f43fd6634 C6 = 1f3ef3fab2bbfc838b9ef71867c3bcbb |
Ketika sampai di Block Cipher C1, proses rekonstruksi akan memberikan nilai Initialization Vector (alias flag yang dicari) yaitu 5930555f4433437259503733445f3137. Ubah dari hex menjadi ascii, dapatkan flagnya Y0U_D3CrYP73D_17.
FULL Codingan:
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 |
import binascii import hashlib import sys from Crypto.Cipher import AES import string def pad(message): padding = bytes((key_len - len(message) % key_len) * chr(key_len - len(message) % key_len), encoding='utf-8') return message + padding def encrypt(message, key, iv): aes = AES.new(key, AES.MODE_CBC, iv) return aes.encrypt(message) def decrypt(message, key, iv): aes = AES.new(key, AES.MODE_CBC, iv) return aes.decrypt(message) Go = True for i in range(33, 127): if Go: for j in range(33, 127): if Go: for k in range(33, 127): key = (chr(i) + 'XhN2' + chr(j)+'8d%8Slp3'+chr(k)+'v').encode() key_len = len(key) h = hashlib.sha256(key).hexdigest() hidden = binascii.unhexlify(h)[:10] message = b'CBC (Cipher Blocker Chaining) is an advanced form of block cipher encryption' + hidden padded = pad(message) ori = binascii.hexlify(padded[80:]) IV = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ciphertext = bytes.fromhex("1f3ef3fab2bbfc838b9ef71867c3bcbb")#C6 (only known ciphertext) decrypted = binascii.hexlify(decrypt(ciphertext, key, IV)) res = hex(int(decrypted, 16)^int(ori, 16))[2:] if "9f43fd6634" in str(res): print(f"HOLLLLDDDD! key: {key} prevcipher:{res}") Go = False break h = hashlib.sha256(key).hexdigest() hidden = binascii.unhexlify(h)[:10] message = b'CBC (Cipher Blocker Chaining) is an advanced form of block cipher encryption' + hidden padded = pad(message) for i in range(80, 0, -16): ori = binascii.hexlify(padded[i-16:i]) IV = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ciphertext = binascii.unhexlify(res) decrypted = binascii.hexlify(decrypt(ciphertext, key, IV)) res = hex(int(decrypted, 16)^int(ori, 16))[2:] print(f"prevcipher:{str(res)}") print('should be iv: ',bytes.fromhex(res).decode()) |
Output:

Flag: TMUCTF{Y0U_D3CrYP73D_17}