Tag List
Sign In

pwnEd Crypto: scuffed_rsa

We're given a broken implementation of RSA in Python:

from Crypto.Util.number import getPrime, inverse, bytes_to_long, long_to_bytes

e = 65536

def gen_key():
    p = getPrime(1024)
    q = getPrime(1024)
    n = p # q
    t = (p-1) * (q-1)
    d = inverse(e, t)
    return n, d

def encrypt(n, e, m):
    c = pow(m, e, n)
    return c

# for some reason my decrypt function doesn't work!
# can you help me recover the message?
def decrypt(n, d, c):
    m = pow(c, d, n)
    return m

FLAG = b'pwnEd{???...?}'

n, d = gen_key()
m    = bytes_to_long(FLAG)

c    = encrypt(n, e, m)
m    = decrypt(n, d, c)

print(f'n = {n}')
print(f'Encrypted flag: {c}')
print(f'It just doesn\'t work!: {long_to_bytes(m)}')

The first things to notice are that e isn't the usual 65537, and isn't prime either. Then n evaluates to just p instead of pq, so the output we're given:

n = 158956010976836742521330254087275425096501583054825136137040889728364290468342445750425376148898452284963095323948276435573809460871703413808672473157217817381946017879464498512288960101002405275174672926496400917470509079465025417877720664744491054992376496575684398645703346912039046842844204816524282238459
Encrypted flag: 17696325213195819360084027228862087546075383230711834052302081670404491942278399339165629822030797913121661625273125782635636475385580385239446207346095935362399429412825040827923795088154904762207215060766109368707427481633970394393914176132798637385151502305355038711511174834777689397585730318578634645332
It just doesn't work!: b'\xb1s\xacL\x841\xff\r\xfd\x9e\x9e\xe29\xb2\xfd\xf7I\xe2?\x1bb\xd4\x8dZ\xce\xa0\x18\xc4\x040\x8c\xe1\xd2\x81B\xb1\xda\x9av\xd5Np\xf4m\xeb\xdc\xfd\xcc0\xe3\xa9h\x86\xb8c\x86\x84\xb7\xda\x99uJ\x0b\xbe\x1d@\xbf\x98\x95\x92\xc7\x04\xef,,\xd4\tt\x82\xba\xd8\x17O\x9bI\xf6\xe8j\x8a\xb3H5n\x0cEA|\x1d\xab(\xd4\x9dU&=\x8aVA\x04\xa62\x11\xa1\xdb\xf0\xf5\xd4%\xde+\xa3+\xe6\xc8*\xffv\xad'

uses the wrong private key. So, we're given n which is prime, thus there isn't really a p or q, the ciphertext c and e. Using sources to work out how RSA works and calculate the correct value for d, the totient φ(n) is n-1 since n is prime. Typically this is all that should be done, and RsaCtfTool managed to decrypt the ciphertext with python3 RsaCtfTool.py -n <N> -e 65536 --uncipher <CIPHER> --attack ecm2, but this was all we could get to during the CTF. Afterwards, it's revealed that since e isn't coprime with φ(n) we can't decrypt the message as you usually would.

One reason RSA works is that ed = 1 (mod φ(n)) holds, it follows that c^d = m^ed = m (mod n), proof here. Since the decryption isn't working even with the correct d then ed = x (mod φ(n)) where x isn't 1. Finding this x is rather simple, we just evaluate e*d%n, which gives us 2. Recall that m^ed = m (mod n), so we've got the message squared. To find the modular square root, we use the Tonelli-Shanks algorithm. Since there's an online implementation, we just enter our m^2 for a and n for p, convert this long into bytes and we get the flag.