# 神盾盃 AEGIS 2025 Writeup ## Result ![image](https://hackmd.io/_uploads/SklQEwvBhgl.png) ![image](https://hackmd.io/_uploads/BkySvwB3lg.png) with Bun, GraspingStew631, ash/, lywslc, ffting, SD 我在賽中解了 random multi 和 M-rsa 這兩題 ## Crypto ### random multi flag 的值會乘上 7691640804 次隨機選 pool 裡面的值 mod p 之後輸出 觀察發現 pool 裡面的值都是 11 的次方且只有 11^0 ~ 11^9,然後因為相乘就是次方相加,所以可以用期望值來算出乘 7691640804 次大概會是乘以 11 的幾次方 然後算出是多少次方之後再往兩側延伸一個 window = 200000,畢竟有隨機性不會一定和期望值相同 然後就是取 inverse 那些的了,逆回去算出 flag (不用每次重算 inverse 的酷酷算法是 ChatGPT 教我的) 要注意的大概就是 window 不能太小,一開始開太小以為寫錯 ```python= import math from Crypto.Util.number import * from sympy import prime from tqdm import trange import random # Given values p = 10270690747750681275848519156345460292282442776941950362299330027993038338823977542405725333740052688659124663537769686180834450426957439995949108234020121 c = 6211227177504225821131033803784639291621321097309823976872498717139250997486591939973272963659554090619408357034740836341244321284116105064793564676501364 n = 7691640804 pool = [11, 11, 2357947691, 19487171, 1, 1, 1, 1, 2357947691, 14641, 14641, 14641, 11, 14641, 214358881, 214358881, 14641, 1, 1771561, 14641, 214358881, 19487171, 11, 214358881, 14641, 121, 11, 14641, 161051, 161051, 1331, 161051, 11, 14641, 19487171, 214358881, 19487171, 121, 19487171, 1, 1331, 161051, 19487171, 19487171, 161051, 161051, 2357947691, 2357947691, 11, 1331, 14641, 11, 11, 19487171, 161051] # 1. Calculate the total exponent of 11 # The pool contains powers of 11. Let's map each number to its exponent. exponents = [round(math.log(x, 11)) for x in pool] # print(exponents) # Calculate the average exponent from the pool total_sum_of_exponents = sum(exponents) total_pool_size = len(exponents) average_exponent = total_sum_of_exponents / total_pool_size # The total exponent E is the number of iterations (n) times the average exponent # Using integer rounding is a good guess for a discrete distribution # average_exponent = 185/41 E = int(round(n * average_exponent)) print(f"Total exponent E: {E}") window = 2000000 base = 11 % p a = pow(base, E - window, p) # 先算起點 inv_base = inverse(base, p) # 11 的逆元 inv_a = inverse(a, p) # a 的逆元 for i in range(2*window+1): decrypted_long = (c * inv_a) % p decrypted_flag = long_to_bytes(decrypted_long) if decrypted_flag.startswith(b"AEGIS"): print(f"Decrypted Flag: {decrypted_flag}") a = (a * base) % p # 11^(e+1) inv_a = (inv_a * inv_base) % p # 對應的逆元 ``` ![image](https://hackmd.io/_uploads/rJ9TpvH2lg.png) ### M-rsa 總之是矩陣版的 Hastad's Broadcast Attack,可以取得一次 a 和很多次 (n, c) ~~以下是把解法跟 ChatGPT 講請它幫我寫的 code~~ ```python= from pwn import remote from Crypto.Util.number import long_to_bytes from sympy import Symbol, Matrix, Poly, nroots import ast from math import prod from sympy import Integer import gmpy2 HOST = "0.cloud.chals.io" PORT = 15520 ROUNDS = 40 # 可以調整,確保 product(n) 足夠大覆蓋矩陣條目大小 E = 19 def chinese_remainder(pairs): N = prod([n for n, _ in pairs]) total = 0 for n, c in pairs: m = N // n inv = int(gmpy2.invert(m, n)) total += int(c) * inv * m return total % N, N def crt_matrix_entries(pairs_list): # pairs_list: list of length 9, 每個元素是 [(n1,c11),(n2,c12),...] B = [[0]*3 for _ in range(3)] for idx, pairs in enumerate(pairs_list): val, N = chinese_remainder(pairs) i = idx // 3 j = idx % 3 B[i][j] = Integer(val) return Matrix(B) def parse_initial_a(conn): # 伺服器在開始會有一行像 [p0, p1, ...] 被 print # 我們讀到那行並解析 line = conn.recvline(timeout=5).decode().strip() # line 可能包含其他文字,嘗試從 line 找到 first '['...']' if '[' in line and ']' in line: arr_s = line[line.find('['):line.rfind(']')+1] a_list = ast.literal_eval(arr_s) return a_list else: # 若沒在單行讀到,嘗試再多讀幾行拼湊 accum = line for _ in range(5): more = conn.recvline(timeout=1).decode() accum += more if '[' in accum and ']' in accum: arr_s = accum[accum.find('['):accum.rfind(']')+1] return ast.literal_eval(arr_s) raise RuntimeError("Failed to parse initial a list from server output") def main(): conn = remote(HOST, PORT) # **先讀並解析伺服器印出的 a 列表(很重要)** a_list = parse_initial_a(conn) print("[+] parsed a_list:", a_list) # 為矩陣 9 個位置準備 CRT pairs storage pairs_list = [[] for _ in range(9)] for r in range(ROUNDS): conn.recvuntil(b"(y):") conn.sendline(b"y") n_line = conn.recvline().strip() c_line = conn.recvline().strip() n = int(n_line.decode()) # c_line 形如 Python 的 matrix literal (伺服器 print 出來) c_mat = ast.literal_eval(c_line.decode()) # 把 3x3 的每個 entry 加到對應的 pairs list for i in range(3): for j in range(3): pairs_list[i*3 + j].append((n, int(c_mat[i][j]))) print(f"[{r+1}/{ROUNDS}] got n (digits={len(str(n))}) and c matrix") print("[+] Running CRT for each entry...") B = crt_matrix_entries(pairs_list) print("[+] Reconstructed integer matrix B = A^e (entries may be huge).") # 構造符號矩陣 A_sym:把 a_list 放入已知位置,中心用符號 m m = Symbol('m') A_sym = Matrix([ [Integer(a_list[0]), Integer(a_list[1]), Integer(a_list[2])], [Integer(a_list[3]), m, Integer(a_list[4])], [Integer(a_list[5]), Integer(a_list[6]), Integer(a_list[7])] ]) print("[+] Building A_sym**E ... this may take some time but is doable for 3x3") A_pow = A_sym**E # Sympy matrix power -> entries are polynomials in m # 選擇某個 entry(通常 [1,1])建立多項式方程: Poly(A_pow[1,1] - B[1,1], m) expr = A_pow[1,1] - B[1,1] poly = Poly(expr, m) print("[+] Polynomial degree:", poly.degree()) # 用數值根尋找可能整根(這一步找複數根並嘗試取整數近似) from sympy import real_roots roots = real_roots(poly) for r in roots: if r.is_integer: m_val = int(r) if poly.eval(m_val) == 0: print("Found integer root:", m_val) flag = long_to_bytes(m_val) print(flag) if __name__ == "__main__": main() ``` ![image](https://hackmd.io/_uploads/SyCipDHnex.png) ## Misc ### cipherRestaurant :::info 賽後解出 ::: 有一個 exe,跑起來會隨機丟出一張 [A-Z].jpg 寫個腳本跑很多次把 A-Z 收集完 (binwalk 也可以拿到但檔名會亂掉就沒有用了;因為是 python 打包成的執行檔所以如果用 [PyInstaller Extractor](https://github.com/extremecoders-re/pyinstxtractor) 復原的話也可以,但沒必要那麼麻煩 ```powershell= # script.ps1 $executionCount = 100 for ($i = 1; $i -le $executionCount; $i++) { Write-Host "$i" .\\cipherRestaurant.exe } ``` ![image](https://hackmd.io/_uploads/H14_yOHnxg.png) 可以看到大部分都是食材,V~Y 是人 丟到 https://futureboy.us/stegano/decinput.html 這個網站可以看到 V, W, X 裡面有隱寫提示內容 ```! V: The chef loves prime numbers, and he will use them as ingredient codes. W: I want to eat the food that the chef made, but he gave me gold coins and asked me to buy the ingredients on the list. The list only says "PRIME". What are these ingredients? X: I am Legendary Utensil, and I can transform ingredients into dishes. ``` 然後有人通靈出 X.jpg 這張指的是 [OpenPuff](https://embeddedsw.net/OpenPuff_Steganography_Home.html) 這個隱寫工具 ![X](https://hackmd.io/_uploads/r1bQ2vr2ex.jpg) 但要 unhide 東西需要 password 以及選到正確的 carrier(正確的圖片) ![image](https://hackmd.io/_uploads/BJzYhvBnlx.png) 然後也有隊友 strings 出來其他圖片裡面都有寫一個質數 ``` A,2 B,3 C,5 D,7 E,11 F,17 G,17 H,19 I,23 J,29 K,31 L,37 M,41 N,43 O,47 P,53 Q,59 R,61 S,67 T,71 U,73 V,(empty) W,(empty) X,(empty) Y,96 Z,101 ``` 然後就是通靈時間,要通靈出 password 和 carrier 是誰 :::info 接下來就是賽後的部分 ::: 總之最後密碼是 PRIME 5 個英文單字對應的質數相乘(53\*61\*23\*41\*11 = 33535909),然後 carrier 是 Y.jpg 那張廚師的照片 ![Y](https://hackmd.io/_uploads/Hk_lTwH3eg.jpg) 然後這樣就成功解出來了。 ![image](https://hackmd.io/_uploads/HyrO6wShxe.png) ![image](https://hackmd.io/_uploads/rJY56DH2lg.png) (賽中有試到把質數乘起來,但 carrier 只試了 V, W, X, A,隊友有試 Z,可惡!!!)