crypto-dollar_lunch(b01lers ctf 2020)

一题比较特殊的RSA题,记录一下。题目附件在这里

200-dollar lunch (by dm)

Yeah, Bud, piece of cake if you say so. That’s why we picked you. The data must be in one of those encrypted messages I’m attaching. Get it to us within 48 hours or we’ll hire someone else…
Users get:

  • capture.tar.gz
  • enc.py

题目源码enc.py

import sys


N = 22518213392401264818411278544481843914232432830458541414885458377878266793498750772354592395933629538430168704357367677366063506437840746114109698561818213653155337516332045907584538560207347457518815750143165409925001430273241092541136748646685328169745125245340633556971837060802857356695229674488893860374426117801802327059524160107136439418599365088385458686062313445632037797173490869675537943240896233769936787893899596578453468394750636934950285496609730610849908463004889068961426952923099134018106324063391090991215384284497435868352255958362702412123106399579166856780539143530223177815706341761329346665123
phi = 22518213392401264818411278544481843914232432830458541414885458377878266793498750772354592395933629538430168704357367677366063506437840746114109698561818213653155337516332045907584538560207347457518815750143165409925001430273241092541136748646685328169745125245340633556971837060802857356695229674488893860374125936771634231083724519781739692120840798867536892089959731316847037589936959996408416801992484839541189805041461052140389233753983174954128996839480404891785287303195083960639773803194332300000270420399549292186960561461628916388255677931409154971065184660947084539883190976066572465271852306890113256664720
e = 65537


def encrypt(m):
   return pow(m, e, N)


with open("secret_data", "rb") as f:
   data = f.read()

chunkSize = 250 
Nchunks = (len(data) // chunkSize) + 1
chunks = [data[i*chunkSize:(i+1)*chunkSize] for i in range(Nchunks)]

for i,msg in enumerate(chunks):
   m = int.from_bytes(msg, byteorder='big', signed=False)
   c = encrypt(m)
   fname = "secret" + ("000" + str(i))[-4:]
   with open(fname, "wb") as f:
       f.write(c.to_bytes(256, byteorder = 'big'))
 

整个加密逻辑比较容易理解,就是将明文按照250字节大小分为若干分组,每个分组进行RSA加密,参数 N,phi,e 已知;输出若干个 secret000xxx 密文文件,放在capture目录中。

由于本题给出了phi值,结合这个条件推导一下:
由于N-phi=pq-(p-1)(q-1)=pq-pq+p+q-1=p+q-1
即q=N-phi+1-p,令B=N-phi+1
则有(B-p)p=N
最终得到N-Bp+p^2=0
利用一元二次方程求根公式,得到N的因子分解 p = B ± B 2 − 4 N 2 p=\frac{B \pm \sqrt{B^2-4N}}{2} p=2B±B24N

但是这样算出来结果貌似有问题,还是需要用yafu分解N以后重新算phi。

最终exp,在输出内容中找到pctf{xxx}:

# correct decoding, uses correct phi (obtains factors of N)
#

N = 22518213392401264818411278544481843914232432830458541414885458377878266793498750772354592395933629538430168704357367677366063506437840746114109698561818213653155337516332045907584538560207347457518815750143165409925001430273241092541136748646685328169745125245340633556971837060802857356695229674488893860374426117801802327059524160107136439418599365088385458686062313445632037797173490869675537943240896233769936787893899596578453468394750636934950285496609730610849908463004889068961426952923099134018106324063391090991215384284497435868352255958362702412123106399579166856780539143530223177815706341761329346665123
phi = 22518213392401264818411278544481843914232432830458541414885458377878266793498750772354592395933629538430168704357367677366063506437840746114109698561818213653155337516332045907584538560207347457518815750143165409925001430273241092541136748646685328169745125245340633556971837060802857356695229674488893860374125936771634231083724519781739692120840798867536892089959731316847037589936959996408416801992484839541189805041461052140389233753983174954128996839480404891785287303195083960639773803194332300000270420399549292186960561461628916388255677931409154971065184660947084539883190976066572465271852306890113256664720
e = 65537


import sys, gmpy2


def gcd(x, y) :
   while x != 0 :
      x,y = y % x, x
   return y

def xgcd(b, a):
    x0, x1, y0, y1 = 1, 0, 0, 1
    while a != 0:
        q, b, a = b // a, a, b % a
        x0, x1 = x1, x0 - q * x1
        y0, y1 = y1, y0 - q * y1
    return  b, x0, y0

def modinv(a, n) :
  g, x, y = xgcd(a, n)
  if g != 1 :
    return 0
  return x % n


def decrypt(c):
   return pow(c, d, N)


# It is straightforward to confirm with standard tools that N is composite (fails primality check). Factoring 
# a 2048-bit (600-some-digit) N is a tall order but it is still worth a try... no cigar, though. So one 
# needs to be creative (investigative).
#
# There must be something mistake regarding phi. At least verify that the value would make sense for
# the naive solution, i.e., that integer p,q really exist such that phi = (p-1)*(q-1), N = p*q:
#
#   p + q = N - phi + 1   =>  q = N - phi + 1 - p = B - p,  so solve (B-p)*p = N for p => p^2 - B*p + N = 0

B = N - phi + 1
D = B*B - 4*N       # discriminant

(sqrtD, exact) = gmpy2.iroot(D, 2)  
if not exact:
   print("no integer p,q possible\n")
   exit(1)

sqrtD = int(sqrtD)    
p = (B - sqrtD) // 2  # needs accurate integer root
q = B - p

# So p,q do exist.. still, phi must be wrong since we cannot decrypt naively. Not all work is lost, however, 
# because now we have a (partial) factorization of N as p*q. Further factoring 300-some-digit p & q is MUCH easier 
# than factoring N directly, though still generally hard. One can try, nevertheless. E.g., within 5 minutes 
# with Yafu: p is (super likely) prime, while q = q1 * q2^2, where q1,q2 are (super likely) primes.
#

q1 = 114246317095974470506773678257568235122518242597047789547048839187083024865020001474658765781086764897848462941975998876745554658010228538590510838090170514826158000814626345298608897564379676590885135757454210473226786479123649044427073066377922690618563
q2 = 1157553415904668982313594731

#
# compute phi for N = p * q1 * q2^2 using Euler's formula, then obtain d and decrypt
#

phi = (p - 1) * (q1 - 1) * (q2 - 1) * q2    # Euler formula for p *q1 * q2^2
d = modinv(e, phi)


# read encryptions, decrypt data

for i in range(1226):
   fname = "secret" + ("000" + str(i))[-4:]
   with open(fname, "rb") as f:
      cmsg = f.read()
   c = int.from_bytes(cmsg, byteorder = 'big')
   m = pow(c, d, N)
   msg = m.to_bytes(256, byteorder = 'big').lstrip(b"\x00")  # strip leading zeroes
   sys.stdout.buffer.write(msg)


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值