# Write-up Crypto UTCTF 2023
## Provably Insecure
- Đề cho 1 file server.py
`server.py`
```python3
from cryptography.hazmat.primitives.asymmetric import rsa
from secrets import randbits
if __name__ == '__main__':
alice = rsa.generate_private_key(public_exponent=65537, key_size=2048)
print("Alice's pk: ", alice.public_key().public_numbers().n, alice.public_key().public_numbers().e)
m = randbits(256)
s = pow(m, alice.private_numbers().d, alice.public_key().public_numbers().n)
print(m, s)
print("Your key: ")
n_prime = abs(int(input("n': ")))
e_prime = abs(int(input("e': ")))
d_prime = abs(int(input("d': ")))
# Checks
x = randbits(256)
assert alice.public_key().public_numbers().n != n_prime or alice.public_key().public_numbers().e != e_prime
assert n_prime > s
assert pow(x, e_prime * d_prime, n_prime) == x
assert e_prime > 1
assert pow(s, e_prime, n_prime) == m
with open('flag.txt', 'r') as f:
print("Flag: " + f.read().strip())
```
- Dựa vào source ta biết rằng mỗi phiên kết nối, server sẽ:
- Tạo key RSA $\text{(2048-bit n, e = 65537, d)}$
- Tạo một số random $\text{256-bit m}$
- Ký số $m$ đó bằng cặp key vừa tạo: $s \equiv m^{d} \pmod n$ và gửi cho chúng ta cặp $(m,s)$
- Tạo một số random $\text{256-bit x}$ khác
- Yêu cầu chúng ta tìm key RSA $(n',e',d')$ với $e'>1,n'\neq n,n'>s,e'\neq e$ sao cho:
- $x^{e'd'}\equiv x \pmod{n'}$
- $s^{e'}\equiv m \pmod{n'}$
- Lưu ý: $n'$ không nhất thiết phải là tích của 2 số nguyên tố $p,q$, có thể là $p$
- Do $\text{n.bit_length()=2048} \implies \text{s.bit_length()<2048}$, ta sẽ tạo ra số nguyên tố $\text{2048-bit p-smooth}$ để bỏ vào thuật toán Polig-Hellman(discrete log)
`gen_smooth_prime.sage`
```python3
def B_smooth(total_size, small_factors_size, big_factor_size):
""" Just picking at random should be enough, there is a very small probability
we will pick the same factors twice
"""
smooth_prime = 2
factors = [2]
# large B-sized prime
large_prime = random_prime(1<<(big_factor_size + 1), lbound=1<<(big_factor_size-3))
factors.append(large_prime)
smooth_prime *= large_prime
# all the other small primes
number_small_factors = (total_size - big_factor_size) // small_factors_size
i = 0
for i in range(number_small_factors - 1):
small_prime = random_prime(1<<(small_factors_size + 1), lbound=1<<(small_factors_size-3))
factors.append(small_prime)
smooth_prime *= small_prime
# we try to find the last factor so that the total number is a prime
# (it should be faster than starting from scratch every time)
prime_test = 0
while not is_prime(prime_test):
last_prime = random_prime(1<<(small_factors_size + 1), lbound=1<<(small_factors_size-3))
prime_test = smooth_prime * last_prime + 1
factors.append(last_prime)
smooth_prime = smooth_prime * last_prime + 1
return smooth_prime, factors
def generate_pq(nbits):
p, factors = B_smooth(nbits+32, 15, 40)
while True:
if is_prime(p):
if 2**nbits < p:
break
p, factors = B_smooth(nbits+32, 15, 40)
q, factors = B_smooth(nbits+32, 15, 40)
while True:
if is_prime(q):
if 2**nbits < q:
if gcd(p-1, q-1) == 2:
break
q, factors = B_smooth(nbits+32, 15, 40)
return p,q
a = generate_pq(2048)
print(a)
```
- Có được p-smooth rồi ta sẽ tìm ra được $\text{d' = discrete_log(p,s,m)}$ và kiểm tra $gcd(d',p-1)=1$ để có thể tìm được $e'$
- Ta sẽ gửi $(p,e',d')$ lên server và có flag
`sol.py`
```python3
from sympy.ntheory.residue_ntheory import discrete_log
from math import gcd
from pwn import *
context.log_level = 'debug'
p = 62143219619681917935136906151929253060951584255243199041636592977128737612765879211852580842443751889475293085575739308499043552527775924407768488072352361355356207305538727324510456921711248898153730337251113711043008330828602641385335360964498324586613712757151309916606721874486110551032007609570622431231166045980262507993234482716169704396228707671836774118010047676485506754490326300709395846944972467897527001865144040919990845270870839846582929305864636064876937300156034551928552824253432978686903375415481206108740585420665182847507515656746566694978845572597313612052932233290404317565105392882920239821327
while True:
io = remote('puffer.utctf.live', 52548)
io.recvuntil(b"\n")
m = int(io.recvuntil(b' ',drop=True).decode())
s = int(io.recvuntil(b'\n',drop=True).decode())
dp = discrete_log(p,s,m)
if gcd(dp,p-1) == 1:
break
ep = pow(dp,-1,p-1)
io.sendlineafter(b"n': ", str(p).encode())
io.sendlineafter(b"e': ", str(ep).encode())
io.sendlineafter(b"d': ", str(dp).encode())
io.interactive()
```
- Flag: utflag{hey_wait_signature_forgery_is_illegal}