Harekaze CTF 2019 Writeup

この大会は2019/5/18 15:00(JST)~2019/5/19 15:00(JST)に開催されました。
今回もチームで参戦。結果は1960点で523チーム中5位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome (Misc 10)

問題にフラグが書いてあった。

HarekazeCTF{Thank_you_for_participating_in_Harekaze_CTF_2019}

ONCE UPON A TIME (Crypto 100)

処理概要は以下の通り。

・flagの長さが25の倍数になるよう%でパディング
・25バイトごとに5行5列の配列を配列にする。
・5行5列の配列ごとに以下のどちらかの処理
 ・takenoko(m2, mat):m2*mat
 ・takenoko(mat, m2):mat*m2

暗号データを25バイトずつに区切り、5×5の行列にしたものをCとする。

M1 * M2 = C
または
M2 * M1 = C

つまり元のデータM1は以下のどちらかで計算できる。

M1 = C * inverse(M2)
または
M1 = inverse(M2) * C

以上のことから逆算して復号する。

# solve.sage
def str2matrix(s):
    mat = []
    for j in range(5):
        row = []
        for i in range(5):
            row.append(ord(s[i+j*5]))
        mat.append(row)

    return mat

def matrix2str(mat):
    s = ''
    for j in range(5):
        for i in range(5):
            code = mat[j][i]
            s += chr(code)
    return s

def is_printable(mat):
    for j in range(5):
        for i in range(5):
            code = mat[j][i]
            if code < 32 or code > 126:
                return False
    return True

enc = 'ea5929e97ef77806bb43ec303f304673de19f7e68eddc347f3373ee4c0b662bc37764f74cbb8bb9219e7b5dbc59ca4a42018'
enc = enc.decode('hex')

M2 = matrix(Zmod(251), [[1,3,2,9,4], [0,2,7,8,4], [3,4,1,9,4], [6,5,3,-1,4], [1,4,5,3,5]])

flag = ''
for i in range(0, len(enc), 25):
    MAT = matrix(Zmod(251), str2matrix(enc[i:i+25]))
    inv_M2 = M2.inverse()
    M1 = MAT * inv_M2
    if is_printable(M1) == False:
        M1 = inv_M2 * MAT
    flag += matrix2str(M1)

flag = flag.rstrip('\x25')
flag = 'HarekazeCTF{%s}' % flag
print flag
HarekazeCTF{Op3n_y0ur_3y3s_1ook_up_t0_th3_ski3s_4nd_s33}

Twenty-five (Crypto 100)

ppencodeをテーマにした換字式暗号の問題。
crypto.txtの文字を換字式暗号としてうまく置換すると、実行結果がフラグになるということのようだ。perl予約語を辞書としてreserved.txtが提供されているので、参考にしながら置換していく。

length
alarm
break
semop
write
until
undef
eval
exec
join
crypt

上記に注目しながら、対応表を作ると、以下のようになる。

abcdefghijklmnopqrstuvwxy
tbwiupohdnvrsyqlkmaxfjcge

"*************************" を "tbwiupohdnvrsyqlkmaxfjcge" にして実行する。

$ perl twenty-five.pl 
HarekazeCTF{en.wikipedia.org/wiki/Frequency_analysis}
HarekazeCTF{en.wikipedia.org/wiki/Frequency_analysis}

Now We Can Play!! (Crypto 200)

Elgamal暗号になっているが、復号に違いがある。

def decrypt(c1, c2, pk, sk):
    p = pk[0]
    m = pow(3, randint(2**16, 2**17), p) * c2 * inverse(pow(c1, sk, p), p) % p
    return m

サーバ処理は上記のようになっているが、本来はこうなる。

m = c2 * inverse(pow(c1, sk, p), p) % p

そこで暗号結果のc1, c2をそのまま渡し、復号する。pow(3, randint(2**16, 2**17), p)の乗算が余計なので、ブルートフォースでinverseを計算し、フラグの形式で復号できるものを探す。

import socket
from Crypto.Util.number import *

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('problem.harekaze.com', 30002))

data = recvuntil(s, '\n').rstrip()
print data
pk = eval(data[17:-1])

data = recvuntil(s, '\n').rstrip()
print data
c1, c2 = eval(data[18:-1])

data = recvuntil(s, 'c1 : ')
print data + str(c1)
s.sendall(str(c1) + '\n')

data = recvuntil(s, 'c2 : ')
print data + str(c2)
s.sendall(str(c2) + '\n')

data = recvuntil(s, '\n').rstrip()
print data
data = recvuntil(s, '\n').rstrip()
print data
m = eval(data[29:-1])

p = pk[0]
for i in range(2**16, 2**17):
    flag = (m * inverse(pow(3, i, p), p)) % p
    flag = long_to_bytes(flag)
    if flag.startswith('HarekazeCTF{'):
        print flag
        break

実行結果は以下の通り。

('Public Key :', (179173323191454988591857397662357046816406002058222699078082170304942344337525772960365297716675606420378518281831622210294670272131150121828106451512482462351605274656707674269628181870458122317495560207260507411107279195253094119477943994404974997604864735179278176112212343855702414394782510683988125734957L, 2, 53000002593913678937986352418727205538196468549418622475874892482076157998246352050156067510851110275400992586567516093249617976637603565776167257902162567157633995357563567618158233405560793326022137088001144379716157892590298561723225360685249181865325251167875039876719827029888693088533710738116269041133L))
('Cipher text :', (13939058318848869969892661769790944708827274443633249255860130775103355992242218114621405714834592066931918925168617885762705383632767654046468600883492671085892643960651879330981339090382368737591290752693040506297361373997196171277757061478625002075739258607973780566868938423203415950325973810026943457883L, 109590941535359400665615600141181086480543183338115818774409180733164575367102887217210860850806360120300452283512297474847898172474905185925208813871580965935842023665869656822510432339398702011062707781447851941826936476730118432113609805691528978006074326578988475630910504994019834070776807197697847292319L))
('------------------------------', '\n')
Input your ciphertext c1 : 13939058318848869969892661769790944708827274443633249255860130775103355992242218114621405714834592066931918925168617885762705383632767654046468600883492671085892643960651879330981339090382368737591290752693040506297361373997196171277757061478625002075739258607973780566868938423203415950325973810026943457883

Input your ciphertext c2 : 109590941535359400665615600141181086480543183338115818774409180733164575367102887217210860850806360120300452283512297474847898172474905185925208813871580965935842023665869656822510432339398702011062707781447851941826936476730118432113609805691528978006074326578988475630910504994019834070776807197697847292319

('Your Decrypted Message :', 39268793095845740212401926030298979200819615217155164098539854509541032469801080785289352965052801469642156674128708697912306078630662670836855612851002988923070651137189060084842307753112310645041440364059957404423987700979196172608023790040770192830357799786699784325482972837364501493640072710378719464216L)
HarekazeCTF{im_caught_in_a_dr3am_and_m7_dr3ams_c0m3_tru3}
HarekazeCTF{im_caught_in_a_dr3am_and_m7_dr3ams_c0m3_tru3}