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

ไฟล์โจทย์#

StageFilesคำอธิบาย
Stage 1stage1_frag_*.txtชุดไฟล์สับเปลี่ยนที่เข้ารหัสค่าของ N ด้วยสัญลักษณ์ (ฐาน 8)
Stage 2stage2_pubA.pem/B.pem/C.pem + stage2_cA.bin/cB.bin/cC.binHastad e=3, No‑padding
Stage 3stage3_pub_aux1.pem, stage3_pub_aux2.pem + stage3_c_aux1.bin/c_aux2.binModulus สองชุดที่แชร์ prime
Stage 4stage4_message.bin, stage4_sign_ok.bin, stage4_sign_faulty.binลายเซ็นถูก/ผิด CRT fault
Finalpublic_main.pem, final_cipher.binเมื่อได้ N และรู้ e แล้ว จงถอดรหัสให้ได้ FLAG

แนวคิดการแก้โจทย์#

Stage 2 จะถอด “ข้อความบอกใบ้” (Hastad e=3, No‑padding)#

  1. อ่าน stage2_pubA.pem/B.pem/C.pem กับ stage2_cA.bin/cB.bin/cC.bin
  2. รวมด้วย CRT คำนวณค่าหนึ่งเดียว C ที่ทำให้ C ≡ cA (mod nA), C ≡ cB (mod nB), C ≡ cC (mod nC)
  3. ตรวจว่า C เป็น perfect cube หรือหาค่า m = ⌊∛C⌉ แล้วเช็คว่า m^3 == C

Stage 1 ใช้ mapping + ลำดับชิ้นส่วนที่ได้#

  1. ใช้ ลำดับไฟล์ และ mapping ที่ได้จาก Stage 2
  2. แทนสัญลักษณ์ด้วยดิจิตฐาน 8 แล้วต่อกันเป็นสตริงฐาน 8 เดียว
  3. แปลงฐาน 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 → ถอดรหัส#

  1. เมื่อได้ prime เพียงพอ ให้พยายาม factor N_main
  2. หา φ(N) และ d = E^{-1} mod φ(N)
  3. ถอดรหัสเป็น m และแปลงเป็นไบต์ → FLAG

solved.py#

import subprocess, math
from 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 = 65537
primes = [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:

Final Result

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/
Author
LUKKID
Published at
2025-12-13
License
CC BY-NC-SA 4.0