五月公开赛writeup|crypto篇
2022-5-16 18:15:54 Author: mp.weixin.qq.com(查看原文) 阅读量:0 收藏

1. 题目考查技术点

LCG,Coppersmith一元、二元攻击,数论

2. 题目详细解题步骤

题目第一个关键的部分

def next(self):
self.seed = (self.a * self.seed * self.seed + self.b * self.seed + self.c) % self.p
return self.seed >> 64
print("state1 =", lcg.next())
print("state2 =", lcg.next())
提供了两次输出的高位,可以联想到用二元Coppersmith进行攻击,就可以恢复低位。
import itertools

def small_roots(f, bounds, m=1, d=None):
if not d:
d = f.degree()
R = f.base_ring()
N = R.cardinality()
f /= f.coefficients().pop(0)
f = f.change_ring(ZZ)

G = Sequence([], f.parent())
for i in range(m + 1):
base = N ^ (m - i) * f ^ i
for shifts in itertools.product(range(d), repeat=f.nvariables()):
g = base * prod(map(power, f.variables(), shifts))
G.append(g)

B, monomials = G.coefficient_matrix()
monomials = vector(monomials)

factors = [monomial(*bounds) for monomial in monomials]
for i, factor in enumerate(factors):
B.rescale_col(i, factor)

B = B.dense_matrix().LLL()

B = B.change_ring(QQ)
for i, factor in enumerate(factors):
B.rescale_col(i, 1 / factor)

H = Sequence([], f.parent().change_ring(QQ))
for h in filter(None, B * monomials):
H.append(h)
I = H.ideal()
if I.dimension() == -1:
H.pop()
elif I.dimension() == 0:
roots = []
for root in I.variety(ring=ZZ):
root = tuple(R(root[var]) for var in f.variables())
roots.append(root)
return roots
return []
a = 68823589009570846705623113091430161477799561031575612135516351257937127579444
b = 76549169049080319489163980188997771079750670038002598167961495813084486794567
c = 99215492498952976642031024510129092308042391285395704888838178561670205468882
p = 12775129699668988473759438271274836254349225413222075887429387682336494103348583050672280757042383792640084197832605411237644937815012509935794275313643031
state1 = 664406108637237317109372377546194289077117372665932150107678757303243619963182733408311739663233548725195723884930183442927293177078507
state2 = 269639459994260392015105629865659210391196381140872041084737976688017858324308345528702533444116282512066085580909718347778743354754352
PR. < s1_low, s2_low > = PolynomialRing(Zmod(p))
f = a * ((state1 << 64) + s1_low) ^ 2 + b * ((state1 << 64) + s1_low) + c - (state2 << 64) - s2_low
state1 = small_roots(f, (2 ^ 64, 2 ^ 64), m=3)[0][0] + (state1 << 64)
state2 = small_roots(f, (2 ^ 64, 2 ^ 64), m=3)[0][1] + (state2 << 64)
print(state2)

到这里,我们就可以预测此后的LCG输出了,但是要解出flag,还需要得到最初的seed。这里有两种方法:第一种是通过一元coppersmith求解方程,此方法脚本如下:

# 求seed 方法1

P.<x> = PolynomialRing(Zmod(p))

f = a * x * x + b * x + c - state1
f = f.monic()
print(f.roots())

第二种方法,由于flag的前5位已知,所以我们可以得到如下等式:

$$f

lag[i]-(cipher[i]\bigoplus (lcg.next() >> 192))=k_i*seed

$$

于是,我们可以计算前5位的值,然后计算他们的GCD,就可以得到seed。此方法脚本如下:

# 求seed 方法2
t = []
flag = 'flag{'
lcg = my_LCG()
for i in range(5):
   tmp = int(sha256(flag[i].encode()).hexdigest(), 16)
   t.append(tmp * tmp * tmp - (cipher[i] ^ (lcg.next() >> 192)))
print(GCD(GCD(GCD(t[0], t[1]), t[2]), t[3]))

得到seed就可以解出flag了。

seed = 93161902675685857786932571331038018595140621249413834179036444009740278201203
hashtable = []
flag = ''
lcg = my_LCG()
for i in printable:
tmp = int(sha256(i.encode()).hexdigest(), 16)
hashtable.append(pow(tmp, 3, seed))
for i in range(len(cipher)):
tmp = lcg.next() >> 192
ind = hashtable.index(tmp ^ cipher[i])
flag += printable[ind]
print(flag)

END


文章来源: https://mp.weixin.qq.com/s?__biz=MzI2OTUzMzg3Ng==&mid=2247490529&idx=4&sn=45be8470ff0d28ceed865d5624cc7145&chksm=eadf8c3adda8052c2a6bddc7249c86d837246fd8a32d130180f5e5051c279f449298ccfca27f&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh