CyBRICS CTF Quals 2019 Writeup

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

Mic Check (Cyber, Baby)

ruleのページにフラグが書いてある。

cybrics{W3lc0M3_t0_t3h_G4M#}

Warmup (Web, Baby)

http://45.32.148.106/にアクセスすると、http://45.32.148.106/final.htmlにリダイレクトされる。curlで直接のレスポンスを見てみる。

$ curl http://45.32.148.106/
    :
She gave me with their long manes, thick legs, and slow even pace, drawing along a perfect right to kill him as strange and shocking sight.<br/>
Here is your base64-encoded flag: Y3licmljc3s0YjY0NmM3OTg1ZmVjNjE4OWRhZGY4ODIyOTU1YjAzNH0=
</p></body></html>
$ echo Y3licmljc3s0YjY0NmM3OTg1ZmVjNjE4OWRhZGY4ODIyOTU1YjAzNH0= | base64 -d
cybrics{4b646c7985fec6189dadf8822955b034}
cybrics{4b646c7985fec6189dadf8822955b034}

Zakukozh (Cyber, Baby)

Affine暗号のようなので、バイナリデータが1バイトごとに以下のような形式で暗号化されている。

(a * x + b) mod 256

復号するときも同様で、PNGから試したところ、あるa, bのときにヘッダがPNGになることがわかり、そのまま復号してみる。

with open('zakukozh.bin', 'rb') as f:
    enc = f.read()

PNG_HEAD = '\x89PNG'

found = False
for a in range(256):
    for b in range(256):
        d0 = (ord(enc[0]) * a + b) % 256
        d1 = (ord(enc[1]) * a + b) % 256
        d2 = (ord(enc[2]) * a + b) % 256
        d3 = (ord(enc[3]) * a + b) % 256
        if d0 == ord(PNG_HEAD[0]) and d1 == ord(PNG_HEAD[1]) \
             and d2 == ord(PNG_HEAD[2]) and d3 == ord(PNG_HEAD[3]):
             found = True
             break
    if found:
        break

flag = ''
for e in enc:
    code = (ord(e) * a + b) % 256
    flag += chr(code)

with open('flag.png', 'wb') as f:
    f.write(flag)

復号したPNG画像にフラグが書かれていた。
f:id:satou-y:20190726185353p:plain

cybrics{W311_C0M3_2_CY13R1C5}

Big RAM (rebyC, Easy)

換字式暗号だが、原則2文字ずつセットで置換している。奇数長の場合の最後の文字の置換については、1文字の置換表を別に持つ。
https://downhousesoftware.wordpress.com/2014/09/08/cryptography/を参考に、頻度分析をしながら、英語の文章になるよう置換していく。

ybn -> the
adybowz -> nothing
btktybowz -> something
btktsnbyn -> somewhere

さらに2066行目にフラグの形式がある。

xanjtou -> cybrics

置換して復号して確認し、調整することを繰り返す。最終的にはフラグの部分だけでも復号できればよい。フラグが判明した時点での復号コードは以下の通り。

import re

map1 = {'n': 'e', 'z': 'g', 'p': 'i', 'v': 'k', 'u': 's', 's': 't', 'h': 'h',
    'y': 'o', 'a': 'r', 'l': 'n', 'q': 'u', 'f': 'y', 'd': 'a', 'x': 'd',
    'g': 'l', 'b': 'p', 'i': 'f', 'e': 'm', 't': 'c', 'w': 'w'
}
map2 = {'yb': 'th', 'ow': 'in', 'ad': 'no', 'bt': 'so', 'kt': 'me', 'cz': 'on',
    'xa': 'cy', 'nj': 'br', 'to': 'ic', 'od': 'is', 'sn': 'wh', 'by': 'er',
    'xq': 'at', 'aq': 'wi', 'pr': 'pe', 'jy': 're', 'cm': 'ss', 'au': 'ag',
    'ac': 'ou', 'dl': 'gh', 'oy': 'ki', 'mw': 'ng', 'hd': 'mb', 'hj': 'ai',
    'ws': 'll', 'zv': 'he', 'uq': 'es', 'cd': 'ed', 'ov': 'ma', 'zs': 'tc',
    'wr': 'an', 'ij': 'yo', 'mu': 'ot', 'ol': 'en', 'em': 'do', 'hn': 'lo',
    'mq': 'ne', 'lv': 'ge', 'oe': 'pr', 'rl': 'si', 'of': 'ar', 'nu': 'as',
    'qy': 'ev', 'md': 'iv', 'vp': 'io', 'qb': 'na', 'pk': 'xt', 'ha': 'mi',
    'hy': 'sw', 'jo': 'to', 'tb': 'ci', 'sb': 'al', 'yh': 'ly', 'sg': 'mo',
    'jd': 'st', 'iq': 'ad', 'wa': 'di', 'dq': 'da', 'hz': 'ak', 'zd': 'yz',
    'qc': 'la', 'zm': 'rl', 'dk': 'ct', 'yx': 'dn', 'sd': 'of', 'tj': 'id',
    'px': 'yt', 'ez': 'hi', 'sq': 'ti', 'sw': 'ok', 'lo': 'rs', 'dg': 'im',
    'th': 'el', 'ex': 've', 'qn': 'li', 'ew': 'ra', 'fb': 'gl', 'ke': 'us',
    'af': 'uk', 'ao': 'nd', 'nw': 'sa', 'xg': 'op', 'uf': 'ri', 'in': 'ec',
    'bq': 'tw', 'bv': 'or', 'da': 'fi', 'uo': 'le', 'sj': 'hu', 'sv': 'sh',
    'qo': 'tt', 'qs': 'un', 'hc': 'ce', 'sr': 'ni', 'qt': 'ze', 'qj': 'sn',
    'fz': 'bu', 'bf': 'pa', 'ef': 'rt', 'xp': 'ke', 'zj': 'tr', 'gm': 'sl',
    'ms': 'nt', 'zo': 'te', 'un': 'de', 'ld': 'ta', 'bh': 'af', 'kc': 'og',
    'te': 'se', 'zc': 'wn', 'zk': 'ea', 'tk': 'vo', 'ru': 'ur', 'bj': 'ca',
    'gu': 'go', 'xh': 'co', 'kg': 'ha', 'aj': 'ex', 'yt': 'nc', 'ea': 'ns',
    'kx': 'ue', 'eh': 'ab', 'xw': 'ua', 'ba': 'tl', 'bx': 'il', 'fi': 'ba',
    'hs': 'ro', 'fn': 'ob', 'nl': 'ul', 'xu': 'ld', 're': 'vi', 'ej': 'be',
    'rq': 'su', 'uc': 'ch', 'sp': 'wo', 'cp': 'rd', 'ji': 'mm', 'ie': 'gs',
    'mr': 'ol', 'ro': 'nu', 'nc': 'fo', 'fd': 'od', 'rh': 'mp', 'bb': 'lf',
    'ry': 'sy', 'kj': 'em', 'ya': 'ho', 'lq': 'ff', 'ki': 'wa', 'rc': 'uc',
    'om': 'we', 'kh': 'nv', 'fk': 'ie', 'gi': 'ac', 'wk': 'cu', 'ty': 'sf',
    'lk': 'ms', 'xc': 'ty', 'mg': 'ee', 'qg': 'ys', 'gn': 'am', 'ev': 'pl',
    'dp': 'eq', 'yo': 'rr', 'di': 'ry', 'ot': 'bl', 'tw': 'gu', 'wn': 'ir',
    'pz': 'ye', 'dw': 'ts', 'if': 'rv', 'ls': 'po', 'xx': 'om', 've': 'ut',
    'nd': 'pt', 'hv': 'oc', 'si': 'dd', 'wd': 'nf', 'tc': 'sc', 'ft': 'qu',
    'gz': 'ks', 'ka': 'yb', 'zy': 'oa', 'ln': 'pi', 'ho': 'ck', 'mz': 'gr',
    'wf': 'kl', 'ay': 'rm', 'ny': 'fe', 'pv': 'ib', 'ip': 'ds', 'fg': 'ef',
    'zw': 'it', 'gc': 'lu'
}

def decrypt(s):
    s = s.group(0)
    res = ''
    for i in range(0, len(s), 2):
        chunk = s[i:i+2]
        if len(chunk) == 2:
            if chunk.lower() in map2:
                plain = map2[chunk.lower()]
                if chunk[0] == chunk[0].upper():
                    plain = plain[0].upper() + plain[1]
                if chunk[1] == chunk[1].upper():
                    plain = plain[0] + plain[1].upper()
            else:
                plain = '**'
        elif len(chunk) == 1:
            if chunk.lower() in map1:
                plain = map1[chunk.lower()]
                if chunk == chunk.upper():
                    plain = plain.upper()
            else:
                plain = '*'
        res += plain
    return res

with open('book.enc', 'rb') as f:
    enc = f.read()

text = re.sub(r'[a-z]+', decrypt, enc, 0, re.I)
print text

このコードを実行した復号結果は以下の通り。

         :
There was a trill of a mo**le **one. Rinat replied:

- Yes. Good. I understood. How **ch time is there? Good. Got it.

He **t the **one on the table and said quietly:

- Worm called. Th** already **** that we wo**. Not yet traced the si**al, but looking.

“Let them search,” grunted the old man. - A **ag in th**r hands (this one: cybrics{ok_i_will_probably_read_it_later_thanksalot}) and a locomotive me**. Look here.

Rinat bent down.

- Same…

- Yes! - the old man **iled, and **shed the keyboard to Rinat. - All kird** server. **t the sn**fer!
         :
cybrics{ok_i_will_probably_read_it_later_thanksalot}