この大会は2019/5/11 9:00(JST)~2019/5/13 9:00(JST)に開催されました。
今回もチームで参戦。結果は727点で1262チーム中62位でした。
ReversingとCryptoの融合問題をずっとやっていたけど、
解析が間違っていたのか、復号できず。
今回もメインの問題は1問も解けず、残念!
Welcome問題ですが、自分で解けた問題をWriteupとして書いておきます。
INS'hAck 2019 Writeup
この大会は2019/5/3 2:00(JST)~2019/5/6 1:30(JST)に開催されました。
今回もチームで参戦。結果は2841点で765チーム中7位でした。
自分で解けた問題をWriteupとして書いておきます。
Sanity (Misc 1)
問題にフラグが書いてあった。
INSA{Welcome}
Telegram (Misc 50)
Telegramのリンク先にアクセスすると、フラグが書かれている。
INSA{is_e2e_8annEd_1n_Russi4_too}
Dashlame - Part 1 (Reverse 50)
Easy Python Decompilerでpycをデコンパイルする。
# Embedded file name: dashlame.py from Crypto.Cipher import AES import os import random import sys import sqlite3 import time import zlib HEADER = " /.m.\\\n /.mnnm.\\ ___\n |.mmnvvnm.\\. .,,,/`mmm.\\\n |.mmnnvvnm.\\:;,. ..,,;;;/.mmnnnmm.\\\n \\ mmnnnvvnm.\\::;;, .,;;;;;;;;/.mmmnnvvnnm.|\n \\`mmnnnvvnm.\\::;::.sSSs sSSs ,;;;;;;;;;;/.mmmnnvvvnnmm'/\n \\`mmnnnvnm.\\:::::SSSS,,,,,,SSSS:::::::;;;/.mmmnnvvvnnmmm'/\n \\`mnvvnm.\\::%%%;;;;;;;;;;;%%%%:::::;/.mnnvvvvnnmmmmm'/\n \\`mmmm.%%;;;;;%%%%%%%%%%%%%%%::/.mnnvvvnnmmmmm'/ '\n \\`%%;;;;%%%%s&&&&&&&&&s%%%%mmmnnnmmmmmm'/ '\n | `%;;;%%%%s&&.%%%%%%.%&&%mmmmmmmmmm'/ '\n\\ | / %;;%%%%&&.%;` '%.&&%%%////// '\n \\ | / %%%%%%s&.%% x %.&&%%%%%//%\n \\ .:::::. ,;%%%%s&&&&.%; ;.&&%%%%%%%%/,\n-!!!- ::#:::::%%%%%%s&&&&&&&&&&&&&&&&&%%%%%%%%%%%\n / :##:::::&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%%%,\n / | `:#:::&&&&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%%\n | `&&&&&&&&&,&&&&&&&&&&&&SS%%%%%%%%%%%%%\n `~~~~~'~~ SSSSSSS%%%%%%%%%%%%%\n SSSSSSSS%%%%%%%%%%%%%%\n SSSSSSSSSS%%%%%%%%%%%%%.\n SSSSSSSSSSSS%%%%%%%%%%%%%%\n SSSSSSSSSSSSS%%%%%%%%%%%%%%%.\n SSSSSSSSSSSSSSS%%%%%%%%%%%%%%%%\n SSSSSSSSSSSSSSSS%%%%%%%%%%%%%%%%%.\n SSSSSSSSSSSSSSSSS%%%%%%%%%%%%%%%%%%%\n SSSSSSSSSSSSSSSSSS%%%%%%%%%%%%%%%%%%%%.\n\n WELCOME TO DASHLAME\n" PEARSON_TABLE = [199, 229, 151, 178, 53, 6, 131, 42, 248, 110, 39, 28, 51, 216, 32, 14, 77, 34, 166, 213, 157, 150, 115, 197, 228, 221, 254, 172, 84, 27, 36, 156, 69, 96, 12, 220, 225, 137, 246, 141, 44, 208, 191, 109, 163, 21, 173, 250, 98, 227, 203, 162, 188, 3, 105, 171, 215, 15, 207, 218, 234, 56, 136, 235, 97, 79, 189, 102, 134, 11, 224, 117, 177, 222, 100, 129, 78, 18, 130, 187, 9, 184, 99, 108, 202, 13, 238, 17, 94, 70, 180, 144, 185, 168, 123, 71, 176, 91, 4, 153, 103, 242, 80, 127, 198, 82, 169, 148, 48, 120, 59, 55, 230, 209, 50, 73, 31, 49, 142, 149, 167, 249, 116, 1, 7, 86, 143, 101, 29, 52, 114, 154, 160, 128, 19, 170, 46, 214, 38, 67, 186, 252, 181, 145, 212, 183, 22, 231, 107, 43, 47, 122, 251, 217, 5, 62, 88, 244, 200, 93, 240, 219, 124, 58, 161, 89, 211, 158, 247, 60, 236, 65, 106, 113, 66, 81, 165, 194, 223, 40, 233, 126, 139, 72, 132, 61, 135, 57, 87, 182, 164, 35, 159, 118, 8, 83, 210, 243, 104, 76, 75, 119, 90, 138, 20, 206, 95, 16, 74, 33, 245, 237, 111, 64, 253, 125, 23, 232, 193, 37, 175, 92, 30, 241, 255, 133, 0, 140, 2, 155, 85, 10, 146, 179, 25, 26, 226, 201, 195, 121, 190, 63, 68, 152, 45, 147, 41, 204, 192, 205, 196, 54, 174, 239, 112, 24] def pad(s): mark = chr(16 - len(s) % 16) while len(s) % 16 != 15: s += chr(random.randint(0, 255)) return s + mark def unpad(s): return s[:-ord(s[-1])] def get_random_passphrase(): sys.stdout.write('Getting random data from atmospheric noise and mouse movements') sys.stdout.flush() for i in range(10): sys.stdout.write('.') sys.stdout.flush() time.sleep(random.randint(1, 20) / 10.0) print '' with open('wordlist.txt', 'rb') as fi: passwords = fi.read().strip().split('\n') return (random.choice(passwords), random.choice(passwords)) def get_pearson_hash(passphrase): key, iv = ('', '') for i in range(32): h = (i + ord(passphrase[0])) % 256 for c in passphrase[1:]: h = PEARSON_TABLE[h ^ ord(c)] if i < 16: key += chr(h) else: iv += chr(h) return (key, iv) def encrypt_stream(data, passphrase): key, iv = get_pearson_hash(passphrase) aes = AES.new(key, AES.MODE_CBC, iv) data = pad(data) return aes.encrypt(data) def decrypt_stream(data, passphrase): key, iv = get_pearson_hash(passphrase) aes = AES.new(key, AES.MODE_CBC, iv) data = unpad(aes.decrypt(data)) return data def encrypt_archive(archive_filename, passphraseA, passphraseB): with open(archive_filename, 'rb') as db_fd: with open(archive_filename.replace('.db', '.dla'), 'wb') as dla_fd: enc1 = encrypt_stream(db_fd.read(), passphraseA) enc2 = encrypt_stream(enc1, passphraseB) dla_fd.write(enc2) os.unlink(archive_filename) def decrypt_archive(archive_filename, passphraseA, passphraseB): with open(archive_filename, 'rb') as dla_fd: with open(archive_filename.replace('.dla', '.db'), 'wb') as db_fd: dec1 = decrypt_stream(dla_fd.read(), passphraseB) dec2 = decrypt_stream(dec1, passphraseA) db_fd.write(dec2) os.unlink(archive_filename) def createArchive(): archive_name = raw_input('Please enter your archive name: ') passphraseA, passphraseB = get_random_passphrase() print 'This is your passphrase :', passphraseA, passphraseB print 'Please remember it or you will lose all your passwords.' archive_filename = archive_name + '.db' with open(archive_filename, 'wb') as db_fd: db_fd.write(zlib.decompress('x\x9c\x0b\x0e\xf4\xc9,IUH\xcb/\xcaM,Q0f`a`ddpPP````\x82b\x18`\x04b\x164>!\xc0\xc4\xa0\xfb\x8c\x9b\x17\xa4\x98y.\x03\x10\x8d\x82Q0\n\x88\x05\x89\x8c\xec\xe2\xf2\xf2\x8c\x8d\x82%\x89I9\xa9\x01\x89\xc5\xc5\xe5\xf9E)\xc5p\x06\x93s\x90\xabc\x88\xabB\x88\xa3\x93\x8f\xab\x02\\X\xa3<5\xa9\x18\x94\xabC\\#Bt\x14J\x8bS\x8b\xf2\x12sa\xdc\x02\xa820W\x13\x927\xcf0\x00\xd1(\x18\x05\xa3`\x08\x03#F\x16mYkh\xe6\x8fO\xadH\xcc-\xc8I\x85\xe5~O\xbf`\xc7\xea\x90\xcc\xe2\xf8\xa4\xd0\x92\xf8\xc4\xf8`\xe7"\x93\x92\xe4\x8cZ\x00\xa8&=\x8f')) encrypt_archive(archive_filename, passphraseA, passphraseB) print 'Archive created successfully.' def updateArchive(): archive_name = raw_input('Please enter your archive name: ') passphrase = raw_input('Please enter your passphrase: ') passphraseA, passphraseB = passphrase.split() website = raw_input('Website: ') username = raw_input('Username: ') password = raw_input('Password: ') dla_filename = archive_name + '.dla' db_filename = archive_name + '.db' decrypt_archive(dla_filename, passphraseA, passphraseB) conn = sqlite3.connect(db_filename) cur = conn.cursor() cur.execute('INSERT INTO Passwords VALUES(?,?,?)', (website, username, password)) conn.commit() conn.close() encrypt_archive(db_filename, passphraseA, passphraseB) print 'Update done.' def accessArchive(): archive_name = raw_input('Please enter your archive name: ') passphrase = raw_input('Please enter your passphrase: ') passphraseA, passphraseB = passphrase.split() website = raw_input('Website: ') dla_filename = archive_name + '.dla' db_filename = archive_name + '.db' decrypt_archive(dla_filename, passphraseA, passphraseB) conn = sqlite3.connect(db_filename) cur = conn.cursor() cur.execute('SELECT Username, Password FROM Passwords WHERE Website=?', (website,)) results = cur.fetchall() conn.close() encrypt_archive(db_filename, passphraseA, passphraseB) if len(results) == 0: print 'No results.' else: for result in results: print result[0], ':', result[1] if __name__ == '__main__': print HEADER print '1. Create a new password archive' print '2. Add a password to an archive' print '3. Access a password from an existing archive' try: res = raw_input() if res == '1': createArchive() elif res == '2': updateArchive() elif res == '3': accessArchive() else: print 'Wrong choice' except: print 'Error.'
dbを暗号化してdlaを作成したあと、dbを削除している。dbを削除する処理をコメントアウトして実行してみる。
$ python dashlame.py /.m.\ /.mnnm.\ ___ |.mmnvvnm.\. .,,,/`mmm.\ |.mmnnvvnm.\:;,. ..,,;;;/.mmnnnmm.\ \ mmnnnvvnm.\::;;, .,;;;;;;;;/.mmmnnvvnnm.| \`mmnnnvvnm.\::;::.sSSs sSSs ,;;;;;;;;;;/.mmmnnvvvnnmm'/ \`mmnnnvnm.\:::::SSSS,,,,,,SSSS:::::::;;;/.mmmnnvvvnnmmm'/ \`mnvvnm.\::%%%;;;;;;;;;;;%%%%:::::;/.mnnvvvvnnmmmmm'/ \`mmmm.%%;;;;;%%%%%%%%%%%%%%%::/.mnnvvvnnmmmmm'/ ' \`%%;;;;%%%%s&&&&&&&&&s%%%%mmmnnnmmmmmm'/ ' | `%;;;%%%%s&&.%%%%%%.%&&%mmmmmmmmmm'/ ' \ | / %;;%%%%&&.%;` '%.&&%%%////// ' \ | / %%%%%%s&.%% x %.&&%%%%%//% \ .:::::. ,;%%%%s&&&&.%; ;.&&%%%%%%%%/, -!!!- ::#:::::%%%%%%s&&&&&&&&&&&&&&&&&%%%%%%%%%%% / :##:::::&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%%%, / | `:#:::&&&&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%% | `&&&&&&&&&,&&&&&&&&&&&&SS%%%%%%%%%%%%% `~~~~~'~~ SSSSSSS%%%%%%%%%%%%% SSSSSSSS%%%%%%%%%%%%%% SSSSSSSSSS%%%%%%%%%%%%%. SSSSSSSSSSSS%%%%%%%%%%%%%% SSSSSSSSSSSSS%%%%%%%%%%%%%%%. SSSSSSSSSSSSSSS%%%%%%%%%%%%%%%% SSSSSSSSSSSSSSSS%%%%%%%%%%%%%%%%%. SSSSSSSSSSSSSSSSS%%%%%%%%%%%%%%%%%%% SSSSSSSSSSSSSSSSSS%%%%%%%%%%%%%%%%%%%%. WELCOME TO DASHLAME 1. Create a new password archive 2. Add a password to an archive 3. Access a password from an existing archive 1 Please enter your archive name: nora Getting random data from atmospheric noise and mouse movements.......... This is your passphrase : foundationalist oudated Please remember it or you will lose all your passwords. Archive created successfully.
作成されたdbをDB Browserで開き、Passwordsテーブルを見ると、フラグが格納されていた。
INSA{Tis_bUt_a_SCr4tch}
Dashlame - Part 2 (Crypto 400)
暗号化の処理概要は以下の通り。
・パスフレーズ2つをwordlist.txtから選択 ・DBファイルを生成 ・パスフレーズAでAES暗号化、さらにパスフレーズBでAES暗号化
ブルートフォースでパスフレーズを探り、復号してSQLiteファイルのヘッダ部になるよう復号する。
from Crypto.Cipher import AES import random PEARSON_TABLE = [199, 229, 151, 178, 53, 6, 131, 42, 248, 110, 39, 28, 51, 216, 32, 14, 77, 34, 166, 213, 157, 150, 115, 197, 228, 221, 254, 172, 84, 27, 36, 156, 69, 96, 12, 220, 225, 137, 246, 141, 44, 208, 191, 109, 163, 21, 173, 250, 98, 227, 203, 162, 188, 3, 105, 171, 215, 15, 207, 218, 234, 56, 136, 235, 97, 79, 189, 102, 134, 11, 224, 117, 177, 222, 100, 129, 78, 18, 130, 187, 9, 184, 99, 108, 202, 13, 238, 17, 94, 70, 180, 144, 185, 168, 123, 71, 176, 91, 4, 153, 103, 242, 80, 127, 198, 82, 169, 148, 48, 120, 59, 55, 230, 209, 50, 73, 31, 49, 142, 149, 167, 249, 116, 1, 7, 86, 143, 101, 29, 52, 114, 154, 160, 128, 19, 170, 46, 214, 38, 67, 186, 252, 181, 145, 212, 183, 22, 231, 107, 43, 47, 122, 251, 217, 5, 62, 88, 244, 200, 93, 240, 219, 124, 58, 161, 89, 211, 158, 247, 60, 236, 65, 106, 113, 66, 81, 165, 194, 223, 40, 233, 126, 139, 72, 132, 61, 135, 57, 87, 182, 164, 35, 159, 118, 8, 83, 210, 243, 104, 76, 75, 119, 90, 138, 20, 206, 95, 16, 74, 33, 245, 237, 111, 64, 253, 125, 23, 232, 193, 37, 175, 92, 30, 241, 255, 133, 0, 140, 2, 155, 85, 10, 146, 179, 25, 26, 226, 201, 195, 121, 190, 63, 68, 152, 45, 147, 41, 204, 192, 205, 196, 54, 174, 239, 112, 24] def pad(s): mark = chr(16 - len(s) % 16) while len(s) % 16 != 15: s += chr(random.randint(0, 255)) return s + mark def unpad(s): return s[:-ord(s[-1])] def get_pearson_hash(passphrase): key, iv = ('', '') for i in range(32): h = (i + ord(passphrase[0])) % 256 for c in passphrase[1:]: h = PEARSON_TABLE[h ^ ord(c)] if i < 16: key += chr(h) else: iv += chr(h) return (key, iv) def encrypt_stream(data, passphrase): key, iv = get_pearson_hash(passphrase) aes = AES.new(key, AES.MODE_CBC, iv) data = pad(data) return aes.encrypt(data) def decrypt_stream(data, passphrase): key, iv = get_pearson_hash(passphrase) aes = AES.new(key, AES.MODE_CBC, iv) data = unpad(aes.decrypt(data)) return data with open('admin.dla', 'rb') as f: dla = f.read() with open('wordlist.txt', 'r') as f: wl = f.read() words = wl.split('\n')[:-1] SQLITE_HEADER = 'SQLite format 3\x00' enc_list = {} for word in words: enc = encrypt_stream(SQLITE_HEADER, word) enc_list[enc[:16]] = word for word in words: dec1 = decrypt_stream(dla, word) if dec1[:16] in enc_list: word2 = enc_list[dec1[:16]] print word2, word dec2 = decrypt_stream(dec1, word2) break with open('admin.db', 'wb') as f: f.write(dec2)
復号を試した結果、以下のパスフレーズで暗号化したことがわかる。
spanish inquisition
復号したdbをDB Browserで開き、Passwordsテーブルを見ると、フラグが格納されていた。
INSA{D0_you_f1nD_it_Risible_wh3N_I_s4y_th3_name}
Jean-Sébastien Bash (Crypto 500)
コマンドをAES-CBC暗号化したものを指定すると、そのコマンドが実行される。例としてls -l の暗号化データのみがわかっている。flag.txtがあるので、その内容を見ることができればよい。いろいろ試した結果、CBC Oracle Padding Attackで2ブロック目でcat flag.txtに復号できるような暗号データを割り出すことができればフラグが得られそう。
本来はプログラムでやりたいところだが、SSHでこのttyに接続する方法がわからず、手動でCBC Oracle Padding Attackを実行する。
$ ssh -i ~/.ssh/id_inshack -p 2227 user@jean-sebastien-bash.ctf.insecurity-insa.fr The authenticity of host '[jean-sebastien-bash.ctf.insecurity-insa.fr]:2227 ([51.83.110.181]:2227)' can't be established. ECDSA key fingerprint is SHA256:Hp/VF/ZZ75+zXkpM05kpIoN/0YXe5Fqlt+pTr3O/kVE. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '[jean-sebastien-bash.ctf.insecurity-insa.fr]:2227,[51.83.110.181]:2227' (ECDSA) to the list of known hosts. ___ _ _ _ ____ ___ _ ___ |_ _|_ __ ___| | | | __ _ ___| | __ |___ \ / _ \/ |/ _ \ | || '_ \/ __| |_| |/ _` |/ __| |/ / __) | | | | | (_) | | || | | \__ \ _ | (_| | (__| < / __/| |_| | |\__, | |___|_| |_|___/_| |_|\__,_|\___|_|\_\ |_____|\___/|_| /_/ =========================================================== You are accessing a sandbox challenge over SSH This sandbox will be killed soon enough. Please wait while we launch your sandbox... =========================================================== Welcome on my server. /help for help >/help This is a tool so that only me can execute commands on my server (without all the GNU/Linux mess around users and rights). - /help for help - /exit to quit - /cmd <encrypted> to execute a command Notes (TODO REMOVE THAT) --------------------------- Ex: /cmd AES(key, CBC, iv).encrypt(my_command) /cmd 7bcfab368dc137d4628dcf45d41f8885 >/cmd 7bcfab368dc137d4628dcf45d41f8885 Running b'ls -l' total 8 -rw-r--r-- 1 root root 21 Apr 25 21:18 flag.txt -rwxr-xr-x 1 root root 2066 Apr 25 21:50 server.py /cmd 7bcfab368dc137d4628dcf45d41f88007bcfab368dc137d4628dcf45d41f8885 What do you mean?! /cmd 7bcfab368dc137d4628dcf45d41f88017bcfab368dc137d4628dcf45d41f8885 What do you mean?! /cmd 7bcfab368dc137d4628dcf45d41f88027bcfab368dc137d4628dcf45d41f8885 What do you mean?! /cmd 7bcfab368dc137d4628dcf45d41f88037bcfab368dc137d4628dcf45d41f8885 What do you mean?! /cmd 7bcfab368dc137d4628dcf45d41f88047bcfab368dc137d4628dcf45d41f8885 What do you mean?! /cmd 7bcfab368dc137d4628dcf45d41f88057bcfab368dc137d4628dcf45d41f8885 What do you mean?! /cmd 7bcfab368dc137d4628dcf45d41f88067bcfab368dc137d4628dcf45d41f8885 What do you mean?! /cmd 7bcfab368dc137d4628dcf45d41f88077bcfab368dc137d4628dcf45d41f8885 Running b'\x1c\x01\xae\xa8\x95\x1b\x02\xa4\x1b{\xe8\x152,\xcb\xf96\xe6\xba]\xda\xe7\x0c\x86\x02\xd6\xa1-\xe3r\xcc' sh: 1: ����{�2,��6��]�� �֡-�r�: not found
これでXORする前の7bcfab368dc137d4628dcf45d41f8885の復号したデータがわかった。あとは前がどんな文字列であっても2ブロック目が ; cat flag.txtに復号できる暗号データを計算してやる。
def str_xor(s1, s2): return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2)) def pad(s): p = 16 - len(s) % 16 return s + chr(p) * p ct1 = '7bcfab368dc137d4628dcf45d41f88077bcfab368dc137d4628dcf45d41f8885'.decode('hex') pt1 = '\x1c\x01\xae\xa8\x95\x1b\x02\xa4\x1b{\xe8\x152,\xcb\xf96\xe6\xba]\xda\xe7\x0c\x86\x02\xd6\xa1-\xe3r\xcc' ct1_1 = ct1[:16] pt1_2 = pad(pt1[16:]) pt2_2 = pad(' ; cat flag.txt') ct2_1 = str_xor(str_xor(ct1_1, pt1_2), pt2_2) ct2_2 = ct1[16:] ct2 = (ct2_1 + ct2_2).encode('hex') print ct2
この結果、暗号データは以下のようになる。
6d12310836521b340c3a0946431530077bcfab368dc137d4628dcf45d41f8885
>/cmd 6d12310836521b340c3a0946431530077bcfab368dc137d4628dcf45d41f8885 Running b'.\x15\xabh\xa1\x18\xd2\x88\xbeF\x7f\xdb\x12\xf9\xc4\xd7 ; cat flag.txt' sh: 1: .�h�҈�F����: not found INSA{or4cle_P4dd1ng}
INSA{or4cle_P4dd1ng}
Yet Another RSA Challenge - Part 1 (Crypto 500)
RSA暗号で、N、e、pの16進表記で'9F'を'FC'に置換したものと、cがわかっている。pの'FC'の一部を'9F'に置換することをブルートフォースで試して、Nを割り切れるものを探す。pがわかれば、qもわかり復号することができる。
from Crypto.Util.number import * import itertools def gen_p(str_p, t): p = str_p for e in t: p = p[:e] + '9F' + p[e + 2:] return eval(p) with open('output.txt', 'r') as f: N = int(f.readline().rstrip()) str_p_rep = f.readline().rstrip() c = int(f.readline().rstrip()) e = 65537 idxes = [] index = 0 while True: index = str_p_rep.find('FC', index) if index < 0: break idxes.append(index) index += 1 found = False for i in range(1, len(idxes) + 1): for j in list(itertools.combinations(idxes, i)): p = gen_p(str_p_rep, j) if N % p == 0: found = True break if found: break assert N % p == 0 q = N / p phi = (p - 1) * (q - 1) d = inverse(e, phi) m = pow(c, d, N) flag = long_to_bytes(m) print flag
INSA{I_w1ll_us3_OTp_n3xT_T1M3}
Yet Another RSA Challenge - Part 2 (Programming 222)
Part1と同様の問題。ただ、pが複数の文字のセットを置換しているので、難易度が上がっている。解き方は同様だが、プログラムとしては複雑化しているので、さまざまな箇所で注意が必要。
from Crypto.Util.number import * import itertools def get_idx(str_p, to_str): idxes = [] index = 0 while True: index = str_p.find(to_str, index) if index < 0: break idxes.append(index) index += 1 return idxes def gen_p(str_p, t, from_str): p = str_p for e in t: p = p[:e] + from_str + p[e + 2:] return p with open('output.txt', 'r') as f: N = int(f.readline().rstrip()) str_p_rep = f.readline().rstrip() c = int(f.readline().rstrip()) e = 65537 found = False str_p = str_p_rep idx0 = get_idx(str_p, '3E') for i0 in range(1, len(idx0) + 1): for c0 in list(itertools.combinations(idx0, i0)): str_p0 = gen_p(str_p, c0, '59') idx1 = get_idx(str_p0, 'E0') for i1 in range(0, len(idx1) + 1): for c1 in list(itertools.combinations(idx1, i1)): str_p1 = gen_p(str_p0, c1, '9E') idx2 = get_idx(str_p1, '89') for i2 in range(0, len(idx2) + 1): for c2 in list(itertools.combinations(idx2, i2)): str_p2 = gen_p(str_p1, c2, '6B') idx3 = get_idx(str_p2, '38') for i3 in range(0, len(idx3) + 1): for c3 in list(itertools.combinations(idx3, i3)): str_p3 = gen_p(str_p2, c3, 'E4') idx4 = get_idx(str_p3, '95') for i4 in range(0, len(idx4) + 1): for c4 in list(itertools.combinations(idx4, i4)): str_p4 = gen_p(str_p3, c4, '09') idx5 = get_idx(str_p4, 'FF') for i5 in range(0, len(idx5) + 1): for c5 in list(itertools.combinations(idx5, i5)): str_p5 = gen_p(str_p4, c5, '5E') idx6 = get_idx(str_p5, 'D4') for i6 in range(0, len(idx6) + 1): for c6 in list(itertools.combinations(idx6, i6)): str_p6 = gen_p(str_p5, c6, '33') idx7 = get_idx(str_p6, '8D') for i7 in range(0, len(idx7) + 1): for c7 in list(itertools.combinations(idx7, i7)): str_p7 = gen_p(str_p6, c7, '12') p = eval(str_p7) if N % p == 0: found = True break if found: break if found: break if found: break if found: break if found: break if found: break if found: break if found: break if found: break if found: break if found: break if found: break if found: break if found: break if found: break assert N % p == 0 q = N / p phi = (p - 1) * (q - 1) d = inverse(e, phi) m = pow(c, d, N) flag = long_to_bytes(m) print flag
INSA{Uh_never_give_4w4y_your_Pr1mes_I_m34n_duhhh}
TSG CTF Writeup
この大会は2019/5/4 16:00(JST)~2019/5/5 16:00(JST)に開催されました。
今回もチームで参戦。結果は868点で334チーム中19位でした。
メインの問題は一問も解けず残念!暗号問題を1問は解きたかった。
一応自分で解けた問題をWriteupとして書いておきます。
Sanity Check (Warmup)
Discordに入ると、#announcementsチャネルのところにフラグが書いてある。
TSGCTF{ur_here_cuz_u_absolutely_won_inshack_ctf?}
Survey (Cooldown)
アンケートに答えたら、フラグが表示された。
TSGCTF{Hosting_a_CTF_is_really..._REALLY_tough_and_challenging!}
ALLMN CTF19 Writeup
この大会は2019/5/1 9:00(JST)~2019/5/4 23:00(JST)に開催されました。
今回もチームで参戦。結果は3475点で417チーム中8位でした。
自分で解けた問題をWriteupとして書いておきます。
Free point (Warmup 10)
問題にフラグが書いてあった。
ALLMN{Y3S_TH1S_1S_Y0UR_FLAG_27SJAL}
Discord (Warmup 10)
Discordに入って、ピン止めされたメッセージを見ると、フラグが書いてあった。
ALLMN{TH4NKS_F0R_J01N1NG_US_2JKLPO}
Saturn.MN (Social Engineering 30)
タブ表示になっている、viewやaboutなどを見ていく。
http://saturn.mn/p/425にフラグが書かれていた。
ALLMN{W3LC0M3_T0_S4TURN_MN}
Easy Math (Warmup 30)
128と56のGCD(最大公約数)は8、LCMは128*56/8で896。
ALLMN{896_8}
Funny Website (Web 50)
HTMLソースコードのコメントにフラグの一部が書いてある。
<!-- ALLMN{W3LL_Y0U -->
http://chall.all.mn/funny/style.cssのコメントにフラグの一部が書いてある。
/*_C4N_1NSP3CT */
http://chall.all.mn/funny/function.jsのコメントにフラグの一部が書いてある。
// _M3_I28DJA}
全部繋げると、フラグになる。
ALLMN{W3LL_Y0U_C4N_1NSP3CT_M3_I28DJA}
Robots (Web 70)
http://chall.all.mn/robot/robots.txtにアクセスする。
User-agent: * Disallow: /allmnflag.php
http://chall.all.mn/robot/allmnflag.phpにアクセスすると、フラグが表示された。
ALLMN{W3B_R0B0TS_TXT_7DHJ9E}
Question & Answer (Crypto 100)
添付ファイルのテキストはシーザー暗号なので、https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。
Rotation 18: hey, you did a nice job! so here is your answer: eqra_cpf_rcuvg_vjku . but make sure it is in reverse form.
さらにeqra_cpf_rcuvg_vjkuの部分を復号する。
Rotation 2: copy_and_paste_this
http://chall.all.mn/asuult/index.php?q=1でcopy_and_paste_thisを答えると、フラグが表示された。
ALLMN{Y3S_Y0U_D1D_1T_4Y7JKP}
What is this? (Crypto 100)
Brainfuck言語なので、https://sange.fi/esoteric/brainfuck/impl/interp/i.htmlで実行すると、フラグが表示された。
ALLMN{1_L0V3_BR41N_F4CK_3IQKAP}
Damn! (Crypto 100)
ChromeのデベロッパーツールのConsoleで実行してみると、以下のエラーが発生。
Uncaught ReferenceError: f4ckjs is not defined at eval (eval at(local-ntp.html:1), :3:1) at :1:4558
f4ckjsがフラグとなるワードとして通った。
ALLMN{f4ckjs}
Find that word (Misc 100)
添付ファイルを解凍すると大量のファイルが展開される。
$ grep -ilr ALLMN findtheflag/* findtheflag/76.txt $ grep ALLMN findtheflag/76.txt Pauses in Heaven doyoureallywanttoknowtheflag?hereitisALLMN{GR3P_1S_S1mple}.
ALLMN{GR3P_1S_S1mple}
SQL (Web 100)
SQLインジェクションで攻撃。以下の通り入力してログインすると、フラグが表示された。
Username: ' or 1=1 #
ALLMN{W0W_Y0U_D1D_SQLi}
Do you know that? (Crypto 100)
https://github.com/JohnHammond/ctf-katanaのDNAコード表を見てデコードする。
RCCDE{1E_DP_UE4}
https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。
Rotation 17: ALLMN{1N_MY_DN4}
ALLMN{1N_MY_DN4}
My favorite number (Reversing 150)
逆アセンブルする。関係する部分は以下のようになっている。
push rbp mov rbp, rsp sub rsp, 10h mov rax, fs:28h mov [rbp+var_8], rax xor eax, eax lea rdi, s ; "Minii durtai toog oruulna uu" call _puts lea rax, [rbp+x] mov rsi, rax lea rdi, format ; "%d" mov eax, 0 call _scanf mov eax, [rbp+x] cmp eax, 4D1h jnz short loc_797
0x4D1と比較している。Pythonで0x4D1の値を確認する。
>>> 0x4D1 1233
ALLMN{1233}
ROCK (Forensics 150)
PDFファイルにパスワードがかかっているので、pdfcrackでパスワードを割り出す。
$ pdfcrack --wordlist=dict/rockyou.txt level-1.pdf PDF version 1.5 Security Handler: Standard V: 2 R: 3 P: -4 Length: 128 Encrypted Metadata: True FileID: a474737d247fa5259a6fc058ef096d2b U: d18066309b9c6a7058223fcfa93890430122456a91bae5134273a6db134c87c4 O: 884bb701492d79d69ff17a588a87adb263143d261bdf1dbb0a616a47944d713e Average Speed: 34703.2 w/s. Current Word: 'avril84' Average Speed: 34329.1 w/s. Current Word: 'porn123tip' Average Speed: 32437.8 w/s. Current Word: 'aritzel' Average Speed: 31514.0 w/s. Current Word: 'xwettie' Average Speed: 35259.1 w/s. Current Word: 'target0899' Average Speed: 33870.6 w/s. Current Word: 'salimd' Average Speed: 34358.9 w/s. Current Word: 'penchalick' Average Speed: 30534.2 w/s. Current Word: 'ms.kylan' Average Speed: 30263.8 w/s. Current Word: 'm0891553577' Average Speed: 30269.2 w/s. Current Word: 'kiri404' Average Speed: 29818.8 w/s. Current Word: 'jaspal209' Average Speed: 28429.8 w/s. Current Word: 'hair821sold381' Average Speed: 28589.2 w/s. Current Word: 'erestodoparamimau' Average Speed: 31237.2 w/s. Current Word: 'crosby87fan' Average Speed: 35053.8 w/s. Current Word: 'bisina' Average Speed: 35201.6 w/s. Current Word: 'ai3office1' Average Speed: 35135.8 w/s. Current Word: 'JenJen!' Average Speed: 33758.6 w/s. Current Word: '898989r' found user-password: '551731'
このパスワードでPDFファイルを開くと、フラグが書いてあった。
ALLMN{Y0U_DID_IT!}
Windows XP (Web 150)
UserAgentを該当するものにして、アクセスする
例)Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
$ curl -H "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)" http://chall.all.mn/windows/ <!DOCTYPE html> <!-- __ __ _____ ______ ______ __ | \/ | /\ | __ \| ____| | _ \ \ / / | \ / | / \ | | | | |__ | |_) \ \_/ / | |\/| | / /\ \ | | | | __| | _ < \ / | | | |/ ____ \| |__| | |____ | |_) | | | |_|__|_/_/ \_\_____/|______| |____/__|_|____ _ _____ _ _ _____ ____ __ __ _ _ _____ _ / ____| /\ | \ | |/ ____| | | |_ _| __ \ ( )_ _| \ | |/ ____|/ __ \| \/ | \ | |_ _| /\ ( ) | (___ / \ | \| | | | |__| | | | | |__) | |/ | | | \| | (___ | | | | \ / | \| | | | / \ |/ \___ \ / /\ \ | . ` | | | __ | | | | _ / | | | . ` |\___ \| | | | |\/| | . ` | | | / /\ \ ____) / ____ \| |\ | |____| | | |_| |_| | \ \ _| |_| |\ |____) | |__| | | | | |\ |_| |_ / ____ \ |_____/_/ _ \_\_| \_|\_____|_|_ |_|_____|_| \_\ |_____|_| \_|_____/ \____/|_| |_|_| \_|_____/_/ \_\ /\ | | | | | \/ | \ | | / \ | | | | | \ / | \| | / /\ \ | | | | | |\/| | . ` | / ____ \| |____| |____ _| | | | |\ | /_/ \_\______|______(_)_| |_|_| \_| --> <html lang="mn" dir="ltr"> <head> <meta charset="utf-8"> <title>ALL.MN Academy</title> <link rel="icon" href="icon.png"> <link rel="stylesheet" href="style.css" type="text/css"> </head> <body> <div class="blocked"> <img src="icon.png" width="200px"><br><br> Sorry, you cannot enter!<br> You can enter if you are using Windows XP and Internet Explorer.<br> ALLMN{CH4NG3_TH3_US3R_AG3NT_WD8KKA} </div> </body> </html>
ALLMN{CH4NG3_TH3_US3R_AG3NT_WD8KKA}
Encrypted (Crypto 250)
暗号は16進数で長さは608。1文字ずつMD5にしてつなげていると、推測して復号する。
import hashlib h_dic = {} for code in range(32, 127): h = hashlib.md5(chr(code)).hexdigest() h_dic[h] = chr(code) with open('encrypted.txt', 'r') as f: data = f.read().rstrip() flag = '' for i in range(0, len(data), 32): flag += h_dic[data[i:i+32]] print flag
ALLMN{MD5_1S_CR4ZY}
Admin panel (Crypto 250)
John the Ripperでパスワードを割り出す。
$ unshadow passwd shadow > passwd_shadow $ john --wordlist=../dict/rockyou.txt passwd_shadow Loaded 1 password hash (crypt, generic crypt(3) [?/64]) Press 'q' or Ctrl-C to abort, almost any other key for status hellokitty (root) 1g 0:00:00:01 100% 0.7462g/s 214.9p/s 214.9c/s 214.9C/s alyssa..brenda Use the "--show" option to display all of the cracked passwords reliably Session completed
root/hellokittyでログインする。
$ ./login Sain uu? Ta nevtreh ner bolon nuuts ugee oruulna uu! CTF19.ALL.MN Admin Login Nevtreh ner: root Nuuts ug: hellokitty ALLMN{G00DJ0B_Y0U_AR3_T00_G00D_SHD8WK}
ALLMN{G00DJ0B_Y0U_AR3_T00_G00D_SHD8WK}
My Little Electric CodeBook (Crypto 300)
ブロック暗号のECBモードの問題。ブロックごとに暗号文が同じであれば平文が同じことを使って解く。
以下の2つに暗号文を16バイトずつ区切って同じものを探す。
4c9e192389e8791554ae342a77069573 661a44fa2a2ab220ed33af9081049b8d
すると、以下の2行が見つかる。
4c9e192389e8791554ae342a77069573fd3281ec97b52ba8817e8560af2b187d:ALLMN{KTZFQVTLIPMONKTERRSCERKLK} 2d3d123382b1fcc266bf0bc5086da8b2661a44fa2a2ab220ed33af9081049b8d:ALLMN{HBSOPKYGXEPHNMSYKYHFXQIVW}
それぞれ前半と後半を切り出す。
ALLMN{KTZFQVTLIP PHNMSYKYHFXQIVW}
結合するとフラグになる。
ALLMN{KTZFQVTLIPPHNMSYKYHFXQIVW}
CSA Capture The Flag 2019 Writeup
この大会は2019/4/25 11:00(JST)~2019/5/2 11:00(JST)に開催されました。
今回もチームで参戦。結果は5076点で363チーム中3位でした。
自分で解けた問題をWriteupとして書いておきます。
Sanity check(0x00)
問題にフラグが書いてあった。
CSACTF{w3lc0m3_t0_csa_ctf_2019}
Zippy (Forensics)
zipのヘッダ4バイトが壊れているので、修正する。
(誤) 00 00 00 00 (正) 50 4b 03 04
展開すると、flag.txtにフラグが書いてあった。
CSACTF{z1ppy_z1p_z1p}
Down to basic (Crypto)
フラグの先頭がCSACTF{となることからXOR鍵を算出し、さらに推測して、復号する。
c = '\x13\x13eg#v\t\x05\x0f#HE\x04CC\x07\x0f0V\x14\x15\\\x17\t\x0f2AU\x02\x01\x00\x01#\x1fE{\x14\\\x13\x17#qG{\x04\x00\x1e\x11$q\x14J\n' pre_flag = 'CSACTF{' key = '' for i in range(len(pre_flag)): code = ord(c[i]) ^ ord(pre_flag[i]) key += chr(code) print key ## guess key ## key += 'd' print key flag = '' for i in range(len(c)): code = ord(c[i]) ^ ord(key[i%len(key)]) flag += chr(code) print flag
これでXOR鍵はP@$$w0rdであることがわかり、復号できた。
CSACTF{a_class1c_pr0blem_requ1res_a_class1c_s0lut10n}
Flag server (Crypto)
サーバ処理の概要は以下の通り。
・入力データはBase64 ・Base64デコードして以下の文字列にする。 rowdy123 + <入力> ++ ・AES暗号化してBase64エンコードして表示
文字列の長さを変えて試すと、10文字にしたとき、80バイトになったので、フラグの長さは46バイトであることもわかり、以下のようなイメージになる。
0123456789abcdef rowdy123XXXXXXXX XXFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF PPPPPPPPPPPPPPPP
0123456789abcdef rowdy123XXXXXXXX XXXXXXXXXXXXXXXC XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFPPP
上記のようなイメージで、1文字ずつフラグをはみ出させ、2ブロック目と5ブロック目が一致するものを探すことを繰り返し、フラグを割り出す。
import socket from base64 import * def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) flag = '' for i in range(46): for c in range(32, 127): print '*** flag: %s ***' % flag print c, chr(c) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('34.74.132.34', 1337)) data = recvuntil(s, '\n').rstrip() #print data if i < 16: text = 'X' * 8 + 'X' * (15 - (i % 16)) + flag + chr(c) \ + 'X' * (47 - len(flag)) print text else: text = 'X' * 8 + flag[i-15:i] + chr(c) \ + 'X' * (47 - len(flag)) print text text = b64encode(text) #print text s.sendall(text + '\n') size = len('rowdy123') + 71 - i + 46 size = size + 16 - size % 16 data = recvuntil(s, '\n').rstrip() #print data b64 = '' for j in range(size * 4 / (3 * 76) + 1): data = recvuntil(s, '\n').rstrip() #print data b64 += data enc = b64decode(b64) block1 = enc[16*1:16*2] block4 = enc[16*4:16*5] if block1 == block4: flag += chr(c) break print flag
CSACTF{ser10usly_1_d0nt_kn0w_what_t0_put_here}
Shakespeare (Crypto)
eの値が小さく、平文の上位bitの大半がわかっている。このことから、以下のようなスクリプトで復号することができる。
# solve.sage n = 1006836100850250538339995467613886290568442651407025704748377068277336663840322807255631910003671238333129747821401677091458273063019582521876226866878065342813920204554381690287329117261946553416320027957955605766622398073392496765842697469834794925876050925562024075006141634323253929247109497487662672405697258396584137509321741864516634651999799054786449300271622909227600883634147669010834991795161523697131426220251208433781244773539764161842505052231971382919967824210675662840663441620651420266255868914927411521784353964212033587445690357323252291991393014956932340980364269027698921907294758481600334348209162447060613941156368610501127037691522939020923578832525761118629677247463329559736428451094014067976185200082383567782696893487896242261214414477694719976590065452677776148568775926581762818443660878148364426201543151986222827020715235149143660874147096198623408316970919847284217769846103787437353010406070576416824522568438694050123611781945190356349091784143598551629255439066014442622323403606886940586598393546395396681244923344136771626115764972416530481118005853215560607832082875977841855163209032040736208394910518736188520737990966314657947406636362327107362260153133199038686467983874712019134795287956681 e = 5 c = 86369436556821828495369673854162831950166653329442831115191625341047835876798749659300641169348489814397657938758507056245452483528302648820602222051650089244062216320589929210206763500165600876025118953025309329553936210852805389539140958630003420335312979086383557665369163943903938569533466433527007050293325014896256907213847510652960137337747717702046541551623527661916693185081633247755853469488660176161381955671005765896319088865538114135666529015146105463393845863266076934785270480138522718706320412272484373838264459071202162126712065303703239606240172920462289657145390815683884595752646574944533803755632028037260059034716445191212182713792979655078091574530502495357406706909373119165806528380721574497499485062395281364120812874772053936952075952742036541816610742884797177390325939200184871385346082698051189479559236698554746173964482593646082680562341702105913029374921266420784671717205191676584949585227373190247242357902713051455202286347791244123645109240863246131183709864782892090906846140511095936549733130467184197878019539182792149521284353941031177248258779486351865884845751206163429977363088579292444338819912920250044064118502755440223212479004336304705210529515747947096857605944524561963438120052349 beta = 1 epsilon = beta^2/7 nbits = n.nbits() kbits = floor(nbits*(beta^2/e-epsilon)) with open('msg.txt', 'r') as f: m0 = int(f.read().rstrip().replace('X', '\x00').encode('hex'), 16) def_string = ' Message ends.' def_size = len(def_string) PR.<x> = PolynomialRing(Zmod(n)) f = (m0 + x * (256**def_size))^e - c f = f.monic() x0 = f.small_roots(X=2^kbits, beta=1)[0] m = m0 + x0 * (256**def_size) msg = ('%x' % m).decode('hex') print msg
実行結果は以下の通り。
Message begins. To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer. The slings and arrows of outrageous fortune. Or to take arms against a sea of troubles. And by opposing end them. To die: to sleep; - Hamlet contemplating suicide in his famous soliloquy. (Hamlet) - CSACTF{w1ll14m-sh4k3sp34r3} Message ends.
CSACTF{w1ll14m-sh4k3sp34r3}
A game of apples (Misc)
90個以上あるリンゴから、サーバプレーヤーと交互に取っていき、最後のリンゴを取れれば、フラグを得ることができる。
残り10個目を取ると負けるので、そうならないようスクリプトを組み実行する。
import socket from base64 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(('34.74.132.34', 1338)) data = recvuntil(s, '?\n').rstrip() print data print '\n' s.sendall('\n') data = recvuntil(s, '\n').rstrip() print data if data.split(' ')[1] == 'I': data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data remain = 999 count = 9 last = False while True: if remain > 10 and remain < 20: count = remain - 10 elif remain < 10: count = remain last = True data = recvuntil(s, '?\n').rstrip() print data print count s.sendall(str(count) + '\n') if last: break data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data remain = int(data.split(' ')[3]) data = recvuntil(s, '\n').rstrip() print data
CSACTF{0n3_4ppl3_tw0_4ppl3_thr33_4ppl3}
Linux 1 (Misc)
$ ssh user@35.231.176.102 -p1773 The authenticity of host '[35.231.176.102]:1773 ([35.231.176.102]:1773)' can't be established. ECDSA key fingerprint is SHA256:jnrxrLz++wBMSIAGbBB4KOK2Qpr9LW8kQ5tNvFN5LKc. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '[35.231.176.102]:1773' (ECDSA) to the list of known hosts. user@35.231.176.102's password: _________ _________ _____ _______________________________ \_ ___ \ / _____/ / _ \ \_ ___ \__ ___/\_ _____/ / \ \/ \_____ \ / /_\ \/ \ \/ | | | __) \ \____/ \/ | \ \____| | | \ \______ /_______ /\____|__ /\______ /|____| \___ / \/ \/ \/ \/ \/ Welcome to CSACTF 2019! If you find any problems, please report to admin. -[ Rule ]- A few rules before you get started: + don't leave orphan processes running + don't leave exploit-files laying around + don't annoy other players + don't share passwords/solutions + last but not least, don't spoil the fun! Have fun! - Blue user@23ee3cd1ea3b:~$ ls -la total 44 drwxr-xr-x 1 user user 4096 Apr 25 03:04 . drwxr-xr-x 1 root root 4096 Apr 22 20:12 .. -rw-r--r-- 1 user user 220 Aug 31 2015 .bash_logout -rw-r--r-- 1 user user 3796 Apr 25 03:03 .bashrc drwx------ 2 user user 4096 Apr 25 03:04 .cache -rw-r--r-- 1 user user 655 May 16 2017 .profile -rw-rw-r-- 1 user user 0 Apr 25 03:04 ahahaha -rwxrwxr-x 1 root root 267 Apr 22 20:09 flag_reader.py -rw-rw-r-- 1 root root 40 Apr 22 19:25 readme.txt -rw-r--r-- 1 user user 19 Apr 25 03:03 temp.txt user@23ee3cd1ea3b:~$ cat flag_reader.py #!/usr/bin/python import time import os f = open('flag.txt', 'r') # ==== Reading the flag flag = f.read() with open('temp.txt','w') as tmp: tmp.write('Reading the flag...') #print flag time.sleep(99999) # ==== Done, Cleaning up os.remove('temp.txt') f.close() user@abb60ff15525:~$ ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 11:44 ? 00:00:00 /bin/bash /root/init.sh user 13 1 0 11:44 ? 00:00:00 python /home/user/flag_reader.py root 16 1 0 11:44 ? 00:00:00 /usr/sbin/sshd -D root 18 16 0 11:44 ? 00:00:00 sshd: user [priv] user 22 18 0 11:44 ? 00:00:00 sshd: user@pts/0 user 23 22 0 11:44 pts/0 00:00:00 -bash user 38 23 0 11:48 pts/0 00:00:00 ps -ef
python /home/user/flag_reader.pyが実行できて、処理中のようだ。実行中のプロセスの標準出力を見る。
user@abb60ff15525:~$ ls -l /proc/13/fd/ total 0 lr-x------ 1 user user 64 Apr 25 11:49 0 -> /dev/null l-wx------ 1 user user 64 Apr 25 11:49 1 -> pipe:[2690442] l-wx------ 1 user user 64 Apr 25 11:49 2 -> pipe:[2690443] lr-x------ 1 user user 64 Apr 25 11:49 3 -> /home/user/flag.txt (deleted) user@abb60ff15525:~$ cat /proc/13/fd/3 CSACTF{f34r_cuts_d33p3r_th4n_sw0rds}
CSACTF{f34r_cuts_d33p3r_th4n_sw0rds}
stephanography (Misc)
ステガノグラフィーの問題だが、使用しているツールに関することが問題文に書かれている。https://github.com/stncal/appaで該当するツールを見つけた。
$ python3 appa.py -d secret_new.png : Appa found an extremely large string in the image. To save your console, results are saved to file: secret_new.results String length: 187142
secret_new.resultsはhexデータだが、jpgになりそう。hexデコードすると、jpg画像にフラグが書いてあった。
CSACTF{y1p_y1p!}
We are CSA (Reconnaissance)
https://ctf.utsacyber.com/challenges#We%20are%20CSAのResponceを見る。
{"data": {"files": [], "description": "There's nothing here.\r\n", "tags": [], "minimum": 50, "id": 6, "type_data": {"templates": {"create": "/plugins/dynamic_challenges/assets/create.html", "update": "/plugins/dynamic_challenges/assets/update.html", "view": "/plugins/dynamic_challenges/assets/view.html"}, "scripts": {"create": "/plugins/dynamic_challenges/assets/create.js", "update": "/plugins/dynamic_challenges/assets/update.js", "view": "/plugins/dynamic_challenges/assets/view.js"}, "id": "dynamic", "name": "dynamic"}, "hints": [], "category": "Reconnaissance", "name": "We are CSA", "solves": 28, "decay": 50, "initial": 500, "value": 369, "state": "visible", "type": "dynamic", "max_attempts": 0}, "success": true}
Responceにはこんなコメントが入っている。
<!-- Oops, you found me! Part 1: Q1NBQ1RGe1VORDNSU1Q Check out: http://utsacyber.com/resources.html. We have tons of resources for beginners, especially about RE. -->
Base64デコードしてみる。
$ echo Q1NBQ1RGe1VORDNSU1Q | base64 -d CSACTF{UND3RSTbase64: 無効な入力
まだ他の場所にもBase64文字列が隠れていそう。http://utsacyber.com/resources.htmlにアクセスしてHTMLソースを見る。
今度はこんなコメントが入っている。
<!-- Part 2: 0TkQxTkdfQ1lCM1JfUz One of our great members, Missing, has an awesome tutorial on Bug Hunting (and MIPS), you might want to check it out.-->
先ほどのBase64文字列と結合して、Base64デコードする。
$ echo Q1NBQ1RGe1VORDNSU1Q0TkQxTkdfQ1lCM1JfUz | base64 -d CSACTF{UND3RST4ND1NG_CYB3R_Sbase64: 無効な入力
まだ情報が足りない。このページにMissing's Bug Hunting Cookbookというリンクがある。そのページ https://bh-cookbook.github.io/ にアクセスする。
コメントにはこう書かれている。
<!-- Part 3: NDVVIxVFlfMU5fNExMX Thank you Eli for designing the fancy logo on the CTF Homepage. -->
先ほどのBase64文字列とさらに結合して、Base64デコードする。
$ echo Q1NBQ1RGe1VORDNSU1Q0TkQxTkdfQ1lCM1JfUzNDVVIxVFlfMU5fNExMX | base64 -d CSACTF{UND3RST4ND1NG_CYB3R_S3CUR1TY_1N_4LLbase64: 無効な入力
次はhttps://ctf.utsacyber.com/のページのロゴ画像をダウンロードすると、PNGファイルの後ろにデータがある。
Part 4: zFUU19EME00MU5TfQo=
先ほどのBase64文字列とさらに結合して、デコードする。
$ echo Q1NBQ1RGe1VORDNSU1Q0TkQxTkdfQ1lCM1JfUzNDVVIxVFlfMU5fNExMXzFUU19EME00MU5TfQo= | base64 -d CSACTF{UND3RST4ND1NG_CYB3R_S3CUR1TY_1N_4LL_1TS_D0M41NS}
CSACTF{UND3RST4ND1NG_CYB3R_S3CUR1TY_1N_4LL_1TS_D0M41NS}
SECCON 令和CTF Writeup
この大会は2019/4/30 23:00(JST)~2019/5/1 2:00(JST)に開催されました。
この大会は個人戦。結果は210点で858人中107位でした。
解けた問題をWriteupとして書いておきます。
フラグの例は?(Misc)
問題にフラグが書いてあった。
SECCON{reiwa}
bREInWAck (Misc)
このファイルが添付されている。
令和和和和和和和和和和和和和和和和「令和 和和和和令和和和和令和和和和和和和令和和 和和和和令和和平平平平平成」令和和和。令 和和和和和。成成。。平成成成成。成。令令 和和和和和和和和和和和。令和和。平平平和 和和和。令和和。和和和和。令令和和和和和 和和和和和和和。平平平和和和和和和和和和 和和和和。成成成成成成成成。令成成成成成 成成成。令令。成成成成成。成成成成成成。 令和。平平和和。令令令和和和和和和和和和 和。
問題タイトルから推測しても、Brainfuck言語で、使われている2バイト文字と1:1対応しているようだ。使われている文字の意味と数を考え、以下のように置き換えた。
>++++++++++++++++[>+ ++++>++++>+++++++>++ ++++>++<<<<<-]>+++.> +++++.--..<----.-.>> +++++++++++.>++.<<<+ +++.>++.++++.>>+++++ +++++++.<<<+++++++++ ++++.--------.>----- ---.>>.-----.------. >+.<<++.>>>+++++++++ +.
以下のBrainfuck言語のオンラインインタプリタでこのコードを実行する。
sange.fi
SECCON{bREIn_WAnic!}
零は? (Misc)
ncで接続すると?を含む式が出てくる。式が成り立つ?を求めていくPPCの問題のようだ。99問目と100問目は方程式として成り立っていないので、解なしの場合は0で答えるようにする。
import socket import re import sympy 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(('zerois-o-reiwa.seccon.jp', 23615)) for i in range(100): data = recvuntil(s, '?=') formula = data.split('\n')[-2].replace('?', 'x').split('=')[1] sol = sympy.solve(formula) if len(sol) == 1: ans = sol[0] else: ans = 0 print data + str(ans) s.sendall(str(ans) + '\n') data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data
SECCON{REIWA_is_not_ZERO_IS}