Description
What if d is too small? Connect to the service and get the flag: nc mercury.picoctf.net 31133
Setup
Connect via netcat to receive e, n, and c.
nc mercury.picoctf.net 31133Solution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1Collect e, n, and c
ObservationOn connecting, the server prints the public exponent e, the modulus n, and the ciphertext c. Those are exactly the three inputs any RSA attack script needs, so capture them before the connection closes.Connect to the server. It prints the public exponent e, modulus n, and ciphertext c. Copy these values for use in the attack script.Learn more
In RSA encryption, the public key consists of two numbers: the modulus
n(a product of two large primes) and the public exponente. The ciphertextcis produced by computingc = m^e mod n, wheremis the plaintext message as an integer.These three values -
e,n, andc- are all that's needed to attempt an attack. The server gives them to you openly becauseeandnare part of the public key; onlyd(the private exponent) is supposed to be secret. The challenge is recoveringdwithout factoringn.Step 2Apply Wiener's Attack
ObservationThe description asks what happens if d is too small, and the title puns on Wiener, since a dachshund is a wiener dog. That is Wiener's attack, which recovers a small private exponent d from the continued fraction expansion of e/n.Wiener's attack recovers the private exponent d when d < n^0.25 / 3. The owiener Python library implements this attack. Install it with pip, then run the script to recover d and decrypt the ciphertext.bashpip install owienerpythonpython3 << 'EOF' import owiener from Crypto.Util.number import long_to_bytes e = <e> n = <n> c = <c> d = owiener.attack(e, n) if d is None: raise SystemExit("Wiener's attack failed - d may not be small enough.") # Decrypt and verify the flag prefix m = pow(c, d, n) flag = long_to_bytes(m) assert flag.startswith(b"picoCTF{"), f"unexpected plaintext: {flag!r}" print(flag.decode()) EOFExpected output
picoCTF{proving_wiener_...}What didn't work first
Tried: Try factoring n directly with sympy.factorint or an online factorization tool to recover p and q.
At 1024 bits or more, factoring the modulus is infeasible: sympy.factorint hangs indefinitely and factordb returns nothing. Wiener's attack skips factorization altogether, exploiting the continued fraction structure of e/n to recover d without ever finding p or q.
Tried: Run owiener.attack(e, n) and assume None means the wrong e and n values were used, so reconnect and try a different session.
owiener returns None when d is not under the n^0.25 / 3 bound, not because your inputs are malformed. Reconnecting draws a fresh keypair, but this server always uses a vulnerable key, so None much more likely means e or n was miscopied, with digits dropped or swapped. Check both were pasted in full before retrying.
Learn more
Wiener's attack (1990) exploits a mathematical weakness in RSA when the private exponent
dis too small relative to the modulus - specifically whend < n^0.25 / 3. In that case, a property of continued fraction approximations allows an attacker to recoverdefficiently from the public valueseandnalone.The attack works because RSA's key relationship
e*d ≡ 1 (mod φ(n))means the fractione/nis very close tok/dfor some small integerk. Whendis small enough, the convergents of the continued fraction expansion ofe/nincludek/das one of its early terms - making it directly recoverable.Verifying the recovered d. The script asserts the decrypted plaintext starts with
picoCTF{. If owiener returns a non-None d but the assertion fails, you've recovered a continued-fraction convergent that's mathematically valid for the equation but isn't the real private exponent - retry with a tweaked search bound, or fall back to factoring n if it's small.Real-world significance: Developers sometimes choose small values of
dto speed up RSA decryption (since modular exponentiation with a smaller exponent is faster). Wiener's attack demonstrates why this is catastrophic. Modern RSA implementations always use random, full-size private exponents. Thelong_to_bytesfunction frompycryptodomeconverts the recovered plaintext integer back into a readable string. For more on RSA attack patterns, see RSA attacks for CTF.
Interactive tools
- RSA CalculatorDecrypt RSA ciphertexts, factor n from the sum of primes, or generate key parameters. Handles arbitrarily large BigInt values.
Alternate Solution
Once Wiener's attack recovers d, paste p, q, e, and the ciphertext into the RSA Calculator on this site to perform the final decryption step in the browser without needing Python.
Flag
Reveal flag
picoCTF{proving_wiener_...}
Wiener's attack exploits RSA when d < n^0.25 / 3 - always use d larger than the fourth root of n.