news 2026/8/7 10:17:52

【CTF-CRYPTO-教学-RSA】第五节:9位数小 n 分解攻击

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【CTF-CRYPTO-教学-RSA】第五节:9位数小 n 分解攻击

背景

前面我们学的攻击手段(共模攻击、dp 泄露)都需要"额外泄露"一些信息才能下手。
但现实里很多新手 RSA 题目根本不需要任何"花活"——只要 n 选得太小,直接把 n 分解掉,私钥就到手了

RSA 的安全性完全建立在大整数分解的困难性上

  • 公钥 (e, n) 是公开的,n = p × q
  • 只要攻击者能把 n 拆回 p 和 q,就能算出 φ(n) = (p-1)(q-1)
  • 有了 φ(n),私钥 d = e⁻¹ mod φ(n) 就能直接算出来
  • 于是任意密文都能解密

当 n 足够大(如 2048 位)时,分解 n 在算力上不可行;但当 n 很小(如几十位、甚至 9~10 位十进制数)时,用最朴素的"试除法"就能秒分解。

什么是"逐字符加密"及其弱点

很多题目为了"看起来密文很长",会把明文的每一个字符单独加密,得到一长串密文:

明文 "flag" → [加密('f'), 加密('l'), 加密('a'), 加密('g')]

这其实就是 RSA 的ECB 模式(无填充),它有两个致命弱点:

  1. 明文空间极小:每个明文块只是一个字符,ASCII 范围 32~126,一共才 95 种可能。如果题目再提示"只有数字和小写字母",那明文只有 36 种可能。
  2. 相同明文 → 相同密文:没有随机填充,同一个字符每次加密结果都一样。密文里只要出现重复值,就说明明文里有重复字符,泄露了模式。

这两个弱点带来两种攻击姿势:

  • 方法一(分解 n):把小 n 分解掉,求出 d,对每个密文m = pow(c, d, n)解密,再chr(m)还原字符。
  • 方法二(暴力查表,无需分解 n):既然明文只有几十种可能,干脆把每个候选字符 m 都加密一次c = pow(m, e, n),建立c → m的反查表,然后对着密文逐个反查即可。连 n 都不用分解!

当明文空间远小于密钥空间时,"加密"变成了一个可逆的查表游戏——这正是无填充逐字符加密的悲哀。

简单例子

我们用p=3, q=11来演示,数字小到可以在草稿纸上算完。

生成密钥

  • n = p × q = 3 × 11 =33
  • φ(n) = (p-1)(q-1) = 2 × 10 =20
  • 选 e = 3(gcd(3, 20) = 1 ✓),公钥 (e, n) = (3, 33)
  • 求私钥 d:3 × d ≡ 1 (mod 20)
    • 3 × 7 = 21 = 20 + 1 ≡ 1 ✓
    • 所以d = 7,私钥 (d, n) = (7, 33)

逐字符加密

为方便手算,我们用"字母表位置"给明文编号:a=1, b=2, c=3, …

要加密消息“cab”→ 明文序列 [3, 1, 2]

加密公式 c = mᵉ mod n = m³ mod 33:

明文 m计算 m³ mod 33密文 c
3 ( c )3³ = 2727
1 ( a )1³ = 11
2 ( b )2³ = 88

攻击者最终只看到:n=33, e=3,以及密文序列[27, 1, 8]

攻击一:分解 n 求私钥

n=33 小到一眼可分:33 = 3 × 11

  • φ(n) = (3-1)*(11-1)=20
  • e*d≡1 mod φ(n)推出 d = 7

解密 m = cᵈ mod n = c⁷ mod 33:

密文 27:27 ≡ -6 (mod 33)

  • (-6)² = 36 ≡ 3
  • (-6)⁴ ≡ 3² = 9
  • (-6)⁷ = (-6)⁴ × (-6)² × (-6) = 9 × 3 × (-6) = -162 ≡ -162 + 5×33 = 3
  • → m = 3 →‘c’

密文 1:1⁷ = 1 → m = 1 →‘a’

密文 8

  • 8² = 64 ≡ 64 - 33 = 31
  • 8⁴ ≡ 31² = 961 = 29×33 + 4 ≡ 4
  • 8⁷ = 8⁴ × 8² × 8 = 4 × 31 × 8 = 992 = 30×33 + 2 ≡ 2
  • → m = 2 →‘b’

恢复明文序列 [3, 1, 2] →“cab”✓ 闭环成功!

攻击二:查表法(不分解 n)

明文空间只有 az(126)共 26 种,干脆全部加密一遍建反查表:

m=1 → c=1 m=2 → c=8 m=3 → c=27 ...

拿到密文 [27, 1, 8] 直接反查:27→3©、1→1(a)、8→2(b) →“cab”

整个过程没有用到 p、q、d,只用了公开的 e 和 n。可见"逐字符无填充"比"n 太小"还要致命——即使 n 大到分不动,只要明文空间小,查表法照样秒杀。

代码实现

# ============================================================# RSA 小 n 分解 + 逐字符加密(手算例子)# ============================================================## 场景: p=3, q=11, n=33, e=3, d=7# 明文用字母表位置 a=1..z=26 编码,逐字符加密# 攻击:# 方法一: 分解 n=33 → 求 d → 解密每个密文# 方法二: 暴力查表,把 1..26 全加密一遍反查(无需分解 n)# ============================================================deffactor_by_trial(n):"""试除法分解小 n,返回 (p, q)"""i=2whilei*i<=n:ifn%i==0:returni,n//i i+=1raiseValueError("n 是素数,无法分解为两个 >1 的因子")defmain():# ---- 公钥 ----n=33e=3# ---- 原始明文 "cab",用字母表位置编码 ----plain="cab"ms=[ord(ch)-96forchinplain]# a=1, b=2, c=3print(f"明文:{plain}-> 明文序列{ms}")# ---- 加密(逐字符)----cs=[pow(m,e,n)forminms]print(f"密文序列:{cs}")print()# ============ 方法一:分解 n 求私钥 ============print("=== 方法一:分解 n 求私钥 ===")p,q=factor_by_trial(n)print(f"分解 n={n}={p}×{q}")phi=(p-1)*(q-1)d=pow(e,-1,phi)print(f"φ(n) ={phi}, d ={d}")recovered1=''.join(chr(pow(c,d,n)+96)forcincs)print(f"解密结果:{recovered1}")print()# ============ 方法二:暴力查表(不分解 n)============print("=== 方法二:暴力查表(不分解 n)===")table={pow(m,e,n):mforminrange(1,27)}# c -> mrecovered2=''.join(chr(table[c]+96)forcincs)print(f"解密结果:{recovered2}")if__name__=="__main__":main()

运行结果:

明文: cab ->明文序列[3,1,2]密文序列:[27,1,8]===方法一:分解 n 求私钥===分解n=33=3×11φ(n)=20, d=7解密结果: cab===方法二:暴力查表(不分解 n)===解密结果: cab

作业:RSA roll

题目

https://ctf2.dasctf.com/dashboard/practice/b9bbb32f-f186-458f-b90b-12440c0f6aea?tab=challenges&challenge=542e42ea-2a5c-44c8-8801-9b30b7e1a973

RSA rollrollroll Only number and a-z don't use editor which MS provide

data.txt

{920139713,19} 704796792 752211152 274704164 18414022 368270835 483295235 263072905 459788476 483295235 459788476 663551792 475206804 459788476 428313374 475206804 459788476 425392137 704796792 458265677 341524652 483295235 534149509 425392137 428313374 425392137 341524652 458265677 263072905 483295235 828509797 341524652 425392137 475206804 428313374 483295235 475206804 459788476 306220148

解题过程

第一步:读懂题目格式

  • {920139713, 19}就是公钥(n, e) = (920139713, 19)
  • 下面一长串数字,每行一个,是密文序列(逐字符加密的结果)
  • 提示解读:
    • “Only number and a-z”:flag 内容只含数字和小写字母 → 明文空间极小,非常适合查表
    • “roll roll roll”:密文逐行"滚动"排列;仔细看会发现有大量重复值(如459788476出现了 6 次),这正是无填充逐字符加密的指纹——同一个字符加密结果必然相同
    • “don’t use editor which MS provide”:暗示题目足够简单,甚至不需要打开微软家的编辑器写复杂代码,靠计算/查表即可拿下

第二步:观察密文规律

把出现过的密文和它在序列里出现的次数统计一下,会发现只有 17 种不同的密文,对应 17 个不同字符,而密文总数有 38 个——重复率极高,坐实了"逐字符无填充加密"。

第三步:方法一 —— 分解 n 求私钥

n = 920139713 只有 9 位十进制,用最朴素的试除法就能秒分:

  • 920139713 = 18443 × 49891
  • φ(n) = (18443-1)(49891-1) = 18442 × 49890 =920071380
  • d = e⁻¹ mod φ(n) = 19⁻¹ mod 920071380 =96849619
  • 验证:19 × 96849619 mod 920071380 = 1 ✓

然后对每个密文m = pow(c, d, n),再chr(m)还原字符。

第四步:方法二 —— 暴力查表(无需分解 n)

明文只是可打印 ASCII(数字、小写字母,外加flag{}几个符号),范围 32~126,共 95 种。

预计算c = pow(m, e, n)for m in 32…126,建c → m反查表,再对 38 个密文逐个反查即可,全程不需要 p、q、d

两种方法殊途同归,得到同一段明文。

具体实现代码

# ============================================================# 作业4: RSA roll —— 小 n 分解 + 逐字符加密# ============================================================## 已知: n=920139713, e=19, 以及一串逐字符加密的密文# 目标: 恢复明文 flag## 思路:# 方法一: n 只有 9 位,试除法分解 → 求 d → 逐个解密 chr(pow(c,d,n))# 方法二: 明文空间极小,把 32~126 全加密一遍建反查表,逐个反查# ============================================================deffactor_by_trial(n):"""试除法分解小 n,返回 (p, q)"""i=2whilei*i<=n:ifn%i==0:returni,n//i i+=1raiseValueError("n 是素数,无法分解为两个 >1 的因子")defmain():# ---- 题目参数 ----n=920139713e=19cs=[704796792,752211152,274704164,18414022,368270835,483295235,263072905,459788476,483295235,459788476,663551792,475206804,459788476,428313374,475206804,459788476,425392137,704796792,458265677,341524652,483295235,534149509,425392137,428313374,425392137,341524652,458265677,263072905,483295235,828509797,341524652,425392137,475206804,428313374,483295235,475206804,459788476,306220148,]print("=== 作业4: RSA roll ===")print(f"公钥 (n, e) = ({n},{e})")print(f"密文个数:{len(cs)}个,不同密文:{len(set(cs))}种")print()# ============ 方法一:分解 n 求私钥 ============print("=== 方法一:分解 n 求私钥 ===")p,q=factor_by_trial(n)print(f"分解 n ={p}×{q}")print(f"验证 p*q == n ?{p*q==n}")phi=(p-1)*(q-1)d=pow(e,-1,phi)print(f"φ(n) ={phi}")print(f"d = e^(-1) mod φ ={d}")print(f"验证 (e*d) mod φ ={(e*d)%phi}")msg1=''.join(chr(pow(c,d,n))forcincs)print(f"解密结果:{msg1}")print()# ============ 方法二:暴力查表(不分解 n)============print("=== 方法二:暴力查表(不分解 n)===")table={pow(m,e,n):mforminrange(32,127)}# c -> mmsg2=''.join(chr(table[c])forcincs)print(f"解密结果:{msg2}")print()# ============ 顺带打印 密文 -> 字符 映射 ============print("=== 出现过的 密文 -> 字符 映射 ===")forcindict.fromkeys(cs):print(f"{c}->{repr(chr(table[c]))}")if__name__=="__main__":main()

运行结果:

===作业4: RSA roll===公钥(n, e)=(920139713,19)密文个数:38个,不同密文:17===方法一:分解 n 求私钥===分解 n=18443×49891验证 p*q==n ? True φ(n)=920071380d=e^(-1)mod φ=96849619验证(e*d)mod φ=1解密结果: flag{13212je2ue28fy71w8u87y31r78eu1e2}===方法二:暴力查表(不分解 n)===解密结果: flag{13212je2ue28fy71w8u87y31r78eu1e2}===出现过的 密文 ->字符 映射===704796792->'f'752211152->'l'274704164->'a'18414022->'g'368270835->'{'483295235->'1'263072905->'3'459788476->'2'663551792->'j'475206804->'e'428313374->'u'425392137->'8'458265677->'y'341524652->'7'534149509->'w'828509797->'r'306220148->'}'

答案

flag{13212je2ue28fy71w8u87y31r78eu1e2}

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/7 10:17:44

工业 IoT 到底该用 Historian 还是时序数据库

工业 IoT 项目里&#xff0c;Historian 和通用 time-series database 经常被放在同一个采购或架构选型表里比较。这个比较本身没有错&#xff0c;但如果问题被简化成“哪一个更先进”&#xff0c;项目很容易走偏。本文的核心结论是&#xff1a;Historian 更适合承接高可靠现场采…

作者头像 李华
网站建设 2026/8/7 10:16:14

神秘黑题,不要外传

从洛谷来的盆友&#xff0c;代码在下面&#xff0c;你不ac我吃。但是需要关注QWQ&#xff0c;球球了。给个赞吧给个赞吧给个赞吧给个赞吧给个赞吧给个赞吧给个赞吧给个赞吧给个赞吧给个赞吧给个赞吧给个赞吧给个赞吧给个赞吧给个赞吧给个赞吧给个赞吧给个赞吧给个赞吧给个赞吧给…

作者头像 李华
网站建设 2026/8/7 10:14:59

(三十六)无RO的公钥加密——ElGamal加密方案

先来看具体方案: 下面给出一个定理: 插入一条分析:如果DDH问题是困难的,则ElGamal加密方案 在IND-CPA安全模型下是可证明安全的, 注意这里的定理中并没有RO。 其规约损失为L=2为什么规约损失是2呢? 先继续往下看证明如下: 假定存在一个敌手A\mathscr A

作者头像 李华
网站建设 2026/8/7 10:14:49

北斗gps卫星同步时钟应用解决方案

一、方案概述本方案以西安同步电子SYN4103卫星同步时钟为核心时频基准设备&#xff0c;针对机房网络设备、安防监控、工控及电力自动化系统&#xff0c;搭建一套部署简单、运行稳定、免人工维护的全网统一时间频率同步体系。设备核心作用是为全场系统提供高精度标准时间、频率基…

作者头像 李华
网站建设 2026/8/7 10:12:47

从零构建PSoC™6的RT-Thread BSP:驱动移植与系统集成实战

1. 项目概述&#xff1a;为什么我们要为PSoC™6制作BSP&#xff1f; 如果你正在嵌入式领域&#xff0c;特别是物联网边缘设备方向深耕&#xff0c;那么Infineon的PSoC™6系列芯片大概率已经进入了你的视野。这款芯片以其独特的双核架构&#xff08;Cortex-M4 Cortex-M0&#x…

作者头像 李华
网站建设 2026/8/7 10:12:02

Java开发中忽略方法返回值的危害与治理实践

在实际开发中&#xff0c;我们经常遇到一个看似简单却容易引发线上故障的场景&#xff1a;一个方法或接口的返回值&#xff0c;在业务逻辑中被直接忽略&#xff0c;没有进行任何处理。例如&#xff0c;调用一个删除文件的方法后&#xff0c;不检查其返回值&#xff1b;或者调用…

作者头像 李华