Maojui

AIS3 2020 EOF - train (Crypto, 334)

2020-01-18

Challenge

題目:train.py

Solution

這題連線後有兩個選項可用:

  1. your ticket
  2. use ticket

選擇 Your ticket 會把該次連線所用的公鑰給你 (n,e) 和一個 ticket 。

ticket 是把 date:2019/1/11|session:{session}|secret:{flag}'.encode() 每 16 bytes 作 RSA 加密,再把密文串起來回傳給使用者,且共有 5 段 : iv + cipher*4

session 為 os.urandom(5).hex() 共 10 bytes,flag 的格式固定為 FLAG{ ... }

如此一來我們至少可以知道,被切割後會長下面這樣:

  1. date:2019/1/11|s
  2. ession:XXXXXXXXX
  3. X|secret:FLAG{??
  4. ????????????????

而選擇 use ticket 需要傳一個 ticket 給他,他會幫你解密,並做一些檢查

try:
    plain = rsa.decrypt(cipher)
    date, session, secret = plain.split(b'|')
    if date.partition(b':')[2] == b'2019/1/11':
        print('Pass')
    else:
        print('Wrong ticket')
except:
    print('Oops, our train has some technical issue')

關於第三段,可以直接爆搜,反正只有 16 * 100 * 100 種組合 ( hex * printable string ** 2)

那麼第四段,我們要先知道他的長度,剛好他的檢查有一行可以用

try:
    ...
    date, session, secret = plain.split(b'|')
    ...
        print('Wrong ticket')
except :
    print('Oops, our train has some technical issue')

如果他在明文中找到兩個 | 那他會回傳 Wrong ticket,反之會回傳 Oops, …

不過為了避免前 1 block 解密出現 | 所以我們把第三段當作 iv 讓 server 只解密處理第四段密文

先爆搜前 2 bytes,什麼時候會 Wrong ticket → || 代表他的長度不到 16 bytes

再試試 |x|, |xx|, |xxx|, … , 都沒用,所以知道他的長度是 14 bytes

再來就是很像 Padding Oracle 的處理方法了,先固定前 2 bytes 為 |x

接著報搜最後一個 bytes ,如果出現 Wrong ticket,可以知道

當時猜的那個 i 讓他解密成, |x?????????????|,那就可以返推出他的明文

|x?????????????} 然後重複這個動作,就能順利把 FLAG 炸出來了

FLAG{cBCNEVERGetSoLD}

Script : solve.py