从CTF到实战:Python实现RSA共模攻击与Base64隐写全解析
密码学实战:当RSA遇上Base64隐写
在网络安全竞赛中,RSA共模攻击与Base64隐写的组合经常出现。这种题型不仅考察选手对密码学原理的理解,更考验将理论转化为实际代码的能力。让我们从一个典型CTF题目出发,完整剖析攻击原理与实现细节。
核心攻击场景:当同一明文使用相同的RSA模数N但不同公钥指数e1、e2加密时,若e1和e2互质,则可通过扩展欧几里得算法恢复原始明文。而Base64隐写则利用了编码过程中被忽略的冗余位来隐藏信息。
RSA共模攻击的数学原理
1. 攻击条件与理论基础
共模攻击成立需要满足三个关键条件:
- 相同的模数N用于两次加密
- 两个加密指数e1和e2互质
- 攻击者能够获取两组密文c1、c2
数学推导过程:
根据RSA加密公式:
c1 ≡ m^e1 mod N c2 ≡ m^e2 mod N由于e1和e2互质,存在整数s1、s2满足:
e1*s1 + e2*s2 = 1通过扩展欧几里得算法求出s1和s2后,可计算:
m ≡ (c1^s1 * c2^s2) mod N
2. 扩展欧几里得算法实现
def exgcd(a, b): if b == 0: return (a, 1, 0) else: g, x, y = exgcd(b, a % b) return (g, y, x - (a // b) * y)注意:实际应用中s1或s2可能为负数,此时需要计算模逆元
Base64隐写技术解析
1. Base64编码原理
标准Base64编码将每3字节(24bit)数据转换为4个6bit字符,对应关系如下:
| 二进制值 | 字符 | 二进制值 | 字符 | 二进制值 | 字符 | 二进制值 | 字符 |
|---|---|---|---|---|---|---|---|
| 000000 | A | 010000 | Q | 100000 | g | 110000 | w |
| ... | ... | ... | ... | ... | ... | ... | ... |
2. 隐写原理与实现
Base64隐写利用了编码末尾的填充位:
- 当原始数据长度不是3的倍数时,需要进行补零操作
- 这些补零位在实际解码时会被丢弃
- 隐写通过修改这些"无用"位来隐藏信息
提取隐写数据的步骤:
- 对每个Base64编码块,检查是否有填充符'='
- 计算有效数据位数与补零位数
- 提取补零位作为隐写数据
def extract_stego(base64_str): padding = base64_str.count('=') if padding == 0: return '' last_char = base64_str[-padding-1] last_bits = bin(BASE64_CHARS.index(last_char))[2:].zfill(6) return last_bits[-(padding*2):]完整攻击脚本实现
1. 环境准备与依赖安装
pip install pycryptodome gmpy22. 核心攻击代码
from Crypto.Util.number import long_to_bytes import gmpy2 import base64 def rsa_common_modulus_attack(c1, c2, e1, e2, N): # 计算s1和s2 g, s1, s2 = gmpy2.gcdext(e1, e2) # 处理负数指数 if s1 < 0: c1 = gmpy2.invert(c1, N) s1 = -s1 if s2 < 0: c2 = gmpy2.invert(c2, N) s2 = -s2 # 恢复明文 m = (pow(c1, s1, N) * pow(c2, s2, N)) % N return long_to_bytes(m) def extract_base64_stego(encoded_str): # Base64字符集 BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" # 移除填充并转换二进制 stripped = encoded_str.rstrip('=') binary_str = ''.join([bin(BASE64_CHARS.index(c))[2:].zfill(6) for c in stripped]) # 计算有效位数 effective_bits = len(stripped) * 6 - (len(stripped) * 6) % 8 # 提取隐写数据 stego_bits = binary_str[effective_bits:] return stego_bits3. 实战案例解析
假设我们有以下CTF题目数据:
- N = 78509541971826... (2048位)
- e1 = 1697
- e2 = 599
- 密文对(c1, c2)共6组
攻击步骤:
- 对每组密文执行共模攻击
- 将解密结果拼接为Base64字符串
- 从Base64中提取隐写数据
- 组合隐写数据得到flag
# 示例密文处理 c1_list = [412629526163..., 494644347943..., ...] c2_list = [592169079372..., 373940646416..., ...] # 执行共模攻击 base64_parts = [] for c1, c2 in zip(c1_list, c2_list): m = rsa_common_modulus_attack(c1, c2, e1, e2, N) base64_parts.append(m.decode()) # 组合Base64并提取隐写 full_base64 = '\n'.join(base64_parts) stego_bits = '' for line in full_base64.split('\n'): stego_bits += extract_base64_stego(line) # 转换比特为字符串 flag = ''.join([chr(int(stego_bits[i:i+8], 2)) for i in range(0, len(stego_bits), 8)]) print("Flag:", flag)防御措施与实战建议
1. 避免RSA共模攻击
- 绝不重复使用模数N:为每个用户生成独立的N
- 使用标准公钥指数:如65537(2^16+1)
- 添加随机填充:采用OAEP等填充方案
2. Base64隐写检测
| 检测方法 | 实现方式 | 效果评估 |
|---|---|---|
| 补零位验证 | 检查Base64末尾补零位是否全为0 | 高准确率 |
| 熵值分析 | 计算补零位的熵值,异常高则可能含隐写 | 中等准确率 |
| 格式验证 | 验证解码后数据是否符合预期格式 | 依赖具体场景 |
3. 进阶实战技巧
- 性能优化:对大整数运算使用gmpy2加速
- 错误处理:添加对异常密文的检测
- 自动化:批量处理多个密文对
- 组合攻击:与其他密码分析技术结合使用
# 优化后的共模攻击函数 def optimized_attack(c1, c2, e1, e2, N): g, s1, s2 = gmpy2.gcdext(e1, e2) c1 = gmpy2.powmod(c1, 1, N) # 确保c1在模N范围内 c2 = gmpy2.powmod(c2, 1, N) # 确保c2在模N范围内 # 处理负指数 part1 = gmpy2.powmod(c1, abs(s1), N) if s1 > 0 else gmpy2.invert(gmpy2.powmod(c1, abs(s1), N), N) part2 = gmpy2.powmod(c2, abs(s2), N) if s2 > 0 else gmpy2.invert(gmpy2.powmod(c2, abs(s2), N), N) m = (part1 * part2) % N return m在真实渗透测试中,这类技术可用于分析某些不当实现的加密协议。曾在一个金融系统审计中,发现其使用固定模数N但不同e值加密交易数据,通过共模攻击成功还原了敏感交易信息。