319 words
2 minutes
TCTT25 Crypto: AdvancedFactorWar
Open‑AdvancedFactorWar [300pts] - Cryptography Write-up
โจทย์
Story
คุณได้คีย์สาธารณะประหลาดที่ดูเหมือน RSA แต่ Modulus ถูกสร้างจาก หลาย prime (multi‑prime)
แถมยังมีร่องรอยว่ามีการลงลายเซ็นด้วย CRT ที่เคยผิดพลาด และมี public key แปลก ๆ อีกสองตัว
เป้าหมายสุดท้ายคือถอดรหัส ciphertext เพื่อกู้ FLAG
ดาวน์โหลดไฟล์
| ไฟล์ | ดาวน์โหลด |
|---|---|
| All Challenge Files | 📦 GitHub Folder |
| solved.py | 📥 Download |
ไฟล์โจทย์
| Stage | Files | คำอธิบาย |
|---|---|---|
| Stage 1 | stage1_frag_*.txt | ชุดไฟล์สับเปลี่ยนที่เข้ารหัสค่าของ N ด้วยสัญลักษณ์ (ฐาน 8) |
| Stage 2 | stage2_pubA.pem/B.pem/C.pem + stage2_cA.bin/cB.bin/cC.bin | Hastad e=3, No‑padding |
| Stage 3 | stage3_pub_aux1.pem, stage3_pub_aux2.pem + stage3_c_aux1.bin/c_aux2.bin | Modulus สองชุดที่แชร์ prime |
| Stage 4 | stage4_message.bin, stage4_sign_ok.bin, stage4_sign_faulty.bin | ลายเซ็นถูก/ผิด CRT fault |
| Final | public_main.pem, final_cipher.bin | เมื่อได้ N และรู้ e แล้ว จงถอดรหัสให้ได้ FLAG |
แนวคิดการแก้โจทย์
Stage 2 จะถอด “ข้อความบอกใบ้” (Hastad e=3, No‑padding)
- อ่าน
stage2_pubA.pem/B.pem/C.pemกับstage2_cA.bin/cB.bin/cC.bin - รวมด้วย CRT คำนวณค่าหนึ่งเดียว
Cที่ทำให้C ≡ cA (mod nA), C ≡ cB (mod nB), C ≡ cC (mod nC) - ตรวจว่า
Cเป็น perfect cube หรือหาค่าm = ⌊∛C⌉แล้วเช็คว่าm^3 == C
Stage 1 ใช้ mapping + ลำดับชิ้นส่วนที่ได้
- ใช้ ลำดับไฟล์ และ mapping ที่ได้จาก Stage 2
- แทนสัญลักษณ์ด้วยดิจิตฐาน 8 แล้วต่อกันเป็นสตริงฐาน 8 เดียว
- แปลงฐาน 8 → จำนวนเต็ม ได้ N
Stage 3 คำนวณ gcd(N, N_aux1) และ gcd(N, N_aux2)
p1 = gcd(N_main, N_aux1)p2 = gcd(N_main, N_aux2)
Stage 4 ใช้สมบัติ fault‑attack
G = gcd(N_main, s_ok − s_faulty)
Final → ถอดรหัส
- เมื่อได้ prime เพียงพอ ให้พยายาม factor N_main
- หา
φ(N)และd = E^{-1} mod φ(N) - ถอดรหัสเป็น
mและแปลงเป็นไบต์ → FLAG
solved.py
import subprocess, mathfrom sympy import mod_inverse
out = subprocess.check_output("openssl rsa -in public_main.pem -pubin -modulus -noout", shell=True, text=True)N_main = int(out.strip().split('=')[1],16)
aux1 = int(subprocess.check_output("openssl rsa -in stage3_pub_aux1.pem -pubin -modulus -noout", shell=True, text=True).split('=')[1],16)aux2 = int(subprocess.check_output("openssl rsa -in stage3_pub_aux2.pem -pubin -modulus -noout", shell=True, text=True).split('=')[1],16)
p1 = math.gcd(N_main, aux1)p2 = math.gcd(N_main, aux2)
s_ok=int.from_bytes(open('stage4_sign_ok.bin','rb').read(),'big')s_faulty=int.from_bytes(open('stage4_sign_faulty.bin','rb').read(),'big')G = math.gcd(N_main, s_ok - s_faulty)
r = G//(p1*p2)p4 = N_main // (p1*p2*r)
c = int.from_bytes(open('final_cipher.bin','rb').read(),'big')E = 65537primes = [p1,p2,p4]
d_vals=[]for p in primes: d = mod_inverse(E, p-1) d_vals.append(d)
a=[]for (p,d) in zip(primes,d_vals): a.append(pow(c, d, p))
from math import prod
def crt(values, moduli): M=1 x=0 for n_i, a_i in zip(moduli, values): M *= n_i x=0 m=1 for n_i,a_i in zip(moduli, values): k = ( (a_i - x) * mod_inverse(m, n_i) ) % n_i x += m*k m *= n_i return x
m_partial = crt(a, primes)msg_bytes = m_partial.to_bytes((m_partial.bit_length()+7)//8,'big')
try: print(msg_bytes.decode())except Exception as e: print('decode error',e)ผลลัพธ์
เมื่อรัน Python Script พร้อมไฟล์โจทย์ จะได้ Flag:
flag{multi_pr1me_cr4ck_by_RSA_CRT_47e1cc2ddc}Credits
Writeup by netw0rk7 | Original Repo
TCTT25 Crypto: AdvancedFactorWar
https://blog.lukkid.dev/posts/tctt25-crypto-advancedfactorwar/