2020 新春战疫网络安全公益赛 WriteUp
🏛️ 组织架构
赛事由永信至诚旗下的春秋 Game 与 i 春秋团队牵头主办
联合发起方:包括安全狗、百度、哔哩哔哩、滴滴、京东、快手、美团、腾讯、小米、字节跳动等在内的 34 家互联网公司及知名企业
联合命题战队:Nu1L、Venom、Xp0int、W&M、kn0ck、VIDAR-TEAM、Ginkgo、Timekeeper、SU、0RAYS等 10 支国内知名网络安全战队参与了命题,并将酬劳兑换为物资捐赠
专家委员会:邀请了 Atum、L1n3、Pcat、seabreeze、Veneno7、吴炜等技术领袖组成专家委员会,负责赛事质量把关
🏆 比赛结果与影响
本次比赛不设现金奖励,官方也未公布完整的个人排名榜单,其核心成果体现在公益捐赠上
公益捐赠:大赛以参赛选手、联合发起单位、出题人及获奖选手的共同名义,向疫区捐赠了 180 箱医疗防护物资,并通过中国邮政的防疫物资运输通道,直接送达武汉市第一医院等一线医护人员手中
赛事评价:作为 2020 年第一场网络安全界大规模比赛,它汇聚了多方安全力量,用网安人特有的方式践行了社会责任,获得了参赛者的一致好评
Misc
磁盘套娃
Crypto
simple_math(RSA Wilson 定理 + 利用 ed 同余关系分解 n)

题目核心代码如下
1 | def gen_p(): |
最后加密
1 | _E = 65537 |
所以真正用来加密 flag 的 RSA 模数是 N = _P * _Q,我们要分别恢复 _P 和 _Q
题目给了 A 和 C,并且
1 | B = (A - 1) // 2 |
其中 C = A - randint(1e3, 1e5),所以 C 和 A 非常接近
令 k = A - C,附件里实际算出来 k = 67381,也就是说 C = A - 67381
如果直接算 C! mod A 肯定不行,因为 C 是一个 513 bit 级别的大数
Wilson 定理一句话:如果 A 是素数,那么 (A - 1)! ≡ -1 (mod A)
阶乘定义是 n! = 1 * 2 * 3 * …… * n,也就是说 1 * 2 * 3 * …… * (A - 1) ≡ A - 1 (mod A)
因为 -1 + A = A - 1,所以 A - 1 ≡ -1 (mod A)
由同余的传递性,得到 (A - 1)! ≡ -1 ≡ A - 1 (mod A)
题目里 C = A - K,因为 K 是正整数,且 K < A,所以 1 < C < A
也就是说 C 是 1 到 A - 1 之间的一个整数
因此 1 * 2 * 3 * …… * (A - 1) 可以拆成两段:第一段是从 1 到 C,第二段是从 C + 1 到 A - 1
1 | (A - 1)! = (1 * 2 * 3 * …… * C) * ((C + 1) * (C + 2) * …… * (A - 1)) |
而 1 * 2 * 3 * …… * C = C!,所以
1 | (A - 1)! = (1 * 2 * 3 * …… * C) * ((C + 1) * (C + 2) * …… * (A - 1)) |
又因为 C = A - K,所以 K = A - C,于是
1 | C + 1 = A - K + 1 = A - (K - 1) |
一直到最后 A - 1 = A - 1,所以第二段变成 (A - (K - 1) * A - (K - 2) * …… * (A - 1))
在模 A 下
1 | A - (K - 1) ≡ -(K - 1) |
所以第二段模 A 等于 (-(K - 1) * (-(K - 2)) * …… * (-1))
一共有 K - 1 个负号,所以 (-1) ^ (K - 1) * (K - 1) * (K - 2) * …… * 1
也就是 (-1) ^ (K - 1) * (K - 1)!,再代回去
1 | (A - 1)! = C! * ((C + 1) * (C + 2) * …… * (A - 1)) |
由于 A 是素数,Wilson 定理得到 (A - 1)! ≡ -1 (mod A)
所以 -1 ≡ C! * (-1) ^ (K - 1) * (K - 1)! (mod A)
两边同时乘以 (-1) ^ (K - 1)
1 | -1 * (-1) ^ (K - 1) = (-1) ^ k |
所以 (-1) ^ k ≡ C! * (K - 1)! (mod A),两边同时乘以 (K - 1)! 的逆元
因为 A 是素数,且 K - 1 < A,所以 1 * 2 * 3 * …… * (K - 1) = (K - 1)! 与 A 互素,即
1 | gcd((K - 1)!, A) = 1 |
所以 (K - 1)! 在模 A 下存在逆元,设这个逆元为 inv = ((K - 1)!) ^ (-1) (mod A)
1 | (-1) ^ k * inv ≡ C! * (K - 1)! * inv (mod A) |
因为 (K - 1)! * inv = 1 (mod A)
1 | (-1) ^ k * inv ≡ C! * ((K - 1)! * inv) (mod A) |
因此 N2 = C! (mod A) = (-1) ^ k * ((K - 1)!) ^ (-1) (mod A)
接下来继续恢复 N1 = ((A - 1) // 2)! mod A,令 m = (A - 1) / 2
根据 Wilson 定理:(A - 1)! ≡ -1 mod A,把 (A - 1)! 从 m 处切开
1 | (A - 1)! = (1 * 2 * 3 * …… * m) * ((m + 1) * (m + 2) * …… * (A - 1)) |
前半段为 1 * 2 * 3 * …… * m = m!,后半段在模 A 下
1 | m + 1 = -m (mod A) |
所以
1 | (m + 1) * (m + 2) * …… * (A - 1) ≡ (-m) * (-(m - 1)) * …… * (-1) (mod A) |
这里一共有 m 个负号,所以
1 | (-m) * (-(m - 1)) * …… * (-1) = (-1) ^ m * m * (m - 1) * …… * 1 |
而 m * (m - 1) * …… * 1 = m!,所以
1 | (m + 1) * (m + 2) * …… * (A - 1) ≡ (-m) * (-(m - 1)) * …… * (-1) (mod A) |
由 Wilson 定理:(A - 1)! ≡ -1 (mod A),所以
1 | (A - 1)! ≡ (-1) ^ m * (m!) ^ 2 (mod A) |
因为 A ≡ 3 (mod 4),设 A = 4t + 3,则
1 | m = (A - 1) / 2 |
因为 m 是奇数,因此
1 | (-1) ^ m = (-1) ^ (2t + 1) |
代入 -1 ≡ (-1) ^ m * (m!) ^ 2 (mod A) 中,得到
1 | -1 ≡ (-1) ^ m * (m!) ^ 2 (mod A) |
两边同时乘以 −1
1 | 1 ≡ (m!) ^ 2 (mod A) |
而 N1 ≡ m! (mod A),所以 N1 ^ 2 ≡ 1 (mod A)
1 | N1 ^ 2 ≡ 1 (mod A) |
因为 A 是素数,所以 A | (N1 - 1) 或 A | (N1 + 1),于是
1 | N1 ≡ 1 (mod A) 或 N1 ≡ -1 (mod A) |
即 N1 = 1 或 N1 = A - 1
题目里
1 | seed1 = 2019 * N1 + 2020 * N2 |
所以对 N1 的两个取值,分别计算一次 seed1,得到两个候选 _p
当 N1 = 1 时,得到 _P = 29980405391237365494597168525833309900267720449837722663797882464292688736399471276643180209621186905684266642360886383187160507504219009609357296973525651439
题目给了
1 | n = p * q |
其中 e * d ≡ 1 mod phi(n),所以 e * d - 1 = k * phi(n)
也就是说,题目泄露了 ed,相当于泄露了一个 phi(n) 的倍数
对于任意与 n 互素的随机数 a,都有 a ^ (ed - 1) ≡ 1 (mod n)
把 ed - 1 拆成 ed - 1 = 2 ^ s * r,其中 r 是奇数,然后取随机底数 a,计算
1 | x = pow(a, r, n) |
不断平方 x = x ^ 2 mod n,如果某一步出现 x ^ 2 ≡ 1 mod n,但是
1 | x != 1 |
那么 x 是模 n 下的非平凡平方根,可以用 gcd(x - 1, n) 分解出 n 的一个因子
1 | import re |