Ouchie
Peeps be coming up with cryptographic scheme like crazy. nc 20.198.224.34 8555
Overview
We are given a chall.py that implements a Paillier-like cryptosystem over a modulus
n = p**2 * q with a 384-bit p and q. It exposes an interactive menu with four options:
Encrypt, Decrypt, Get Flag, and Exit.
from Crypto.Util.number import bytes_to_long, getPrime, isPrime
from Crypto.Random.random import randint
import signal
# from secret import FLAG
bit_length = 384
p = getPrime(bit_length)
q = getPrime(bit_length)
n = p**2 * q
while True :
g = randint(2, n - 1)
if pow(g, p - 1, p**2) != 1:
break
h = pow(g, n, n)
r = randint(0, n - 1)
def encrypt(m):
c = pow(g, m, n) * pow(h, r, n) % n
return c
L = lambda x : (x - 1) // p
def decrypt(c):
a = pow(c, p - 1, p**2)
b = pow(g, p - 1, p**2)
a = L(a)
b = L(b)
m = (a * pow(b, -1, p)) % p
return m
options = ["Encrypt", "Decrypt", "Get Flag", "Exit"]
def menu() :
[print(f"{i + 1}. {opt}") for i, opt in enumerate(options)]
return int(input(">> "))
Analysis
The cryptography looks intimidating, but the solution is simple. The flag is handed out by option 3 (Get Flag):
elif choice == 3 :
challenge = randint(p + 1, p << 10)
print(f"Challenge: {challenge}")
response = int(input("Plaintext: "))
if challenge == response :
print("Congrats!")
print(FLAG)
exit()
else :
print("Wrong!")
exit()
The challenge is generated randomly between p + 1 and p << 10, then printed in the
clear. The program simply asks us to echo the same integer back as our Plaintext. There is
no cryptography to break here at all — we just read the challenge value and send it back
verbatim.
Exploitation
from pwn import *
r = remote('20.198.224.34', 8555, level='debug')
def recv_menu():
return r.recvuntil(b">> ").decode()
def send_choice(choice):
r.sendline(str(choice).encode())
def send_data(data):
r.sendline(str(data).encode())
def main():
try:
recv_menu()
send_choice(3) # pick "Get Flag"
challenge_response = r.recvline().decode().strip()
# Extract the challenge value from the response
if "Challenge:" in challenge_response:
challenge = int(challenge_response.split(":")[1].strip())
else:
print("Unexpected response format!")
return
# Send the exact same value back
send_data(challenge)
# Receive the flag
r.recvall().decode()
except Exception as e:
print(f"Error: {e}")
finally:
r.close()
if __name__ == "__main__":
main()
Echoing the printed challenge back makes the equality check pass and the server prints the flag.
Flag
CTFITB{yeaaaaayyyyy_kita_ketemu_lagi_di_gelatik_nanti!!!, yeah like there even is a gelatik}