ez_login
题目说以 admin 身份登录,即可获得 flag
不管是不是真的,总得先试试吧,而且下面也验证了这句话是对的
1 return render_template('index.html' , username=username, flag=FLAG if username == "admin" else None )
在未配置环境变量的情况下,ADMIN_PASS = os.getenv(“ADMIN_PASS”, “admin123”)
值得注意的是,代码里还有 AES 加密,但加密并不应用于首次登录。当用户登录成功时,网站会通过 create_session 函数加密 cookie,从而使得正常登录的用户保持登录态(即无需再次输入密码)并拦截试图篡改 cookie 以绕过密码的行为。想法很好,但关我正常用户什么事(
这道题并没有配置额外的环境变量,所以访问后输入 username = admin, password = admin123 即可
神秘学
鉴定为高中数学
代码中,get_poly 函数先给了一个函数
p o l y = x 3 − a x 2 + b x − c − k n
,用 randint 函数对参数赋随机值之后,返回它的一阶导数
p o l y ′ = 3 x 2 − 2 a x + b
。
在最后,
p o l y ′
和
x
的值都被给出了。虽然没有
p o l y
的值,但最后的负数项通常表示这个函数是移项得到的,即
p o l y = 0
。于是有
1 2 3 b = deriv1_num % x1 a = (3 * x1 * x1 + b - deriv1_num) // (2 * x1) c = pow (x1, 3 ) - a * x1 * x1 + b * x1 - k * n
同时我们发现
k
很小,只有 8 位,允许爆破。
在主函数中,
e = i n v e r s e ( c , p h i )
。
c
充当了标准 RSA 中的
d
。
于是有脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from Crypto.Util.number import long_to_bytesfrom sympy import isprime n = x1 = deriv1_num = cipher = b = deriv1_num % x1 a = (3 * x1 * x1 + b - deriv1_num) // (2 * x1)for k in range (2 **8 ): if isprime(k) == False : continue try : c = pow (x1, 3 ) - a * x1 * x1 + b * x1 - k * n m = pow (cipher, c, n) flag = long_to_bytes(m) print (flag.decode()) except : pass
truck
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 for _ in range (10 ): A, B, C = bytes .fromhex(input ('A > ' )), bytes .fromhex(input ('B > ' )), bytes .fromhex(input ('C > ' )) ha, hb, hc = _H(A), _H(B), _H(C) assert ha == hb == hc D, E, F = bytes .fromhex(input ('D > ' )), bytes .fromhex(input ('E > ' )), bytes .fromhex(input ('F > ' )) hd, he, hf = _H(ha + D), _H(hb + E), _H(hc + F) assert hd == he == hf G, H, I = bytes .fromhex(input ('G > ' )), bytes .fromhex(input ('H > ' )), bytes .fromhex(input ('I > ' )) assert _H(hd + G) == _H(he + H) == _H(hf + I) cur = (A, B, C, D, E, F, G, H, I) assert len (set (cur)) == 9 assert not any (x in S for x in cur) S.update(cur)
在容器中,我们每次被要求输入 9 个数据,循环 10 次。在每轮中,输入的数据需要满足:
A, B, C 的 md5 相同
md5(A)+D, md5(B)+E, md5©+F 的 md5 相同
上一步的 md5 拼接 G, H, I 后 md5 依然相同
并且 90 个原始数据不能相同。
在 github 上搜索 fastcoll,这个软件专门用于生成 md5 碰撞。
对于三个 md5 的碰撞,可以先生成两个,再用其中一个.bin 文件做前缀,继续生成两个:
1 2 fastcoll -p prefix.txt -o msg1 msg2 fastcoll -p msg1 -o msg3 msg4
最后取三个就行了。根据这个思想,用 ai 写一个能迭代使用的碰撞函数:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 def generate_three_collisions (prefix: bytes ) -> list : """ 给定一个二进制前缀 prefix,生成三个不同的后缀数据块, 使得 md5(prefix + suffix) 全部相等。 返回包含三个 bytes 对象的列表。 """ with tempfile.NamedTemporaryFile(delete=False ) as f: prefix_file = f.name f.write(prefix) out1 = tempfile.NamedTemporaryFile(delete=False ).name out2 = tempfile.NamedTemporaryFile(delete=False ).name subprocess.run( [FASTCOLL_PATH, "-p" , prefix_file, "-o" , out1, out2], check=True , stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) with open (out1, "rb" ) as f: data1 = f.read() with open (out2, "rb" ) as f: data2 = f.read() suffix1 = data1[-128 :] suffix2 = data2[-128 :] out3 = tempfile.NamedTemporaryFile(delete=False ).name out4 = tempfile.NamedTemporaryFile(delete=False ).name subprocess.run( [FASTCOLL_PATH, "-p" , out1, "-o" , out3, out4], check=True , stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) with open (out3, "rb" ) as f: data3 = f.read() with open (out4, "rb" ) as f: data4 = f.read() suffix3 = data3[-128 :] suffix4 = data4[-128 :] result = [ suffix1 + suffix3, suffix1 + suffix4, suffix2 + suffix3, ] for path in [prefix_file, out1, out2, out3, out4]: os.remove(path) return result
重复执行 10 轮就能得到所有数据。
sda
四个断言中:
1 2 3 4 z_abs_bound = ((pi - qi) / (3 * (pi + qi))) * (y**2 ) * (Ai ** (1 / 4 ))assert abs (zs[i]) < z_abs_boundassert xs[i] ** 2 < A**deltaassert y**2 < A**delta
和 x i 2 < A δ 和 y i 2 < A δ , | z i | < p i − q i 3 ( p i + q i ) ⋅ y 2 ⋅ A i 1 / 4 .
1 assert Bs[i] * xs[i] ** 2 - y**2 * euler_phi(Ai) == zs[i]
B i x i 2 − y 2 ϕ ( A i ) = z i
值得注意的是,
A i
都很小,从而能被质因数分解得到
p i
和
q i
,进而得到
ϕ ( A i )
设
P i = ϕ ( A i )
,再设
X i = x i 2
,
Y = y 2
,就能得到三个等式:
B 1 X 1 − P 1 Y = z 1 B 2 X 2 − P 2 Y = z 2 B 3 X 3 − P 3 Y = z 3
在这个方程组里,已知量是
B i
和
P i
,未知量是
X 1 , X 2 , X 3 , Y
以及误差项
z 1 , z 2 , z 3
。使用格密码,基向量设为
V = ( X 1 , X 2 , X 3 , Y )
。根据上面的等式,我们要让
z i
在目标向量上出现。设目标向量
T = ( X 1 , X 2 , X 3 , Y , z 1 , z 2 , z 3 )
根据关系构造未配平的格基:
M = ( 1 0 0 0 B 1 0 0 0 1 0 0 0 B 2 0 0 0 1 0 0 0 B 3 0 0 0 1 − P 1 − P 2 − P 3 )
但根据三条规定了未知量大小的断言,
z i
的量级大约是
A 5 / 8
,而
X i
和
Y
的量级大约只有
A 3 / 8
。为了让目标向量的长度远小于其它杂向量,我们加入权重系数
W = A 1 / 4
,得到新格基:
M = ( W 0 0 0 B 1 0 0 0 W 0 0 0 B 2 0 0 0 W 0 0 0 B 3 0 0 0 W − P 1 − P 2 − P 3 )
经过 LLL 约简,得到向量
( W X 1 , W X 2 , W X 3 , W Y , z 1 , z 2 , z 3 )
进而得到
x 1 2 , x 2 2 , x 3 2
和
y 2
,最后 AES 解密即可。
ecc
学了一段时间,来补一下这道题的 wp。
定义的三个函数是椭圆曲线上的加法(包括了相同和相异的点)以及倍加。尝试直接破解,sage 回显“defines a singular curve”,说明
Δ = 0
,这是一条奇异曲线。
调用奇异曲线脚本,得到私钥,进而得到 flag
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 def singular_curve_attack (p, a1, a2, a3, a4, a6, P, Q ): p = p F = GF(p) R_x.<x> = F[] a1 = F(a1);a2 = F(a2);a3 = F(a3);a4 = F(a4);a6 = F(a6) Px = F(P[0 ]);Py = F(P[1 ]) Qx = F(Q[0 ]);Qy = F(Q[1 ]) yx = - (a1*x + a3)/F(2 ) fx = a1*yx - F(3 )*x*x - F(2 )*a2*x - a4 roots = fx.roots() x0, y0 = None , None for root_couple in roots: x_temp = root_couple[0 ] y_temp = - (a1*x_temp + a3)/F(2 ) val = y_temp^2 + a1*x_temp*y_temp + a3*y_temp - x_temp^3 - a2*x_temp^2 - a4*x_temp - a6 if val == F(0 ): x0, y0 = x_temp, y_temp print ("得到奇点坐标" ) break if x0 is None : print ("未找到奇点" ) exit() c = F(3 )*x0 + a2 R_t.<t> = F[] ft = t^2 + a1*t - c roots = ft.roots() if len (roots) == 2 : print ("奇点是结点" ) m1 = roots[0 ][0 ] m2 = roots[1 ][0 ] PX = Px - x0;PY = Py - y0 QX = Qx - x0;QY = Qy - y0 uP = (PY - m1*PX) / (PY - m2*PX) uQ = (QY - m1*QX) / (QY - m2*QX) k = discrete_log(uQ, uP) print (f"得到k = {k} " ) elif len (roots) == 1 : print ("奇点是尖点" ) m = roots[0 ][0 ] PX = Px - x0;PY = Py - y0 QX = Qx - x0;QY = Qy - y0 tauP = PX / (PY - m*PX) tauQ = QX / (QY - m*QX) k = tauQ / tauP print (f"得到k = {k} " ) else : print ("奇点是非分裂结点" ) K.<a> = F.extension(2 ) ft_ext = ft.change_ring(K) roots = ft.ext.roots() m1 = roots[0 ][0 ] m2 = roots[1 ][0 ] PX = K(Px - x0);PY = K(Py - y0) QX = K(Qx - x0);QY = K(Qy - y0) uP = (PY - m1*PX) / (PY - m2*PX) uQ = (QY - m1*QX) / (QY - m2*QX) k = discrete_log(uQ, uP, ord = p + 1 ) print (f"得到k = {k} " )