DASCTF 2023 & 0X401七月暑期挑战赛——ezAlgebra
1、题目描述:
题目代码
from Crypto.Util.number import getPrime, bytes_to_long
def YiJiuJiuQiNian(Wo, Xue, Hui, Le, Kai):
Qi = 1997
Che = Wo+Hui if Le==1 else Wo*Hui
while(Xue):
Qi += (pow(Che, Xue, Kai)) % Kai
Xue -= 1
return Qi
l = 512
m = bytes_to_long(flag)
p = getPrime(l)
q = getPrime(l//2)
r = getPrime(l//2)
n = p * q * r
t = getrandbits(32)
c1 = YiJiuJiuQiNian(t, 4, p, 1, n)
c2 = YiJiuJiuQiNian(m, 19, t, 0, q)
c3 = YiJiuJiuQiNian(m, 19, t, 1, q)
print(f"n = {n}")
print(f"c1 = {c1}")
print(f"c2 = {c2}")
print(f"c3 = {c3}")
"""
n = 119156144845956004769507478085325079414190248780654060840257869477965140304727088685316579445017214576182010373548273474121727778923582544853293534996805340795355149795694121455249972628980952137874014208209750135683003125079012121116063371902985706907482988687895813788980275896804461285403779036508897592103
c1 = 185012145382155564763088060801282407144264652101028110644849089283749320447842262397065972319766119386744305208284231153853897076876529326779092899879401876069911627013491974285327376378421323298147156687497709488102574369005495618201253946225697404932436143348932178069698091761601958275626264379615139864425
c2 = 722022978284031841958768129010024257235769706227005483829360633993196299360813
c3 = 999691052172645326792013409327337026208773437618455136594949462410165608463231
"""
2、题目分析:
先简化一下:
n = p q r
c 1 = ( t + p ) ^4 + ( t + p )^ 3 + ( t + p ) ^2 + ( t + p ) + 1997 ( m o d n )
c 2 = ( m t ) ^19 + ( m t ) ^18 + ⋯ + ( m t ) ^2 + ( m t ) + 1997 ( m o d q )
c 3 = ( m + t ) ^19 + ( m + t ) ^18 + ⋯ + ( m + t ) + 1997 ( m o d q )
已知 n , c 1 , c 2 , c 3 , 求 m
c 1 = t^ 4 + t ^3 + t ^2 + t + 1997 ( m o d p ) = >
k p = c 1 − ( t ^4 + t^ 3 + t^ 2 + t + 1997 )
联想到 p 的高位攻击
此处相当于高位已知,求低位, c o p p e r 解未知数 t
求出后带入原式, g c d ( k p , n ) 求出 p
# sage
from Crypto.Util.number import *
n = 119156144845956004769507478085325079414190248780654060840257869477965140304727088685316579445017214576182010373548273474121727778923582544853293534996805340795355149795694121455249972628980952137874014208209750135683003125079012121116063371902985706907482988687895813788980275896804461285403779036508897592103
c1 = 185012145382155564763088060801282407144264652101028110644849089283749320447842262397065972319766119386744305208284231153853897076876529326779092899879401876069911627013491974285327376378421323298147156687497709488102574369005495618201253946225697404932436143348932178069698091761601958275626264379615139864425
c2 = 722022978284031841958768129010024257235769706227005483829360633993196299360813
c3 = 999691052172645326792013409327337026208773437618455136594949462410165608463231
PR.<x> = PolynomialRing(Zmod(n))
f = x ^ 4 + x ^ 3 + x ^ 2 + x + 1997 - c1
t = f.small_roots(X = 2 ^ 32,beta = 0.4)[0]
# print(t)
t = 2915836867
kp = t ^ 4 + t ^ 3 + t ^ 2 + t + 1997 - c1
p = gcd(kp,n)
qr = n // p
观察到
c2=(mt)^19+(mt)^18+⋯+(mt)^2+(mt)+1997(modq)
c3=(m+t)^19+(m+t)^18+⋯+(m+t)+1997(modq)
这形式不跟题二差不多,不过是项数多了点
但(x−m)还是c2,c3的解,公因子还是(x−m)
可利用groebner_basis()求出q(理解groebner_basis()可参考此文章https://zhuanlan.zhihu.com/p/278946425)
个人理解:打印出的结果是多项式的解集,最后的那位纯数字是多项式理想G所对应的代数集合的维度,即所生成的代数集合中的点的个数,正好可对应于q。
在上述代码基础上添加
P.<x,y> = PolynomialRing(Zmod(n))
f1 = 1997 - c3
f2 = 1997 - c2
for i in range(1,20):
f1 += (x + t)^i
f2 += (x * t)^i
G = [f1,f2]
B = Ideal(G).groebner_basis()
res = [x.constant_coefficient() for x in B]
q = res[1]
m = -res[0] % q
print(m)
print(q)
求出m转字符,发现得不到结果,结果是m % q,猜测比q大,爆破一下。爆破结果是以小写dasctf开头,上传的答案又要求大写DASCTF。
q = 87038069032840052005520908272237788908169043580221040711149494083975743478969
x = 56985796272753226120469211992443340429346162287195965942430959147227534853120
for i in range(10000000):
flag = long_to_bytes(x + i * q)
if b'dasctf' in flag :
print(flag)
print(i)
# dasctf{ShangPoXiaPoYaSiLeYiQianDuo}
# i = 8751845 24bits
完整exp:
# sage
from Crypto.Util.number import *
n = 119156144845956004769507478085325079414190248780654060840257869477965140304727088685316579445017214576182010373548273474121727778923582544853293534996805340795355149795694121455249972628980952137874014208209750135683003125079012121116063371902985706907482988687895813788980275896804461285403779036508897592103
c1 = 185012145382155564763088060801282407144264652101028110644849089283749320447842262397065972319766119386744305208284231153853897076876529326779092899879401876069911627013491974285327376378421323298147156687497709488102574369005495618201253946225697404932436143348932178069698091761601958275626264379615139864425
c2 = 722022978284031841958768129010024257235769706227005483829360633993196299360813
c3 = 999691052172645326792013409327337026208773437618455136594949462410165608463231
PR.<x> = PolynomialRing(Zmod(n))
f = x ^ 4 + x ^ 3 + x ^ 2 + x + 1997 - c1
t = f.small_roots(X = 2 ^ 32,beta = 0.4)[0]
# print(t)
t = 2915836867
kp = t ^ 4 + t ^ 3 + t ^ 2 + t + 1997 - c1
p = gcd(kp,n)
qr = n // p
P.<x,y> = PolynomialRing(Zmod(n))
f1 = 1997 - c3
f2 = 1997 - c2
for i in range(1,20):
f1 += (x + t)^i
f2 += (x * t)^i
G = [f1,f2]
B = Ideal(G).groebner_basis()
res = [x.constant_coefficient() for x in B]
q = res[1]
m = -res[0] % q
print(m)
print(q)
q = 87038069032840052005520908272237788908169043580221040711149494083975743478969
x = 56985796272753226120469211992443340429346162287195965942430959147227534853120
for i in range(10000000):
flag = long_to_bytes(x + i * q)
if b'dasctf' in flag :
print(flag)
print(i)
# dasctf{ShangPoXiaPoYaSiLeYiQianDuo}
# i = 8751845 24bits