原题地址:https://adworld.xctf.org.cn/challenges/details?hash=08957e3e-3b35-11ed-abf3-fa163e4fa609&task_category_id=5

原题代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Sage
for _ in range(3):
p = random_prime(2^1024)
q = random_prime(2^1024)
n = p*q
p1=p>>724
ct=n * inverse_mod(q % (2 ** 265), 2^265) % 2^265
print('p1=',p1)
print('ct=',ct)
print("n=",n)
e = 65537
alarm(80)
m = randint(2,n-1)
c=pow(m,e,n)
print('c=', c)
print('---------------------------------------------')
m1 = int(input("m="))
print('---------------------------------------------')
print()
if m1!=m:
print("Nope")
exit()

print(open("flag.txt","r").read())


分析题目,取两个1024位大质数p、q相乘得n,输出p的高300位p1处理q得到的ct。m任取(所以不能通过自然语言判断)后加密为c并输出。我们的任务是通过p1、ct和n推出p、q来解密m。验证3轮都正确后输出flag。

线性同余方程的解法

首先对ct进行分析可得线性同余方程组:

q1=q mod 2265i=inverse(q1,2265)则有{ct=n×i mod 22651q1×i mod 2265 \text{设} q_1 = q \space mod \space 2^{265} \\ \text{设} i=inverse(q_1, 2^{265}) \\ \text{则有} \begin{cases} ct = n \times i \space mod \space 2^{265} \\ 1 \equiv q_1 \times i \space mod \space 2^{265} \end{cases}

对于线性同余方程axb (mod n)ax \equiv b \space (mod \space n)
d=gcd(a,n)d = gcd(a, n)dd 整除 bb ,那么bd\frac b d为整数。由裴蜀定理,存在整数对(r,s)(r,s)(可用扩展欧几里得算法求得)使得ar+sn=dar+sn=d,因此x0=r×bdx_0=r \times \frac b d是方程 (1) 的一个解。其他的解都关于nd\frac n dxx 同余。即xx0+(nd)×t (mod n) (0td1)x \equiv x_0+(\frac n d) \times t \space (mod \space n) \space (0 \leqslant t \leqslant d-1)

根据上述线性同余方程的求解方法,可以求得i=r×ct mod 2265i=r \times ct \space mod \space 2^{265},进而求得q1=inverse(i,2265)q_1=inverse(i,2^{265})

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import gmpy2
from Crypto.Util.number import *

def ext_gcd(a, b): #扩展欧几里得算法
if b == 0:
return 1, 0, a
else:
x, y, gcd = ext_gcd(b, a % b)
x, y = y, (x - (a // b) * y)
return x, y, gcd

ct = int(input("ct="))
n = int(input("n="))
i = (ext_gcd(n,2**265)[0] * ct) % 2**265
q1 = gmpy2.invert(i,2**265)
print(q1)

至此,我们已经获得了p的高300位p1q的低265位q1

Coppersmith已知高位攻击

知道p或q的高位为其位数的约一半时即可
已知e/n,爆破1024位的p,至少需要知道570位二进制。

网上搜了搜发现有道数据一模一样的题:鹤城杯2021 Crypto babyrsa
原理是知道n和q的低265位可以求出p的低265位,结合p的高300位就是565位,爆破剩下5位。

跑脚本罢。

摘录代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from gmpy2 import *
from Crypto.Util.number import *

p1 =
q1 =
n =
mod=pow(2,265)
p0=n*invert(q1,mod)%mod
pbar=(p1<<724)+p0
PR.<x> = PolynomialRing(Zmod(n))

for i in range(32):
f=pbar+x*mod*32
f=f.monic()
pp=f.small_roots(X=2^454,beta=0.4)
if(pp):
break
pbar+=mod

p=pbar+pp[0]*32*mod
assert n%p==0
print(p)

拿到了p后就是常规操作了。

写在最后

标号难度1…无官方wp,我交上去的时候也只有9个人出来。
纠结了半个月。
还是要善于搜索,前置知识很重要。