2018. 12. 18. 01:29, CTF/Crypto
from Crypto.Util.number import isPrime
from random import getrandbits
def genKey(k):
while True:
r=getrandbits(k)
while(r%2):
r=getrandbits(k)
p = 3 * r**2 + 2 * r + 7331
q = 17 * r**2 + 18 * r + 1339
n = p * q
if(isPrime(p) and isPrime(q)):
return (p,q) , n
def encrypt(m,pubkey):
c=m**2 % pubkey
return c
privkey,pubkey = genKey(256)
flag = open('flag.txt').read().strip()
while(len(flag)<125):
flag+='X'
flag = int(flag.encode('hex'),16),
ct=encrypt(flag,pubkey)
with open('flag.enc','w') as f:
f.write('ct = ' + str(ct))
with open('pubkey.txt','w') as f:
f.write('pubkey = ' + str(pubkey))
with open('privkey.txt','w') as f:
f.write('privkey = ' + str(privkey))
문제의 코드는 위와 같습니다.
$R$이 256bit이기 때문에 $P$와 $Q$는 512bit의 소수이고, $N(=PQ)$가 공개키이나 이를 소인수분해하는건 불가능에 가깝습니다. 하지만 $N$은 굉장히 쉽게 소인수분해를 해낼 수 있는데, 바로 $N$이 어떤 자연수 $r$에 대한 4차식으로 표현된다는 점 때문입니다. 이 점을 이용해 $r$을 binary search로 찾을 수 있습니다.
이렇게 $P$와 $Q$를 찾고 나면 $m$을 $P$로 나눈 나머지와 $m$을 $Q$로 나눈 나머지를 이차합동식을 해결함으로서 알아낼 수 있습니다.($P$와 $Q$가 $4k+3$ 꼴이기 때문에 $x^2=a \, mod \, P$의 해는 $ x=\pm a ^ {\frac{P+1}{4} }$ 로 계산할 수 있습니다.)
이후에 4가지 가능성에 대해 중국인의 나머지 정리로 m을 복원하면 됩니다.
코드
import binascii
def gcd(a, b):
if a == 0: return b
return gcd(b%a, a)
def egcd(a, b):
if a == 0:
return (b, 0, 1)
g, y, x = egcd(b%a,a)
return (g, x - (b//a) * y, y)
def inv(a, m):
g, x, y = egcd(a, m)
if g != 1:
raise Exception('No modular inverse')
return x%m
# x**2 = a (mod m), m is prime
def quad_congruence_equation(a, m):
assert((m+1)%4 == 0)
return pow(a, (m+1)//4, m)
# m must satisfies pairwise relatively prime
def crt(a, m):
n = len(m)
ret = a[0]
mod = m[0]
for i in range(1,n):
m1 = mod
mod *= m[i]
m2inv = inv(m[i],m1)
m1inv = inv(m1,m[i])
ret = (ret*m[i]*m2inv+a[i]*m1*m1inv)%mod
return ret
def f(r):
return 51*r**4 + 88*r**3 + 128680*r**2 + 134636*r + 9816209
# 0x6161 -> 'AA'
def hexint2str(x):
s = hex(x)[2:]
if len(s) % 2 == 1: s = '0'+s
return binascii.unhexlify(s).decode()
n = 577080346122592746450960451960811644036616146551114466727848435471345510503600476295033089858879506008659314011731832530327234404538741244932419600335200164601269385608667547863884257092161720382751699219503255979447796158029804610763137212345011761551677964560842758022253563721669200186956359020683979540809
c = 66888784942083126019153811303159234927089875142104191133776750131159613684832139811204509826271372659492496969532819836891353636503721323922652625216288408158698171649305982910480306402937468863367546112783793370786163668258764837887181566893024918981141432949849964495587061024927468880779183895047695332465
#### Recover p, q by binary search ####
st = 1
en = 2**256
while st < en:
mid = (st+en)//2
val = f(mid)
if val > n: en = mid-1
elif val == n: st,en = mid,mid
else: st = mid+1
r = st
assert(f(r)==n)
p = 3 * r**2 + 2 * r + 7331
q = 17 * r**2 + 18 * r + 1339
########################################
#### Recover m mod p (=mp), m mod q (=mq) ####
ap = c % p
aq = c % q
mp = quad_congruence_equation(ap,p)
mq = q-quad_congruence_equation(aq,q) # two possibilities
assert((mp*mp - ap) % p==0)
assert((mq*mq - aq) % q==0)
###############################################
#### Recover m ####
m = crt([mp,mq],[p,q])
assert(m*m % n == c)
print(hexint2str(m))
'CTF > Crypto' 카테고리의 다른 글
[0CTF/TCTF 2019] zer0lfsr (0) | 2019.03.28 |
---|---|
[0CTF/TCTF 2019] zero0des (0) | 2019.03.28 |
[0CTF/TCTF 2019] babysponge (0) | 2019.03.28 |
[2018 X-MAS CTF] Santa's list 2.0 (0) | 2018.12.19 |
[2018 X-MAS CTF] Santa's list (0) | 2018.12.19 |
[2018 X-MAS CTF] Special Christmas Wishlist (0) | 2018.12.19 |
Comments