# Chatroom ###### tags: `solved` ```python md5(flag).hexdigest() = 'c0bdcfc620a83e9063a0da44917fdeca' ``` <!-- 我在嘗試爆搜最後一個cypher看能不能用出'男' ``` 系統訊息: b"'utf-8' codec can't decode byte 0xed in position 1: invalid continuation byte\x00\x00\x00" 系統訊息: b"'utf-8' codec can't decode byte 0xf8 in position 2: invalid start byte\x00\x00" ``` 有時候會pad 2個 有時候會pad 3個 這樣的error code大概是9到10個block 應該可以從block個數 推是哪一種error 把md5的那串hash丟進去 通常是解不出utf-8可以看懂的東西 我覺得是要從error code你知道plaintext和ciphertext去回推什麼key之類的 我覺得輸出男不是重點 只知道有或沒有 而且就會break掉 不能繼續用了 我自己跑然後print他encrypt之前的東西 感覺每次encrypt都會生成他的iv "但你丟cipher進去會哈哈哈" 因為解出來是字串 都屬於utf-8? key 有2^64吧 8 byte random 我找網路上md5 decrypt的 都沒有 **cbc mode好像只有padding oracle attack?** ``` except UnicodeDecodeError as e: print(f'系統訊息: {encrypt(str(e).encode()).hex()}') ``` 你做什麼會跑出這個error 要丟cypher才可以吧 他會decrypt 然後check decrypt出來的值有沒有'男' 你從哪裡得到這個errorcode的 我發現同樣的input error code不一樣 每次都不一樣 但你丟cipher進去會哈哈哈 但他iv其實會付給你 所以用你已經找到的兩個可能性 我感覺md5是不是有弱點 男 = e794b7 ```python= # -*- coding: utf-8 -*- # -*- coding: utf-8 -*- from pwn import * from hashlib import md5 from Crypto.Cipher import Blowfish key = 0 def pad(m): padlen = 8 - len(m) % 8 return m + bytes([0] * padlen) def decrypt(c): iv, c = c[:8], c[8:] fish = Blowfish.new(key, Blowfish.MODE_CBC, iv=iv) return fish.decrypt(c) def encrypt(m): fish = Blowfish.new(key, Blowfish.MODE_CBC) return fish.iv + fish.encrypt(pad(m)) r = remote('eofqual.zoolab.org',10110) print(r.recvline().decode()) get = r.recvline().decode() print(get) get = get[9:-1] c_hex = get[:-32] md5 = get[-32:] print(c_hex) print(md5) boy_hex = '男'.encode('utf-8').hex() print('男 = '+ boy_hex) base = 16 target = 10 for value in range(1,16777216): val_hex = ('00000' + hex(value)[2:])[-6:] send_text = c_hex[base:base+target]+val_hex+c_hex[base+target+6:] print(send_text) r.sendlineafter('輸入訊息: ', send_text) print(r.recvline().decode()) ``` ```python= from pwn import * #p = process('./server.py') p = remote('eofqual.zoolab.org',10110) p.recvuntil('間號碼: ') res = p.recvuntil('系統訊息:',drop=True)[:-1] res = str(res)[2:-1] print(res) res = bytes.fromhex(res) print(res) iv = res[:8] c = res[8:-16] b0 = res[8:16] b1 = res[16:24] b2 = res[24:32] md5 = res[-16:] print(iv.hex(),b0.hex(),b1.hex(),b2.hex(),md5.hex()) boy_hex = '男'.encode('utf-8').hex() print('男 = '+ boy_hex) #男 = e794b7 kone = ['8','9','a','b','c','d','e','f'] k = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'] flag = "" prev = bytes([0] * 2) found = 0 print(prev) for pos in range(6): for a in kone: for q in k: xor = (a+q+"94b7") tmp = int.from_bytes(b1, 'big') ^ (int(xor,16)<< (8*pos)) ^ (int.from_bytes(prev,'big')<< (8*pos)) tmp = tmp.to_bytes(8,'big') p.sendlineafter('輸入訊息: ',tmp.hex()+b2.hex()) get = p.recvline().decode() #print(get) if '離' in get: print(b1.hex(), xor) tmp = int(boy_hex,16) ^ int(xor,16) tmp = tmp.to_bytes(8,'big') print(tmp) flag = tmp.decode()[-3] + flag print(flag) prev = tmp.decode()[-3].encode() + prev.decode()[0].encode() print(prev) found = 1 break if found == 1: break found = 0 for a in kone: for q in k: xor = (a+q+"94b7") x = int(xor,16) ^ int.from_bytes(prev,'big') tmp1 = (int.from_bytes(b1, 'big')) ^ ((x & 0x00ffff)<<48) tmp0 = (int.from_bytes(b0, 'big')) ^ ((x & 0xff0000)>>16) ^ ((x & 0x00ffff)<<48) tmp1 = tmp1.to_bytes(8,'big') tmp0 = tmp0.to_bytes(8,'big') p.sendlineafter('輸入訊息: ',tmp0.hex()+tmp1.hex()+b2.hex()) get = p.recvline().decode() #print(get) if '離' in get: print(b1.hex(), xor) tmp = int(boy_hex,16) ^ int(xor,16) tmp = tmp.to_bytes(8,'big') print(tmp) flag = tmp.decode()[-3] + flag print(flag) prev = tmp.decode()[-3].encode() + prev.decode()[0].encode() print(prev) found = 1 break if found == 1: break p.interactive() ``` --> 1. In the oracle, we can query if the output contains `男`. Since CBC block cipher XORs the IV with the decrypted string, try searching through the IVs and XOR back the modified bits with `男` and the plain text can be recovered. 2. UTF-8 encodes Chinese characters into 3 bytes, following the rules below. For a single byte ASCII character, the leading bit is always `0`, while 3-bytes Chinese characters, the first byte starts with `1110` and the following 2 bytes starts with `10`. ``` 0x00000000 - 0x0000007F: 0xxxxxxx 0x00000080 - 0x000007FF: 110xxxxx 10xxxxxx 0x00000800 - 0x0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx ``` ref: https://man7.org/linux/man-pages/man7/UTF-8.7.html 3. Assume the flag contains only ASCII characters and the padding is at least two bytes. Then for the last block, we only need to search through `(hex with leading 1)*(all possible hex)`. The complexity is `O(2^(3+4))` for each byte. 4. For the blocks without padding, we have to search through the bytes that matches the utf-8 encoded convention (printing 哈), and then try if '男' appears. Practice this part by trial and error. 5. Repeat step 4 until all blocks are decrypted, and the flag is found. ```python from pwn import * #p = process('./server.py') p = remote('eofqual.zoolab.org',10110) p.recvuntil('間號碼: ') res = p.recvuntil('系統訊息:',drop=True)[:-1] res = str(res)[2:-1] print(res) res = bytes.fromhex(res) print(res) iv = res[:8] c = res[8:-16] b0 = res[8:16] b1 = res[16:24] b2 = res[24:32] md5 = res[-16:] print(iv.hex(),b0.hex(),b1.hex(),b2.hex(),md5.hex()) boy_hex = '男'.encode('utf-8').hex() print('男 = '+ boy_hex) #男 = e794b7 kone = ['8','9','a','b','c','d','e','f'] k = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'] flag = "" prev = bytes([0] * 2) found = 0 print(prev) for pos in range(6): for a in kone: for q in k: xor = (a+q+"94b7") tmp = int.from_bytes(b1, 'big') tmp = tmp ^ (int(xor,16)<< (8*pos)) ^ (int.from_bytes(prev,'big')<< (8*pos)) tmp = tmp.to_bytes(8,'big') p.sendlineafter('輸入訊息: ',tmp.hex()+b2.hex()) get = p.recvline().decode() if '離' in get: print(b1.hex(), xor) tmp = int(boy_hex,16) ^ int(xor,16) tmp = tmp.to_bytes(8,'big') print(tmp) flag = tmp.decode()[-3] + flag print(flag) prev = tmp.decode()[-3].encode() + prev.decode()[0].encode() print(prev) found = 1 break if found == 1: break found = 0 def isEnglish(s): try: s.encode('ascii') except UnicodeEncodeError: return False else: return True for a in ['a']: #kone for b in ['c']: #kone for c in ['d']: #kone xor = (a+'0'+b+'0'+c+'0') tmp = int.from_bytes(b0, 'big') ^ int(xor,16) tmp = tmp.to_bytes(8,'big') p.sendlineafter('輸入訊息: ',tmp.hex()+b1.hex()) get = p.recvline().decode() if '離' in get: found = 1 elif ('哈' in get and isEnglish((int(boy_hex,16) ^ int(xor,16)).to_bytes(8,'big').decode()[-3:])): print(xor) for q in ['2']: #k for r in ['2']: #k for s in ['2']: #k xor = (a+q+b+r+c+s) tmp = int.from_bytes(b0, 'big') ^ int(xor,16) tmp = tmp.to_bytes(8,'big') p.sendlineafter('輸入訊息: ',tmp.hex()+b1.hex()) get = p.recvline().decode() if '離' in get: print(b1.hex(), xor) tmp = int(boy_hex,16) ^ int(xor,16) tmp = tmp.to_bytes(8,'big') print(tmp) flag = tmp.decode()[-3:] + flag print(flag) prev = tmp.decode()[-3:-1].encode() print(prev) found = 1 break if found == 1: break if found == 1: break if found == 1: break if found == 1: break if found == 1: break found = 0 for pos in range(6): for a in kone: for q in k: xor = (a+q+"94b7") tmp = int.from_bytes(b0, 'big') tmp = tmp ^ (int(xor,16)<< (8*pos)) ^ (int.from_bytes(prev,'big')<< (8*pos)) tmp = tmp.to_bytes(8,'big') p.sendlineafter('輸入訊息: ',tmp.hex()+b1.hex()) get = p.recvline().decode() if '離' in get: print(b1.hex(), xor) tmp = int(boy_hex,16) ^ int(xor,16) tmp = tmp.to_bytes(8,'big') print(tmp) flag = tmp.decode()[-3] + flag print(flag) prev = tmp.decode()[-3].encode() + prev.decode()[0].encode() print(prev) found = 1 break if found == 1: break found = 0 for a in ['d']: #kone for b in ['e']: #kone for c in ['8']: #kone xor = (a+'0'+b+'0'+c+'0') tmp = int.from_bytes(iv, 'big') ^ int(xor,16) tmp = tmp.to_bytes(8,'big') p.sendlineafter('輸入訊息: ',tmp.hex()+b0.hex()) get = p.recvline().decode() for q in ['7']: #k for r in ['6']: #k for s in ['3']: #k xor = (a+q+b+r+c+s) tmp = int.from_bytes(iv, 'big') ^ int(xor,16) tmp = tmp.to_bytes(8,'big') p.sendlineafter('輸入訊息: ',tmp.hex()+b0.hex()) get = p.recvline().decode() if '離' in get: print(b1.hex(), xor) tmp = int(boy_hex,16) ^ int(xor,16) tmp = tmp.to_bytes(8,'big') print(tmp) flag = tmp.decode()[-3:] + flag print(flag) prev = tmp.decode()[-3:-1].encode() print(prev) found = 1 break if found == 1: break if found == 1: break if found == 1: break if found == 1: break if found == 1: break found = 0 for pos in range(6): for a in kone: for q in k: xor = (a+q+"94b7") tmp = int.from_bytes(iv, 'big') tmp = tmp ^ (int(xor,16)<< (8*pos)) ^ (int.from_bytes(prev,'big')<< (8*pos)) tmp = tmp.to_bytes(8,'big') p.sendlineafter('輸入訊息: ',tmp.hex()+b0.hex()) get = p.recvline().decode() if '離' in get: print(b1.hex(), xor) tmp = int(boy_hex,16) ^ int(xor,16) tmp = tmp.to_bytes(8,'big') print(tmp) flag = tmp.decode()[-3] + flag print(flag) prev = tmp.decode()[-3].encode() + prev.decode()[0].encode() print(prev) found = 1 break if found == 1: break found = 0 p.interactive() ``` ``` FLAG{0r4cL3_nEVeR_D1e} ```