この大会は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画像にフラグが書かれていた。
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}