Description
Can you factor this small RSA modulus? Find p and q, then use them to decrypt the flag.
Setup
Download the challenge file with n, e, and c values.
wget <url>/certificateSolution
Walk me through it- Step 1Extract n, e, and c from the certificateOpen the certificate file. It contains the RSA public key (n and e) and the ciphertext c. The modulus n is small enough to factor by trial division or using factordb.com.bash
cat certificateLearn more
RSA security depends on the difficulty of factoring large numbers. For a 2048-bit RSA key, factoring is computationally infeasible. But for small moduli (a few digits to a few hundred digits), factoring is easy using trial division, Pollard's rho algorithm, or pre-computed databases.
factordb.com maintains a database of pre-factored numbers. For CTF challenges, the modulus is usually small enough to be in the database already.
- Step 2Factor n to get p and qFactor the modulus n to get the two prime factors p and q. Use Python, factordb, or an online factoring service.python
python3 << 'EOF' # Small n can be factored by trial division import math n = <PASTE_N_HERE> # Trial division for p in range(2, int(math.isqrt(n)) + 1): if n % p == 0: q = n // p print(f"p = {p}") print(f"q = {q}") break EOFLearn more
Why "John Pollard". The challenge name names John M. Pollard, who designed two of the classic factoring algorithms: Pollard's rho (1975) and Pollard's p-1 (1974). Trial division costs
O(sqrt(n)); Pollard's rho costs aboutO(n^(1/4)); both crush small CTF moduli in milliseconds.Pollard's rho intuition. Iterate
x_(i+1) = x_i^2 + 1 (mod n). By the birthday paradox, after aboutsqrt(p)steps two values collide modulo the smallest prime factorp, even though they have not collided modulon. Floyd's tortoise-and-hare detects this collision: at every step computeg = gcd(|x - y|, n)withymoving twice as fast. When1 < g < n,gis a non-trivial factor.def pollard_rho(n): x = y = 2 g = 1 while g == 1: x = (x*x + 1) % n y = (y*y + 1) % n y = (y*y + 1) % n # tortoise vs hare g = math.gcd(abs(x - y), n) return g if g != n else None # retry with new f if g == nWorked toy example. Factor
n = 8051:step x y gcd(|x-y|, 8051) 1 5 26 1 2 26 7474 1 3 677 871 1 4 7474 1244 83 <- factor! 8051 = 83 * 97 (both prime)Pollard's p-1. Different family of attack: works when
p - 1is "B-smooth" (only small prime factors). Computea = 2^(B!) mod nfor a chosen boundB; ifp - 1 | B!then Fermat's little theorem gives2^(B!) ≡ 1 (mod p), sogcd(a - 1, n) = p. This is why prime-generation libraries pick safe primes wherep = 2q + 1withqalso prime - that makesp - 1have a giant prime factor and defeats p-1.Database lookup. For competition speed, paste
nintofactordb.com. The database stores millions of pre-factored RSA challenge numbers, including most CTF moduli that have appeared online. If FactorDB returnsFF (Fully Factored), you havep, qinstantly. - Step 3Compute d and decryptWith p and q known, compute phi(n) = (p-1)(q-1), then d = modular inverse of e mod phi(n). Finally decrypt: m = pow(c, d, n).python
python3 << 'EOF' from sympy import mod_inverse n = <N> e = <E> c = <C> p = <P> q = <Q> phi = (p - 1) * (q - 1) d = mod_inverse(e, phi) m = pow(c, d, n) print(bytes.fromhex(hex(m)[2:]).decode()) EOFLearn more
Why this works. RSA encrypts as
c = m^e mod n, decrypts asm = c^d mod n. The private exponentdsatisfiese*d ≡ 1 (mod φ(n)), soe*d = 1 + k*φ(n)for some integerk. Then by Euler's theoremm^φ(n) ≡ 1 (mod n), givingc^d = m^(e*d) = m^(1 + k*φ(n)) = m * (m^φ(n))^k ≡ m (mod n).Worked toy example. Take
p = 11, q = 13, n = 143, φ = 120, e = 7. Computed = e^(-1) mod 120:Extended Euclidean on (7, 120): 120 = 17*7 + 1 => 1 = 120 - 17*7 => d = -17 mod 120 = 103 Verify: 7*103 = 721 = 6*120 + 1 ✓ Encrypt m = 42: c = 42^7 mod 143 = 230539333248856064 mod 143 = 95 Decrypt c = 95: m = 95^103 mod 143 = 42 ✓Square-and-multiply.
pow(c, d, n)never builds the literal integerc^d. It walks the bits ofdfrom MSB to LSB, maintaining an accumulator: square at every step, multiply bycwhen the current bit is 1, reduce modnafter every operation. That isO(log d)multiplications - milliseconds for 2048-bit RSA.Why
mod_inverseexists. The inverse ofemoduloφ(n)exists if and only ifgcd(e, φ(n)) = 1. RSA picksecoprime toφ(n)exactly so that this step succeeds. Common choices:e = 3, 17, 65537- all primes, all small enough for fast encryption, all rarely sharing a factor withφ(n)for randomly chosen primes.
Alternate Solution
Once you have factored n into p and q, use the RSA Calculator on this site - enter n, e, p, and q, and it automatically computes d and decrypts the ciphertext c without writing any Python.
Flag
picoCTF{...}
Factor the small RSA modulus to get p and q, compute d via modular inverse, and decrypt with pow(c, d, n).