この大会は2023/1/7 9:00(JST)~2023/1/9 9:00(JST)に開催されました。
今回もチームで参戦。結果は4168点で730チーム中13位でした。
自分で解けた問題をWriteupとして書いておきます。
Sanity Check (Welcome)
問題にフラグが書いてあった。
irisctf{w31c0m3_t0_1r15ctf_2023}
Discord (Welcome)
Discordに入り、#miscチャネルのトピックを見ると、フラグが書いてあった。
irisctf{d15c0rd_c0nn3cts_y0u_t0_0ur_0rg4n1z3rs}
Meaning of Python 1 (Reverse Engineering)
スクリプトの処理概要は以下の通り。
・flag_to_check: 第一引数 ・flag_length: flag_to_checkの長さ ・flag_lengthが44より小さい場合、NGで終了 ・scramble1(flag_to_check) ※内部の処理結果を使用していないため、何もしていないのと同じ。 ・flag_compressed = zlib.compress(flag_to_check.encode("utf-8")) ・flag_compressed_length: flag_compressedの長さ ・flag_compressed_lengthが52より小さい場合、NGで終了 ・scramble2(flag_compressed) ※内部の処理結果を使用していないため、何もしていないのと同じ。 ・flag_compressedがb'x\x9c\xcb,\xca,N.I\xab.\xc9\xc8,\x8e7,\x8eOIM3\xcc3,1\xce\xa9\x8c7\x89/\xa8,\xc90\xc8\x8bO\xcc)2L\xcf(\xa9\x05\x00\x83\x0c\x10\xf9' と一致していたら正しい。
最後の結果をdecompressするだけでよい。
#!/usr/bin/env python3 import zlib flag_compressed = b'x\x9c\xcb,\xca,N.I\xab.\xc9\xc8,\x8e7,\x8eOIM3\xcc3,1\xce\xa9\x8c7\x89/\xa8,\xc90\xc8\x8bO\xcc)2L\xcf(\xa9\x05\x00\x83\x0c\x10\xf9' flag = zlib.decompress(flag_compressed).decode() print(flag)
irisctf{this_1s_def1n1t3ly_4_pyth0n_alr1ght}
Meaning of Python 2 (Reverse Engineering)
途中まで、難読化を解除すると、以下のようになる。
from builtins import *; WWXWXWXXXXXWWWWXXWWXXWX,lIIIlIIIllIllIIllIII,LILJIJJIJJILLIIJJLL,S2S2SS2S222S2S2SS2222,wxwwxxwwwxxxxwxxxwxx=(lambda wxxwwxwxwxxxwwxwwwwxw:wxxwwxwxwxxxwwxwwwwxw(__import__('zlib'))),(lambda wxxwwxwxwxxxwwxwwwwxw:wxxwwxwxwxxxwwxwwwwxw['decompress']),(lambda wxxwwxwxwxxxwwxwwwwxw:globals()['eval'](globals()['compile'](globals()['str']("globals()['eval'](wxxwwxwxwxxxwwxwwwwxw)"),filename=nmnnnmnnnnmnnnnnmnnmnnmnn',mode='eval'))),(lambda DDDDDDOOooOoDoDooD,wxxwwxwxwxxxwwxwwwwxw:DDDDDDOOooOoDoDooD(wxxwwxwxwxxxwwxwwwwxw)),(lambda:(lambda wxxwwxwxwxxxwwxwwwwxw:globals()['eval'](globals()['compile'](globals()['str']("globals()['eval'](wxxwwxwxwxxxwwxwwwwxw)"),filename='nmnnnmnnnnmnnnnnmnnmnnmnn',mode='eval')))('__import__('builtins').exec'));
その後、b'x\x9c\xe4'から始まるデータ部をzlib.decompressすれば良さそうという推測ができる。
#!/usr/bin/env python3 import zlib enc = b'x\x9c\xe4\xbd\xdbn\xe3\xc8\x92.|\xbf\x9eb\xdd\xa9\nc\x0f(\x9e\xb9\x80\xba3~@・・・(省略)・・・=\xfe\xe8\xd5s\xc2@zzx\xc6"\xff\xc7\xff\x01\t\xaa\x1b!' dec = zlib.decompress(enc).decode() print(dec)
結果以下のようになり、再び難読化コードになる。
: (省略) : elif locals()[MNMNMMMNNNNNNNNNMNMNNM]==LJJJLLLIJLLIJJLJIJJIILJ: locals()[XWXXXWWXXWWWWWXWX]=nmnmnnnmmmnmnnnnmmmmmnnnn(locals()[IIllIllIllIlIlIlIlIIlllll],lIlIIlIlIlIlIIllIIIlIl,nmnnmmmmnmmmmnmmmnmnm) locals()[XXXXWXWXWXXXXXWWWXX]=NMMNMNMNNMMMMNMMMMMMNMNNN elif locals()[IIllIllIllIlIIlllI]==NMMNNMMMNNMNMNMNNMNMNNN: locals()[IlIIlllIIIlIIIIIIllII]=nmnmnnnmmmnmnnnnmmnmmnnnn(locals()[oODDooDoOOOoDODoDoODooD],IJILIIJJJJLLILIILJLI,JIIILLLJJJJJJJILJJJJLLII) locals()[MNMMNNMMNMMMNMMNMNN]=S2S2SS222S2SS222SS2 elif locals()[IIllIllIIIIlIIIIIllI]==mmmnnmmmmmnmmnnnmmmnmnm: return def main(): if len(sys.argv)<=LIJIIILJLJJILIILLILJL: print(S22222S22S2SSS2S22SS2S22S2) exit(Oo00ooO0OOO00Oo0O0) locals()[SSS22SS222S222SSS2S]=sys.argv[DoOoODOoooDOoODoD] locals()[oDDDoOOoooOOoooOoODDO]=len(locals()[wxxwxwxxxxxwwxxxwxwxwxww]) if locals()[S2222SSS2SSS22222S]<IIlIllIIlIlIIIlIIl: print(ODOOOoDDDDOOoooDo) exit(IIILLJIIIJIIJJLIJIJLJLILI) SS2SS2S22S22SSSSSS2SS22S2S(locals()[lljlililiiljliiij]) locals()[S2S2SS222S22S222S22SSSS]=zlib.compress(locals()[mnmmmnnmnmnnmnnmmmmmmm].encode(O00Oo0OO00OOOOoo00OooOo0O)) locals()[LJLJJLJIJIIJILLJJ]=len(locals()[OoO0O000000ooo0oOo0ooOO0O0]) if locals()[xwwxxxwxxxxwwxwwxww]<wwxwxwxxwxwxxwxxwwwwx: print(WWWXXWXWXWWXWXWWXWXXWX) exit(ljijlljjiiiljjljjjiji) SS2SS2S22S22SSSSSS22S22S2S(locals()[SS2SS2SS22S222SSSS22SSS2]) if locals()[SSSS222S22SS2222SS2S2S]==OO0oOO00oOo0O000oo0OOO0: print(XXWWWWXXWWWWWXWXW) else: print(nmnmnmnnnnmnnmnmnmmnnm) main()
main関数の中のパラメータを標準出力しながら、コードを書き換える。
def main(): if len(sys.argv)<=1: print("Missing argument") exit(1) locals()["nmnmnmnnmmnmmnmmmm"]=sys.argv[1] locals()["MNNMMNMNMNNMMNNMNMN"]=len(locals()["nmnmnmnnmmnmmnmmmm"]) if locals()["MNNMMNMNMNNMMNNMNMN"]<44: print("Incorrect!") exit(1) SS2SS2S22S22SSSSSS2SS22S2S(locals()["nmnmnmnnmmnmmnmmmm"]) locals()["NMMNMNMNMNNNNNNNNNNM"]=zlib.compress(locals()["nmnmnmnnmmnmmnmmmm"].encode("utf-8")) locals()["NMMNMNMNMMNNNNNNNNNM"]=len(locals()["NMMNMNMNMNNNNNNNNNNM"]) if locals()["NMMNMNMNMMNNNNNNNNNM"]<56: print("Incorrect!") exit(1) SS2SS2S22S22SSSSSS22S22S2S(locals()["NMMNMNMNMNNNNNNNNNNM"]) if locals()["NMMNMNMNMNNNNNNNNNNM"]==b'x\x9c\xcb,\xca,N.I\xab\xce\xa8,H-\xca\xcc\xcf\x8b7,\x8e\xcf3(\x89O\x8c/3.\xaa\x8cO70H\x897HJ+5M6)1(R\xac\x05\x00\xce\xff\x11\xdb': print("Correct!") else: print("Incorrect!")
最後の比較バイト文字列をzlib.decompressする。
#!/usr/bin/env python3 import zlib flag_compressed = b'x\x9c\xcb,\xca,N.I\xab\xce\xa8,H-\xca\xcc\xcf\x8b7,\x8e\xcf3(\x89O\x8c/3.\xaa\x8cO70H\x897HJ+5M6)1(R\xac\x05\x00\xce\xff\x11\xdb' flag = zlib.decompress(flag_compressed).decode() print(flag)
irisctf{hyperion_1s_n0t_a_v3ry_g00d_0bfu5c4t0r!}
babystrechy (Web Exploitation)
$ nc stretchy.chal.irisc.tf 10704 == proof-of-work: enabled == please solve a pow first You can run the solver with: python3 <(curl -sSL https://goo.gle/kctf-pow) solve s.ADQ6.AAAusC+HkKwoFsogo/cf6NgW =================== Solution? s.AABcs2780SHoJWB+tOoePCMgzK96TY0BKE8b7pzQ+ANGYQQ0K0eh0u+naVKDcEta+gzt8zcH81PtBMLMG+xvZFYYpDxyNLXbzhY0XWjLIB5h46/uQncyKET0SXtevB0EHf7VXBNoyMxptfXndzQyvZoMiWAdzXPuup/0cfET50vFem4r4a8Agv7fAer3jrCVlVA3LoGOfQo/2b2MzRVOKTa9 Correct Fear my 4096 byte password! >
・$password: ランダム64バイト文字列の16進数表記 ・$stretched_password: $passwordの各文字を64個ずつ連結した文字列 ・$h = password_hash($stretched_password, PASSWORD_DEFAULT); ・$line: 入力 ・password_verify(trim($line), $h)がtrueの場合にフラグを表示
password_hash関数は最大72バイトで文字列を切り、ハッシュ値を取る。このため、以下の場合のブルートフォースで条件を満たすものを探す。
xxx.....xxxyyyyyyyy(16進文字 x を64バイト+16進文字 y を8バイト)
#!/usr/bin/env python3 import socket import subprocess import string def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('stretchy.chal.irisc.tf', 10704)) data = recvuntil(s, b'===\n').rstrip() print(data) cmdline = data.splitlines()[3].lstrip() cmd = ['python3', 'pow.py'] cmd += cmdline.split(' ')[-2:] sol = subprocess.check_output(cmd).decode() print(sol) data = recvuntil(s, b'? ') print(data + sol) s.sendall(sol.encode() + b'\n') data = recvuntil(s, b'!\n').rstrip() print(data) found = False for c1 in string.hexdigits[:16]: for c2 in string.hexdigits[:16]: password = c1 * 64 + c2 * 8 data = s.recv(2).decode() if data == '> ': print(data + password) s.sendall(password.encode() + b'\n') else: found = True data += recvuntil(s, b'}') print(data) break if found: break
実行結果は以下の通り。
== proof-of-work: enabled == please solve a pow first You can run the solver with: python3 <(curl -sSL https://goo.gle/kctf-pow) solve s.ADQ6.AAAR/vy+Kp2TLgkDdHqzdh8s =================== Solution: s.AAAjxFawR0RZldfC+wpq19M5XFWgOlSTEWYOMIV7gTvDGI3ObRCq/JD5vEGrSRi2TVutbh4qmfilYSp0E7eixFBH99qDXF2kj+Re1QdCVSF5UUl/uOp3W8QIdTudUkzmTXKAa/slj++ORh9Y88Q3rqO0OH+tWI7XwBpnbUvritDTG08ES83LHF9S1pJMOhgXOM0+YU0BsJOmKKgEX169aBmX Solution? s.AAAjxFawR0RZldfC+wpq19M5XFWgOlSTEWYOMIV7gTvDGI3ObRCq/JD5vEGrSRi2TVutbh4qmfilYSp0E7eixFBH99qDXF2kj+Re1QdCVSF5UUl/uOp3W8QIdTudUkzmTXKAa/slj++ORh9Y88Q3rqO0OH+tWI7XwBpnbUvritDTG08ES83LHF9S1pJMOhgXOM0+YU0BsJOmKKgEX169aBmX Correct Fear my 4096 byte password! > 000000000000000000000000000000000000000000000000000000000000000000000000 > 000000000000000000000000000000000000000000000000000000000000000011111111 > 000000000000000000000000000000000000000000000000000000000000000022222222 > 000000000000000000000000000000000000000000000000000000000000000033333333 > 000000000000000000000000000000000000000000000000000000000000000044444444 > 000000000000000000000000000000000000000000000000000000000000000055555555 > 000000000000000000000000000000000000000000000000000000000000000066666666 > 000000000000000000000000000000000000000000000000000000000000000077777777 > 000000000000000000000000000000000000000000000000000000000000000088888888 > 000000000000000000000000000000000000000000000000000000000000000099999999 > 0000000000000000000000000000000000000000000000000000000000000000aaaaaaaa > 0000000000000000000000000000000000000000000000000000000000000000bbbbbbbb > 0000000000000000000000000000000000000000000000000000000000000000cccccccc > 0000000000000000000000000000000000000000000000000000000000000000dddddddd > 0000000000000000000000000000000000000000000000000000000000000000eeeeeeee > 0000000000000000000000000000000000000000000000000000000000000000ffffffff > 111111111111111111111111111111111111111111111111111111111111111100000000 > 111111111111111111111111111111111111111111111111111111111111111111111111 > 111111111111111111111111111111111111111111111111111111111111111122222222 > 111111111111111111111111111111111111111111111111111111111111111133333333 > 111111111111111111111111111111111111111111111111111111111111111144444444 > 111111111111111111111111111111111111111111111111111111111111111155555555 > 111111111111111111111111111111111111111111111111111111111111111166666666 > 111111111111111111111111111111111111111111111111111111111111111177777777 > 111111111111111111111111111111111111111111111111111111111111111188888888 > 111111111111111111111111111111111111111111111111111111111111111199999999 > 1111111111111111111111111111111111111111111111111111111111111111aaaaaaaa > 1111111111111111111111111111111111111111111111111111111111111111bbbbbbbb > 1111111111111111111111111111111111111111111111111111111111111111cccccccc > 1111111111111111111111111111111111111111111111111111111111111111dddddddd > 1111111111111111111111111111111111111111111111111111111111111111eeeeeeee > 1111111111111111111111111111111111111111111111111111111111111111ffffffff > 222222222222222222222222222222222222222222222222222222222222222200000000 > 222222222222222222222222222222222222222222222222222222222222222211111111 > 222222222222222222222222222222222222222222222222222222222222222222222222 > 222222222222222222222222222222222222222222222222222222222222222233333333 > 222222222222222222222222222222222222222222222222222222222222222244444444 > 222222222222222222222222222222222222222222222222222222222222222255555555 > 222222222222222222222222222222222222222222222222222222222222222266666666 > 222222222222222222222222222222222222222222222222222222222222222277777777 > 222222222222222222222222222222222222222222222222222222222222222288888888 > 222222222222222222222222222222222222222222222222222222222222222299999999 > 2222222222222222222222222222222222222222222222222222222222222222aaaaaaaa > 2222222222222222222222222222222222222222222222222222222222222222bbbbbbbb > 2222222222222222222222222222222222222222222222222222222222222222cccccccc > 2222222222222222222222222222222222222222222222222222222222222222dddddddd > 2222222222222222222222222222222222222222222222222222222222222222eeeeeeee > 2222222222222222222222222222222222222222222222222222222222222222ffffffff > 333333333333333333333333333333333333333333333333333333333333333300000000 : > 9999999999999999999999999999999999999999999999999999999999999999ffffffff > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa00000000 > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa11111111 > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa22222222 > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa33333333 > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa44444444 > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa55555555 > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa66666666 > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa77777777 > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa88888888 > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa99999999 > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbb > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacccccccc > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadddddddd > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaeeeeeeee irisctf{truncation_silent_and_deadly}
irisctf{truncation_silent_and_deadly}
babyshark (Networks)
httpでフィルタリングする。No.307のパケットで GET /babyshark.gifのレスポンスが返ってきているので、エクスポートする。エクスポートした画像にフラグが書いてあった。
irisctf{welc0m3_t0_n3tw0rks}
wi-the-fi (Networks)
WPAのキーをクラックする。
$ aircrack-ng -w dict/rockyou.txt BobertsonNet.cap Reading packets, please wait... Opening BobertsonNet.cap Read 20980 packets. # BSSID ESSID Encryption 1 A0:28:ED:C3:CC:C1 BobertsonNet WPA (1 handshake) Choosing first network as target. Reading packets, please wait... Opening BobertsonNet.cap Read 20980 packets. 1 potential targets Aircrack-ng 1.6 [00:00:01] 13774/10303727 keys tested (23513.52 k/s) Time left: 7 minutes, 17 seconds 0.13% KEY FOUND! [ billybob1 ] Master Key : 84 F1 82 B2 91 7C D3 DD 26 B2 19 34 C0 7B 27 2F AC CF 32 B2 DF A3 56 01 9E FE 8C 7A 23 38 15 F8 Transient Key : BA 3F 2A 6D D0 DB 35 F8 D4 60 FF BB EF CB B5 1A CE 60 FD 5F 58 D0 1F C4 3A EA 03 EA D4 DE 41 C9 4B 0B 91 19 1F 47 2D E3 8A 26 19 85 69 89 37 F3 2D 24 11 A4 C7 31 F9 D6 8F 6D 87 4F 17 87 01 9D EAPOL HMAC : 80 8E 84 12 3F EA 18 4E 2D 25 9A 19 10 54 E2 51
Wiresharkの[編集]>[設定]から[Protocols]>[IEEE 802.11]の画面を表示後、Decryption keysの編集から以下を設定し、通信パケットを復号する。
・Key type: wpa-pwd ・Key : billybob1
tcpでフィルタリングし、No.20422のパケットを見ると、TCPペイロードにフラグが入っていた。
irisctf{4ircr4ck_g0_brrrrrrrrrrrrrrr}
babyforens (Forensics)
JPEGの先頭10バイトが壊れているので、以下に書き換える。
FF D8 FF E0 00 10 4A 46 49 46
書き換えたJPG画像には以下のsecretの情報が書いてある。
exif_data_can_leak_a_lot_of_info
次にJPGのEXIFを見る。
$ exiftool IMG_0917_fix.jpg : Date/Time Original : 2022:08:27 10:04:56 Create Date : 2022:08:27 10:04:56 : Serial Number : 392075057288 : GPS Latitude : 37 deg 44' 49.46" N GPS Longitude : 119 deg 35' 46.77" W :
緯度、経度は小数で小数点以下2桁に切り捨てた値が必要なので、Googleマップで以下を検索する。
37°44' 49.46" N, 119°35' 46.77" W →37.747072, -119.596325
場所はUSのカリフォルニア州なので、GMT-8。このタイムゾーンで、2022-08-27 10:04:56 の Unixtime は 1661619896。
irisctf{37.74_-119.59_1661619896_392075057288_exif_data_can_leak_a_lot_of_info}
babynotrsa (Cryptography)
暗号は以下の計算で行っている。
encrypted = (flag * e) % n
flagは以下の計算で求めることができる。
flag = (encrypted * inverse(e, n)) % n
#!/usr/bin/env python3 from Crypto.Util.number import * with open('output.txt', 'r') as f: params = f.read().splitlines() n = int(params[0].split(' ')[-1]) e = int(params[1].split(' ')[-1]) encrypted = int(params[2].split(' ')[-1]) flag = (encrypted * inverse(e, n)) % n flag = long_to_bytes(flag).decode() print(flag)
irisctf{discrete_divide_isn't_hard}
babymixup (Cryptography)
暗号処理の概要は以下の通り。
・key: ランダム16バイト文字列 ・flagの長さは16の倍数 ・iv: ランダム16バイト文字列 ・ivを表示 ・以下の文字列をkey=iv, iv=keyでAES-CBC暗号化 b"Hello, this is a public message. This message contains no flags." ・iv: ランダム16バイト文字列 ・ivを表示 ・flagをkey=key, iv=ivでAES-CBC暗号化
最初の暗号化で鍵がわかっているので、それからXORでIVを求める。あとはその情報を使ってフラグを復号する。
#!/usr/bin/env python3 from Crypto.Cipher import AES from Crypto.Util.strxor import strxor with open('output.txt', 'r') as f: params = f.read().splitlines() IV1 = bytes.fromhex(params[0].split(' ')[-1]) CT1 = bytes.fromhex(params[1].split(' ')[-1]) IV2 = bytes.fromhex(params[2].split(' ')[-1]) CT2 = bytes.fromhex(params[3].split(' ')[-1]) PT1 = b"Hello, this is a public message. This message contains no flags." tmp_key = b'\x00' * 16 cipher = AES.new(IV1, AES.MODE_CBC, tmp_key) tmp_pt = cipher.decrypt(CT1) assert tmp_pt[16:] == PT1[16:] key = strxor(tmp_pt[:16], PT1[:16]) cipher = AES.new(key, AES.MODE_CBC, IV2) flag = cipher.decrypt(CT2).decode() print(flag)
irisctf{the_iv_aint_secret_either_way_using_cbc}
Nonces and Keys (Cryptography)
AES暗号OFBモードで暗号化されている。
OFBモードの場合以下のように暗号化される。 IV --(AES暗号化)--> TMP_CT0、TMPCT0 ^ PT0 = CT0 TMP_CT0 --(AES暗号化)--> TMP_CT1、TMPCT1 ^ PT1 = CT1 TMP_CT1 --(AES暗号化)--> TMP_CT2、TMPCT2 ^ PT2 = CT2 :
鍵が提示されているので、平文の先頭16バイト(PT1)と、暗号文(CT1, CT2, ...)がわかれば、TMP_CT1, TMP_CT2, ...も算出でき、復号できる。
#!/usr/bin/env python3 from Crypto.Cipher import AES from Crypto.Util.strxor import strxor key = (0x13371337133713371337133713371337).to_bytes(16, 'big') with open('challenge_enc.sqlite3', 'rb') as f: enc = f.read() pt0 = b'SQLite format 3\x00' tmp_ct = strxor(pt0, enc[:16]) dec = pt0 for i in range(16, len(enc), 16): cipher = AES.new(key, mode=AES.MODE_ECB) tmp_ct = cipher.encrypt(tmp_ct) c = strxor(tmp_ct, enc[i:i+16]) dec += c with open('password.sqlite3', 'wb') as f: f.write(dec)
復号したsqlite3ファイルをDB Browser for SQLiteで見てみる。usersテーブルのnameがMichaelのレコードのpasswordにフラグが設定されていた。
irisctf{g0tt4_l0v3_s7re4mciph3rs}
SMarT 1 (Cryptography)
暗号の処理概要は以下の通り。
・SBOX: 256個の既知固定数値配列 ・TRANSPOSE: 8個の既知固定数値配列の8個の配列 ・RR: 8個の既知固定数値配列 ・MASK: ランダム8バイト文字列 ・KEYLEN = 12 ・key: ランダム12バイト文字列 ・MASK, keyを出力 ・pairs = [] ・以下8回繰り返し ・pt: ランダム8バイト文字列 ・ptとencrypt(pt, key)の16進数表記の配列をpairsに追加 ・pairsを出力 ・ecb(FLAG, key).hex()を出力
鍵がわかっているので、逆算していけば、フラグを割り出せる。
#!/usr/bin/env python3 from Crypto.Util.strxor import strxor SBOX = [99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148,155, 30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22] TRANSPOSE = [ [3, 1, 4, 5, 6, 7, 0, 2], [1, 5, 7, 3, 0, 6, 2, 4], [2, 7, 5, 4, 0, 6, 1, 3], [2, 0, 1, 6, 4, 3, 5, 7], [6, 5, 0, 3, 2, 4, 1, 7], [2, 0, 6, 1, 5, 7, 4, 3], [1, 6, 2, 5, 0, 7, 4, 3], [4, 5, 6, 1, 2, 3, 7, 0] ] RR = [4, 2, 0, 6, 9, 3, 5, 7] ROUNDS = 2 def rev_rr(c, n): n = n % 8 return ((c >> (8 - n)) | (c << n)) & 0xff def decrypt(block, key): block = bytearray(block) for r in range(ROUNDS-1, -1, -1): block = strxor(block, MASK) temp = bytearray(8) for i in range(8): for j in range(8): temp[i] |= ((block[j] >> i) & 1) << TRANSPOSE[i][j] block = temp for i in range(8): block[i] = rev_rr(block[i], RR[i]) block[i] = SBOX.index(block[i]) block = strxor(block, key[r*4:(r+2)*4]) return block def rev_ecb(ct, key): out = b'' for i in range(0, len(ct), 8): out += decrypt(ct[i:i+8], key) return out.rstrip(b'\x00') with open('output1.txt', 'r') as f: params = f.read().splitlines() MASK = bytes.fromhex(params[0].split(': ')[1]) key = bytes.fromhex(params[1].split(': ')[1]) ct = bytes.fromhex(params[3].split(': ')[1]) flag = rev_ecb(ct, key).decode() print(flag)
irisctf{ok_at_least_it_works}