# HOW AES WORKS ## Keyed Permutations ![image](https://hackmd.io/_uploads/rJN3zQF5p.png) - Chall giới thiệu về hoán vị khóa. - Search gg ta thu được flag. ![image](https://hackmd.io/_uploads/r1OsMXtca.png) ## Resisting Bruteforce ![image](https://hackmd.io/_uploads/BJzJQmt5T.png) - Search gg ta nhận được thông tin cần biết, đó chính là flag. ![image](https://hackmd.io/_uploads/S1CINXK56.png) - Biclique attack là một biến thể của meet in the middle - Flag: `crypto{Biclique}` ## Structure of AES ![image](https://hackmd.io/_uploads/ryj3HQY56.png) - Chall cung cấp thông tin về AES cho ta, cũng như cách chuyển 1 đoạn plantext thành từng phần mỗi phần 16 bytes, rồi xếp mỗi phần đó thành ma trận 4x4. ```Python def bytes2matrix(text): """ Converts a 16-byte array into a 4x4 matrix. """ return [list(text[i:i+4]) for i in range(0, len(text), 4)] def matrix2bytes(matrix): """ Converts a 4x4 matrix into a 16-byte array. """ return bytes(sum(matrix, [])) matrix = [ [99, 114, 121, 112], [116, 111, 123, 105], [110, 109, 97, 116], [114, 105, 120, 125], ] print(matrix2bytes(matrix)) ``` - Flag: `crypto{inmatrix}` ## Round Keys ![image](https://hackmd.io/_uploads/HJ4JPmY56.png) - Chall cung cấp thông tin về AddRoundKey . - Với chall này ta chỉ cần xor từng phần tử trong matrix với phần từ tương ứng trong matrix state. ```Python from pwn import * state = [ [206, 243, 61, 34], [171, 11, 93, 31], [16, 200, 91, 108], [150, 3, 194, 51], ] round_key = [ [173, 129, 68, 82], [223, 100, 38, 109], [32, 189, 53, 8], [253, 48, 187, 78], ] def add_round_key(s, k): x= str() for i in range(4): for j in range(4): s[i][j]= (xor(state[i][j], round_key[i][j])) a= (s[i][j]).decode() x+= str(a) return x print(add_round_key(state, round_key)) ``` - Flag: `crypto{r0undk3y}` ## Confusion through Substitution ![image](https://hackmd.io/_uploads/HkBba7F56.png) - Chall cho ta hiểu về SubBytes với S-Box. ```Python s_box = ( 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16, ) inv_s_box = ( 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D, ) state = [ [251, 64, 182, 81], [146, 168, 33, 80], [199, 159, 195, 24], [64, 80, 182, 255], ] def matrix2bytes(matrix): """ Converts a 4x4 matrix into a 16-byte array. """ return bytes(sum(matrix, [])) def sub_bytes(s, sbox): for i in range(4): for j in range(4): s[i][j] = sbox[s[i][j]] return s print(matrix2bytes(sub_bytes(state, inv_s_box))) ``` - Flag: `crypto{l1n34rly}` ## Diffusion through Permutation ![image](https://hackmd.io/_uploads/SyMPCmK9p.png) - Chall cung cấp thông tin về ShiftRows và MixColumn. - Tuy nhiên hàm ShiftRows chall này lại dịch chuyển theo hàng ngang thay vì dọc như ta từng biết. ```Python def shift_rows(s): s[0][1], s[1][1], s[2][1], s[3][1] = s[1][1], s[2][1], s[3][1], s[0][1] s[0][2], s[1][2], s[2][2], s[3][2] = s[2][2], s[3][2], s[0][2], s[1][2] s[0][3], s[1][3], s[2][3], s[3][3] = s[3][3], s[0][3], s[1][3], s[2][3] def inv_shift_rows(s): s[1][1], s[2][1], s[3][1], s[0][1] = s[0][1], s[1][1], s[2][1], s[3][1] s[2][2], s[3][2], s[0][2], s[1][2] = s[0][2], s[1][2], s[2][2], s[3][2] s[3][3], s[0][3], s[1][3], s[2][3] = s[0][3], s[1][3], s[2][3], s[3][3] # learned from http://cs.ucsb.edu/~koc/cs178/projects/JT/aes.c xtime = lambda a: (((a << 1) ^ 0x1B) & 0xFF) if (a & 0x80) else (a << 1) def mix_single_column(a): # see Sec 4.1.2 in The Design of Rijndael t = a[0] ^ a[1] ^ a[2] ^ a[3] u = a[0] a[0] ^= t ^ xtime(a[0] ^ a[1]) a[1] ^= t ^ xtime(a[1] ^ a[2]) a[2] ^= t ^ xtime(a[2] ^ a[3]) a[3] ^= t ^ xtime(a[3] ^ u) def mix_columns(s): for i in range(4): mix_single_column(s[i]) def inv_mix_columns(s): # see Sec 4.1.3 in The Design of Rijndael for i in range(4): u = xtime(xtime(s[i][0] ^ s[i][2])) v = xtime(xtime(s[i][1] ^ s[i][3])) s[i][0] ^= u s[i][1] ^= v s[i][2] ^= u s[i][3] ^= v mix_columns(s) state = [ [108, 106, 71, 86], [96, 62, 38, 72], [42, 184, 92, 209], [94, 79, 8, 54], ] inv_mix_columns(state) inv_shift_rows(state) print(bytes(sum(state, []))) ``` - Flag: `crypto{d1ffUs3R}` ## Bringing It All Together ![image](https://hackmd.io/_uploads/HygsxNt56.png) - Chall này là tổng hợp của các chall trên. ```Python from pwn import* N_ROUNDS = 10 key = b'\xc3,\\\xa6\xb5\x80^\x0c\xdb\x8d\xa5z*\xb6\xfe\\' ciphertext = b'\xd1O\x14j\xa4+O\xb6\xa1\xc4\x08B)\x8f\x12\xdd' s_box = ( 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16, ) inv_s_box = ( 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D, ) def shift_rows(s): s[0][1], s[1][1], s[2][1], s[3][1] = s[1][1], s[2][1], s[3][1], s[0][1] s[0][2], s[1][2], s[2][2], s[3][2] = s[2][2], s[3][2], s[0][2], s[1][2] s[0][3], s[1][3], s[2][3], s[3][3] = s[3][3], s[0][3], s[1][3], s[2][3] def inv_shift_rows(s): s[1][1], s[2][1], s[3][1], s[0][1] = s[0][1], s[1][1], s[2][1], s[3][1] s[2][2], s[3][2], s[0][2], s[1][2] = s[0][2], s[1][2], s[2][2], s[3][2] s[3][3], s[0][3], s[1][3], s[2][3] = s[0][3], s[1][3], s[2][3], s[3][3] def matrix2bytes(matrix): s="" for i in matrix: for j in i: s=s+chr(j) return s def bytes2matrix(text): return [list(text[i:i+4]) for i in range(0, len(text), 4)] def expand_key(master_key): r_con = ( 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36, 0x6C, 0xD8, 0xAB, 0x4D, 0x9A, 0x2F, 0x5E, 0xBC, 0x63, 0xC6, 0x97, 0x35, 0x6A, 0xD4, 0xB3, 0x7D, 0xFA, 0xEF, 0xC5, 0x91, 0x39, ) key_columns = bytes2matrix(master_key) iteration_size = len(master_key) // 4 i = 1 while len(key_columns) < (N_ROUNDS + 1) * 4: word = list(key_columns[-1]) if len(key_columns) % iteration_size == 0: word.append(word.pop(0)) word = [s_box[b] for b in word] word[0] ^= r_con[i] i += 1 elif len(master_key) == 32 and len(key_columns) % iteration_size == 4: word = [s_box[b] for b in word] word = bytes(i^j for i, j in zip(word, key_columns[-iteration_size])) key_columns.append(word) return [key_columns[4*i : 4*(i+1)] for i in range(len(key_columns) // 4)] def xtime(a): return (((a << 1) ^ 0x1B) & 0xFF) if (a & 0x80) else (a << 1) def mix_single_column(a): t = a[0] ^ a[1] ^ a[2] ^ a[3] u = a[0] a[0] ^= t ^ xtime(a[0] ^ a[1]) a[1] ^= t ^ xtime(a[1] ^ a[2]) a[2] ^= t ^ xtime(a[2] ^ a[3]) a[3] ^= t ^ xtime(a[3] ^ u) def mix_columns(s): for i in range(4): mix_single_column(s[i]) def inv_mix_columns(s): for i in range(4): u = xtime(xtime(s[i][0] ^ s[i][2])) v = xtime(xtime(s[i][1] ^ s[i][3])) s[i][0] ^= u s[i][1] ^= v s[i][2] ^= u s[i][3] ^= v mix_columns(s) def sub_bytes(s, sbox): for i in range(4): for j in range(4): s[i][j] = sbox[s[i][j]] return s def add_round_key(s, k): return xor(s, k) def decrypt(key, ciphertext): round_keys = expand_key(key) state = bytes2matrix(ciphertext) state = bytes2matrix(add_round_key(state, round_keys[10])) for i in range(N_ROUNDS - 1, 0, -1): inv_shift_rows(state) state = bytes2matrix(sub_bytes(state, inv_s_box)) state = bytes2matrix(add_round_key(state, round_keys[i])) inv_mix_columns(state) inv_shift_rows(state) state = bytes2matrix(sub_bytes(state, inv_s_box)) state = bytes2matrix(add_round_key(state, round_keys[0])) return matrix2bytes(state) print(decrypt(key, ciphertext)) ``` - Flag: `crypto{MYAES128}` # SYMMETRIC STARTER ## Modes of Operation Starter ![image](https://hackmd.io/_uploads/Hy26N4Yc6.png) - Chall có liên quan đến AES mode ECB. - Đầu tiên ta sẽ lấy output của encrypt chính là ciphertext, sau đó gửi ciphertext đi để decrypt là ta thu được flag. ```Python import requests BASE_URL = "http://aes.cryptohack.org/block_cipher_starter" r = requests.get(f"{BASE_URL}/encrypt_flag") data = r.json() ciphertext = data["ciphertext"] r = requests.get(f"{BASE_URL}/decrypt/{ciphertext}") data = r.json() plaintext = data["plaintext"] print(bytearray.fromhex(plaintext).decode()) ``` - Flag: `crypto{bl0ck_c1ph3r5_4r3_f457_!}` ## Passwords as Keys ![image](https://hackmd.io/_uploads/HykOP4Fq6.png) - Chall này là AES mode ECB với key là md5 của 1 key có trong [link sau](https://gist.githubusercontent.com/wchargin/8927565/raw/d9783627c731268fb2935a731a618aa8e95cf465/words). - Ta lưu nội dung trong link về và tiến hành bruteforce. ```Python from Crypto.Cipher import AES import hashlib import requests url = 'http://aes.cryptohack.org/passwords_as_keys/' r = requests.get(f'{url}encrypt_flag/') first_request = r.json() ciphertext = first_request['ciphertext'] ciphertext = bytes.fromhex(ciphertext) with open("words.txt") as f: words = [w.strip() for w in f.readlines()] for w in words: key = hashlib.md5(w.encode()).digest() cipher = AES.new(key, AES.MODE_ECB) decrypted = cipher.decrypt(ciphertext) if b'crypto' in decrypted: print("plaintext", decrypted) exit ``` - Flag: `crypto{k3y5__r__n07__p455w0rdz?}` # Block Cipher ## ECB CBC WTF ![image](https://hackmd.io/_uploads/ryQuAkcc6.png) - Chall cung cấp thông tin về CBC, ECB. Source chall cho ta biết flag được mã hóa bằng CBC như lại được giải mã bằng ECB. - Ta có sơ đồ encrypt CBC: ![image](https://hackmd.io/_uploads/rkCfklq9a.png) - Sơ đồ decrypt ECB: ![image](https://hackmd.io/_uploads/S1kryg996.png) - Từ source ta biết output của encrypt sẽ là IV(16Bytes)+ encrypt. - Từ đó ta có được IV. Ta thấy CBC với ECB khác nhau ở bước xor. ```Python from Crypto.Cipher import AES from Crypto.Util.Padding import* from pwn import xor import requests url = 'https://aes.cryptohack.org/ecbcbcwtf/' def encrypt_flag(): r = requests.get(f"{url}/encrypt_flag") js = r.json() return (js["ciphertext"]) def decrypt(ciphertext): r = requests.get(f"{url}/decrypt/{ciphertext}") js = r.json() return (js["plaintext"]) cipher = encrypt_flag() plaintext = decrypt(cipher) iv1 = bytes.fromhex(cipher[:32]) ciphertext = cipher[32:] iv2 = bytes.fromhex(ciphertext[:32]) plaintext1 = bytes.fromhex(plaintext[32:64]) plaintext2 = bytes.fromhex(plaintext[64:]) print(xor(plaintext1, iv1) + xor(plaintext2, iv2)) ``` - Flag: `crypto{3cb_5uck5_4v01d_17_!!!!!}` ## ECB Oracle ![image](https://hackmd.io/_uploads/HkqHvxq9p.png) - ECB có điểm yếu là với những block plaintext giống nhau thì sẽ cho ra những ciphertext giống nhau. - Ta sẽ sẽ làm như sau: - Gửi plaintext 32 bytes với: 16 bytes đầu là các số 0+ 'crypto{' , 16 bytes sau: byte đầu chạy từ 33-> 128 các bytes còn lại là 0. - Sau đó ta so sánh 2 phần đầu cuối của cipher thu được, nếu nó giống nhau thì byte ta bruteforce là byte thuộc flag. ```Python import requests from Crypto.Cipher import AES flag = b'crypto{' def encrypt(plaintext): url = 'https://aes.cryptohack.org/ecb_oracle/encrypt/' + plaintext.hex() r = requests.get(url).json() return bytes.fromhex(r['ciphertext']) b = ''.join([chr(i) for i in range(32, 127)]) while True: plaintext = b'1' * (31 - len(flag)) target = encrypt(plaintext)[16:32] plaintext += flag found = False for j in b: plaintext_temp = plaintext[:31] + j.encode() bullet = encrypt(plaintext_temp)[16:32] if bullet == target: flag += j.encode() print(flag) found = True break if not found: break ``` - Flag: `crypto{p3n6u1n5_h473_3cb}` ## Flipping Cookie ![image](https://hackmd.io/_uploads/By6VZvcqT.png) - Chall liên quan đến mode CBC. Từ source ta biết được sever sẽ trả cho ta flag khi mà b"admin=True" nằm trong cookie. - Cookie ban đầu là `admin=False;expiry={expires_at}`. Ta cần chuyển về `admin=True`. - Đây là CBC decrypt: ![image](https://hackmd.io/_uploads/H10lND99p.png) - Ta có: $$ P= IV \oplus D(C) <=> (b'admin=False') =IV \oplus D(C)$$ $$<=> (b'admin=False') \oplus (b'admin=True') \oplus IV= D(C) \oplus (b'admin=True')$$ ```Python from Crypto.Cipher import AES from Crypto.Util.Padding import* from pwn import xor import requests def get_cookie(): url = "https://aes.cryptohack.org/flipping_cookie/get_cookie/" r = requests.get(url) js = r.json() return bytes.fromhex(js["cookie"]) def check_admin(cookie, iv): url = "http://aes.cryptohack.org/flipping_cookie/check_admin/" url += cookie.hex() + "/" + iv.hex() + "/" r = requests.get(url) js = r.json() print(js) cookie = get_cookie() adminfalse= (b"admin=False;expiry=")[:16] admintrue= pad(b"admin=True;", 16) IV= cookie[:16] IV= xor(xor(adminfalse, admintrue), IV) check_admin(cookie[16:32], IV) ``` - Flag: `crypto{4u7h3n71c4710n_15_3553n714l}` ## Lazy CBC ![image](https://hackmd.io/_uploads/H1Pyqw556.png) - Chall này là mode CBC, từ source ta biết được sever sẽ đưa flag cho ta khi mà ta gửi key đúng bằng KEY của chall. - `cipher = AES.new(KEY, AES.MODE_CBC, KEY)` ta thấy cipher mà hóa với IV= KEY. Dựa vào điều này ta sẽ tìm KEY để gửi. - Đây là CBC decrypt: ![image](https://hackmd.io/_uploads/H10lND99p.png) - Theo sơ đồ ta có: $$P_{i+1}= C_i \oplus D(C_{i+1}); C_0= IV$$$$=> P_1 \oplus P_2= C_0 \oplus D(C_1) \oplus C_1 \oplus D(C_2)$$ - Khi này nếu ta gửi tới sever 1 chuỗi với 32 bytes '0' thì ta có được: $$P_0 \oplus P_1= D(0) \oplus C_0 \oplus D(0) \oplus D(0)= C_0= IV= KEY$$ ```Python from Crypto.Cipher import AES from Crypto.Util.Padding import * from Crypto.Util.number import * from pwn import xor import requests def encrypt(plaintext): url = "https://aes.cryptohack.org/lazy_cbc/encrypt/" url += plaintext.hex() r = requests.get(url) js = r.json() return (js["ciphertext"]) def get_flag(key): url = "https://aes.cryptohack.org/lazy_cbc/get_flag/" url += key.hex() + "/" r = requests.get(url) js = r.json() print(bytes.fromhex(js["plaintext"])) def receice(ciphertext): url = "https://aes.cryptohack.org/lazy_cbc/receive/" url += ciphertext.hex() + "/" r = requests.get(url) js = r.json() return bytes.fromhex(js["error"][len("Invalid plaintext: "):]) ciphertext= long_to_bytes(0)* 32 plaintext = receice(ciphertext) get_flag(xor(plaintext[:16], plaintext[16:32])) ``` - Flag: `crypto{50m3_p30pl3_d0n7_7h1nk_IV_15_1mp0r74n7_?}` ## Triple DES ![image](https://hackmd.io/_uploads/Hy7Rxdq56.png) - Chall này là Triple DES, với chall này ta biết được điểm yếu của DES chính là các khóa yếu. Nên ta tiến hành brute force với các khóa yếu. - Source challenge: ```Python from Crypto.Cipher import AES from Crypto.Util.Padding import* from pwn import xor import requests def encrypt_flag(key): url = "https://aes.cryptohack.org/triple_des/encrypt_flag/" url += key r = requests.get(url) js = r.json() return (js["ciphertext"]) def encrypt(key, plaintext): url = "https://aes.cryptohack.org/triple_des/encrypt/" url += key + "/" + plaintext + "/" r = requests.get(url) js = r.json() print(bytes.fromhex(js["ciphertext"])) a= ['01010101', '01010101','FEFEFEFE', 'FEFEFEFE','E0E0E0E0', 'F1F1F1F1','1F1F1F1F', '0E0E0E0E'] for i in a: for j in a: for k in a: for f in a: try: key = i+ j+ k+ f ciphertext = encrypt_flag(key) flag = encrypt(key, ciphertext) except: continue ``` - Flag: `crypto{n0t_4ll_k3ys_4r3_g00d_k3ys}` # AUTHENTICATED ENCRYPTION ## Paper Plane ![image](https://hackmd.io/_uploads/SycZ9O5cp.png) ![image](https://hackmd.io/_uploads/SkBqGas9a.png) ```Python from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad import os KEY = ? FLAG = ? class AesIge: def __init__(self, key): self.cipher = AES.new(key, AES.MODE_ECB) def encrypt(self, data, m0=os.urandom(16), c0=os.urandom(16)): data = pad(data, 16, 'pkcs7') last_block_plaintext = m0 last_block_ciphertext = c0 result = b'' for i in range(0, len(data), 16): block = data[i: i + 16] x = AesIge._xor_blocks(block, last_block_ciphertext) x = self.cipher.encrypt(x) x = AesIge._xor_blocks(x, last_block_plaintext) result += x last_block_plaintext = block last_block_ciphertext = x return result, m0, c0 def decrypt(self, data, m0, c0): last_block_plaintext = m0 last_block_ciphertext = c0 result = b'' for i in range(0, len(data), 16): block = data[i: i + 16] x = AesIge._xor_blocks(block, last_block_plaintext) x = self.cipher.decrypt(x) x = AesIge._xor_blocks(x, last_block_ciphertext) result += x last_block_ciphertext = block last_block_plaintext = x if AesIge._is_pkcs7_padded(result): return unpad(result, 16, 'pkcs7') else: return None @staticmethod def _is_pkcs7_padded(message): padding = message[-message[-1]:] return all(padding[i] == len(padding) for i in range(0, len(padding))) @staticmethod def _xor_blocks(a, b): return bytes([x ^ y for x, y in zip(a, b)]) @chal.route('/paper_plane/encrypt_flag/') def encrypt_flag(): ciphertext, m0, c0 = AesIge(KEY).encrypt(FLAG.encode()) return {"ciphertext": ciphertext.hex(), "m0": m0.hex(), "c0": c0.hex()} @chal.route('/paper_plane/send_msg/<ciphertext>/<m0>/<c0>/') def send_msg(ciphertext, m0, c0): ciphertext = bytes.fromhex(ciphertext) m0 = bytes.fromhex(m0) c0 = bytes.fromhex(c0) if len(ciphertext) % 16 != 0: return {"error": "Data length must be a multiple of the blocksize!"} if len(c0) != 16 or len(m0) != 16: return {"error": "m0 and c0 must be 16 bytes long!"} plaintext = AesIge(KEY).decrypt(ciphertext, m0, c0) if plaintext is not None: return {"msg": "Message received"} else: return {"error": "Can't decrypt the message."} ``` - Trước hết thì ta sẽ tìm hiểu về `Padding Oracle Attack`. Hãy xem qua video [sau](https://www.youtube.com/watch?v=uDHo-UAM6_4). - Padding Oracle là một kỹ thuật lợi dụng việc xác thực padding của một thông điệp được mã hóa để giải mã nội dung thông điệp đó. - Ta có hiểu như sau: - Lấy ciphertext được mã hóa. - Sửa đổi một byte trong ciphertext. - Gửi ciphertext được chỉnh sửa đến sever. - Sever sẽ phản hồi thông tin về việc padding có hợp lệ hay không. - Phân tích phản hồi để suy ra vị trí byte bị lỗi trong padding. - Sửa đổi ciphertext một lần nữa để sửa lỗi padding. - Lặp lại quá trình này cho đến khi giải mã được toàn bộ plaintext. - Source solve: ```Python import requests from pwn import xor from Crypto.Util.number import * def encrypt_flag(): url = 'https://aes.cryptohack.org/paper_plane/encrypt_flag/' r = requests.get(url).json() return bytes.fromhex(r['ciphertext']), bytes.fromhex(r["m0"]), bytes.fromhex(r["c0"]) def send_msg(ciphertext, m0, c0): url = 'https://aes.cryptohack.org/paper_plane/send_msg/' url += ciphertext.hex() + "/" + m0.hex() + "/" + c0.hex() r = requests.get(url).json() return 'error' not in r def decrypt_block(ctt, m0, c0): plaintext = b"" new_xor = b"" for i in range(1, 17): tmp = c0[:16-i] for j in range(255, -1, -1): if len(plaintext) > 0: pad = long_to_bytes(i)*(i-1) send = tmp + long_to_bytes(j) + xor(pad, new_xor) else: send = tmp + long_to_bytes(j) if send_msg(ctt, m0, send): new_xor = xor(long_to_bytes(i),(j)) +new_xor plaintext = xor(xor(long_to_bytes(i),(j)), (c0[16-i:17-i])) + plaintext print(plaintext) break return plaintext ciphertext, m0, c0 = encrypt_flag() print(c0.hex()) ciphertext1 = ciphertext[:16] ciphertext2 = ciphertext[16:] pt1 = decrypt_block(ciphertext1, m0, c0) print("block1 done") print(f"{pt1 = }") pt2 = decrypt_block(ciphertext2, pt1, ciphertext1) print("flag: " , pt1 + pt2 ) #Source: ldv ``` - Flag: `crypto{h3ll0_t3l3gr4m}` ## Forbidden Fruit ![image](https://hackmd.io/_uploads/Hk0AjRj5T.png) - Trước hết ta sẽ tìm hiểu về Galois Counter Mode (GCM). - Tham khảo ở [đây](https://eprint.iacr.org/2016/475.pdf) và [đây](https://www.youtube.com/watch?v=V2TlG3JbGp0&t=40s) trước nha. - Xét $T= g(X)$, $g(X)$ là hàm GHASH. - Ta có: $$g(X)= A_1X^{m+n+1}+ ...+ A_mX^{n+2}+ C_1X^{n+1}+...+ C_nX^2+ LX+ S$$ - Với $A_i,\ C_i$ là các data blocks và cipher blocks, L= len(message), S là nonce value. Các block đều là 128bits trong trường hữu hạn $GF(2^{128})$ $$f_1(X)= A_{1,\ 1}X^5+ C_{1,\ 1}X^4+ C_{1,\ 2}X^3+ C_{1,\ 3}X^2+ LX+ S$$ $$f_2(X)= A_{2,\ 1}X^5+ C_{2,\ 1}X^4+ C_{2,\ 2}X^3+ C_{2,\ 3}X^2+ LX+ S$$ - Mặt khác: $$f'_1(X)=A_{1,1}X^5+C_{1,1}X^4+C_{1,2}X^3+C_{1,3}X^2+LX+S+T_1$$ $$f'_2(X)=A_{2,1}X^5+C_{2,1}X^4+C_{2,2}X^3+C_{2,3}X^2+LX+S+T_2$$ $$=> g(X)= f'_1(X)+f'_2(X)$$ $$= (A_{1,1}+ A_{2,1})X^5+ (C_{1,1}+ C_{2,1})X^4+...+ LX +T1 +T2$$ - Áp dụng vào ta có: $$TAG = (((((((((((H * AD0) + AD1) * H) + c0) * H) + c1) * H) + c2) * H) + L) * H) + S$$ $$TAG = AD0*H^6 + AD1*H^5 + c0*H^4 + c1*H^3 + c2*H^2 + L*H + S$$ - Nhập 2 block ta thu được: $$TAG = (((((H * c_0) + c_1) * H) + L) * H + S)$$ $$<=> TAG = c_0*H^3 + c_1*H^2 + L*H + S$$ - Ta có: $$TAG_1 = A*H^3 + c_1*H^2 + L*H + S$$ $$TAG_2 = A*H^3 + c_2*H^2 + L*H + S$$ $$ TAG_1 - TAG_2 = (c_1-c_2)*H^2$$ $$TAG_1 - c_1*H^2 = TAG_2 - c_2*H^2$$ - X là TAG giả mạo. Ta có: $$X = TAG_1 - c_1*H^2 == TAG_2 - c_2*H^2$$ - Mặt khác: $$ forge(tag) = c*H^2 + tag_1 - c_1*H^2 $$ $$ forge(tag) = c*H^2 + X $$ - Giờ chỉ cần gửi nonce, ciphertext, forge(tag), AD("Cryptohack") vào server và get flag. ```Python from Crypto.Util.number import * from Crypto.Util.number import * from sage.all import * import struct import requests import json F.<x> = GF(2^128, x^128+x^7+x^2+x+1) def polynomial_to_bytes(X): return int(f"{X.integer_representation():0128b}"[::-1], 2) def bytes_to_polynomial(X): return F.fetch_int(int(f"{X:0128b}"[::-1], 2)) def ENCRYPT(plaintext): url = 'http://aes.cryptohack.org/forbidden_fruit/encrypt/' url += plaintext.hex() r = requests.get(url).json() if "error" in r: return None return bytes.fromhex(r["nonce"]), bytes.fromhex(r["ciphertext"]), bytes.fromhex(r["tag"]), bytes.fromhex(r["associated_data"]) def DECRYPT(nonce, ciphertext, tag, associated_data): url = 'http://aes.cryptohack.org/forbidden_fruit/decrypt/' url += nonce.hex() + '/' + ciphertext.hex() + '/' + tag + '/' + associated_data.hex() r = requests.get(url).json() if "plaintext" in r: return bytes.fromhex(r["plaintext"]) return None payload = b"\x00"*16 nonce, c1, tag1, AD = ENCRYPT(payload) payload = b"\x01"*16 _nonce, c2, tag2, _AD = ENCRYPT(payload) c1 = bytes_to_polynomial(int.from_bytes(c1, 'big')) c2 = bytes_to_polynomial(int.from_bytes(c2, 'big')) tag1 = bytes_to_polynomial(int.from_bytes(tag1, 'big')) tag2 = bytes_to_polynomial(int.from_bytes(tag2, 'big')) print(f"{c1 = }\n") print(f"{c2 = }\n") print(f"{tag1 = }\n") print(f"{tag2 = }\n") H2= (tag1-tag2)/(c1-c2) X = tag1 - c1*H2 assert X == tag2-c2*H2 ciphertext = requests.get("http://aes.cryptohack.org/forbidden_fruit/encrypt/" + (b"give me the flag").hex()) ciphertext = json.loads(ciphertext.text) ciphertext = bytes.fromhex(ciphertext["ciphertext"]) tag = bytes_to_polynomial(int.from_bytes(ciphertext, 'big'))*H2 + X tag = polynomial_to_bytes(tag) print(f"{tag = }\n") print(f"{nonce.hex() = }\n") print(f"{AD.hex() = }\n") print(DECRYPT(nonce, ciphertext, hex(tag)[2:], AD)) #Source: ldv ``` # Chall ## Challenge 1: Source chall: ```Python from Crypto.Cipher import AES from Crypto.Util.Padding import pad from hashlib import md5 from os import urandom FLAG = b"KCSC{???????????????????????????}" assert len(FLAG) % 16 == 1 # hint key1 = md5(urandom(3)).digest() key2 = md5(urandom(3)).digest() cipher1 = AES.new(key1, AES.MODE_ECB) cipher2 = AES.new(key2,AES.MODE_ECB) enc = cipher1.encrypt(pad(FLAG,16)) enc = cipher2.encrypt(enc) print(key1, key2) print(enc.hex()) #ouput= 21477fac54cb5a246cb1434a1e39d7b34b91e5c135cd555d678f5c01b2357adc0c6205c3a4e3a8e6fb37c927de0eec95 ``` - Chall này ta biết được block cuối là b'}' pad thêm 15 bytes. - Từ thông tin đó ta có thể brute force key1, key2. - Ta có output khi decryt block cuối với key2 của output chall sẽ bằng với output khi encrypt block đã biết với key1. Ta sẽ brute force key1, key2 thỏa điều kiện trên. ```Python from Crypto.Cipher import AES from Crypto.Util.Padding import pad from hashlib import md5 from os import urandom c = '21477fac54cb5a246cb1434a1e39d7b34b91e5c135cd555d678f5c01b2357adc0c6205c3a4e3a8e6fb37c927de0eec95' enc = bytes.fromhex(c) c1 = enc[:16] c2 = enc[16:32] c3 = enc[32:] p3 = b"}" x = pad(p3, 16) key_list = [] list_enc = [] for i in range(256): for j in range(256): for k in range(256): key1 = md5(bytes([i, j, k])).digest() key_list.append(key1) cipher3 = AES.new(key1, AES.MODE_ECB) enc3 = cipher3.encrypt(x).hex() list_enc.append(enc3) for i in range(256): for j in range(256): for k in range(256): key2 = md5(bytes([i, j, k])).digest() plan3 = AES.new(key2, AES.MODE_ECB) dec3 = plan3.decrypt(c3).hex() if dec3 in list_enc: index = list_enc.index(dec3) print(f"Key2: {key2}") print(f"Key1: {key_list[index]}") ``` -Flag: `KCSC{MeEt_In_tHe_mIdDLe_AttaCk__}` ## Challenge 2: **Source Chall** ```Python from Crypto.Cipher import AES from Crypto.Util.Padding import pad from random import choice from os import urandom import socket import threading FLAG = b'KCSC{Bingo!_PKCS#7_padding}' class ThreadedServer(object): def __init__(self, host, port): self.host = host self.port = port self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.bind((self.host, self.port)) def listen(self): self.sock.listen(5) while True: client, address = self.sock.accept() client.settimeout(60) threading.Thread(target = self.listenToClient,args = (client,address)).start() def listenToClient(self, client, address): size = 1024 for i in range(100): x = choice(['ECB','CBC']) if x == 'ECB': cipher = AES.new(urandom(16), AES.MODE_ECB) else: cipher = AES.new(urandom(16), AES.MODE_CBC, urandom(16)) try: msg = bytes.fromhex(client.recv(size).strip().decode()) assert len(msg) <= 16 client.send(cipher.encrypt(pad(msg,16)).hex().encode() + b'\n') ans = client.recv(size).strip().decode() assert ans == x client.send(b'Correct!\n') except: client.send(b"Exiting...\n") client.close() return False client.send(FLAG) client.close() return False if __name__ == "__main__": ThreadedServer('',2000).listen() ``` - Chall yêu cầu ta gửi 1 chuỗi x, với len(x)<= 16. Sau đó cho ta output của encrypt và yêu cầu ta đoán xem đó là 'ECB' hay 'CBC'. Đoán đúng 100 lần thì sẽ cho ta flag. - Như ta đã biết thì khi encrypt bằng 'ECB' với cùng 1 đoạn plantext thì output sẽ cho ra giống nhau. Dựa vào điều này ta sẽ giải quyết chall này. ```Python from pwn import * r= remote("localhost", 1337) for i in range(100): r.sendline(b'10'*16) a= r.recvline().decode() print(a) a1= a[:32] a2= a[32:] if a1== a2: r.sendline(b'ECB') else: r.sendline(b'CBC') r.recvutil() r.recvuntil() ``` ## Challenge 3: **Source chall** ```Python from Crypto.Cipher import AES from os import urandom from base64 import b64encode import string import socket import threading chars = string.ascii_lowercase + string.ascii_uppercase + '!_{}' FLAG = b'KCSC{Chosen_Plaintext_Attack___ECB_ECB_ECB___you_made_it!}' assert all(i in chars for i in FLAG.decode()) def pad(msg, block_size): if len(msg) % block_size == 0: return msg return msg + bytes(block_size - len(msg) % block_size) def chall(usrname): key = urandom(16) cipher = AES.new(key, AES.MODE_ECB) msg = b'Hello ' + usrname + b', here is your flag: ' + FLAG + b'. Have a good day, we hope to see you soon.' enc = cipher.encrypt(pad(msg,16)) return b64encode(enc) class ThreadedServer(object): def __init__(self, host, port): self.host = host self.port = port self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.bind((self.host, self.port)) def listen(self): self.sock.listen(5) while True: client, address = self.sock.accept() client.settimeout(60) threading.Thread(target = self.listenToClient,args = (client,address)).start() def listenToClient(self, client, address): size = 1024 while True: try: usrname = client.recv(size).strip() client.send(chall(usrname) + b'\n') except: client.close() return False if __name__ == "__main__": ThreadedServer('',2003).listen() ``` - Chall cho ta gửi tới sever một chuỗi msg bất kỳ, sau đó encrypt msg với cấu trúc sau `msg = b'Hello ' + usrname + b', here is your flag: ' + FLAG + b'. Have a good day, we hope to see you soon.'` bằng ECB. - Ta sẽ vẫn dựa vào việc ECB trả lại cùng 1 giá trị với input giống nhau để giải quyết chall. Ta sẽ check từng byte của flag nếu giống nhau thì sẽ nhận byte đó. ```Python from pwn import * from base64 import b64decode r = remote('localhost', 1337) flag = 'KCSC{' msg = 'a'*32+ 'ahere is your flag: KCSC{' for x in range(80): b = msg for i in range(33, 129): a = b + chr(i) + 'a' * (5 + 64-x) r.sendline(a.encode()) cipher = r.recvuntil(b'\n').decode() m= cipher[2:len(cipher)-2] cipher= m+ '==' cipher = b64decode(cipher) c1 = cipher[48:64] c2 = cipher[144:160] if c1 == c2: flag += chr(i) msg= msg[1:]+ chr(i) break if chr(i)== '.': break print(flag) ``` ![image](https://hackmd.io/_uploads/B12gmy-ja.png) ## Challenge 4 **Source chall:** ```Python import socket import threading from Crypto.Cipher import AES from os import urandom import string chars = string.ascii_lowercase + string.ascii_uppercase + string.digits + '_{}' FLAG = b'KCSC{CBC_p4dd1ng_0racle_}' assert all(i in chars for i in FLAG.decode()) def pad(msg, block_size): pad_len = 16 - len(msg) % block_size return msg + bytes([pad_len])*pad_len def encrypt(key): iv = urandom(16) cipher = AES.new(key, AES.MODE_CBC, iv) return (iv + cipher.encrypt(pad(FLAG,16)) ).hex().encode() def decrypt(enc,key): enc = bytes.fromhex(enc) iv = enc[:16] ciphertext = enc[16:] cipher = AES.new(key, AES.MODE_CBC, iv) decrypted = cipher.decrypt(ciphertext) pad_len = decrypted[-1] if all(i == pad_len for i in decrypted[-pad_len:]): return b'Decrypted successfully.' else: return b'Incorrect padding.' class ThreadedServer(object): def __init__(self, host, port): self.host = host self.port = port self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.bind((self.host, self.port)) def listen(self): self.sock.listen(5) while True: client, address = self.sock.accept() client.settimeout(60) threading.Thread(target = self.listenToClient,args = (client,address)).start() def listenToClient(self, client, address): size = 1024 key = urandom(16) while True: try: choice = client.recv(size).strip() if choice == b'encrypt': client.send(encrypt(key) + b'\n') elif choice == b'decrypt': client.send(b'Ciphertext: ') c = client.recv(size).strip().decode() client.send(decrypt(c,key) + b'\n') except: client.close() return False if __name__ == "__main__": ThreadedServer('',2004).listen() ``` **Solve:** - Ta sẽ build lại để chạy local nha. - Với chall này thì ta sẽ thấy flag của ta đã được pad sau khi decrypt. Ta sẽ dựa vào điểm này để giải mã. - Tương tự các bài trước bài này ta sẽ bruteforce từng byte, nếu output khi decrypt là `Decrypted successfully.` thì ta sẽ nhận byte đó. ```python from pwn import * r= remote("localhost", 1337) r.sendline(b'encrypt') output= r.recvuntil(b'\n').decode() output= bytes.fromhex(output) iv= output[:16] cipher= output[16:] flag = str() for k in range(2): cipher0 = cipher[16* k: 16* k+ 16] plantext = b'' fakeplaintext = [0]*16 fakecipher = bytes(fakeplaintext) x= b'' for i in range(1, 17): for j in range(256): fakecipher= fakecipher[:len(fakeplaintext)- i] + bytes([j]) + plantext x= iv + fakecipher + cipher0 r.sendline(b'decrypt') r.recvuntil(b'Ciphertext: ') r.sendline(x.hex().encode()) a= r.recvuntil(b'\n').decode() a= a[2:len(a)- 2] if a== "Decrypted successfully.": fakeplaintext[len(fakeplaintext)- i] = j ^ i plantext= b'' plantext= xor(bytes([i+ 1])*i , bytes(fakeplaintext[len(fakeplaintext)- i:])) break flag += str(xor(bytes(fakeplaintext),output[16* k: 16* k+ 16])) print(flag) r.interactive() ```