2018 网鼎杯-第四场 WriteUp
📝 赛事概况
比赛赛制:采用线上 CTF(夺旗赛) 模式。参赛队伍通过解题获取 Flag 以获得分数
参赛对象:主要面向电子政务、科技、互联网企业、网信等行业
参赛规模:据当时报道,共有 1483 支队伍参与本场比赛的角逐
晋级规则:线上预选赛每场取前 50 名晋级线下半决赛,四场共计 200 支队伍入围
Crypto
shanghai(维吉尼亚密码)

密文第一行是
1 | bju lcogx fisep vjf pyztj sdgh 13 gifc qsxw. pkiowxc |
其中 fisep vjf pyztj sdgh,根据题目名 shanghai 和数字 13,可以猜测它对应
1 | shang hai metro line |
如果用维吉尼亚密码反推出 key,可以得到一段密钥流
1 | nbsrj ojx dugcv hvtd |
继续结合高频词,例如密文中的 glv 很可能是英文高频词 the,可以推出密钥片段 ner
并最终推测出 key 和 vigenere 相关
最后可以确定周期密钥本质是 vigenereicq
但从全文第一个字母开始对齐时,实际使用的 key 相位是它的一个循环位移
1 | icqvigenere |

apl(APL 表达式)
文件内容本身是 Base64,解码后得到 APL 表达式
核心是中间这个函数,它把输入字符串做一堆矩阵变换、二进制编码、异或操作后,与左侧密文数组比较
1 | {⍵(~⍵)/('No_Please_continue')('Yes,This_is_flag')} |
1 | from pathlib import Path |
shenyue2(RSA 近似分解 n)

源码里先把 FLAG 转成整数,然后设置 RSA 参数:e=65537,随机生成两个 2048 bit 素数 p,q,并给了一个固定的 100 bit 左右的 r
关键泄露点在这里:
1 | phi = (p-1)*(q-1) |
标准 RSA 中:
1 | n = p * q |
所以一定存在一个整数 t,使得
1 | e * d = 1 + t * phi |
所以
1 | d = (1 + t * phi) / e |
因为 0 < d < phi,所以
1 | 1 <= t < e |
这就给了我们爆破空间,把泄露的 k 代入
1 | k = (p - r) * d |
又因为 e * d = 1 + t * phi,所以
1 | e * k = (p - r) * (1 + t * phi) |
在 RSA 中
1 | phi = (p - 1)(q - 1) |
代入得到
1 | e * k = (p - r) * (1 + t * (n - p - q + 1)) |
这个式子里,n,e,k,r 已知,t,p,q 未知
对于真实的 t
1 | phi = n - p - q + 1 |
而 p + q 相比 n 非常小,所以
1 | phi = n - (p + q - 1) |
于是
1 | e * k = (p - r) * (1 + t * phi) |
所以
1 | p ≈ e * k / (t * n) + r |
于是解题策略变成:
枚举
t = 1 ~ 65536计算
p0 = floor(e*k / (t*n)) + r在
p0附近小范围搜索如果某个候选
p_candidate可以整除n,说明找到了真正的p
1 | import re |
运行结果
1 | [+] found t = 38634 |
Misc
双色块(文件附加隐写 + 色块隐写 + Base64 + DES)

文件尺寸为 240 × 240,动画共有 576 帧,每帧中都有一个 10×10 的彩色方块,方块位置依次覆盖整个 24×24 网格
两种颜色分别为绿色和品红色,因此可以将两种颜色看成二进制 0 和 1
末尾附加了一张 PNG 图片

将二进制转为 ASCII 码得到
1 | o8DlxK+H8wsiXe/ERFpAMaBPiIcj1sHyGOMmQDkK+uXsVZgre5DSXw==hhhhhhhhhhhhhhhh |
去除末尾的填充得到
1 | o8DlxK+H8wsiXe/ERFpAMaBPiIcj1sHyGOMmQDkK+uXsVZgre5DSXw== |
Base 64 解码得到
1 | a3c0e5c4af87f30b225defc4445a4031 |
DES 解密得到 flag{2ce3b416457d4380dc9a6149858f71db}\x00\x00,密钥在图片中给出了