---
# System prepended metadata

title: SYMMETRIC CRYPTOGRAPHY

---

# SYMMETRIC CRYPTOGRAPHY
## 1. Keyed Permutations
`flag: crypto{Bijective}`

## 2. Resisting Bruteforce
`flag: crypto{Biclique}`

## 3. Structure of AES
```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.  """
    ????

matrix = [
    [99, 114, 121, 112],
    [116, 111, 123, 105],
    [110, 109, 97, 116],
    [114, 105, 120, 125],
]

print(matrix2bytes(matrix))

```
Để chuyển ma trận thành 1 chuỗi ta dùng hàm for chạy từ phần tử đầu tới phần tử cuối của ma trân và chuyển các số thành kiểu char và ghép chúng lại.

```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.  """
    flag = ''
    for i in matrix:
        for j in range(0, 4):
            flag += chr(i[j])
    return flag

matrix = [
    [99, 114, 121, 112],
    [116, 111, 123, 105],
    [110, 109, 97, 116],
    [114, 105, 120, 125],
]

print(matrix2bytes(matrix))
```
`flag: crypto{inmatrix}`

## 4. Round Keys
```python!
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))

```
Để có thể thực hiện `add_round_key` thì ta phải xor lần lượt các phần tử của `state` và `round_key`. Sau đó sẽ dùng `matrix2bytes` để lấy flag.

```python!
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):
    matrix = []
    for i in range(0, 4):
        list = []
        for j in range(0, 4):
            list.append(s[i][j] ^ k[i][j])
        matrix.append(list)
    return matrix 

def matrix2bytes(matrix):
    """ Converts a 4x4 matrix into a 16-byte array.  """
    flag = ''
    for i in matrix:
        for j in range(0, 4):
            flag += chr(i[j])
    return flag

print(matrix2bytes(add_round_key(state, round_key)))
```
`flag: crypto{r0undk3y}`

## 5. Confusion through 
```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 sub_bytes(s, sbox=s_box):
    

print(sub_bytes(state, sbox=inv_s_box))
```
Để giải quyết bài này ta sẽ duyệt từ phần tử đầu đến phần tử cuối của ma trận. Giá trị của các phần tử trong `state` chính là vị trí của các phần tử mới trong `s_box` hoặc `inv_s_box`. Sau đó lấy được các giá trị ta sẽ chuyển nó thành kiểu char và chuyển nó sang byte để lấy flag.


```python!
def sub_bytes(s, sbox=s_box):
    flag = ''
    for i in range(0, 4):
        for j in range(0, 4):
            print(s[i][j], sbox[s[i][j]])
            flag += chr(sbox[s[i][j]])
    return flag.encode()

print(sub_bytes(state, sbox=inv_s_box))
```
`flag: crypto{l1n34rly}`

## 6. Diffusion through Permutation
```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):
    ???


# 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],
]
```

Để giải được bài này ta cần xây dựng hàm `inv_shift_rows` bằng cấch đảo vị trí các phần tử trong mảng theo một thứ tự nhất định. Sau đó chạy hàm `inv_mix_columns` rồi lấy kết quả bỏ vào trong hàm `inv_shift_rows` rồi chuyển thành chuỗi bytes sẽ thu được flag.

```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]
    return s

def inv_shift_rows(s):
    s[0][1], s[1][1], s[2][1], s[3][1] = s[3][1], s[0][1], s[1][1], s[2][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[1][3], s[2][3], s[3][3], s[0][3]
    return 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])
    return s


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

    return mix_columns(s)


state = [
    [108, 106, 71, 86],
    [96, 62, 38, 72],
    [42, 184, 92, 209],
    [94, 79, 8, 54],
]


state_imc = inv_mix_columns(state)
state_isr = inv_shift_rows(state_imc)
flag = ''
for i in range(0, 4):
    for j in range(0, 4):
        print(state_isr[i][j], end=' ')
        flag += chr(state_isr[i][j])
    print()
print(flag.encode())

```

`flag: crypto{d1ffUs3R}`

## 7. Bringing It All Together
```python!
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))
```
Ta chỉ cần kết hợp các hàm đã xay dựng từ các bài trước đó để lấy flag của bài này.

```python!
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
    matrix = bytes2matrix(ciphertext)
    # Initial add round key step

    addRoundKey = add_round_key(matrix, round_keys[10])
    invShiftRows = inv_shift_rows(addRoundKey)
    subBytes = sub_bytes(invShiftRows, inv_s_box)
    
    
    
    for i in range(N_ROUNDS - 1, 0, -1):
        addRoundKey = add_round_key(subBytes, round_keys[i])
        invMixColumns = inv_mix_columns(addRoundKey)
        invShiftRows = inv_shift_rows(invMixColumns)
        subBytes = sub_bytes(invShiftRows, inv_s_box)

    # Run final round (skips the InvMixColumns step)
    cipherkey = bytes2matrix(key)
    addRoundKey = add_round_key(subBytes, cipherkey)
    # Convert state matrix to plaintext
    plaintext = ""
    for i in range(0, 4):
        for j in range(0, 4):
            plaintext += chr(addRoundKey[i][j])
            
    return plaintext


print(decrypt(key, ciphertext))

```
`flag: crypto{MYAES128}`

## 8. Modes of Operation Starter 
```python!
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()}
```

Ta lấy được ciphertext bằng cách ấn nút `submit` của hàm `encrypt_flag()` sau đó ta sẽ truyền chuỗi mà ta nhận được vào hàm `decrypt(ciphertext)` dể lấy ra kết quả và `decode()` là ta sẽ lấy được flag

`encrypt_flag(): 188bc9522883a992cd99f9b2b8ec84fb150c1abe17e7a8ed04a768ad1d11fc07`

`decrypt(ciphertext): 63727970746f7b626c30636b5f633170683372355f3472335f663435375f217d`

`flag: crypto{bl0ck_c1ph3r5_4r3_f457_!}`

## 9. Passwords as Keys
```python!
from Crypto.Cipher import AES
import hashlib
import random


# /usr/share/dict/words from
# https://gist.githubusercontent.com/wchargin/8927565/raw/d9783627c731268fb2935a731a618aa8e95cf465/words
with open("/usr/share/dict/words") as f:
    words = [w.strip() for w in f.readlines()]
keyword = random.choice(words)

KEY = hashlib.md5(keyword.encode()).digest()
FLAG = ?


@chal.route('/passwords_as_keys/decrypt/<ciphertext>/<password_hash>/')
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 {"plaintext": decrypted.hex()}


@chal.route('/passwords_as_keys/encrypt_flag/')
def encrypt_flag():
    cipher = AES.new(KEY, AES.MODE_ECB)
    encrypted = cipher.encrypt(FLAG.encode())

    return {"ciphertext": encrypted.hex()}
```
Ta truy cập vào đường dẫn `https://gist.githubusercontent.com/wchargin/8927565/raw/d9783627c731268fb2935a731a618aa8e95cf465/words` để lấy được các key sau đó ghi vào 1 danh sách. Duyệt từ phần tử đầu đến phần tử cuối của danh sách đó để truyền vào hàm `decrypt(ciphertext, password_hash)` để lấy ra flag.


```python!
from Crypto.Cipher import AES
import hashlib
import random


# /usr/share/dict/words from
# https://gist.githubusercontent.com/wchargin/8927565/raw/d9783627c731268fb2935a731a618aa8e95cf465/words
with open("D://Code/Python/word.txt", encoding='utf-8') as f:
    words = [w.strip() for w in f.readlines()]

ciphertext = 'c92b7734070205bdf6c0087a751466ec13ae15e6f1bcdd3f3a535ec0f4bbae66'

def decrypt(ciphertext, password_hash):
    ciphertext = bytes.fromhex(ciphertext)
    key = password_hash

    cipher = AES.new(key, AES.MODE_ECB)
    try:
        decrypted = cipher.decrypt(ciphertext)
    except ValueError as e:
        return {"error": str(e)}

    return decrypted

for i in words:
    KEY = hashlib.md5(i.encode()).digest()
    flag = decrypt(ciphertext, KEY)
    if b'crypto' in flag:
        print(flag)
        break
```

`flag: crypto{k3y5__r__n07__p455w0rdz?}`

## 10. ECB CBC WTF
```python!
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}
```
- Ta thấy đoạn `ciphertext = iv.hex() + encrypted.hex()` nên từ đó ta có thể lấy được giá trị của iv và encrypted. Vì iv có độ dài là 16 bytes nên ta chia các đoạn của encrypted thành các đoạn có độ dài 16 bytes dể lấy được các đoạn ciphertext. 
- Do ECB và CBC đều dùng chung 1 key nên ta có thể cho các đoạn ciphertext lên hàm `DECRYPT(CIPHERTEXT)` để thu được các đoạn plaintext.

![900px-CBC_decryption.svg](https://hackmd.io/_uploads/B1MyBXLc6.png)

- Ghép các đoạn plaintext ta sẽ thu được flag
```python!
from pwn import xor
from Crypto.Util.number import long_to_bytes

ciphertext = '4a01d628528c1c4d5d84dd70b549b0abc72b9272504b22be8a3e0c0233ef7bd01984dcbf2690642e8fc83b23dcbf3f85'
print(len(ciphertext))
print(bytes.fromhex(ciphertext))
iv_hex = ciphertext[:32]
for i in range(1, (len(ciphertext) - 32)//32 + 1):
    print(ciphertext[32*i: 32*i + 32])


ptECB1 = '2973af5826e3677e3ee68245c02adb9e' # Cho cipherstate[32:64] đi qua hàm DECRYPT(CIPHERTEXT) trên web 
ptECB2 = '981fe442612f7d8fbd612d2312ce5aad' # Cho cipherstate[64:96] đi qua hàm DECRYPT(CIPHERTEXT) trên web 
plaintext1 = xor(bytes.fromhex(ptECB1), bytes.fromhex(iv_hex))
plaintext2 = xor(bytes.fromhex(ciphertext[32:64]), bytes.fromhex(ptECB2))
print(plaintext1 + plaintext2)
```
`flag: crypto{3cb_5uck5_4v01d_17_!!!!!}`

## ECB Oracle
```python!
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 rằng `padded = pad(plaintext + FLAG.encode(), 16)` và flag thì có dạng: `crypto{` nên ta có thể truyền vào 1 chuỗi plaintext và nó sẽ có dạng là: `plaintext + crypto{ + i`.
- Ta sử dụng bruteforce để tìm lần lượt từng `i` cho tới khi đến được dấu `}`.

```python!
import requests
import json
import string

alphabet = string.ascii_letters + string.digits + "!{_}?"
def encrypt(plaintext):
    URL = 'https://aes.cryptohack.org/ecb_oracle/encrypt/'+plaintext
    r = requests.get(URL)
    json_ans = json.loads(r.text)
    ct = json_ans['ciphertext']
    return ct

flag = 'crypto{'
while True:
    pt_sent = '1' * (31-len(flag))
    response = encrypt(pt_sent.encode().hex())
    for j in alphabet:
        plaintext = pt_sent + flag + j
        response1 = encrypt(plaintext.encode().hex())
        if response1[32:64] == response[32:64]:
            flag = flag + j
            break
    if flag[-1] == '}':
        break


print(flag)
```
`flag: crypto{p3n6u1n5_h473_3cb}`

## Flipping Cookie
```python!
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}
```

- Để có thể lấy được flag thì cookie và iv mà ta gửi lên được giải mã phải có `b'admin=True'`.
- Ta thấy rằng `ciphertext = iv.hex() + encrypted.hex()` từ đây ta có thể lấy được chuỗi `iv` và `encrypted`. Mà chuỗi `encrypted` là chuỗi có dạng `b'admin=False'` xor với `iv`.
- Trong CBC ta có:
    - plaintext = ciphertext^iv
    - ciphertext = plaintext^iv
- Như vậy để plaintext có dạng `b'admin=True'` thì ta có: `b'admin=True' = ciphertext^iv'`   . Từ đây ta có thể suy ra:
    - `iv' = 'b'admin=True'^ciphertext`
    - Mà `ciphertext = b'admin=False'^iv`
    => `iv' = 'b'admin=True'^b'admin=False'^iv`
- Vậy nếu ta gửi lên `check_admin(cookie, iv)` với `cookie = encrypted` và `iv = iv'`.Thì khi giải mã ta sẽ được chuỗi: `b'admin=True' ^ b'admin=False' ^ iv ^ b'admin=False' ^ iv` = `b'admin=True'`

```python!
import requests
import json
from pwn import xor


URL_GETCOOKIE = 'https://aes.cryptohack.org/flipping_cookie/get_cookie/'

def get_check_admin(cookie, iv):
    URL_CHECKAD = 'https://aes.cryptohack.org/flipping_cookie/check_admin/'+cookie+'/'+iv
    r = requests.get(URL_CHECKAD)
    try:
        flag = (json.loads(r.text))['flag']
    except:
        flag = (json.loads(r.text))['error']
    return flag

def getCookie():
    r = requests.get(URL_GETCOOKIE)
    json_ans = json.loads(r.text)
    return json_ans['cookie']

true = "admin=True;expir".encode()
false = 'admin=False;expi'.encode()
ciphertext = getCookie()
iv_hex = ciphertext[:32]
ct = ciphertext[32:]
iv_hex1 = xor(xor(true, false),bytes.fromhex(iv_hex))
print(get_check_admin(ct, iv_hex1.hex()))
```
`flag: crypto{4u7h3n71c4710n_15_3553n714l}`
## Lazy CBC
```python!
from Crypto.Cipher import AES


KEY = ?
FLAG = ?


@chal.route('/lazy_cbc/encrypt/<plaintext>/')
def encrypt(plaintext):
    plaintext = bytes.fromhex(plaintext)
    if len(plaintext) % 16 != 0:
        return {"error": "Data length must be multiple of 16"}

    cipher = AES.new(KEY, AES.MODE_CBC, KEY)
    encrypted = cipher.encrypt(plaintext)

    return {"ciphertext": encrypted.hex()}


@chal.route('/lazy_cbc/get_flag/<key>/')
def get_flag(key):
    key = bytes.fromhex(key)

    if key == KEY:
        return {"plaintext": FLAG.encode().hex()}
    else:
        return {"error": "invalid key"}


@chal.route('/lazy_cbc/receive/<ciphertext>/')
def receive(ciphertext):
    ciphertext = bytes.fromhex(ciphertext)
    if len(ciphertext) % 16 != 0:
        return {"error": "Data length must be multiple of 16"}

    cipher = AES.new(KEY, AES.MODE_CBC, KEY)
    decrypted = cipher.decrypt(ciphertext)

    try:
        decrypted.decode() # ensure plaintext is valid ascii
    except UnicodeDecodeError:
        return {"error": "Invalid plaintext: " + decrypted.hex()}

    return {"success": "Your message has been received"}
```
- Ta hãy để ý dòng `cipher = AES.new(KEY, AES.MODE_CBC, KEY)` ta thấy rằng đối số `KEY` thứ 2 chính là việc sử dụng `KEY = iv`.
- Mặt khác:
    - `P0 = iv ^ D[C0]`
    - `P1 = C0 ^ D[C1]`
    - Từ đây nếu như các `C0 = 0` và `D[C0] = D[C1]` thì ta có thể lấy được `iv`. Từ đó ta có thể lấy được `KEY`

```python!
import requests
import json
from pwn import xor

def encrypt(plaintext):
    URL = 'https://aes.cryptohack.org/lazy_cbc/encrypt/'+plaintext
    r = requests.get(URL)
    json_ans = json.loads(r.text)
    ct = json_ans['ciphertext']
    return ct

def receive(ciphertext):
    URL = 'https://aes.cryptohack.org/lazy_cbc/receive/'+ciphertext
    r = requests.get(URL)
    try:
        json_ans = (json.loads(r.text))
    except:
        json_ans = (json.loads(r.text))["error"]
    return json_ans

def get_flag(flag):
    URL = 'http://aes.cryptohack.org/lazy_cbc/get_flag/'+flag
    r = requests.get(URL)
    json_ans = json.loads(r.text)
    return json_ans['plaintext']

ciphertext = b"\0" * 32
decrypted = receive(ciphertext.hex())['error'][-64:]

c0 = decrypted[:32] 
c1 = decrypted[32:]
xr = xor(bytes.fromhex(c0), bytes.fromhex(c1)).hex()
print(bytes.fromhex(get_flag(xr))) 
```

`flag: crypto{50m3_p30pl3_d0n7_7h1nk_IV_15_1mp0r74n7_?}`
## Triple DES
```python!
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())
```
Với bài này ta chỉ cần sử dụng các khóa yếu của DES truyền vào hàm `ENCRYPT_FLAG(KEY)` để lấy được kết quả:
![Screenshot 2024-01-31 234956](https://hackmd.io/_uploads/Hydrcldca.png)
![Screenshot 2024-01-31 235052](https://hackmd.io/_uploads/SyIY9gu9T.png)

- Sau đó ta lấy ciphertext và key vừa truyền vào để truyền vào hàm `ENCRYPT(KEY,PLAINTEXT)` sẽ thu được 1 chuỗi hex.
![Screenshot 2024-01-31 235247](https://hackmd.io/_uploads/S1vMig_q6.png)
![Screenshot 2024-01-31 235305](https://hackmd.io/_uploads/HkwMsgd9T.png)

- Giải mã chuỗi hex ta sẽ được flag.
![Screenshot 2024-01-31 235320](https://hackmd.io/_uploads/SJ4Bsluq6.png)

`flag: crypto{n0t_4ll_k3ys_4r3_g00d_k3ys}`


