この大会は2022/10/14 23:00(JST)~2022/10/15 23:00(JST)に開催されました。
今回もチームで参戦。結果は144点で532チーム中135位でした。
自分で解けた問題をWriteupとして書いておきます。
Binned (Cryptography, Warmup)
暗号処理の概要は以下の通り。
・p, q: 512ビット素数 ・n = p * q ・enc = pow(n + 1, m, n**3) ・n, encを出力
二項定理を使う。さらにn**3以上はn**3で割ると0になることから以下の式が成り立つ。
enc = pow(n + 1, m, n**3) = mC2 * n**2 + mC1 * n + mC0 = m * (m - 1) // 2 * n**2 + m * n + 1 enc * 2 = m * (m - 1) * n ** 2 + m * n * 2 + 2
mの2次方程式になるので、これを解けば、フラグがわかる。
#!/usr/bin/env python3 from Crypto.Util.number import * import sympy with open('output.txt', 'r') as f: params = f.read().splitlines() n = int(params[0].split(' ')[-1]) enc = int(params[1].split(' ')[-1]) m = sympy.Symbol('m') eq = m * (m - 1) * n ** 2 + m * n * 2 + 2 - enc * 2 ans = sympy.solve(eq) for m in ans: if m > 0: break flag = long_to_bytes(m).decode() print(flag)
ASIS{8!N0miaL_3XpAn5iOn_Us4G3_1N_cRyp7o_9rApHy!}
Chaffymasking (Cryptography)
サーバの処理概要は以下の通り。
・m, n = 512, 64 ・IVK: 512個の固定整数の配列 ・LTC: n行m列の0の配列 ・LTCの0行目にIVKをセット ・LTCの1行目以降、以下のようにセットする。 ・LTC[i, 0:64] = IVK[0:64]を右にiシフト ・LTC[i, 64:128] = IVK[64:128]を右にiシフト : ・以下、5回繰り返し ・SALT: 入力 ・SALT: 128バイトになるようパディング ・MASKED_FLAG = chaffy_mask(SALT, LTC, m, n) ・q = n ** 2 ・half1_salt: SALTの前半(64バイト) ・half2_salt: SALTの後半(64バイト) ・xor_salts: half1_saltとhalf2_saltのXOR ・xor_saltsが0の場合、half1_saltとランダム文字列とのXORをセット ・half1_binStr: half1_saltの2進数表記(512バイト) ・half2_binStr: half2_saltの2進数表記(512バイト) ・vec_1: half1_binStrのリストを512行1列に整形 ・vec_2: half2_binStrのリストを512行1列に整形 ・out_1 = LTC * vec_1 % q ・out_2 = LTC * vec_2 % q ・flag_vector: フラグの各文字のASCIIコードの配列を64行1列に整形 ・masked_flag = (flag_vector ^ out_1 ^ out_2) % 256 ・masked_flag: 1行n列に整形 ・masked_flagの各要素を16進数表記にして、結合したものを返却 ・MASKED_FLAGを表示
SALTを128バイト適当に指定すれば、out_1とout_2は算出できる。あとはXORでflag_vectorを算出でき、フラグを得ることができる。
#!/usr/bin/env python3 import socket import numpy as np def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) m, n = 512, 64 IVK = [ 3826, 476, 3667, 2233, 1239, 1166, 2119, 2559, 2376, 1208, 2165, 2897, 830, 529, 346, 150, 2188, 4025, 3667, 1829, 3987, 952, 3860, 2574, 959, 1394, 1481, 2822, 3794, 2950, 1190, 777, 604, 82, 49, 710, 1765, 3752, 2970, 952, 803, 873, 2647, 2643, 1096, 1202, 2236, 1492, 3372, 2106, 1868, 535, 161, 3143, 3370, 1, 1643, 2147, 2368, 3961, 1339, 552, 2641, 3222, 2505, 3449, 1540, 2024, 618, 1904, 314, 1306, 3173, 4040, 1488, 1339, 2545, 2167, 394, 46, 3169, 897, 4085, 4067, 3461, 3444, 118, 3185, 2267, 3239, 3612, 2775, 580, 3579, 3623, 1721, 189, 650, 2755, 1434, 35, 3167, 323, 589, 3410, 652, 2746, 2787, 3665, 828, 3200, 1450, 3147, 720, 3741, 1055, 505, 2929, 1423, 3629, 3, 1269, 4066, 125, 2432, 3306, 4015, 2350, 2154, 2623, 1304, 493, 763, 1765, 2608, 695, 30, 2462, 294, 3656, 3231, 3647, 3776, 3457, 2285, 2992, 3997, 603, 2342, 2283, 3029, 3299, 1690, 3281, 3568, 1927, 2909, 1797, 1675, 3245, 2604, 1272, 1146, 3301, 13, 3712, 2691, 1097, 1396, 3694, 3866, 2066, 1946, 3476, 1182, 3409, 3510, 2920, 2743, 1126, 2154, 3447, 1442, 2021, 1748, 1075, 1439, 3932, 3438, 781, 1478, 1708, 461, 50, 1881, 1353, 2959, 1225, 1923, 1414, 4046, 3416, 2845, 1498, 4036, 3899, 3878, 766, 3975, 1355, 2602, 3588, 3508, 3660, 3237, 3018, 1619, 2797, 1823, 1185, 3225, 1270, 87, 979, 124, 1239, 1763, 2672, 3951, 984, 869, 3897, 327, 912, 1826, 3354, 1485, 2942, 746, 833, 3968, 1437, 3590, 2151, 1523, 98, 164, 3119, 1161, 3804, 1850, 3027, 1715, 3847, 2407, 2549, 467, 2029, 2808, 1782, 1134, 1953, 47, 1406, 3828, 1277, 2864, 2392, 3458, 2877, 1851, 1033, 798, 2187, 54, 2800, 890, 3759, 4085, 3801, 3128, 3788, 2926, 1983, 55, 2173, 2579, 904, 1019, 2108, 3054, 284, 2428, 2371, 2045, 907, 1379, 2367, 351, 3678, 1087, 2821, 152, 1783, 1993, 3183, 1317, 2726, 2609, 1255, 144, 2415, 2498, 721, 668, 355, 94, 1997, 2609, 1945, 3011, 2405, 713, 2811, 4076, 2367, 3218, 1353, 3957, 2056, 881, 3420, 1994, 1329, 892, 1577, 688, 134, 371, 774, 3855, 1461, 1536, 1824, 1164, 1675, 46, 1267, 3652, 67, 3816, 3169, 2116, 3930, 2979, 3166, 3944, 2252, 2988, 34, 873, 1643, 1159, 2822, 1235, 2604, 888, 2036, 3053, 971, 1585, 2439, 2599, 1447, 1773, 984, 261, 3233, 2861, 618, 465, 3016, 3081, 1230, 1027, 3177, 459, 3041, 513, 1505, 3410, 3167, 177, 958, 2118, 326, 31, 2663, 2026, 2549, 3026, 2364, 1540, 3236, 2644, 4050, 735, 280, 798, 169, 3808, 2384, 3497, 1759, 2415, 3444, 1562, 3472, 1151, 1984, 2454, 3167, 1538, 941, 1561, 3071, 845, 2824, 58, 1467, 3807, 2191, 1858, 106, 3847, 1326, 3868, 2787, 1624, 795, 3214, 1932, 3496, 457, 2595, 3043, 772, 2436, 2160, 3428, 2005, 2597, 1932, 101, 3528, 1698, 3663, 900, 3298, 1872, 1179, 3987, 3695, 3561, 1762, 3785, 3005, 2574, 6, 1524, 2738, 1753, 2350, 558, 800, 3782, 722, 886, 2176, 3050, 221, 1925, 564, 1271, 2535, 3113, 1310, 2098, 3011, 964, 3281, 6, 1326, 741, 189, 2632, 373, 1176, 548, 64, 1445, 2376, 1524, 2690, 1316, 2304, 1336, 2257, 3227, 2542, 3911, 3460 ] LTC = np.zeros([n, m], dtype=(int)) LTC[0,:] = IVK for i in range(1, n): for j in range(m // n + 1): LTC[i, j * n:(j + 1) * n] = np.roll(IVK[j * n:(j + 1) * n], i) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('65.21.255.31', 31377)) for _ in range(6): data = recvuntil(s, b'\n').rstrip() print(data) salt = 'A' * 64 + 'B' * 64 data = recvuntil(s, b'\n').rstrip() print(data) print(salt) s.sendall(salt.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) masked_flag = eval(data.split(' ')[-1]) q = n ** 2 half1_salt = salt.encode()[:m // 8] half2_salt = salt.encode()[m // 8:] half1_binStr = "{:08b}".format(int(half1_salt.hex(),16)) if(len(half1_binStr) < m): half1_binStr = "0" * (m - len(half1_binStr)%m) + half1_binStr half2_binStr = "{:08b}".format(int(half2_salt.hex(),16)) if(len(half2_binStr) < m): half2_binStr = "0" * (m - len(half2_binStr)%m) + half2_binStr vec_1 = np.array(list(half1_binStr), dtype=int) vec_1 = np.reshape(vec_1, (m, 1)) vec_2 = np.array(list(half2_binStr), dtype=int) vec_2 = np.reshape(vec_2, (m, 1)) out_1 = LTC.dot(vec_1) % q out_2 = LTC.dot(vec_2) % q masked_flag = [int(masked_flag[i:i+2], 16) for i in range(0, len(masked_flag), 2)] masked_flag = np.reshape(np.array(masked_flag), (n, 1)) flag_vector = (masked_flag ^ out_1 ^ out_2) % 256 flag = ''.join([chr(i[0]) for i in flag_vector]) print(flag)
実行結果は以下の通り。
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| | Welcome to chaffymask combat, we implemented a masking method to | | hide our secret. Masking is done by your 1024 bit input salt. Also | | I noticed that there is a flaw in my method. Can you abuse it and | | get the flag? In each step you should send salt and get the mask. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| | Give me your salt: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB | masked_flag = b'dae0e7842d70958fefdacdb2095e9588fed7f1bf374f9ca4f8dcc2bb3f4f9d94f5ecc7a3094b9588c4c0c1b839539b94f4dcc1b839539b94f4eccbb62565d586' ASIS{Lattice_based_hash_collision_it_was_sooooooooooooooo_easY!}
ASIS{Lattice_based_hash_collision_it_was_sooooooooooooooo_easY!}