# SYMMETRIC CIPHERS CRYPTOHACK
### Structure of AES

- Chall này giúp ta biết cách chuyển từ 1 đoạn bytes thành 4 phần mỗi phần rồi xếp thành 1 ma trận 4x4, mỗi cột là 4 bytes và biết cách chuyển ngược lại
- Đoạn code ban đầu như thế này:
```
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. """
????
matrix = [
[99, 114, 121, 112],
[116, 111, 123, 105],
[110, 109, 97, 116],
[114, 105, 120, 125],
]
print(matrix2bytes(matrix))
```
- Hiện tại ta cần hoàn thành function ``matrix2bytes`` để tìm được flag
- ``bytes2matrix`` là 1 hàm chia 1 đoạn bytes thành 4 phần
- Nhiệm vụ của ta là phải biết cách chuyển lại
- Ta thấy dòng đầu tiên, 99 = 'c', 114 = 'r', 121 = 'y', 112 = 'p' --> ta sẽ dùng thuật toán như sau:
```
def matrix2bytes(matrix):
""" Converts a 4x4 matrix into a 16-byte array. """
s=""
for i in matrix:
for j in i:
s=s+chr(j)
return s
```
- Hoặc có thể sử dụng hàm này:
```
def matrix2bytes_2(matrix):
""" Converts a 4x4 matrix into a 16-byte array. """
return bytes(sum(matrix, []))
```
- Đoạn code cụ thể sẽ như sau:
```
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. """
s=""
for i in matrix:
for j in i:
s=s+chr(j)
return s
def matrix2bytes_2(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_2(matrix))
```
> crypto{inmatrix}
### Round Key

- Chall này cho ta hiểu làm thế nào để Add_round_key trong AES
- Thực chất, ta chỉ cần lấy từng phần tử của Matrix trạng thái, XOR với ma trận Round, ta sẽ thu được ma trận sau khi Add_round_key
- Đoạn code ban đầu của chall như sau:
```
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):
???
print(add_round_key(state, round_key))
```
- Ta cần hoàn thành function add_round_key bằng cách sử dụng 2 vòng for để có thể lần lượt XOR phần tử của state với round_key
- Thuật toán sẽ như sau:
```
def add_round_key(s, k):
for i in range(4):
for j in range(4):
s[i][j]=s[i][j]^k[i][j] #must xor
return s
```
- Hoặc có thể sẽ như sau:
```
def add_round_key(s, k):
return [[sss^kkk for sss, kkk in zip(ss, kk)] for ss, kk in zip(s, k)]
```
- Sau khi add_round_key, ta cần dùng hàm matrix2bytes để lấy flag
- Đoạn code cụ thể sẽ như sau:
```
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):
for i in range(4):
for j in range(4):
s[i][j]=s[i][j]^k[i][j] #must xor
return s
def matrix2bytes(matrix):
""" Converts a 4x4 matrix into a 16-byte array. """
s=""
for i in matrix:
for j in i:
s=s+chr(j)
return s
print(matrix2bytes(add_round_key(state, round_key)))
```
> crypto{r0undk3y}
### Confusion through Substitution
___

- Chall này sẽ cho ta hiểu bước Sub Bytes sẽ như thế nào
- Đoạn code mà chall cho ta là:
```
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 sub_bytes(s, sbox=s_box):
???
print(sub_bytes(state, sbox=inv_s_box))
```
- Giờ ta cần phải hoàn thành function sub_bytes, hãy đọc và hiểu ví dụ bên dưới nha <3
```
#Ma trận cần Sub Bytes
state = [
[251, 64, 182, 81],
[146, 168, 33, 80],
[199, 159, 195, 24],
[64, 80, 182, 255],
]
#Ma trận Sbox (lúc chạy thì xóa các tung và hoành độ đi, mình để thế cho dễ nhìn lúc đọc thui nhaaa)
s_box = (
| 0 1 2 3 4 5 6 7 8 9 a b c d e f
---| -- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
00 |0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
10 |0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
20 |0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
30 |0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
40 |0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
50 |0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
60 |0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
70 |0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
80 |0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
90 |0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
a0 |0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
b0 |0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
c0 |0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
d0 |0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
e0 |0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
f0 |0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D,
)#Cách Sub Bytes
def sub_bytes(s,s_box):
for i in range(4):
for j in range(4):
s[i][j] = s_box[s[i][j]]
return s#Output:
[[99, 114, 121, 112],
[116, 111, 123, 108],
[49, 110, 51, 52],
[114, 108, 121, 125]]
```
- Với số đầu tiên là 251, Hexa là fb, sau khi Sub Bytes, 251 --> 99 ví s_box(f,b) = 63 (sang decimal là 99), tương tự với các phần tử khác của ma trận state
- Đó chính là thuật toán của chall này, giờ ta cùng hoàn thành code mà đề giao
- Đoạn code sẽ như sau:
```
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)))
```
> crypto{l1n34rly}
### Diffusion though Permutation

- Chall này giúp ta hiểu về 2 bước cùng 1 lúc đó là ShiftRows và MixColumn
- Để hiểu thêm về 2 bước này, bạn có thể đọc lý thuyết của mình tại đây [Clickhere](https://github.com/trananhnhatviet/AES_cipher/blob/main/AES_Cipher_Knowledge.md)
- Đoạn code chall cho ta như sau:
```
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):
???
# 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],
]
```
- Ta thấy rằng, hàm shift_rows của chall, thay vì chuyển dịch theo hàng ngang thì lại chuyển thành hàng dọc, vì thế ta cần hoàn thành hàm inv_shift_rows để có thể cho ma trận về ban đầu
- Đơn giản thui, ta chỉ cần cho ngược lại như sau:
```
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]
```
- Sau khi hoàn thàn được hàm inv_shift_rows, ta bổ sung thêm function matrix2bytes nữa rồi ta chỉ cần chạy hàm inv_mix_columns rồi tới inv_shift_rows rồi matrix2bytes là tìm được flag
- Đoạn code hoàn chỉnh sẽ như sau:
```
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 matrix2bytes(matrix):
""" Converts a 4x4 matrix into a 16-byte array. """
s=""
for i in matrix:
for j in i:
s=s+chr(j)
return s
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)
for i in state:
s=""
for j in i:
s=s+' '+chr(j)
print(s)
print(matrix2bytes(state))
```
> crypto{d1ffUs3R}
### Bringing It All Together

- Chall cho ta 1 đoạn code như sau:
```
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'
def expand_key(master_key):
"""
Expands and returns a list of key matrices for the given master_key.
"""
# Round constants https://en.wikipedia.org/wiki/AES_key_schedule#Round_constants
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,
)
# Initialize round keys with raw key material.
key_columns = bytes2matrix(master_key)
iteration_size = len(master_key) // 4
# Each iteration has exactly as many columns as the key material.
i = 1
while len(key_columns) < (N_ROUNDS + 1) * 4:
# Copy previous word.
word = list(key_columns[-1])
# Perform schedule_core once every "row".
if len(key_columns) % iteration_size == 0:
# Circular shift.
word.append(word.pop(0))
# Map to S-BOX.
word = [s_box[b] for b in word]
# XOR with first byte of R-CON, since the others bytes of R-CON are 0.
word[0] ^= r_con[i]
i += 1
elif len(master_key) == 32 and len(key_columns) % iteration_size == 4:
# Run word through S-box in the fourth iteration when using a
# 256-bit key.
word = [s_box[b] for b in word]
# XOR with equivalent word from previous iteration.
word = bytes(i^j for i, j in zip(word, key_columns[-iteration_size]))
key_columns.append(word)
# Group key words in 4x4 byte matrices.
return [key_columns[4*i : 4*(i+1)] for i in range(len(key_columns) // 4)]
def decrypt(key, ciphertext):
round_keys = expand_key(key) # Remember to start from the last round key and work backwards through them when decrypting
# Convert ciphertext to state matrix
# Initial add round key step
for i in range(N_ROUNDS - 1, 0, -1):
pass # Do round
# Run final round (skips the InvMixColumns step)
# Convert state matrix to plaintext
return plaintext
# print(decrypt(key, ciphertext))
```
- Chall này là siêu tổng hợp tất cả các chall trước, ta chỉ hoàn thành function decrypt theo các bước giải mã thuiii
- Trước hết, ta cần nhớ các bước giải mã:
- AddRoundKey giữa ciphertext và round_key thứ 10
- 1 Vòng lặp giảm dần từ 9 về 2 để:
- inv_shift_rows: Dịch chuyển ngược lại các hàng của khối dữ liệu.
- inv_sub_bytes: Sử dụng bảng inv_S-box để thay thế ngược lại các giá trị của từng byte trong khối dữ liệu.
- addroundkey: Tính toán XOR giữa khóa vòng hiện tại và khối dữ liệu sau khi đã dịch chuyển hàng và thay thế byte.
- inv_mix_columns: Thực hiện phép nhân ma trận ngược của ma trận MixColumns trên khối dữ liệu sau khi đã thực hiện các bước trên.
- Vòng cuối cùng thì ta bỏ inv_mix_columns đi và chỉ làm với 3 bước còn lại
- Cuối cùng, ta chuyển matrix về bytes bằng hàm matrix2bytes
- Đoạn code sẽ như sau:
```
from pwn import*
# Dữ liệu ban đầu
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'
# Hai s_box
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,
)
# 2 hàm shift_rows và inverse nhằm dịch chuyển byte
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]
# 2 hàm chuyển từ matrix về text và ngược lại
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)]
# Hàm để tạo 10 khóa
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)]
# Các hàm Mix Column và inverse của nó
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)
# Hàm thay thế bytes
def sub_bytes(s, sbox):
for i in range(4):
for j in range(4):
s[i][j] = sbox[s[i][j]]
return s
# Hàm AddRoundKey để xor từng ký tự
def add_round_key(s, k):
return xor(s, k)
def decrypt(key, ciphertext):
round_keys = expand_key(key) # Hãy nhớ là giải mã thì bắt đầu với key cuối cùng
# Chuyển ciphertext về matrix
state = bytes2matrix(ciphertext)
# Đầu tiên thì phải AddRoundKey với khóa cuối cùng là khóa 10
state = bytes2matrix(add_round_key(state, round_keys[10]))
# 9 Vòng tiếp theo giảm dần
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)
# Vòng cuối thì sẽ không có inv_mix_columns
inv_shift_rows(state)
state = bytes2matrix(sub_bytes(state, inv_s_box))
state = bytes2matrix(add_round_key(state, round_keys[0]))
# Chuyển từ matrix về byte
return matrix2bytes(state)
print(decrypt(key, ciphertext))
```
> crypto{MYAES128}
### Modes of Operation Starter

- Chúng ta bắt đầu với các loại mã hõa AES, nếu bạn chưa biết gì về ECB thì hãy dừng lại và đọc cái này đi [clickhere](https://github.com/trananhnhatviet/AES_cipher/blob/main/Block_cipher_modes_of_operation.md) hoặc là lên ytb xem đi nhaaa
- Chall này giúp ta biết các mã hóa bằng ECB là như thế nào và cách dùng thư viện request
- Ta có 1 source code như sau:
```
from Crypto.Cipher import AES
KEY = ?
FLAG = ?
@chal.route('/block_cipher_starter/decrypt/<ciphertext>/')
def decrypt(ciphertext):
ciphertext = bytes.fromhex(ciphertext)
cipher = AES.new(KEY, AES.MODE_ECB)
try:
decrypted = cipher.decrypt(ciphertext)
except ValueError as e:
return {"error": str(e)}
return {"plaintext": decrypted.hex()}
@chal.route('/block_cipher_starter/encrypt_flag/')
def encrypt_flag():
cipher = AES.new(KEY, AES.MODE_ECB)
encrypted = cipher.encrypt(FLAG.encode())
return {"ciphertext": encrypted.hex()}
```
- Khi ta request tới /encrypt_flag/ thì ta thu được 1 json chứa ciphertext như sau:

- Giờ ta chỉ cần request tới /decrypt/ciphertext/ là sẽ thu được plaintext dưới dạng hexadecimal rồi ta chuyển về byte thì sẽ thu được flag của chall này
- Mình hiện tại chưa quen dùng request nên mình có tham khảo wu của anh [Thắng](https://hackmd.io/@Thangcoithongminh/rkungRrUo) nha
```
import requests
url = "http://aes.cryptohack.org/block_cipher_starter"
# Kết nối lần 1 với http://aes.cryptohack.org/block_cipher_starter/encrypt_flag
first_request = requests.get(f"{url}/encrypt_flag")
# Nhận giá trị của json
data_ciphertext = first_request.json()
print(data_ciphertext)
ciphertext = data_ciphertext['ciphertext']
print(ciphertext)
#Ciphertext là: 11bbdda0a15bd72de52f8b2af8bacd9c806bd93bd6ba80e556b291ddec068455
# Kết nối lần 1 với http://aes.cryptohack.org/block_cipher_starter/decrypt/11bbdda0a15bd72de52f8b2af8bacd9c806bd93bd6ba80e556b291ddec068455
second_request = requests.get(f'{url}/decrypt/{ciphertext}/')
# Nhận giá trị json
data_plaintext_hex = second_request.json()
print(data_plaintext_hex)
plaintext = data_plaintext_hex['plaintext']
print(plaintext)
# Chuyển về bytes
flag = bytes.fromhex(plaintext)
print(flag)
```
> crypto{bl0ck_c1ph3r5_4r3_f457_!}
### Passwords as Keys

- Chall này cũng là dạng ECB, thế nhưng ta lại không biết Key là gì, chỉ biết Key là kết quả của băm MD5 của 1 từ trong file /usr/share/dict/words
- Giờ để thu được các từ này vào 1 file thì ta vào đường link này [link](https://gist.githubusercontent.com/wchargin/8927565/raw/d9783627c731268fb2935a731a618aa8e95cf465/words)
- Xong giờ ta copy và lưu vào 1 file password.txt như thế này (nhớ là lưu cùng với folder của solve.py)

- Như bài trước, ta cần request tới địa chỉ /encrypt_flag/ để thu được ciphertext
```
url = 'http://aes.cryptohack.org/passwords_as_keys/'
r = requests.get(f'{url}encrypt_flag/')
first_request = r.json()
print(first_request)
ciphertext = first_request['ciphertext']
```
- Giờ ta brute force hết cái file password.txt đó từng từ cho tới khi thu được flag
```
with open("password.txt") as f:
words = [w.strip() for w in f.readlines()]
for i in words:
keyword = i
KEY = hashlib.md5(keyword.encode()).digest().hex()
s= decrypt(ciphertext,KEY)
a=(bytes.fromhex(s))
if b'crypto' in a:
print(a)
break
```
- Đoạn code full sẽ như sau:
```
from Crypto.Util.number import*
from Crypto.Cipher import AES
import hashlib
import requests
import random
# Hàm decrypt lấy ở trang web đó lunnn
def decrypt(ciphertext, password_hash):
ciphertext = bytes.fromhex(ciphertext)
key = bytes.fromhex(password_hash)
cipher = AES.new(key, AES.MODE_ECB)
try:
decrypted = cipher.decrypt(ciphertext)
except ValueError as e:
return {"error": str(e)}
return decrypted.hex()
url = 'http://aes.cryptohack.org/passwords_as_keys/'
r = requests.get(f'{url}encrypt_flag/')
first_request = r.json()
print(first_request)
ciphertext = first_request['ciphertext']
with open("password.txt") as f:
words = [w.strip() for w in f.readlines()]
for i in words:
keyword = i
KEY = hashlib.md5(keyword.encode()).digest().hex()
s= decrypt(ciphertext,KEY)
a=(bytes.fromhex(s))
if b'crypto' in a:
print(a)
print(keyword)
break
```
> crypto{k3y5__r__n07__p455w0rdz?}
### ECB CBC WTF

- Chall này cho ta biết thêm 1 loại mã hóa mới, Cipher Block Chainning (CBC)
- Source của chall này là:
```
from Crypto.Cipher import AES
KEY = ?
FLAG = ?
@chal.route('/ecbcbcwtf/decrypt/<ciphertext>/')
def decrypt(ciphertext):
ciphertext = bytes.fromhex(ciphertext)
cipher = AES.new(KEY, AES.MODE_ECB)
try:
decrypted = cipher.decrypt(ciphertext)
except ValueError as e:
return {"error": str(e)}
return {"plaintext": decrypted.hex()}
@chal.route('/ecbcbcwtf/encrypt_flag/')
def encrypt_flag():
iv = os.urandom(16)
cipher = AES.new(KEY, AES.MODE_CBC, iv)
encrypted = cipher.encrypt(FLAG.encode())
ciphertext = iv.hex() + encrypted.hex()
return {"ciphertext": ciphertext}
```
- AES.CBC sẽ như sau:

- Ta thấy ban đầu, flag sẽ được mã hóa loại CBC thế nhưng khi giải mã lại là ECB
- Khi ta request tới /encrypt_flag/ thì ta sẽ thu được 1 đoạn hexadecimal gồm (iv) và (encrypted)
- Đoạn đó có độ dài là 48 bytes --> Chia thành 3 đoạn là IV, encrypt_cbc_block_flag_1, encrypt_cbc_block_flag_2

- Khi ta request tới /decrypt/ciphertext thì server chỉ decrypt theo AES.ECB và ta cũng thu được 1 đoạn có độ dài là 48 bytes --> Chia thành 3 đoạn là decrypt_ecb_iv, decrypt_ecb_block_flag_1, decrypt_ecb_block_flag_2

- Đây là giải mã theo kiểu ECB, thế nhưng để tìm được flag thì cần giải mã theo kiểu CBC, nhưng server lỏ nên chỉ có ECB, thế nên giờ mình phải tự giải
- Cách làm sẽ như thế này

- Ta cần chia ra các đoạn và làm theo sơ đồ trên
- Đoạn code sẽ như sau:
```
import requests
from pwn import *
from hashlib import md5
from Crypto.Cipher import AES
url = 'https://aes.cryptohack.org/ecbcbcwtf/'
first_request = requests.get(f"{url}/encrypt_flag")
flag = b''
# Nhận giá trị của json
data_ciphertext = first_request.json()
ciphertext = data_ciphertext['ciphertext']
ct = bytes.fromhex(ciphertext)
print(ct, len(ct))
second_request = requests.get(f'{url}decrypt/{ciphertext}/')
data_2 = second_request.json()
plaintext = data_2['plaintext']
pt = bytes.fromhex(plaintext)
print(pt, len(pt))
print('')
for i in range(0,len(ciphertext),16):
print(ct[i:i+16]," ",pt[i+16:i+32])
flag += xor(ct[i:i+16],pt[i+16:i+32])
print(flag)
print('')
```
> crypto{3cb_5uck5_4v01d_17_!!!!!}
### ECB Oracle

- Chall cho ta 1 source code như sau:
```
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
KEY = ?
FLAG = ?
@chal.route('/ecb_oracle/encrypt/<plaintext>/')
def encrypt(plaintext):
plaintext = bytes.fromhex(plaintext)
padded = pad(plaintext + FLAG.encode(), 16)
cipher = AES.new(KEY, AES.MODE_ECB)
try:
encrypted = cipher.encrypt(padded)
except ValueError as e:
return {"error": str(e)}
return {"ciphertext": encrypted.hex()}
```
- Ta thấy Chall này chỉ nhận Plaintext, sau đó, plaintext sẽ cộng với Flag của chall rùi padding thêm byte, sau đó mã hóa theo dạng ECB
- Chall này cho ta thấy được ECB là 1 loại mã hóa có bảo mật kém, dựa vào điểm yếu này, Hacker sẽ tấn công và sẽ có được thông tin cần bảo mật
- Ví dụ ta có 1 bài encrypt ECB cơ bản như sau:
```
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad
from binascii import*
key = b'1122334455667788' # Đây là key (phải là 16 ký tự nếu là AES 128)
# hello_world_baby và hello_world_haha đã thành 1 block và từ _a trở đi sẽ thuộc block 2
data_1 = 'hello_world_baby_a' # Đây là dữ liệu 1 cần mã hóa
data_2 = 'hello_world_haha_a' # Đây là dữ liệu 2 cần mã hóa
def encrypt(raw):
raw = pad(raw.encode(),16) # Thêm các byte không có giá trị để đảm bảo độ dài là bội số của 16
print('raw after pad:', raw)
cipher = AES.new(key, AES.MODE_ECB)
return (cipher.encrypt(raw)).hex()
def decrypt(enc):
enc = unhexlify(enc)
cipher = AES.new(key, AES.MODE_ECB)
print('cipher:', cipher.decrypt(enc))
return unpad(cipher.decrypt(enc),16) # Bỏ bớt các byte không có giá trị
encrypted = encrypt(data_1)
print('encrypted ECB Hexa:',encrypted)
print('')
encrypted = encrypt(data_2)
print('encrypted ECB Hexa:',encrypted)
# raw after pad: b'hello_world_baby_a\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e'
# encrypted ECB Hexa: b099b3742f1ac1d6ea1aab28aecd20a5(fad79d4599da81b1c2f478d7fe60bfa6)
# raw after pad: b'hello_world_baba_a\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e'
# encrypted ECB Hexa: 8f44c2423d5bc8f88d4f95bd122c0f0e(fad79d4599da81b1c2f478d7fe60bfa6)
```
- Ta thấy rằng, khi mã hóa ECB 1 block plaintext giống nhau, thì ta sẽ thu được các block ciphertext giống nhau
- Và bây giờ, ta cần phải nhập plaintext sao cho đủ 32 byte
- Khi ta nhập plaintext dưới dạng hex ``0000000000000000000000000000006300000000000000000000000000000000000000000000000000000000000000`` ('c' = 63 (hexadecimal)), thì server sẽ gửi về 1 đoạn hexa và khi ta chuyển về byte, ta được đoạn như sau:
``
b'\xd7\x8e\x8b\xfc\x95\x1dFx\xfa\xa1\t[}\xf4\xd2\n\x0bfa!\xa8\xf3w\xae)\xfex\xabE\xbe\x96]\xd7\x8e\x8b\xfc\x95\x1dFx\xfa\xa1\t[}\xf4\xd2\ni\xf1\xa2D\xdc\x13\xfd\x02\xcdX\xca#\xae\xffq\x88u\xd1Tu\xb8b\x18\xa76HO\x97\x93k\n\xcd'
``
- Ta thấy có 2 đoạn giống nhau đều là ``\xd7\x8e\x8b\xfc\x95\x1dFx\xfa\xa1\t[}\xf4\xd2\n`` --> Ký tự 'c' thuộc Flag cần tìm
- Tiếp tục như thế cho tới khi bruteforce hết flag
- Đoạn code sẽ như sau:
```
import requests
url = 'http://aes.cryptohack.org/ecb_oracle/'
flag = 'crypto{'
char_add = ''
# Đây là đoạn crypto{
load_1 = '00000000000000000063727970746f7b'
for i in range(18):
load_1 = load_1[2:] + char_add
for j in range(33,128):
load = load_1 + bytes([j]).hex() + '00'*(24-i)
print(load)
r = requests.get(f'{url}encrypt/{load}')
data = r.json()
ciphertext = data['ciphertext']
ct = bytes.fromhex(ciphertext)
if ct[:16] == ct[32:48]:
flag += chr(j)
char_add = bytes([(j)]).hex()
print(flag)
break
```
> crypto{p3n6u1n5_h473_3cb}
### Flipping Cookie

- Chall đưa cho ta source code như sau:
```
from Crypto.Cipher import AES
import os
from Crypto.Util.Padding import pad, unpad
from datetime import datetime, timedelta
KEY = ?
FLAG = ?
@chal.route('/flipping_cookie/check_admin/<cookie>/<iv>/')
def check_admin(cookie, iv):
cookie = bytes.fromhex(cookie)
iv = bytes.fromhex(iv)
try:
cipher = AES.new(KEY, AES.MODE_CBC, iv)
decrypted = cipher.decrypt(cookie)
unpadded = unpad(decrypted, 16)
except ValueError as e:
return {"error": str(e)}
if b"admin=True" in unpadded.split(b";"):
return {"flag": FLAG}
else:
return {"error": "Only admin can read the flag"}
@chal.route('/flipping_cookie/get_cookie/')
def get_cookie():
expires_at = (datetime.today() + timedelta(days=1)).strftime("%s")
cookie = f"admin=False;expiry={expires_at}".encode()
iv = os.urandom(16)
padded = pad(cookie, 16)
cipher = AES.new(KEY, AES.MODE_CBC, iv)
encrypted = cipher.encrypt(padded)
ciphertext = iv.hex() + encrypted.hex()
return {"cookie": ciphertext}
```
- Trước khi đọc tiếp, tui nghĩ nên đọc cái này thì sẽ dễ hiểu hơn [Click_here](https://bernardoamc.com/cbc-bitflipping-attack/?fbclid=IwAR0zODFIiucy0LP6CUpTlF1D5JTOTWR1V3CaEOTFmU-_kW24KTLOl9LlEq4)
- Có nghĩa là khi check_admin, nhập cookie và iv sao cho xuất hiện ``admin=True`` thì sẽ xuất hiện flag
- Thế nhưng cookie ban đầu lại có dạng là ``admin=False;expiry=1681921755`` (1681921755 là thời gian hiện tại)
- Làm thế nào để ta có thể chuyển về thành ``admin=True`` ???
- Khi ta thu cookie từ hàm get_cookie(), ta sẽ thu được 1 đoạn ciphertext = iv.hex() + encrypted.hex(), ta chia ra thành 3 phần là:
- iv = cookie[:16] # Đây là iv ban đầu
- block1 = cookie[16:32] # block_data_1_after_encrypt
- block2 = cookie[32:] # đoạn còn lại của cookie
- Trước hết, ta cần ôn lại cách mã hóa và giải mã của CBC

- Cho ``origin = b'admin=False;expi'``
- Ta thấy rằng, đoạn ``admin=False;expi`` chắc chắn sẽ được Xor với iv và nó chính là block1 --> block1 có dạng: ``block1 = iv ^ origin'``
- Tiếp theo, ta có 1 đoạn 16 byte ``target_to_get_flag = b'admin=True_yeah\x01'`` (Mình thêm 'yeah' chỉ để vui, và \x01 là PKCS#7 Padding)
- Ta có tính chất của Xor như sau: ``a ^ b ^ c ^ a ^ b = c``
- Suy ra, để đạt được target thì ta cần làm các bước như sau:
- Ta cần 1 biến xor 3 phần iv, block1 và target_to_get_flag: ``send_iv = xor(xor(origin, target_to_get_flag),(iv))``
- Thực ra hàm check_admin cũng chỉ là hàm giải mã của CBC, và khi giải mã, ta sẽ gửi block1 và send_iv vào hàm check_admin: ``check_admin(block1, send_iv)``
- Khi giải mã, IV sẽ xor với đầu ra của block cipher decrytion để ra plaintext, tức là block1 sẽ tiếp tục xor với send_iv --> ``output = target_to_get_flag ^ iv ^ origin ^ iv ^ origin = target_to_get_flag = b'admin=True_yeah\x01'``
- Khi đạt được target, ta sẽ thu được cờ

- Đoạn code minh họa quá trình giải mã như sau:
```
from Crypto.Cipher import AES
import os
from Crypto.Util.number import *
from Crypto.Util.Padding import pad, unpad
KEY = b'this_is_a_keycbc' # KEY 16 byte
# a ^ b
def xor(a, b):
return long_to_bytes(bytes_to_long(a) ^ bytes_to_long(b))
# Hàm check_admin này giống hệt trong challenge đưa ra
def check_admin(cookie, iv):
cipher = AES.new(KEY, AES.MODE_CBC, iv)
decrypted = cipher.decrypt(cookie)
unpadded = unpad(decrypted, 16)
# Nếu ra admin=True thì là đúng
if b"admin=True" in unpadded:
print(unpadded)
else:
print('false')
# Hàm get_cookie này giống hệt trong challenge đưa ra
def get_cookie():
expires_at ='1681921755' #Đây là (datetime.today() + timedelta(days=1)).strftime("%s") tại thời điểm hiện tại
data = f"admin=False;expiry={expires_at}".encode()
print(data)
iv = os.urandom(16)
padded = pad(data, 16)
cipher = AES.new(KEY, AES.MODE_CBC, iv)
encrypted = cipher.encrypt(padded)
ciphertext = iv.hex() + encrypted.hex()
return {"cookie": ciphertext}
# Nhận giá trị cho cookie
cookie = get_cookie()
cookie = cookie['cookie']
cookie = bytes.fromhex(cookie) # Chuyển sang byte để iv là 1 chuỗi byte
iv = cookie[:16] # Đây là iv ban đầu
block1 = cookie[16:32] # block_data_1_after_encrypt
block2 = cookie[32:] # block_data_2_after_encrypt
origin = b'admin=False;expi' # Lấy 16 byte
target_to_get_flag = b'admin=True_yeah\x01' # PKCS#7 Padding thêm để đủ 16 byte (Mình thích thì mình thêm yeah)
# send_iv = origin ^ iv ^ target_to_get_flag
send_iv = xor(xor(origin, target_to_get_flag),(iv))
# Check Admin
check_admin(block1, send_iv)
```
- Đọc đoạn code trên, bạn sẽ hiểu được các bước mình đã hướng dẫn ở trên
- Đoạn code để lấy flag sẽ như sau:
```
import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Util.number import*
# a ^ b
def xor(a, b):
return long_to_bytes(bytes_to_long(a) ^ bytes_to_long(b))
# Hàm lấy giá trị cookie từ server
def get_cookie():
url = "http://aes.cryptohack.org/flipping_cookie/get_cookie/"
get_cookie_value_on_server = requests.get(url)
json_cookie = get_cookie_value_on_server.json()
return bytes.fromhex(json_cookie["cookie"])
def check_admin(cookie, iv):
# Kết hợp các url lại với nhau
url = "http://aes.cryptohack.org/flipping_cookie/check_admin/"
url += cookie.hex()
url += "/"
url += iv.hex()
url += "/"
# Lấy flag thui nèooooo
get_flag = requests.get(url)
flag = get_flag.json()
print(flag)
cookie = get_cookie()
origin = b'admin=False;expi'
# Vì trong server có đoạn code này if b"admin=True" in unpadded.split(b";"): nên phải để target_to_get_flag thế
target_to_get_flag = b'admin=True;\x05\x05\x05\x05\x05'
iv = cookie[:16] # Đây là iv ban đầu
block1 = cookie[16:32] # block_data_1_after_encrypt
block2 = cookie[32:] # Các đoạn còn lại
# send_iv = origin ^ iv ^ target_to_get_flag
send_iv = xor(xor(origin, target_to_get_flag), iv)
# Check Admin
check_admin(block1, send_iv)
```
> crypto{4u7h3n71c4710n_15_3553n714l}
### Triple Des

- Source code của chall này như sau:
```
from Crypto.Cipher import DES3
from Crypto.Util.Padding import pad
IV = os.urandom(8)
FLAG = ?
def xor(a, b):
# xor 2 bytestrings, repeating the 2nd one if necessary
return bytes(x ^ y for x,y in zip(a, b * (1 + len(a) // len(b))))
@chal.route('/triple_des/encrypt/<key>/<plaintext>/')
def encrypt(key, plaintext):
try:
key = bytes.fromhex(key)
plaintext = bytes.fromhex(plaintext)
plaintext = xor(plaintext, IV)
cipher = DES3.new(key, DES3.MODE_ECB)
ciphertext = cipher.encrypt(plaintext)
ciphertext = xor(ciphertext, IV)
return {"ciphertext": ciphertext.hex()}
except ValueError as e:
return {"error": str(e)}
@chal.route('/triple_des/encrypt_flag/<key>/')
def encrypt_flag(key):
return encrypt(key, pad(FLAG.encode(), 8).hex())
```
- Chall này là mã hóa loại TripleDes, ta thấy trên server không có hàm decrypt, ta khó có thể tìm ra flag được, thế nhưng TripleDes lại có 1 điểm yếu là [Weak_Key](https://en.wikipedia.org/wiki/Weak_key). Weak key là những khóa mà khi ta mã hóa 2 lần, ta sẽ thu được plaintext ban đầu, tức là ``Encrypt(Encrypt(Plaintext)) = Plaintext``
- Gồm các Weak Key như sau:

- Chall này, ta cần sử dụng các Weak Key để có thể tìm được Flag
- Chọn 3 Weak Key để làm, mình chọn 3 khóa là ``b'\x00'*8``, ``b'\xff'*8``, ``b'\x01'*8 ``
- Sơ đồ cách làm như sau:

- Đoạn code minh họa cho sơ đồ trên:
```
from Crypto.Cipher import DES3,DES
import os
from Crypto.Util.Padding import pad,unpad
IV = os.urandom(8)
FLAG = b'Flag{This_is_flag}'
def xor(a, b):
# xor 2 bytestrings, repeating the 2nd one if necessary
return bytes(x ^ y for x,y in zip(a, b * (1 + len(a) // len(b))))
def decrypt_des(key, ciphertext):
key = bytes.fromhex(key)
ciphertext = bytes.fromhex(ciphertext)
cipher = DES.new(key,DES.MODE_ECB)
plaintext = cipher.decrypt(ciphertext)
return plaintext.hex()
def encrypt_des(key, plaintext):
key = bytes.fromhex(key)
plaintext = bytes.fromhex(plaintext)
cipher = DES.new(key, DES.MODE_ECB)
ciphertext = cipher.encrypt(plaintext)
return ciphertext.hex()
key_1 = b'\x00'*8
key_2 = b'\xff'*8
key_3 = b'\xfe'*8
key_1 = key_1.hex()
key_2 = key_2.hex()
key_3 = key_3.hex()
FLAG_pad = pad(FLAG,8)
FLAG_after_xor = xor(FLAG_pad,IV)
# First block 3DES
flag_first = (encrypt_des(key_1,(FLAG_after_xor).hex()))
flag_second = (decrypt_des(key_2,flag_first))
flag_third = (encrypt_des(key_3,flag_second))
#Second block 3DES
flag_fourth = (encrypt_des(key_1,flag_third))
flag_fifth = (decrypt_des(key_2,flag_fourth))
flag_sixth = (encrypt_des(key_3,flag_fifth))
print(unpad((xor(bytes.fromhex(flag_sixth),IV)),8).decode())
```
- Đoạn code của chall này là:
```
import requests
def encrypt(key, plain):
url = "http://aes.cryptohack.org/triple_des/encrypt/"
url += key
url += "/"
url += plain.hex()
url += "/"
r = requests.get(url).json()
return bytes.fromhex(r["ciphertext"])
def encrypt_flag(key):
url = "http://aes.cryptohack.org/triple_des/encrypt_flag/"
r = requests.get(url + key + '/').json()
return bytes.fromhex(r["ciphertext"])
key1 = b'\x00'*8
key2 = b'\xff'*8
key3 = b'\x01'*8
key = key1.hex() + key2.hex() + key3.hex()
enc_flag = encrypt_flag(key)
ciphertext = encrypt(key, enc_flag)
print(ciphertext)
```
> crypto{n0t_4ll_k3ys_4r3_g00d_k3ys}
### Symmetry
Source code của chall như sau
```
from Crypto.Cipher import AES
KEY = ?
FLAG = ?
@chal.route('/symmetry/encrypt/<plaintext>/<iv>/')
def encrypt(plaintext, iv):
plaintext = bytes.fromhex(plaintext)
iv = bytes.fromhex(iv)
if len(iv) != 16:
return {"error": "IV length must be 16"}
cipher = AES.new(KEY, AES.MODE_OFB, iv)
encrypted = cipher.encrypt(plaintext)
ciphertext = encrypted.hex()
return {"ciphertext": ciphertext}
@chal.route('/symmetry/encrypt_flag/')
def encrypt_flag():
iv = os.urandom(16)
cipher = AES.new(KEY, AES.MODE_OFB, iv)
encrypted = cipher.encrypt(FLAG.encode())
ciphertext = iv.hex() + encrypted.hex()
return {"ciphertext": ciphertext}
```
Chall này, ta sẽ được gửi 1 plaintext bất kỳ cho server rồi sẽ thu được ciphertext tương ứng.
Trước hết, ta cần nhìn thấy sơ đồ OFB như nào đã

Ta thấy rằng, plaintext sẽ xor với Block(Key, IV) rồi sẽ ra Ciphertext.
Qua đó, ta sẽ gửi các byte ``x\00`` để tấn công. Nhìn vào sơ đồ này bạn sẽ hiểu mình sẽ làm như thế nào.

Sau đó chỉ cần xor với ciphertext của flag là sẽ thu được flag thui.
```
from pwn import*
import requests
def encrypt(plain, iv):
url = "https://aes.cryptohack.org/symmetry/encrypt"
url += "/"
url += plain
url += "/"
url += iv
r = requests.get(url).json()
return bytes.fromhex(r["ciphertext"])
def encrypt_flag():
url = "https://aes.cryptohack.org/symmetry/encrypt_flag"
r = requests.get(url).json()
return bytes.fromhex(r["ciphertext"])
iv_ciphertext = encrypt_flag()
iv = iv_ciphertext[:16].hex()
ciphertext = iv_ciphertext[16:]
ciphertext_chosen = encrypt("00"*33, iv)
print(xor(ciphertext,ciphertext_chosen).decode())
```
> crypto{0fb_15_5ymm37r1c4l_!!!11!}
### BEAN COUNTER
Source code như sau:
```
from Crypto.Cipher import AES
KEY = ?
class StepUpCounter(object):
def __init__(self, step_up=False):
self.value = os.urandom(16).hex()
self.step = 1
self.stup = step_up
def increment(self):
if self.stup:
self.newIV = hex(int(self.value, 16) + self.step)
else:
self.newIV = hex(int(self.value, 16) - self.stup)
self.value = self.newIV[2:len(self.newIV)]
return bytes.fromhex(self.value.zfill(32))
def __repr__(self):
self.increment()
return self.value
@chal.route('/bean_counter/encrypt/')
def encrypt():
cipher = AES.new(KEY, AES.MODE_ECB)
ctr = StepUpCounter()
out = []
with open("challenge_files/bean_flag.png", 'rb') as f:
block = f.read(16)
while block:
keystream = cipher.encrypt(ctr.increment())
xored = [a^b for a, b in zip(block, keystream)]
out.append(bytes(xored).hex())
block = f.read(16)
return {"encrypted": ''.join(out)}
```
Ta thấy chall có hàm sinh khóa như sau
```
class StepUpCounter(object):
def __init__(self, step_up=False):
self.value = os.urandom(16).hex()
self.step = 1
self.stup = step_up
def increment(self):
if self.stup:
self.newIV = hex(int(self.value, 16) + self.step)
else:
self.newIV = hex(int(self.value, 16) - self.stup)
self.value = self.newIV[2:len(self.newIV)]
return bytes.fromhex(self.value.zfill(32))
```
Nếu ``self.stup == True`` thì newIV sẽ + 1 và fill 16 bytes, còn nếu ``self.stup == False`` thì newIV sẽ giữ nguyên tại vì False tương đương với 0.
Mà step_up mặc định là False, thế nên tất cả các keystream sẽ điều là như nhau với độ dai là 16 bytes.
Mà file PNG thì luôn luôn sẽ có 16 bytes như thế này nếu file không bị hư hỏng gì.

Thế nên, ta chỉ cần lấy 16 bytes đầu của encrypted xor với header PNG ``89504e470d0a1a0a0000000d49484452`` này, ta sẽ thu được key, và xor ngược lại thì sẽ thu được các bytes của ảnh.
```
from pwn import*
import requests
url = "https://aes.cryptohack.org/bean_counter/encrypt"
r = requests.get(url).json()
encrypt_flag = bytes.fromhex(r["encrypted"])
png_header = bytes.fromhex("89504e470d0a1a0a0000000d49484452")
key = xor(encrypt_flag[:16],png_header)
flag_png = xor(encrypt_flag,key)
with open("flag.png", "wb") as file:
file.write(flag_png)
```

> crypto{hex_bytes_beans}
### CTRIME
```python
from Crypto.Cipher import AES
from Crypto.Util import Counter
import zlib
KEY = ?
FLAG = ?
@chal.route('/ctrime/encrypt/<plaintext>/')
def encrypt(plaintext):
plaintext = bytes.fromhex(plaintext)
iv = int.from_bytes(os.urandom(16), 'big')
cipher = AES.new(KEY, AES.MODE_CTR, counter=Counter.new(128, initial_value=iv))
encrypted = cipher.encrypt(zlib.compress(plaintext + FLAG.encode()))
return {"ciphertext": encrypted.hex()}
```
Ta thấy rằng, trước khi mã hóa, thì plaintext mình nhập sẽ được cộng với flag, sau đó sẽ sử dụng ``zlib.compress`` là một thuật toán nén văn bản để ra một đoạn byte ngẫu nhiên.
Hàm này có một cái hay là
```python
import zlib
flag = b'crypto{day-la-flag}'
print(len(zlib.compress(b"crypto"+flag)))
print(len(zlib.compress(b"helloo"+flag)))
print(len(zlib.compress(b"crypto{d"+flag)))
print(len(zlib.compress(b"crypto{h"+flag)))
```
```yaml
29
33
29
30
```
Nó sẽ rút gọn bớt đi các ký tự trùng lặp, sau đó sẽ nén lại, vì thế nếu ta thu được độ dài ngắn hơn bình thường, thì đó chính là ký tự đúng.
```python
from Crypto.Cipher import AES
from Crypto.Util import Counter
import zlib
import requests
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_{}0123456789abcdefghijklmnopqrstuvwxyz"
def encrypt(plaintext):
url = 'https://aes.cryptohack.org/ctrime/encrypt/'
url = url + plaintext.hex()
url = url + '/'
r = requests.get(url)
js = r.json()
output = js['ciphertext']
return output
flag = b"crypto{"
last_chr = b""
while last_chr != b"}":
send = flag + b"*"
out = encrypt(send*2)
for c in alpha:
print(c)
send = flag + c.encode()
out2 = encrypt(send*2)
print(len(out),len(out2))
if len(out2) < len(out):
flag += c.encode()
last_chr = c.encode()
break
print(flag)
```
> crypto{CRIME_571ll_p4y5}
### Stream of Consciousness
```python
from Crypto.Cipher import AES
from Crypto.Util import Counter
import random
KEY = ?
TEXT = ['???', '???', ..., FLAG]
@chal.route('/stream_consciousness/encrypt/')
def encrypt():
random_line = random.choice(TEXT)
cipher = AES.new(KEY, AES.MODE_CTR, counter=Counter.new(128))
encrypted = cipher.encrypt(random_line.encode())
return {"ciphertext": encrypted.hex()}
```
Chall này không có gì khó, chỉ là mình sẽ mất thời gian đoán plaintext thui =(((
Trước hết mình chạy để lấy hết các trường hợp
```python
from Crypto.Cipher import AES
from Crypto.Util import Counter
import requests
from pwn import*
def encrypt():
url = 'https://aes.cryptohack.org/stream_consciousness/encrypt/'
r = requests.get(url)
js = r.json()
output = js['ciphertext']
return output
lst = []
for i in range(100):
lst.append(encrypt())
print(lst)
```
Sau khi lấy được hết các ciphertext rồi, thì mình sẽ tiến hành xor với plaintext, bắt đầu từ form flag. Và để bạn phải đau khổ như mình thì mình không đưa plaintext lên đâu hihihi
```python
from pwn import*
lst = ['4b2c7f3cc93700920bb1a77df8c3f148292fb7210067c2f11f3a41d347467f50158903b613c73cfcdbf420b8455a2b0d8c2f3d2ac1', '55636b3cd836488713a4a771bfe5f143227ca2321f6c96f1057141c641477f54019503a65dda7bb1d3f629b411503b59d80f7722de33c10323a1def28bf69abd9263b9a292c0d34f3d6d782dd45beb58179374', '482c633cd93d52940ab6b238bfd8b94e2968e7291a29c2f0102941c641477f4201931bf219c835fbceb82efd1146215f96663f3a8a66cd1262b8daf88be599bec664ff', '5d372675cb7869c60bb5ba7dfec2a8073066b428497dd9b8133841db47022b5a05c01dbb1dc12ffd9ad16cfb505c6959d9', '5d2a623ce478538e02b8b27df6cbbf48356ae7291d27', '7f367f6cd9375b8d50adeb6aed9fe54a187df4355c3ae9a9440207861e16334f', '482c636fc878488911a7bb2eb38ca54f2e7ce723087bc4f1103a04920402375d17c026f216c63aa8d2fd6cf548412b419e663921de32c70231f1cdead9e59fb0d572f1ea92c7da4721662a209156f7145a9e33036b31fa33bb0d3eec34791ce6e032b300b8b53c60253c1f3b19b8b21f2831e1c461279a', '582b6a70d478578f0fb8fe29f7c5bf4c677baf211d29ffbf1c7d0dd74854365c07c00ef209cc38b3d4fc6cf044412c4c9622702e90228f1f2ab0daabdfff93a3d771beb5d793fb0235342b319144ee0a1f9a23503274b82fa10d03a422310af8e37cba4b', '4c217474cc2853c60bb1fe35fedff14a2e7cb4250d29c2f0147d15c0484b3112018e0bf213da7bbedbfb27b8534b6e4397317e6fa927c11f62bcc1f9ceb79ea4df7ebdaed3c7db4d3660', '5d372675cb7869c60bb5ba7dfec2a8073066b428497dd9b8133841db47022b5a05c01dbb1dc12ffd9ad16cfb505c6959d9', '5d2a623ce478538e02b8b27df6cbbf48356ae7291d27', '4b2c7f3cc93700920bb1a77df8c3f148292fb7210067c2f11f3a41d347467f50158903b613c73cfcdbf420b8455a2b0d8c2f3d2ac1', '7f367f6cd9375b8d50adeb6aed9fe54a187df4355c3ae9a9440207861e16334f', '482c636fc878488911a7bb2eb38ca54f2e7ce723087bc4f1103a04920402375d17c026f216c63aa8d2fd6cf548412b419e663921de32c70231f1cdead9e59fb0d572f1ea92c7da4721662a209156f7145a9e33036b31fa33bb0d3eec34791ce6e032b300b8b53c60253c1f3b19b8b21f2831e1c461279a', '4c217474cc2853c60bb1fe35fedff14a2e7cb4250d29c2f0147d15c0484b3112018e0bf213da7bbedbfb27b8534b6e4397317e6fa927c11f62bcc1f9ceb79ea4df7ebdaed3c7db4d3660', '5e31723ce478578f0fb8fe2ef7c3a6072f66aa6e', '542b713cdd2a4f9307f4bf33fb8cb946377fbe60016c91f41d7d03d7095537570ec007b75ace3ea8c9b821e1115c21599d67', '4c217474cc2853c60bb1fe35fedff14a2e7cb4250d29c2f0147d15c0484b3112018e0bf213da7bbedbfb27b8534b6e4397317e6fa927c11f62bcc1f9ceb79ea4df7ebdaed3c7db4d3660', '55647574cc344cc60fbbad38bfc9a7423576b3280067d1b810330592474d2b1207851bf212c036fcd8f92ff31f', '482c633cd93d52940ab6b238bfd8b94e2968e7291a29c2f0102941c641477f4201931bf219c835fbceb82efd1146215f96663f3a8a66cd1262b8daf88be599bec664ff', '5d372675cb7869c60bb5ba7dfec2a8073066b428497dd9b8133841db47022b5a05c01dbb1dc12ffd9ad16cfb505c6959d9', '5530267fcc36079243b6bb7debc3a3496760b2344529d4ed057d08c609413e5c40820af213ce35b3c8fd28b6', '482c7479c87842891aa7fe2feac2bf4e2968eb601965d7e11833069248567f5a0f921cb709857b8fdfea35f74b5a2f0c', '542b713cdd2a4f9307f4bf33fb8cb946377fbe60016c91f41d7d03d7095537570ec007b75ace3ea8c9b821e1115c21599d67', '4b2c67688d39008a0ca0fe32f98ca54f2e61a033497ddef9057d15da4c4c7f41058502b71e892fb39af529b8425d6e409934262a922ac01e31f1cfe5cfb783bfd363a5a6dbddd34034247465d956ed1d5a943f133f7cfd66a64304a5207f14ece571bc0bbafc6e21383d5a6f05b5f7066039e8c27b69fd9a58bc7dc7360857ef2ad05186eebed07aa534a27c0fa995fcccd2e942fa15847c6f015511077b42', '5530267fcc36079243b6bb7debc3a3496760b2344529d4ed057d08c609413e5c40820af213ce35b3c8fd28b6', '55647574cc344cc60fbbad38bfc9a7423576b3280067d1b810330592474d2b1207851bf212c036fcd8f92ff31f', '4b2c7f3cc93700920bb1a77df8c3f148292fb7210067c2f11f3a41d347467f50158903b613c73cfcdbf420b8455a2b0d8c2f3d2ac1', '542b713cdd2a4f9307f4bf33fb8cb946377fbe60016c91f41d7d03d7095537570ec007b75ace3ea8c9b821e1115c21599d67', '5d2a623ce478538e02b8b27df6cbbf48356ae7291d27', '582b6a70d478578f0fb8fe29f7c5bf4c677baf211d29ffbf1c7d0dd74854365c07c00ef209cc38b3d4fc6cf044412c4c9622702e90228f1f2ab0daabdfff93a3d771beb5d793fb0235342b319144ee0a1f9a23503274b82fa10d03a422310af8e37cba4b', '502b7079817850940cb6bf3ff3d5ee071367a239496dd9f6562941d9474d2812088f18f21edb3ebdc8e16cf14512275ed46638208966c71e2fb8c2e2cae39fbfd539ffe992c7da4778002e20df42fe581b983e503379f12aab5f12a269', '502b7079817850940cb6bf3ff3d5ee071367a239496dd9f6562941d9474d2812088f18f21edb3ebdc8e16cf14512275ed46638208966c71e2fb8c2e2cae39fbfd539ffe992c7da4778002e20df42fe581b983e503379f12aab5f12a269', '4b2c7f3cc93700920bb1a77df8c3f148292fb7210067c2f11f3a41d347467f50158903b613c73cfcdbf420b8455a2b0d8c2f3d2ac1', '4c217474cc2853c60bb1fe35fedff14a2e7cb4250d29c2f0147d15c0484b3112018e0bf213da7bbedbfb27b8534b6e4397317e6fa927c11f62bcc1f9ceb79ea4df7ebdaed3c7db4d3660', '482c636fc878488911a7bb2eb38ca54f2e7ce723087bc4f1103a04920402375d17c026f216c63aa8d2fd6cf548412b419e663921de32c70231f1cdead9e59fb0d572f1ea92c7da4721662a209156f7145a9e33036b31fa33bb0d3eec34791ce6e032b300b8b53c60253c1f3b19b8b21f2831e1c461279a', '522b2a3ce47f4c8a43b3b17df6c2f153282f832f0565cfb8103305925d47335e40880aa05ada2faedbf12bf0451221588c', '5e31723ce478578f0fb8fe2ef7c3a6072f66aa6e', '502b7079817850940cb6bf3ff3d5ee071367a239496dd9f6562941d9474d2812088f18f21edb3ebdc8e16cf14512275ed46638208966c71e2fb8c2e2cae39fbfd539ffe992c7da4778002e20df42fe581b983e503379f12aab5f12a269', '502b7079817850940cb6bf3ff3d5ee071367a239496dd9f6562941d9474d2812088f18f21edb3ebdc8e16cf14512275ed46638208966c71e2fb8c2e2cae39fbfd539ffe992c7da4778002e20df42fe581b983e503379f12aab5f12a269', '482c636fc878488911a7bb2eb38ca54f2e7ce723087bc4f1103a04920402375d17c026f216c63aa8d2fd6cf548412b419e663921de32c70231f1cdead9e59fb0d572f1ea92c7da4721662a209156f7145a9e33036b31fa33bb0d3eec34791ce6e032b300b8b53c60253c1f3b19b8b21f2831e1c461279a', '5e31723ce478578f0fb8fe2ef7c3a6072f66aa6e', '4c217474cc2853c60bb1fe35fedff14a2e7cb4250d29c2f0147d15c0484b3112018e0bf213da7bbedbfb27b8534b6e4397317e6fa927c11f62bcc1f9ceb79ea4df7ebdaed3c7db4d3660', '482c7479c87842891aa7fe2feac2bf4e2968eb601965d7e11833069248567f5a0f921cb709857b8fdfea35f74b5a2f0c', '55647574cc344cc60fbbad38bfc9a7423576b3280067d1b810330592474d2b1207851bf212c036fcd8f92ff31f', '482c7479c87842891aa7fe2feac2bf4e2968eb601965d7e11833069248567f5a0f921cb709857b8fdfea35f74b5a2f0c', '522b2a3ce47f4c8a43b3b17df6c2f153282f832f0565cfb8103305925d47335e40880aa05ada2faedbf12bf0451221588c', '522b2a3ce47f4c8a43b3b17df6c2f153282f832f0565cfb8103305925d47335e40880aa05ada2faedbf12bf0451221588c', '4b2b7370c97869c60bb5a838bfceb44b2e6ab1250d29c2f0143341c641432b1229c00cbd0fc53ffcc8fd2dfb59123d589b2e702b9b36db0331f1c1ed8bff83bcdb7bb8a6c6dadd4c67', '4b2c67688d39008802a7aa24bfdfbc422b63e7340160c5b8013c08dc5d02375304ce', '482c7479c87842891aa7fe2feac2bf4e2968eb601965d7e11833069248567f5a0f921cb709857b8fdfea35f74b5a2f0c', '55647574cc344cca439df931f38cbd48346ae7251f6cc4e1053508dc4e02365440880af21ec63eafd4bf38b8525d2348d824312c9568', '5d2a623ce478538e02b8b27df6cbbf48356ae7291d27', '5e31723ce478578f0fb8fe2ef7c3a6072f66aa6e', '55636b3cd836488713a4a771bfe5f143227ca2321f6c96f1057141c641477f54019503a65dda7bb1d3f629b411503b59d80f7722de33c10323a1def28bf69abd9263b9a292c0d34f3d6d782dd45beb58179374', '502b7079817850940cb6bf3ff3d5ee071367a239496dd9f6562941d9474d2812088f18f21edb3ebdc8e16cf14512275ed46638208966c71e2fb8c2e2cae39fbfd539ffe992c7da4778002e20df42fe581b983e503379f12aab5f12a269', '5530267fcc36079243b6bb7debc3a3496760b2344529d4ed057d08c609413e5c40820af213ce35b3c8fd28b6', '4c217474cc2853c60bb1fe35fedff14a2e7cb4250d29c2f0147d15c0484b3112018e0bf213da7bbedbfb27b8534b6e4397317e6fa927c11f62bcc1f9ceb79ea4df7ebdaed3c7db4d3660', '4b2c67688d39008802a7aa24bfdfbc422b63e7340160c5b8013c08dc5d02375304ce', '502b7079817850940cb6bf3ff3d5ee071367a239496dd9f6562941d9474d2812088f18f21edb3ebdc8e16cf14512275ed46638208966c71e2fb8c2e2cae39fbfd539ffe992c7da4778002e20df42fe581b983e503379f12aab5f12a269', '5d2a623ce478538e02b8b27df6cbbf48356ae7291d27', '4b2b7370c97869c60bb5a838bfceb44b2e6ab1250d29c2f0143341c641432b1229c00cbd0fc53ffcc8fd2dfb59123d589b2e702b9b36db0331f1c1ed8bff83bcdb7bb8a6c6dadd4c67', '4b2c67688d39008a0ca0fe32f98ca54f2e61a033497ddef9057d15da4c4c7f41058502b71e892fb39af529b8425d6e409934262a922ac01e31f1cfe5cfb783bfd363a5a6dbddd34034247465d956ed1d5a943f133f7cfd66a64304a5207f14ece571bc0bbafc6e21383d5a6f05b5f7066039e8c27b69fd9a58bc7dc7360857ef2ad05186eebed07aa534a27c0fa995fcccd2e942fa15847c6f015511077b42', '5530267fcc36079243b6bb7debc3a3496760b2344529d4ed057d08c609413e5c40820af213ce35b3c8fd28b6', '4c217474cc2853c60bb1fe35fedff14a2e7cb4250d29c2f0147d15c0484b3112018e0bf213da7bbedbfb27b8534b6e4397317e6fa927c11f62bcc1f9ceb79ea4df7ebdaed3c7db4d3660', '55647574cc344cc60fbbad38bfc9a7423576b3280067d1b810330592474d2b1207851bf212c036fcd8f92ff31f', '5d2a623ce478538e02b8b27df6cbbf48356ae7291d27', '55636b3cd836488713a4a771bfe5f143227ca2321f6c96f1057141c641477f54019503a65dda7bb1d3f629b411503b59d80f7722de33c10323a1def28bf69abd9263b9a292c0d34f3d6d782dd45beb58179374', '4c217474cc2853c60bb1fe35fedff14a2e7cb4250d29c2f0147d15c0484b3112018e0bf213da7bbedbfb27b8534b6e4397317e6fa927c11f62bcc1f9ceb79ea4df7ebdaed3c7db4d3660', '7f367f6cd9375b8d50adeb6aed9fe54a187df4355c3ae9a9440207861e16334f', '542b713cdd2a4f9307f4bf33fb8cb946377fbe60016c91f41d7d03d7095537570ec007b75ace3ea8c9b821e1115c21599d67', '582b6a70d478578f0fb8fe29f7c5bf4c677baf211d29ffbf1c7d0dd74854365c07c00ef209cc38b3d4fc6cf044412c4c9622702e90228f1f2ab0daabdfff93a3d771beb5d793fb0235342b319144ee0a1f9a23503274b82fa10d03a422310af8e37cba4b', '55636b3cd836488713a4a771bfe5f143227ca2321f6c96f1057141c641477f54019503a65dda7bb1d3f629b411503b59d80f7722de33c10323a1def28bf69abd9263b9a292c0d34f3d6d782dd45beb58179374', '5836636fde754d8708bdb03abfcdbf436742ae2c0560d8fd0324', '4b2c67688d39008802a7aa24bfdfbc422b63e7340160c5b8013c08dc5d02375304ce', '582b6a70d478578f0fb8fe29f7c5bf4c677baf211d29ffbf1c7d0dd74854365c07c00ef209cc38b3d4fc6cf044412c4c9622702e90228f1f2ab0daabdfff93a3d771beb5d793fb0235342b319144ee0a1f9a23503274b82fa10d03a422310af8e37cba4b', '4b2b7370c97869c60bb5a838bfceb44b2e6ab1250d29c2f0143341c641432b1229c00cbd0fc53ffcc8fd2dfb59123d589b2e702b9b36db0331f1c1ed8bff83bcdb7bb8a6c6dadd4c67', '4b2c7f3cc93700920bb1a77df8c3f148292fb7210067c2f11f3a41d347467f50158903b613c73cfcdbf420b8455a2b0d8c2f3d2ac1', '5d372675cb7869c60bb5ba7dfec2a8073066b428497dd9b8133841db47022b5a05c01dbb1dc12ffd9ad16cfb505c6959d9', '4b2b7370c97869c60bb5a838bfceb44b2e6ab1250d29c2f0143341c641432b1229c00cbd0fc53ffcc8fd2dfb59123d589b2e702b9b36db0331f1c1ed8bff83bcdb7bb8a6c6dadd4c67', '5d372675cb7869c60bb5ba7dfec2a8073066b428497dd9b8133841db47022b5a05c01dbb1dc12ffd9ad16cfb505c6959d9', '55647574cc344cca439df931f38cbd48346ae7251f6cc4e1053508dc4e02365440880af21ec63eafd4bf38b8525d2348d824312c9568', '4b2c67688d39008a0ca0fe32f98ca54f2e61a033497ddef9057d15da4c4c7f41058502b71e892fb39af529b8425d6e409934262a922ac01e31f1cfe5cfb783bfd363a5a6dbddd34034247465d956ed1d5a943f133f7cfd66a64304a5207f14ece571bc0bbafc6e21383d5a6f05b5f7066039e8c27b69fd9a58bc7dc7360857ef2ad05186eebed07aa534a27c0fa995fcccd2e942fa15847c6f015511077b42', '4b2c67688d39008a0ca0fe32f98ca54f2e61a033497ddef9057d15da4c4c7f41058502b71e892fb39af529b8425d6e409934262a922ac01e31f1cfe5cfb783bfd363a5a6dbddd34034247465d956ed1d5a943f133f7cfd66a64304a5207f14ece571bc0bbafc6e21383d5a6f05b5f7066039e8c27b69fd9a58bc7dc7360857ef2ad05186eebed07aa534a27c0fa995fcccd2e942fa15847c6f015511077b42', '4b2c67688d39008802a7aa24bfdfbc422b63e7340160c5b8013c08dc5d02375304ce', '7f367f6cd9375b8d50adeb6aed9fe54a187df4355c3ae9a9440207861e16334f', '55647574cc344cca439df931f38cbd48346ae7251f6cc4e1053508dc4e02365440880af21ec63eafd4bf38b8525d2348d824312c9568', '7f367f6cd9375b8d50adeb6aed9fe54a187df4355c3ae9a9440207861e16334f', '55636b3cd836488713a4a771bfe5f143227ca2321f6c96f1057141c641477f54019503a65dda7bb1d3f629b411503b59d80f7722de33c10323a1def28bf69abd9263b9a292c0d34f3d6d782dd45beb58179374', '4b2c67688d39008802a7aa24bfdfbc422b63e7340160c5b8013c08dc5d02375304ce', '55647574cc344cca439df931f38cbd48346ae7251f6cc4e1053508dc4e02365440880af21ec63eafd4bf38b8525d2348d824312c9568', '482c7479c87842891aa7fe2feac2bf4e2968eb601965d7e11833069248567f5a0f921cb709857b8fdfea35f74b5a2f0c', '5e31723ce478578f0fb8fe2ef7c3a6072f66aa6e', '55647574cc344cc60fbbad38bfc9a7423576b3280067d1b810330592474d2b1207851bf212c036fcd8f92ff31f', '522b2a3ce47f4c8a43b3b17df6c2f153282f832f0565cfb8103305925d47335e40880aa05ada2faedbf12bf0451221588c', '5d2a623ce478538e02b8b27df6cbbf48356ae7291d27', '502b7079817850940cb6bf3ff3d5ee071367a239496dd9f6562941d9474d2812088f18f21edb3ebdc8e16cf14512275ed46638208966c71e2fb8c2e2cae39fbfd539ffe992c7da4778002e20df42fe581b983e503379f12aab5f12a269', '482c633cd93d52940ab6b238bfd8b94e2968e7291a29c2f0102941c641477f4201931bf219c835fbceb82efd1146215f96663f3a8a66cd1262b8daf88be599bec664ff', '4b2c67688d39008a0ca0fe32f98ca54f2e61a033497ddef9057d15da4c4c7f41058502b71e892fb39af529b8425d6e409934262a922ac01e31f1cfe5cfb783bfd363a5a6dbddd34034247465d956ed1d5a943f133f7cfd66a64304a5207f14ece571bc0bbafc6e21383d5a6f05b5f7066039e8c27b69fd9a58bc7dc7360857ef2ad05186eebed07aa534a27c0fa995fcccd2e942fa15847c6f015511077b42', '4c217474cc2853c60bb1fe35fedff14a2e7cb4250d29c2f0147d15c0484b3112018e0bf213da7bbedbfb27b8534b6e4397317e6fa927c11f62bcc1f9ceb79ea4df7ebdaed3c7db4d3660']
data = []
for i in range(len(lst)):
for j in range(i,len(lst)):
x = b"crypto{"
try:
a = (xor(xor(x,bytes.fromhex(lst[i])),bytes.fromhex(lst[j]))[:len(x)]).decode()
if a not in data and not any(char in a for char in "#$%&'()*+,-./:;<=>?@[\]^"):
data.append(a)
except:
continue
for i in data:
print(i)
```
> crypto{k3y57r34m_r3u53_15_f474l}