Challenge Description
Oops! I have a typo in my first message so I sent it again! I used RSA twice so this is secure right?
Approach
Understanding the Vulnerability
This challenge is a textbook Franklin-Reiter Related Message Attack on RSA. The scenario:
- A sender encrypts a message
m1with RSA public key(n, e)to get ciphertextc1. - The sender realizes there is a typo, so they “fix” it and send a corrected message
m2, encrypted with the same public key to getc2. - The two messages are related by a known linear function:
m2 = a * m1 + b(e.g., a single character difference amounts tom2 = m1 + bfor some knownb).
The sender assumes encrypting twice with RSA is safe, but when two plaintexts have a known polynomial relationship and share the same modulus and exponent, the Franklin-Reiter attack can recover both messages.
Mathematical Foundation
Given:
- Public key
(n, e)(typicallye = 65537, but the attack is most efficient with smallelikee = 3) c1 = m1^e mod nc2 = m2^e mod n- Known relationship:
m2 = f(m1)wheref(x) = a*x + b
We construct two polynomials in the ring Z_n[x]:
g1(x) = x^e - c1(has rootm1)g2(x) = f(x)^e - c2 = (a*x + b)^e - c2(also has rootm1)
Since m1 is a common root, (x - m1) divides both polynomials. Computing gcd(g1, g2) in the polynomial ring modulo n yields a linear polynomial x - m1, from which we recover m1.
Why This Works
- For small
e(especiallye = 3), the polynomial GCD computation is efficient and direct. - For larger
e, the attack still works in principle but may require more sophisticated polynomial GCD algorithms. - The attack requires no factoring of
n— it operates entirely in the polynomial ringZ_n[x].
Challenge Files (Typical)
The challenge typically provides:
output.txtcontainingn,e,c1,c2, and the relationship parametersaandb(or these can be inferred from the “typo” description)- Sometimes a Python script showing how the encryption was performed
Solution
Step 1: Read the Challenge Data
Parse the provided values: n, e, c1, c2, and determine the relationship f(x) = a*x + b.
In the “typo” scenario, the relationship is often:
m2 = m1 + bwherebis the difference caused by the typo (e.g., a small integer offset), meaninga = 1.
Step 2: Construct the Polynomials
In the polynomial ring Z_n[x]:
g1(x) = x^e - c1
g2(x) = (a*x + b)^e - c2
Step 3: Compute the GCD
Using SageMath or Python (sympy), compute:
result = gcd(g1, g2)
The result should be a linear polynomial x - m1 (or a scalar multiple thereof).
Step 4: Extract the Message
From the GCD result, extract m1 = -result.coefficients[-1] (the negation of the constant term of the monic GCD). Then convert the integer back to bytes to reveal the flag.
Step 5: Recover the Flag
from Crypto.Util.number import long_to_bytes
flag = long_to_bytes(m1)
Solution Script
python3 solve.py
Flag
picoCTF{...} (placeholder - actual flag varies per instance)