Challenge Overview

  • Category: Cryptography
  • Objective: Decrypt the DNA of the “Vexillum Rex” dinosaur

Source Analysis

The Python server used RSA-like encryption:

def get_encrypted_dna(self):
    transmission_key = getPrime(primesize)          # p — fresh every request
    dinosaur_modulation_index = transmission_key * self.vault_key  # N = p * q
    evergreen_number = 2**16 + 1                    # e = 65537
    resampled_dna = pow(bytes_to_long(self.dna.encode()), evergreen_number, dinosaur_modulation_index)

Key observation: vault_key (q) is generated once and reused. Every request gets a different p, but the same q.

The Vulnerability: Common Factor Attack

Two different moduli from the same dinosaur:

  • N₁ = p₁ × q
  • N₂ = p₂ × q

Since they share q:

gcd(N₁, N₂) = q

Once q is recovered, computing p₁ = N₁ / q gives us full factorization. Then we reconstruct the private key.

Exploitation

Step 1: Collect two ciphertexts

Attempt 1: C₁, N₁
Attempt 2: C₂, N₂

Step 2: Factor

from math import gcd
q = gcd(N1, N2)
p1 = N1 // q

Step 3: Reconstruct private key

phi = (p1 - 1) * (q - 1)
d = pow(e, -1, phi)          # modular inverse
M = pow(C1, d, N1)           # decrypt

Step 4: Decode DNA

The challenge used a custom to_dna mapping (bits → A/T/G/C). Reversed the mapping to recover ASCII.

Flag: SHC{...} — found in: “Has a crown and SHC{…} written on its back.”

Conclusion

This challenge demonstrates why both primes in RSA must be unique across all sessions. Reusing even one prime allows an attacker to break encryption with simple GCD — a calculation taking microseconds.