---
# System prepended metadata

title: HW 0x09 Write Up
tags: [Course, CS, NCTU, SecureProgramming]

---

###### tags: `NCTU` `CS` `Course` `SecureProgramming`

HW 0x09 Write Up
==
yysung

## [Lab 9-1] Crypto Lab 1

題目有兩個 command

register 可以輸入 Username 並用 urllib encode `{'usr': Username 'admin': 'N'}`，再用 AES-ECB 加密後作為 token 回傳

login 可以輸入 token 解密後檢查 admin 欄位是否為 'Y'來回傳 flag

可以用程式建構兩個 token
```
usr=aaaaa&admin=|N...............|................|
usr=aaaaaaaaaaaa|YOOOOOOOOOOOOOOO|&admin=N........|
```
因為是用 ECB 做區塊加密，所以直接替換加密過的區塊也可以成功被解密，所以可以用以上資訊再自行建構 token
```
A = token1[0]
B = token2[1]
token = A + B * 5
```

因爲 unpad 的行為是看最後一個 bytes 的值往前刪去值，
所以放入 O 就可以剛好往前刪去 79 個 bytes，所以建構 5 個區塊，就可以刪到只剩下 Y 了

回傳 token 後，就可以拿到 flag 了

## [Lab 9-2] Crypto Lab 2

題目會一直讀入兩個 plaintext 並以相同的 key 和 iv 來以 AES-CBC 加密 plaintext

再用 AEC-ECB 來對最後一個 ciphertext 的 block 加密當作 MAC

輸入的 plaintext 必須不同，如果 MAC 不同就回傳兩者的 MAC，如果 MAC 相同就回傳 flag

```
enc 為 AES-CBC-MAC
E 為 AES encrypt
A = 'a' * 30
B  = 'b' * 30
X = enc(A)
Y = enc(B)
A' = A with padding
B' = B with padding
O = '0' * 32

證明 E(A'OX) == E(B'OY)
enc(A') = E(E(A' ^ IV)) = X
E(A'OX) = E(E(A'O) ^ X) = E(E(E(A' ^ IV) ^ O) ^ X) = E(E(E(A' ^ IV)) ^ X) = E(X ^ X) = E(O)
E(B'OY) 如上，最後也會等於 E(O)
```

以上面方法建構 plaintext 後就可以拿到 plaintext 不同但 MAC 相同的兩個 plaintext，回傳後就可以拿到 flag 了

## [Lab 9-3] Crypto Lab 3

題目有兩個 command

register 可以輸入 Username，並用 md5('Crypto is fun' + usr + key) 算出 MAC，接下來用 AEC-CTR 對 usr + mac 作加密並和 iv 一起回傳，在程式裡註冊 Username

login 會將 usr 和 mac 解密出來，並用解密的 usr 再用相同的做法算出 mac2，檢查 mac != mac2 和 usr 是否之前沒有在程式註冊過，都符合就可以拿到 flag

首先先找出前綴為 'Crypto is fun' 並且 md5 相同的兩組
值

可以使用 fastcoll 在兩秒左右的時間找到

因爲前面區塊的 md5 已經相同，所以在後面放入相同的字串 md5 也會相同。可以在後面放 0 做 padding 到 16 bytes 的倍數

將以上的字串作為 Username 傳給程式得到 iv 和加密過的 usr + mac

因為 AES-CTR 的加密方法是
```
aes(iv + counter) ^ plaintext = ciphertext
```
所以在有 plaintext 的情況下可以生成任意 plaintext 的 ciphertext
```
ciphertext ^ plaintext ^ new_plaintext
= aes_encrypt(iv + counter) ^ plaintext ^ plaintext ^ new_plaintext =
= aes_encrypt(iv + counter) ^ new_plaintext
= new_ciphertext
```

就可以把 usr 換成另一組 md5 相同的字串，使用這個 usr 算出的 mac2 會和 mac 相同，並且 usr 沒有在程式裡註冊過，符合條件，就可以拿到 flag 了

## [HW 0x09] Oracle's Revenge

題目有兩個 command

login 是讀入 usr 和 pwd，隨機生成 vc，組成 `{'usr': usr, 'pwd': pwd, 'vc': vc}` 用 msgpack 編碼，再用 AES-CBC 加密作為 token 回傳，然而 vc 卻沒有回傳

verify 是讀入 token 和 vc，將 token 解密後，檢查 token 裡帶的 vc 和輸入的 vc 是否相同，不過程式會在 vc 相同時將 usr 的值以 ascii 印出，所以不太能任意修改

因為 AES-CBC 的解密方法是

```
aes-cbc-dec(ciphertext[i])
= aes_decrypt(ciphertext[i]) ^ ciphertext[i - 1]
= plaintext[i]
```

所以可以用以下的方法建構任意 new_plaintext

```
fake_cihpertext = ciphertext[i - 1] ^ plaintext[i] ^ new_plaintext
aes-cbc-dec(ciphertext[i])
= aes_decrypt(ciphertext[i]) ^ fake_ciphertext
= aes_decrypt(ciphertext[i]) ^ ciphertext[i - 1] ^ plaintext[i] ^ new_plaintext
= plaintext[i] ^ plaintext[i] ^ new_plaintext
= new_plaintext
```

所以就可以將 ciphertext 換掉，讓 vc 可控

```
Username: a * 5
Password: a * 27
plaintext: \x83\xa3usr\xa5aaaaa\xa3pwd\xbb|aaaaaaaaaaaaaaaa|aaaaaaaaaaa\xa2vc\xda\x00|...
new_plaintext: \x83\xa3usr\xa5aaaaa\xa3pwd\xbb|decrypt(fake ciphertext)|aaaaaaaaaaa\xa2vc\xa10
aes_decrypt(token[64:96]) ^ (fake ciphertext) == aaaaaaaaaaa\xa2vc\xa10
aes_decrypt(token[64:96]) = plaintext[64:96] ^ token[32:64] = aaaaaaaaaaa\xa2vc\xda\x00 ^ token[32:64]
fake ciphertext = aaaaaaaaaaa\xa2vc\xa10 ^ aaaaaaaaaaa\xa2vc\xda\x00 ^ token[32:64]
{usr: aaaaa, pwd: (courrpted data)aaaaaaaaaaa, vc: 0}
```

將 vc 換成 0 後輸入 0 就可以拿到 flag 了