Category: Crypto
Points: 554
Flag: MCTF25{r4d1c4l_s3cur1ty}
We are given:
10.240.2.11:1337crypto.py that implements RSA with a small key sizeFrom crypto.py (simplified):
from Cryptodome.Util.number import getPrime, inverse, bytes_to_long, long_to_bytes
KEY_BITS = 256
E = 65537
p = getPrime(KEY_BITS // 2)
q = getPrime(KEY_BITS // 2)
n = p * q
phi = (p - 1) * (q - 1)
d = inverse(E, phi)
So the server is using RSA with:
(n, e)dKEY_BITS = 256, which means p and q are 128-bit primes and n is only 256 bitsA 256-bit RSA modulus is way too small and can be factored easily. That’s the core vulnerability.
We connect to the service:
nc 10.240.2.11 1337
The service responds with:
Connection established to Secure Crypto Oracle [SCO v1.0]
Session ready.
Options:
1. Encrypt message
2. Get Secret Message
3. Get Public Key
We need:
(n, e) from option 3First, get the public key:
Enter your choice (1-3): 3
Public Key (n, e): (74453105459613851322187047665994238471320654596065423955454287400990254712301, 65537)
Then, get the secret message:
Enter your choice (1-3): 2
57404030761199998498847049987163372359776031729451339753283863067115090061488
So we have:
n = 74453105459613851322187047665994238471320654596065423955454287400990254712301
e = 65537
C = 57404030761199998498847049987163372359776031729451339753283863067115090061488
The server is doing standard RSA encryption:
C = m^e mod n
where m is the secret message (the flag) converted to an integer.
From crypto.py:
KEY_BITS = 256
p = getPrime(KEY_BITS // 2)
q = getPrime(KEY_BITS // 2)
n = p * q
This means:
p is a 128-bit primeq is a 128-bit primen is a 256-bit modulusReal-world RSA typically uses at least 2048-bit moduli. A 256-bit modulus is trivial to factor with modern tools.
So the attack strategy is:
n into its prime factors p and qphi = (p - 1) * (q - 1)d = e^(-1) mod phiC using d to recover mm back to bytes to reveal the flag stringnWe paste the 77-digit number n into a big integer factorization tool (e.g. FactorDB / WebAssembly factorizer) and obtain:
p = 238310156548665930651417811579815413711
q = 312421033739741558015235161390562658691
Both are ~128-bit primes, and indeed:
n = p * q
So this is the correct factorization of the RSA modulus.
Given p, q, n, e, and the ciphertext C, we can perform textbook RSA decryption locally.
phi = (p - 1) * (q - 1)
dd is the modular inverse of e modulo phi:
d = pow(e, -1, phi) # e^(-1) mod phi
m = pow(C, d, n) # m = C^d mod n
m to bytes and decodeBelow is the complete Python script used:
# rsa_decrypt.py
from math import gcd
# Prime factors from factorization
p = 238310156548665930651417811579815413711
q = 312421033739741558015235161390562658691
n = p * q
e = 65537
C = 57404030761199998498847049987163372359776031729451339753283863067115090061488
phi = (p - 1) * (q - 1)
# Compute private exponent d
d = pow(e, -1, phi)
# Decrypt ciphertext: m = C^d mod n
m = pow(C, d, n)
# Convert integer m to bytes
def long_to_bytes(x: int) -> bytes:
if x == 0:
return b""
length = (x.bit_length() + 7) // 8
return x.to_bytes(length, "big")
pt = long_to_bytes(m)
print(pt)
print(pt.decode())
Run it:
python3 rsa_decrypt.py
Output:
b'MCTF25{r4d1c4l_s3cur1ty}'
MCTF25{r4d1c4l_s3cur1ty}
So the decrypted plaintext string is the flag.
MCTF25{r4d1c4l_s3cur1ty}
(n, e)C)n into p and q, we can:
phi = (p - 1) * (q - 1)d = e^(-1) mod phim = C^d mod nm back to bytes to recover the flagIn one sentence:
Because the RSA key size was only 256 bits, we could factor n, reconstruct the private key, decrypt the oracle’s secret, and obtain the flag:
MCTF25{r4d1c4l_s3cur1ty}