不仅仅是RSA–CTF密码学综合题目完整解析

admin 2026-01-12 01:18:30 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 文档详细解析了一道CTF密码学题目,核心在于通过代码审计发现RSA加密脚本中复用素数q的漏洞,导致两个模数共享公因子。解题过程涵盖公钥提取、音频摩斯电码解码获取密文、利用GCD算法分解模数以及最终解密还原Flag。文章深入剖析了共享因子攻击原理,并结合Python实践代码,强调了密码学实现中代码审计与密钥管理的重要性。 综合评分: 88 文章分类: CTF,代码审计,漏洞分析


cover_image

不仅仅是RSA – CTF密码学综合题目完整解析

原创

破镜安全

破镜安全

2026年1月11日 08:00 四川

不仅仅是RSA – CTF密码学综合题目完整解析

题目背景

本题是一道综合性的CTF密码学挑战题,题目名称”不仅仅是RSA”揭示了这道题的特点:虽然核心是RSA加密算法,但解题过程涉及多个技术领域的知识。这道题目考察了参赛者对RSA密码学原理的理解、代码审计能力、信号处理技能以及综合运用多种工具的能力。

题目文件清单

题目提供了以下文件:

  1. RSA.py – 加密脚本源代码
  2. pubkey1.pem – 第一个RSA公钥文件
  3. pubkey2.pem – 第二个RSA公钥文件
  4. C1.wav – 第一个音频文件
  5. C2.wav – 第二个音频文件

从文件类型可以看出,这道题结合了密码学、逆向分析和信号处理等多个方向。

解题思路概览

解题的整体流程可以分为以下几个阶段:

  1. 代码审计:分析RSA.py源码,寻找加密实现中的漏洞
  2. 公钥分析:从PEM文件中提取RSA公钥参数
  3. 密文获取:从音频文件中解码摩斯电码,获取密文
  4. 密钥破解:利用发现的漏洞分解RSA模数
  5. 解密还原:使用破解的密钥解密密文,获取flag

接下来,我们将逐步深入分析每个环节。

第一阶段:代码审计与漏洞发现

RSA.py源码分析

首先,我们来分析题目提供的加密脚本RSA.py:

from flag import flag
from Crypto.Util.number import *
import random

p=getPrime(256)
q=getPrime(256)
e=random.randint(1,65537)
n=p*q
m=bytes_to_long(flag1)
c=pow(m,e,n)
print c,e,n

p=getPrime(256)
n=p*q
m=bytes_to_long(flag2)
c=pow(m,e,n)
print c,e,n

代码执行流程分析

让我们逐行分析这段代码的执行流程:

第一组加密(第1-8行):

  1. 生成256位素数p
  2. 生成256位素数q
  3. 生成随机公钥指数e(范围1到65537)
  4. 计算模数n = p * q
  5. 将flag的第一部分转换为整数m
  6. 使用RSA加密:c = m^e mod n
  7. 输出密文c、公钥指数e和模数n

第二组加密(第10-14行):

  1. 重新生成256位素数p
  2. 计算模数n = p * q
  3. 将flag的第二部分转换为整数m
  4. 使用RSA加密:c = m^e mod n
  5. 输出密文c、公钥指数e和模数n

关键漏洞识别

通过仔细对比两组加密代码,我们发现了一个严重的安全漏洞:

在第二组加密中,变量q没有被重新赋值!

具体来说:

  • 第一组加密:p=getPrime(256)q=getPrime(256)n=p*q
  • 第二组加密:p=getPrime(256)n=p*q(注意:q没有重新生成)

这意味着:

  • 第一个模数:n1 = p1 * q
  • 第二个模数:n2 = p2 * q

两个模数共享了同一个素因子q!这是RSA实现中的致命错误。

漏洞利用原理

当两个RSA模数共享一个公因子时,我们可以使用最大公约数(GCD)算法轻松破解:

数学原理:

如果 n1 = p1 * q 且 n2 = p2 * q
那么 gcd(n1, n2) = q

一旦获得q,就可以计算:

p1 = n1 / q
p2 = n2 / q

为什么这个攻击如此有效?

计算两个大整数的最大公约数(使用欧几里得算法)的时间复杂度是O(log n),这是一个非常快速的算法。而直接分解一个大整数需要指数级时间。因此,共享因子攻击可以在毫秒级完成,而暴力分解可能需要数年甚至数百年。

第二阶段:提取RSA公钥参数

PEM文件格式说明

题目提供的两个公钥文件采用PEM(Privacy Enhanced Mail)格式,这是一种常见的密钥存储格式。PEM文件使用Base64编码,包含在-----BEGIN PUBLIC KEY----------END PUBLIC KEY-----标记之间。

提取公钥参数的方法

我们可以使用Python的PyCrypto或PyCryptodome库来解析PEM格式的公钥文件:

from Crypto.PublicKey import RSA

# 读取第一个公钥
f = open("pubkey1.pem", "r")
key1 = RSA.importKey(f.read())
n1 = key1.n
e1 = key1.e
f.close()

# 读取第二个公钥
f = open("pubkey2.pem", "r")
key2 = RSA.importKey(f.read())
n2 = key2.n
e2 = key2.e
f.close()

提取结果

运行上述代码后,我们得到以下RSA公钥参数:

N1 = 10285341668836655607404515118077620322010982612318568968318582049362470680277495816958090140659605052252686941748392508264340665515203620965012407552377979
E1 = 41221

N2 = 8559553750267902714590519131072264773684562647813990967245740601834411107597211544789303614222336972768348959206728010238189976768204432286391096419456339
E2 = 41221

可以看到,两个公钥使用了相同的加密指数E = 41221,这与我们在源码中看到的逻辑一致(e在两次加密中没有改变)。

第三阶段:从音频文件中获取密文

音频文件分析

题目提供了两个WAV格式的音频文件C1.wav和C2.wav。通过文件名可以推测,这两个文件包含了密文C1和C2。

首先检查音频文件的基本信息:

file C1.wav C2.wav

输出结果:

C1.wav: RIFF (little-endian) data, WAVE audio, Microsoft PCM, 8 bit, mono 8000 Hz
C2.wav: RIFF (little-endian) data, WAVE audio, Microsoft PCM, 8 bit, mono 8000 Hz

这些是标准的8位单声道WAV音频文件,采样率为8000 Hz。

摩斯电码识别

播放音频文件后,可以听到典型的摩斯电码信号:短音”滴”(点)和长音”嗒”(划)。摩斯电码是一种时通时断的信号代码,通过不同的排列顺序来表达字母、数字和标点符号。

摩斯电码基础知识:

  • 点(.):短信号
  • 划(-):长信号,长度约为点的3倍
  • 字母之间的间隔为3个点的长度
  • 单词之间的间隔为7个点的长度

解码工具选择

可以使用以下工具来解码音频中的摩斯电码:

  1. Morse Code Reader(在线工具)- 直接上传音频文件自动解码
  2. Audacity – 可视化波形,手动识别点划
  3. CW Decoder – 专业的连续波信号解码软件

解码结果

使用Morse Code Reader等工具对两个音频文件进行解码后,得到两个大整数:

C1 = 4314251881242803343641258350847424240197348270934376293792054938860756265727535163218661012756264314717591117355736219880127534927494986120542485721347351

C2 = 485162209351525800948941613977942416744737316759516157292410960531475083863663017229882430859161458909478412418639172249660818299099618143918080867132349

这两个数值就是RSA加密后的密文。

第四阶段:利用共享因子破解RSA

使用GCD算法分解模数

现在我们已经获得了所有必要的信息:

  • 两个模数 N1 和 N2
  • 公钥指数 E(都是41221)
  • 两个密文 C1 和 C2

接下来利用第一阶段发现的漏洞,使用GCD算法来分解模数。

方法一:使用Python的math.gcd()函数

import math

n1 = 10285341668836655607404515118077620322010982612318568968318582049362470680277495816958090140659605052252686941748392508264340665515203620965012407552377979
n2 = 8559553750267902714590519131072264773684562647813990967245740601834411107597211544789303614222336972768348959206728010238189976768204432286391096419456339

# 计算最大公约数
q = math.gcd(n1, n2)
print(f"共享因子 q = {q}")

# 计算另一个因子
p1 = n1 // q
p2 = n2 // q
print(f"p1 = {p1}")
print(f"p2 = {p2}")

运行结果:

共享因子 q = 95652716952085928904432251307911783641637100214166105912784767390061832540987
p1 = 107527961531806336468215094056447603422487078704170855072884726273308088647617
p2 = 89485735722023752007114986095340626130070550475022132484632643785292683293897

方法二:使用FactorDB在线分解

FactorDB(http://www.factordb.com/)是一个大整数分解数据库。对于CTF题目中使用的较小素数(如256位),通常已经被分解并存储在数据库中。

将N1和N2分别提交到FactorDB查询,可以直接获得分解结果:

N1的分解:
p1 = 95652716952085928904432251307911783641637100214166105912784767390061832540987
q1 = 107527961531806336468215094056447603422487078704170855072884726273308088647617

N2的分解:
p2 = 89485735722023752007114986095340626130070550475022132484632643785292683293897
q2 = 95652716952085928904432251307911783641637100214166105912784767390061832540987

验证分解结果

注意到一个关键事实:q2 == p1,这正好验证了我们在代码审计阶段的发现。

验证分解的正确性:

# 验证N1的分解
print(f"p1 * q1 == N1: {p1 * q1 == n1}")  # True

# 验证N2的分解
print(f"p2 * q2 == N2: {p2 * q2 == n2}")  # True

至此,我们成功获得了两组RSA密钥的所有素因子。

第五阶段:RSA解密与获取Flag

RSA解密数学原理

在获得素因子p和q后,我们可以按照以下步骤进行RSA解密:

  1. 计算欧拉函数:φ(n) = (p – 1) * (q – 1)
  2. 计算私钥指数:d = e^(-1) mod φ(n)
  3. 解密密文:m = c^d mod n
  4. 转换为明文:将整数m转换为字符串

欧拉函数的意义:

欧拉函数φ(n)表示小于n且与n互质的正整数的个数。当n是两个素数的乘积时,φ(n) = (p-1)(q-1)。这个值在RSA算法中用于计算私钥。

模逆元的计算:

私钥d是e在模φ(n)下的乘法逆元,即满足:e * d ≡ 1 (mod φ(n))

可以使用扩展欧几里得算法或gmpy2库的invert函数来计算。

完整解密脚本

下面是完整的解密脚本:

import gmpy2
from Crypto.Util.number import *
from Crypto.PublicKey import RSA

# 步骤1: 从公钥文件提取参数
f = open("pubkey1.pem", "r")
key = RSA.importKey(f.read())
n1 = key.n
e1 = key.e
f.close()

f = open("pubkey2.pem", "r")
key = RSA.importKey(f.read())
n2 = key.n
e2 = key.e
f.close()

print('N1 =', n1)
print('E1 =', e1)
print('N2 =', n2)
print('E2 =', e2)

# 步骤2: 从FactorDB或GCD获得的分解结果
p1 = 95652716952085928904432251307911783641637100214166105912784767390061832540987
q1 = 107527961531806336468215094056447603422487078704170855072884726273308088647617
p2 = 89485735722023752007114986095340626130070550475022132484632643785292683293897
q2 = 95652716952085928904432251307911783641637100214166105912784767390061832540987

# 步骤3: 从音频文件解码得到的密文
c1 = 4314251881242803343641258350847424240197348270934376293792054938860756265727535163218661012756264314717591117355736219880127534927494986120542485721347351
c2 = 485162209351525800948941613977942416744737316759516157292410960531475083863663017229882430859161458909478412418639172249660818299099618143918080867132349

继续添加解密代码:

# 步骤4: 解密第一部分
phi1 = (p1 - 1) * (q1 - 1)
d1 = gmpy2.invert(e1, phi1)
m1 = gmpy2.powmod(c1, d1, n1)
flag1 = long_to_bytes(int(m1))
print("Flag第一部分:", flag1.decode())

# 步骤5: 解密第二部分
phi2 = (p2 - 1) * (q2 - 1)
d2 = gmpy2.invert(e2, phi2)
m2 = gmpy2.powmod(c2, d2, n2)
flag2 = long_to_bytes(int(m2))
print("Flag第二部分:", flag2.decode())

# 步骤6: 拼接完整flag
print("\n完整Flag:", flag1.decode() + flag2.decode())

运行结果

执行上述脚本后,得到以下输出:

Flag第一部分: UNCTF{ac01dff95336a
Flag第二部分: a470e3b55d3fe43e9f6}

完整Flag: UNCTF{ac01dff95336aa470e3b55d3fe43e9f6}

成功获取完整flag:UNCTF{ac01dff95336aa470e3b55d3fe43e9f6}

技术总结与深度分析

本题涉及的核心技术

通过这道题目,我们综合运用了以下技术:

1. 代码审计能力

  • 静态分析Python源码
  • 识别变量作用域和生命周期
  • 发现编程逻辑错误

2. RSA密码学知识

  • RSA加密解密原理
  • 欧拉函数的计算
  • 模逆元的求解
  • 大整数运算

3. 共享因子攻击

  • GCD算法的应用
  • 时间复杂度分析
  • 攻击可行性评估

4. 信号处理技能

  • 音频文件格式识别
  • 摩斯电码解码
  • 数字信号分析

5. 工具使用能力

  • Python密码学库(PyCrypto/PyCryptodome)
  • gmpy2大整数运算库
  • FactorDB在线分解工具
  • 摩斯电码解码工具

共享因子攻击的深度剖析

攻击原理:

当两个RSA模数n1和n2共享一个素因子时,攻击者可以通过计算gcd(n1, n2)快速获得这个共享因子。这是因为:

n1 = p1 * q
n2 = p2 * q
gcd(n1, n2) = gcd(p1*q, p2*q) = q * gcd(p1, p2)

由于p1和p2是不同的素数,gcd(p1, p2) = 1,因此gcd(n1, n2) = q。

攻击效率:

欧几里得算法的时间复杂度为O(log n),对于512位的RSA模数,只需要几毫秒就能完成计算。相比之下,直接分解一个512位的RSA模数需要数天到数周的时间。

历史案例:

2012年,研究人员对互联网上公开的数百万个RSA公钥进行了大规模分析,发现约0.2%的密钥存在共享因子问题。这些脆弱的密钥主要来自:

  • 嵌入式设备和路由器
  • 随机数生成器实现不当的系统
  • 启动时熵池不足的设备

这次研究证明了共享因子攻击不仅是理论威胁,在现实世界中确实存在。

RSA安全实现的最佳实践

基于本题揭示的安全问题,总结以下RSA实现的安全建议:

1. 密钥生成原则

  • 每个RSA密钥对必须使用独立生成的素数
  • 绝对不要在不同密钥对之间重用素数p或q
  • 每次生成新密钥时都应该生成全新的随机素数

2. 素数大小要求

  • 现代标准建议使用至少2048位的RSA模数
  • 高安全场景应使用3072位或4096位模数
  • 本题使用的256位素数在实际应用中完全不安全

3. 随机数生成器

  • 必须使用密码学安全的随机数生成器(CSPRNG)
  • 不要使用普通的伪随机数生成器(如Python的random模块)
  • 确保系统熵池有足够的熵值

4. 公钥指数选择

  • 推荐使用65537(0x10001)作为公钥指数
  • 避免使用过小的e值(如3),可能导致特定攻击
  • 确保e与φ(n)互质

5. 使用成熟的密码学库

  • 不要自己实现RSA算法
  • 使用经过广泛测试的库(OpenSSL、PyCryptodome、cryptography)
  • 这些库已内置各种安全检查和最佳实践

学习建议与资源推荐

给初学者的学习路径

1. 数学基础

  • 模运算、素数、最大公约数
  • 欧拉函数和费马小定理
  • 模逆元的概念和计算

2. RSA算法学习

  • RSA的数学原理和证明
  • 加密解密的完整流程
  • 常见的RSA攻击方法

3. Python密码学编程

  • PyCryptodome库的使用
  • gmpy2大整数运算
  • 数据格式转换技巧

4. CTF实战练习

  • 从简单题目开始
  • 学习使用常用工具
  • 参加在线CTF平台

推荐工具和资源

在线工具:

  • FactorDB – 大整数分解数据库
  • Morse Code Reader – 摩斯电码解码
  • CyberChef – 综合数据处理工具

Python库:

  • PyCryptodome – Python密码学库
  • gmpy2 – 高性能大整数运算
  • cryptography – 现代密码学库

学习资源:

  • 《应用密码学》- Bruce Schneier
  • 《密码编码学与网络安全》- William Stallings
  • Cryptopals Challenges – 密码学挑战平台

全文总结

本文通过对”不仅仅是RSA”这道CTF题目的完整分析,展示了一个综合性密码学挑战的解题全过程。

核心要点回顾

漏洞本质:题目的核心漏洞是RSA加密实现中的素数重用问题。在第二次加密时,变量q没有重新生成,导致两个RSA模数共享了同一个素因子。

攻击方法:利用GCD算法可以在毫秒级时间内从两个共享因子的模数中提取出公因子,从而完全破解RSA加密。

技术综合性:这道题目不仅考察RSA密码学知识,还涉及代码审计、信号处理(摩斯电码)、工具使用等多个方面,充分体现了CTF题目的综合性特点。

安全启示

通过这道题目,我们获得了以下重要启示:

  1. 实现细节决定安全性即使是数学上安全的算法,如果实现不当,也会导致严重的安全问题。一个看似微小的编程错误(重用变量q),就可能导致整个加密系统的崩溃。
  2. 代码审计的重要性在密码学应用中,代码审计是发现潜在漏洞的关键手段。需要仔细检查变量的作用域、生命周期和重用情况。
  3. 密码学知识的实践价值理论知识必须与实践相结合。了解各种攻击方法(如共享因子攻击)可以帮助我们更好地评估系统安全性。
  4. 多层防御的必要性不应该依赖单一的安全机制。在实际应用中,应该采用多层防御策略,包括密钥管理、访问控制、审计日志等。
  5. 持续学习的重要性密码学是一个不断发展的领域,新的攻击方法和防御技术层出不穷。保持学习和关注最新研究成果是必要的。

结语

“不仅仅是RSA”这道题目名副其实,它确实不仅仅是一道简单的RSA题目。通过这道题,我们学习了:

  • 如何通过代码审计发现安全漏洞
  • 共享因子攻击的原理和实现
  • 摩斯电码解码等信号处理技能
  • Python密码学编程的实践技巧
  • RSA安全实现的最佳实践

密码学的魅力在于数学的严谨性和实现的复杂性之间的平衡。一个理论上完美的算法,如果实现不当,就会变得脆弱不堪。这也是为什么在实际应用中,我们应该始终使用经过充分测试和验证的密码学库,而不是自己从零开始实现。

希望这篇文章能够帮助密码学初学者更好地理解RSA算法及其安全性,同时也提醒开发者在实现密码系统时必须格外谨慎,避免类似的安全漏洞。

感谢出题者设计了这道富有教育意义的题目,让我们在实践中深入理解了RSA的安全性问题。


题目信息:

  • 题目名称:不仅仅是RSA
  • 题目类型:Crypto(密码学)
  • 难度评级:中等
  • Flag:UNCTF{ac01dff95336aa470e3b55d3fe43e9f6}

涉及知识点:

  • RSA加密算法
  • 共享因子攻击(Common Factor Attack)
  • 代码审计
  • 摩斯电码解码
  • 大整数分解
  • Python密码学编程

免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:破镜安全 破镜安全《不仅仅是RSA – CTF密码学综合题目完整解析》

AI手机,过不了风控这一关 网络安全文章

AI手机,过不了风控这一关

文章总结: 文章分析AI手机因缺乏人类操作特征被风控系统判定为机器人,难以通过大厂风控。核心矛盾在于AI代操作卖点与风控反机器目标对立。若放宽风控将引发黑产滥用
评论:0   参与:  0