この大会は2019/4/6 8:00(JST)~2019/4/10 8:00(JST)に開催されました。
今回は久々に個人で参戦。結果は330点で608チーム中98位でした。
解けた問題をWriteupとして書いておきます。
Blurry (Web 5)
HTMLソースにフラグが書いてある。
tjctf{cl0se_1nspecti0n}
Double Duty (Crypto 5)
シーザー暗号。https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。
tjctf{sekret_code}
Touch Base (Crypto 5)
Base64デコードする。
$ echo dGpjdGZ7ajJzdF9zMG0zX2I0c2U2NH0= | base64 -d tjctf{j2st_s0m3_b4se64}
tjctf{j2st_s0m3_b4se64}
Corsair (Forensics 5)
Stegsolveで開き、Blue plane 4を見る。
tjctf{c0l0r_pl4n3s_ar3_c00l}
Python in One Line (Reversing 10)
jとmが同じコードになっているので、調整してデコードする。
table = {'a':'...-', 'b':'--..', 'c':'/', 'd':'-.--', 'e':'.-.', 'f':'...', 'g':'.-..', 'h':'--', 'i':'---', 'j':'-', 'k':'-..-', 'l':'-..', 'm':'..', 'n':'.--', 'o':'-.-.', 'p':'--.-', 'q':'-.-', 'r':'.-', 's':'-...', 't':'..', 'u':'....', 'v':'--.', 'w':'.---', 'y':'..-.', 'x':'..-', 'z':'.--.', '{':'-.', '}':'.'} codes = '.. - / .. ... -. - / -- --- .-. ... / -.-. --- -.. .'.split(' ') flag = '' for code in codes: for k, v in table.items(): if v == code: flag += k break flag = flag.replace('m', 't') print flag
tjctf{jchiefcoil}
Sportsmanship (Cryptography 10)
Playfair暗号。https://www.dcode.fr/playfair-cipherで復号する。
PRACTICALPLAYFAIRX
tjctf{PRACTICALPLAYFAIRX}
Guess My Hashword (Cryptography 10)
条件から総当たりで目的のmd5になるものを探す。
import hashlib import string import itertools target = '31f40dc5308fa2a311d2e2ba8955df6c' found = False for a in string.uppercase: for b in string.lowercase: for c in string.lowercase: for d in string.digits: chars = a + b + c + d + '_' for s in list(itertools.permutations(chars)): word = ''.join(s) flag = 'tjctf{%s}' % word if hashlib.md5(flag).hexdigest() == target: print flag found = True break if found: break if found: break if found: break if found: break
tjctf{w0w_E}
Easy as RSA (Cryptography 20)
nをfactordbで素因数分解する。
379557705825593928168388035830440307401877224401739990998883 = 564819669946735512444543556507 * 671998030559713968361666935769
あとはそのまま復号する。
from Crypto.Util.number import * n = 379557705825593928168388035830440307401877224401739990998883 e = 65537 c = 29031324384546867512310480993891916222287719490566042302485 p = 564819669946735512444543556507 q = 671998030559713968361666935769 phi = (p - 1) * (q - 1) d = inverse(e, phi) m = pow(c, d, n) flag = long_to_bytes(m) print flag
tjctf{RSA_2_3asy}
Galaxy (Forensics 20)
Stegsolveで開き、Data ExtractでRGBのLSBにチェックをつけると、フラグが文字として現れた。
tjctf{last_but_n0t_l3ast}
Mind Blown (Forensics 30)
EXIFを見てみる。
$ exiftool 694003b3deecf2382b3aa510e5f3e5f5153bb9c062e4f20878c0d343bc297767_meme.jpg ExifTool Version Number : 10.10 File Name : 694003b3deecf2382b3aa510e5f3e5f5153bb9c062e4f20878c0d343bc297767_meme.jpg Directory : . File Size : 87 kB File Modification Date/Time : 2019:04:06 18:39:14+09:00 File Access Date/Time : 2019:04:06 18:45:37+09:00 File Inode Change Date/Time : 2019:04:06 18:39:14+09:00 File Permissions : rwxrwxrwx File Type : JPEG File Type Extension : jpg MIME Type : image/jpeg JFIF Version : 1.01 Exif Byte Order : Big-endian (Motorola, MM) X Resolution : 1 Y Resolution : 1 Resolution Unit : None Y Cb Cr Positioning : Centered Compression : JPEG (old-style) Thumbnail Offset : 202 Thumbnail Length : 62058 Comment : Compressed by jpeg-recompress Image Width : 524 Image Height : 332 Encoding Process : Progressive DCT, Huffman coding Bits Per Sample : 8 Color Components : 3 Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2) Image Size : 524x332 Megapixels : 0.174 Thumbnail Image : (Binary data 62058 bytes, use -b option to extract) $ exiftool -b 694003b3deecf2382b3aa510e5f3e5f5153bb9c062e4f20878c0d343bc297767_meme.jpg > extract.jpg
先頭のごみを削除すると、jpgになる。またEXIFを見てみる。
$ exiftool extract.jpg ExifTool Version Number : 10.10 File Name : extract.jpg Directory : . File Size : 61 kB File Modification Date/Time : 2019:04:06 18:47:38+09:00 File Access Date/Time : 2019:04:06 18:48:17+09:00 File Inode Change Date/Time : 2019:04:06 18:47:38+09:00 File Permissions : rwxrwxrwx File Type : JPEG File Type Extension : jpg MIME Type : image/jpeg JFIF Version : 1.01 Exif Byte Order : Big-endian (Motorola, MM) X Resolution : 1 Y Resolution : 1 Resolution Unit : None Y Cb Cr Positioning : Centered Compression : JPEG (old-style) Thumbnail Offset : 202 Thumbnail Length : 25641 Comment : Compressed by jpeg-recompress Image Width : 534 Image Height : 380 Encoding Process : Progressive DCT, Huffman coding Bits Per Sample : 8 Color Components : 3 Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2) Image Size : 534x380 Megapixels : 0.203 Thumbnail Image : (Binary data 25641 bytes, use -b option to extract) $ exiftool -b extract.jpg > flag.jpg
先頭のごみを削除する。flag.jpgにフラグが書いてある。
tjctf{kn0w_y0ur_m3tad4ta}
Checker (Reversing 30)
Javaコードの処理は以下のようになっている。
1.flagの先頭からASCIIコードをpush 2.popしていき、2進数にして連結する。 3.0と1を逆にする。 4.9ビットシフトする。
これを逆算していき、途中いろいろ試しながら、2進数を分離し、フラグに戻す。
def dec_wow(b, s): r = '' for x in range(len(b)): r += b[(x-s)%len(b)] return r def dec_woah(b): r = '' for x in range(len(b)): if b[x] == '0': r += '1' else: r += '0' return r encoded = '1100001110000111000011000010100001110000111000010100001110000010000110010001011001110000101010001011000001000' b = dec_wow(encoded, 9) b = dec_woah(b) print b ## manual decode ## codes = '1111101 110011 1100011 110001 1110011 1101011 1100011 110001 1110101 1110001 1111011 1100110 1110100 1100011 1101010 1110100' ################### codes = codes.split(' ') flag = '' for code in codes: flag += chr(int(code, 2)) flag = flag[::-1] print flag
tjctf{qu1cks1c3}
Cable Selachimorpha (Forensics 40)
tcp.stream eq 14でHTTP Streamを見る。認証でPOSTパケットが見える。
POST /verify.php HTTP/1.1 Host: 192.168.56.101 Connection: keep-alive Content-Length: 42 Cache-Control: max-age=0 Origin: http://192.168.56.101 Upgrade-Insecure-Requests: 1 Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3 Referer: http://192.168.56.101/ Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.9 usr=omkar&pwd=tjctf%7Bb0mk4r_br0k3_b10n%7D
tjctf{b0mk4r_br0k3_b10n}
Comprehensive (Reversing 50)
以下のような処理の流れ。
★f k[0] ^ m[0+0] k[1] ^ m[1+0] : k[7] ^ m[7+0] k[0] ^ m[0+8] k[1] ^ m[1+8] : k[7] ^ m[7+8] k[0] ^ m[0+16] k[1] ^ m[1+16] : k[7] ^ m[7+16] ★g(fを1つの配列に!) k[0] ^ m[0+0] k[1] ^ m[1+0] : k[7] ^ m[7+0] k[0] ^ m[0+8] k[1] ^ m[1+8] : k[7] ^ m[7+8] k[0] ^ m[0+16] k[1] ^ m[1+16] : k[7] ^ m[7+16] ★h(7個飛ばしで、3個ずつの組み合わせにする) [g[0], g[8], g[16]], [g[1], g[9], g[17],... ★i h[0][0] ^ k[0] = g[0] ^ k[0] = m[0] ^ k[0] ^ k[0] h[0][1] ^ k[1] = g[8] ^ k[1] = m[8] ^ k[0] ^ k[1] h[0][2] ^ k[2] = g[16] ^ k[2] = m[16] ^ k[0] ^ k[2] h[1][0] ^ k[0] = g[1] ^ k[0] = m[1] ^ k[1] ^ k[0] h[1][1] ^ k[1] = g[9] ^ k[1] = m[9] ^ k[1] ^ k[1] h[1][2] ^ k[2] = g[17] ^ k[2] = m[17] ^ k[1] ^ k[2] h[2][0] ^ k[0] = g[2] ^ k[0] = m[2] ^ k[2] ^ k[0] h[2][1] ^ k[1] = g[10] ^ k[1] = m[10] ^ k[2] ^ k[1] h[2][2] ^ k[2] = g[18] ^ k[2] = m[18] ^ k[2] ^ k[2] : h[7][0] ^ k[0] = g[7] ^ k[0] = m[7] ^ k[7] ^ k[0] h[7][1] ^ k[1] = g[15] ^ k[0] = m[15] ^ k[7] ^ k[1] h[7][2] ^ k[2] = g[23] ^ k[0] = m[23] ^ k[7] ^ k[2] ★print k[0]プラス
このことから以下が言える。
k[1] = (o[3] - k[0]) ^ m[1] ^ k[0] k[2] = (o[6] - k[0]) ^ m[2] ^ k[0] k[3] = (o[9] - k[0]) ^ m[3] ^ k[0] k[4] = (o[12] - k[0]) ^ m[4] ^ k[0] k[5] = (o[15] - k[0]) ^ m[5] ^ k[0] k[7] = (o[23] - k[0]) ^ m[23] ^ k[2]
以上のことを踏まえスクリプトにすると、以下のようになりフラグが得られる。
enc = '225, 228, 219, 223, 220, 231, 205, 217, 224, 231, 228, 210, 208, 227, 220, 234, 236, 222, 232, 235, 227, 217, 223, 234, 2613' sum_code = int(enc.split(', ')[-1]) enc = map(int, enc.split(', ')[:-1]) def decrypt(enc, key): m = [0] * len(enc) for i in range(len(enc)): idx = (i % 3) * 8 + i // 3 m[idx] = (enc[i] - key[0]) ^ key[i//3] ^ key[i%3] s = '' for code in m: s += chr(code) return s def get_sum(s): sum = 0 for c in s: sum += ord(c) return sum flag_head = 'tjctf{' flag_tail = '}' ks = [-1] * 8 ks[0] = enc[0] - ord(flag_head[0]) for i in range(1, 6): ks[i] = (enc[i*3] - ks[0]) ^ ord(flag_head[i]) ^ ks[0] ks[7] = (enc[-1] - ks[0]) ^ ord(flag_tail) ^ ks[2] print chr(ks[7]) for k_6 in range(32, 127): ks[6] = k_6 flag = decrypt(enc, ks) if get_sum(flag) == sum_code: print flag break
tjctf{oooowakarimashita}
Is this the real life (Cryptography 90)
処理をデバッグしながら確認すると、以下のようなことがわかる。
block = 1 n = 1.00000000000000 phi = phiの先頭4バイト+(phiの末尾4バイトの逆) nlistはphiのリスト(8個) flagの各文字について ・nm = flagの文字のASCIIコード(2進数) いつも同じ ・cipher[i] = nm[i] * nlist[i] 例) nm[0] = '01101110', nlist[0] = 42798447 ・cipher[i] *= nm[i] ・cipher[i] = cipher[i] % modulus
平文1文字が暗号文1文字に対応するので、1文字ずつブルートフォースでフラグを求める。
nlist = [42798447, 60070844, 64372735, 50740679, 96064802, 42258424, 44356317, 77336984] ct = [292296909762800, 215653060477940, 208434519524352, 292296909762800, 265338299876870, 338411113077906, 217961079953287, 344375715089844, 205288667438400, 16912457697000, 11315622010000, 341537164927645, 314135413320574, 169124576970000, 32145056144756, 344375715089844, 15964193777715, 292296909762800, 344375715089844, 388451782884159, 16912457697000, 26533829987687, 344375715089844, 150925396356060, 281783803794437, 87154851154764, 398222847250224] def decrypt(c): for code in range(32, 127): nm = str(int(bin(code).replace('0b', ''))) cipher = 0 for b in range(len(nm)): cipher += int(nm[b]) * nlist[b] cipher = cipher * int(nm) if cipher == c: return chr(code) return chr(0) flag = '' for i in range(len(ct)): flag += decrypt(ct[i]) print flag
tjctf{i_T40ugh7_1t_w43_RsA}