rsa Writeup by AI
rsa Writeup by AI
题目信息
- 题目来源: 攻防世界 (Adworld)
- 题目类型: Crypto (密码学)
- 题目名称: rsa
题目描述
本题是一个 RSA 加密挑战,提供了 RSA 公钥文件 (pubkey.pem) 和加密后的 flag 文件 (flag.enc),需要解密获取原始 flag。
考点分析
- RSA 公钥解析: 从 PEM 格式文件中提取 n 和 e
- Fermat 分解攻击: 当 RSA 的两个素数 p 和 q 非常接近时,可以使用 Fermat 分解法快速分解大整数 n
- RSA 解密: 使用私钥 d 对密文进行解密
- 数据编码转换: 处理二进制文件和字符串转换
解题思路
1. 分析公钥
首先读取 pubkey.pem 文件,提取 RSA 公钥参数:
- n: 4096 位的大整数
- e: 65537 (常用的 RSA 指数)
2. 分解 n
由于 n 是 4096 位,无法直接分解。但题目设计时可能让 p 和 q 很接近,因此尝试使用 Fermat 分解法。
Fermat 分解原理:
如果 n = p × q,且 p ≈ q,那么可以令:
- a = ⌈√n⌉
- b² = a² - n
通过不断递增 a,直到 b² 成为完全平方数,此时:
- p = a - b
- q = a + b
3. 计算私钥
一旦分解得到 p 和 q,就可以计算:
- φ(n) = (p-1)(q-1)
- d = e⁻¹ mod φ(n)
4. 解密 flag
使用 RSA 解密公式:m = c^d mod n
然后将解密结果转换为字符串格式。
详细步骤
步骤 1: 导入必要的库
from Crypto.PublicKey import RSA
import gmpy2
from libnum import n2s, s2n
import math
步骤 2: 读取公钥
with open('pubkey.pem', 'r') as f:
pubkey = RSA.import_key(f.read())
n = pubkey.n
e = pubkey.e
步骤 3: Fermat 分解
def fermat_factor(n):
a = math.isqrt(n) + 1
for i in range(10**6):
b2 = (a+i)*(a+i) - n
if b2 < 0:
continue
b = gmpy2.iroot(b2, 2)
if b[1]: # b2 是完全平方数
p = (a+i) - b[0]
q = (a+i) + b[0]
return p, q
return None, None
p, q = fermat_factor(n)
步骤 4: 计算私钥并解密
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
with open('flag.enc', 'rb') as f:
cipher_bytes = f.read()
c = int.from_bytes(cipher_bytes, 'big')
m = pow(c, d, n)
flag_bytes = m.to_bytes((m.bit_length() + 7) // 8, 'big')
flag = flag_bytes.decode('utf-8', errors='ignore')
步骤 5: 提取 flag
从解码结果中查找符合 flag{...} 格式的内容。
完整代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
攻防世界 Crypto 12-rsa 解题脚本
考点:RSA 公钥解析与 Fermat 分解攻击
当 RSA 的两个素数 p 和 q 非常接近时,可以使用 Fermat 分解法快速分解 n
"""
from Crypto.PublicKey import RSA
import gmpy2
from libnum import n2s, s2n
import math
def fermat_factor(n):
"""Fermat 分解法:适用于 p 和 q 很接近的情况"""
a = math.isqrt(n) + 1
b2 = a*a - n
# 尝试最多 10^6 次
for i in range(10**6):
b2 = (a+i)*(a+i) - n
if b2 < 0:
continue
b = gmpy2.iroot(b2, 2)
if b[1]:
p = (a+i) - b[0]
q = (a+i) + b[0]
return p, q
return None, None
def solve():
# 读取公钥
with open('pubkey.pem', 'r') as f:
pubkey = RSA.import_key(f.read())
n = pubkey.n
e = pubkey.e
print("=" * 60)
print("攻防世界 Crypto 12-rsa 解题")
print("=" * 60)
print(f"\n公钥参数:")
print(f"n 的位数:{n.bit_length()} bits")
print(f"e = {e}")
# 使用 Fermat 分解
print("\n使用 Fermat 分解法分解 n...")
p, q = fermat_factor(n)
if p and q:
print(f"✓ 分解成功!")
print(f"p = {p}")
print(f"q = {q}")
# 验证
assert p * q == n, "分解错误!"
print("✓ 验证通过:p * q = n")
# 计算私钥 d
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
print(f"\n私钥 d 已计算")
# 读取密文
with open('flag.enc', 'rb') as f:
cipher_bytes = f.read()
c = int.from_bytes(cipher_bytes, 'big')
# RSA 解密:m = c^d mod n
print("\n解密中...")
m = pow(c, d, n)
# 尝试多种方式转换
print("\n尝试解码明文...")
# 方法 1:直接转换为字节
try:
flag_bytes = m.to_bytes((m.bit_length() + 7) // 8, 'big')
# 尝试解码为字符串
try:
flag = flag_bytes.decode('utf-8', errors='ignore')
# 提取有效的 flag 格式
if 'flag{' in flag:
start = flag.find('flag{')
end = flag.find('}', start) + 1
if end > start:
clean_flag = flag[start:end].strip()
print(f"\n✅ 最终 Flag: {clean_flag}")
return clean_flag
except:
pass
except Exception as ex:
print(f"方法 1 失败:{ex}")
return None
else:
print("✗ Fermat 分解失败")
return None
if __name__ == "__main__":
solve()
总结
关键知识点
- Fermat 分解攻击适用条件: 当 RSA 的两个素数 p 和 q 非常接近时(即 |p-q| 很小),可以使用 Fermat 分解法在合理时间内分解 n
- 为什么 p 和 q 接近会有危险: 因为 Fermat 分解的时间复杂度取决于 |p-q| 的大小,当两者接近时,只需要很少的迭代次数就能找到因子
- 实际 RSA 应用中的防范: 在实际应用中,生成 RSA 密钥时应确保 p 和 q 足够随机且相差较大,避免此类攻击
解题技巧
- 遇到大整数分解问题时,先检查是否有特殊性质(如完全平方数、接近平方根等)
- 对于 CTF题目,如果是 RSA 相关且 n 不是特别大(如 4096 位以下),通常存在某种数学上的弱点
- Fermat 分解是一种简单有效的分解方法,应该作为首选尝试
用户提问记录
问题 1: 如何从 PEM 文件中提取 RSA 公钥参数?
解答: 使用 Python 的 Crypto.PublicKey.RSA 模块,通过 RSA.import_key() 函数读取 PEM 文件,然后访问 .n 和 .e 属性即可获取模数和指数。
问题 2: Fermat 分解的原理是什么?
解答: Fermat 分解基于一个数学事实:任何奇合数都可以表示为两个数的平方差。对于 n = p×q,如果能找到 a 和 b 使得 n = a² - b²,那么 p = a-b,q = a+b。当 p 和 q 接近时,a 会很接近 √n,因此从 √n 开始搜索很快就能找到。
问题 3: 为什么解密后的数据需要特殊处理才能看到 flag?
解答: RSA 解密后得到的是一个大整数,需要转换为字节串。由于 PKCS#1 填充或其他原因,解密结果可能包含非 ASCII 字符,因此需要使用 errors='ignore' 参数来过滤掉无效字符,然后通过查找 flag{ 模式来提取有效内容。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)