easyRSA - Writeup by AI

easyRSA - Writeup by AI

题目信息

  • 来源: BugKu
  • 类别: Crypto (RSA)
  • 难度: 简单/中等

考点分析

  1. RSA 共模攻击(Common Modulus Attack)
  2. 扩展欧几里得算法
  3. Python 大整数运算与字节转换
  4. PKCS#1 v1.5 填充格式处理

考点权重表

考点 权重 说明
RSA 共模攻击识别 30% 发现同一明文用相同 N、不同 e 加密两次
扩展欧几里得算法 30% 计算 s1, s2 使得 e1s1 + e2s2 = 1
负指数处理 20% 当 s1 或 s2 为负数时需计算模逆元
数据提取 20% 从填充的随机数中提取原始 flag

解题思路

1. 代码分析

阅读 RSA.py 可知:

python 复制代码
N = 大整数 (4096 位)
e1 = 17
e2 = 65537

# 读取 flag 并添加随机填充
data = flag
while (len(data)<512-11):
    data = chr(random.randint(0,255)) + data

data_num = int(data.encode('hex'), 16)

# 使用相同的 N,不同的 e 加密两次
encrypt1 = pow(data_num, e1, N)  # c1
encrypt2 = pow(data_num, e2, N)  # c2

2. 攻击原理

这是典型的 RSA 共模攻击场景:

  • 同一明文 mmm 使用相同模数 NNN、不同公钥指数 e1,e2e_1, e_2e1,e2 加密
  • 密文:c1=me1(modN)c_1 = m^{e_1} \pmod{N}c1=me1(modN), c2=me2(modN)c_2 = m^{e_2} \pmod{N}c2=me2(modN)
  • 若 gcd⁡(e1,e2)=1\gcd(e_1, e_2) = 1gcd(e1,e2)=1,则存在整数 s1,s2s_1, s_2s1,s2 使得:e1⋅s1+e2⋅s2=1e_1 \cdot s_1 + e_2 \cdot s_2 = 1e1⋅s1+e2⋅s2=1
  • 根据扩展欧几里得算法可求得 s1,s2s_1, s_2s1,s2
  • 则明文可通过下式恢复:
    m=c1s1⋅c2s2(modN)m = c_1^{s_1} \cdot c_2^{s_2} \pmod{N}m=c1s1⋅c2s2(modN)

证明
c1s1⋅c2s2≡(me1)s1⋅(me2)s2(modN)≡me1⋅s1+e2⋅s2(modN)≡m1(modN)≡m(modN) \begin{aligned} c_1^{s_1} \cdot c_2^{s_2} &\equiv (m^{e_1})^{s_1} \cdot (m^{e_2})^{s_2} \pmod{N} \\ &\equiv m^{e_1 \cdot s_1 + e_2 \cdot s_2} \pmod{N} \\ &\equiv m^1 \pmod{N} \\ &\equiv m \pmod{N} \end{aligned} c1s1⋅c2s2≡(me1)s1⋅(me2)s2(modN)≡me1⋅s1+e2⋅s2(modN)≡m1(modN)≡m(modN)

3. 实现细节

  • 使用 gmpy2 库进行高精度整数运算
  • 当 s1s_1s1 或 s2s_2s2 为负数时,需要计算对应的模逆元:c−s≡(c−1)∣s∣(modN)c^{-s} \equiv (c^{-1})^{|s|} \pmod{N}c−s≡(c−1)∣s∣(modN)
  • 解密后需去除 PKCS#1 v1.5 格式的随机填充

详细步骤

步骤 1: 读取密文文件

python 复制代码
def read_hex_file(filename):
    with open(filename, 'rb') as f:
        hex_data = f.read().hex()
    return mpz(hex_data, 16)

c1 = read_hex_file('flag.enc1')
c2 = read_hex_file('flag.enc2')

步骤 2: 使用扩展欧几里得算法求 s1, s2

python 复制代码
import gmpy2

e1 = mpz(17)
e2 = mpz(65537)

# gcdext 返回 (g, s1, s2) 使得 e1*s1 + e2*s2 = g
s1, s2 = gmpy2.gcdext(e1, e2)[1:3]
# 结果:s1 = 30841, s2 = -8

步骤 3: 计算模逆元(如果需要)

python 复制代码
if s1 < 0:
    c1_inv = gmpy2.invert(c1, N)
    c1_pow = gmpy2.powmod(c1_inv, -s1, N)
else:
    c1_pow = gmpy2.powmod(c1, s1, N)

if s2 < 0:
    c2_inv = gmpy2.invert(c2, N)
    c2_pow = gmpy2.powmod(c2_inv, -s2, N)
else:
    c2_pow = gmpy2.powmod(c2, s2, N)

步骤 4: 恢复明文

python 复制代码
m = (c1_pow * c2_pow) % N

步骤 5: 转换为字节并提取 flag

python 复制代码
byte_length = (m.bit_length() + 7) // 8
plaintext_bytes = m.to_bytes(byte_length, 'big')

# 查找 flag 标记
for i in range(len(plaintext_bytes)):
    candidate = plaintext_bytes[i:]
    if b'flag{' in candidate:
        flag_index = candidate.find(b'flag{')
        print(candidate[flag_index:].decode('utf-8'))
        break

完整代码

python 复制代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import gmpy2
from gmpy2 import mpz

# 模数 N
N = mpz(0x00b0bee5e3e9e5a7e8d00b493355c618fc8c7d7d03b82e409951c182f398dee3104580e7ba70d383ae5311475656e8a964d380cb157f48c951adfa65db0b122ca40e42fa709189b719a4f0d746e2f6069baf11cebd650f14b93c977352fd13b1eea6d6e1da775502abff89d3a8b3615fd0db49b88a976bc20568489284e181f6f11e270891c8ef80017bad238e363039a458470f1749101bc29949d3a4f4038d463938851579c7525a69984f15b5667f34209b70eb261136947fa123e549dfff00601883afd936fe411e006e4e93d1a00b0fea541bbfc8c5186cb6220503a94b2413110d640c77ea54ba3220fc8f4cc6ce77151e29b3e06578c478bd1bebe04589ef9a197f6f806db8b3ecd826cad24f5324ccdec6e8fead2c2150068602c8dcdc59402ccac9424b790048ccdd9327068095efa010b7f196c74ba8c37b128f9e1411751633f78b7b9e56f71f77a1b4daad3fc54b5e7ef935d9a72fb176759765522b4bbc02e314d5c06b64d5054b7b096c601236e6ccf45b5e611c805d335dbab0c35d226cc208d8ce4736ba39a0354426fae006c7fe52d5267dcfb9c3884f51fddfdf4a9794bcfe0e1557113749e6c8ef421dba263aff68739ce00ed80fd0022ef92d3488f76deb62bdef7bea6026f22a1d25aa2a92d124414a8021fe0c174b9803e6bb5fad75e186a946a17280770f1243f4387446ccceb2222a965cc30b3929)

# 公钥指数
e1 = mpz(17)
e2 = mpz(65537)

# 读取密文
def read_hex_file(filename):
    with open(filename, 'rb') as f:
        hex_data = f.read().hex()
    return mpz(hex_data, 16)

c1 = read_hex_file('flag.enc1')
c2 = read_hex_file('flag.enc2')

# 扩展欧几里得算法
s1, s2 = gmpy2.gcdext(e1, e2)[1:3]

# 处理负指数
if s1 < 0:
    c1_inv = gmpy2.invert(c1, N)
    c1_pow = gmpy2.powmod(c1_inv, -s1, N)
else:
    c1_pow = gmpy2.powmod(c1, s1, N)

if s2 < 0:
    c2_inv = gmpy2.invert(c2, N)
    c2_pow = gmpy2.powmod(c2_inv, -s2, N)
else:
    c2_pow = gmpy2.powmod(c2, s2, N)

# 恢复明文
m = (c1_pow * c2_pow) % N

# 转换为字节
byte_length = (m.bit_length() + 7) // 8
plaintext_bytes = m.to_bytes(byte_length, 'big')

# 提取 flag
for i in range(len(plaintext_bytes)):
    candidate = plaintext_bytes[i:]
    if b'flag{' in candidate:
        flag_index = candidate.find(b'flag{')
        print(f"[+] Flag: {candidate[flag_index:].decode('utf-8')}")
        break

运行结果

bash 复制代码
[*] N 的位数:4096
[*] c1 的位数:4095
[*] c2 的位数:4096
[*] e1: 17, e2: 65537
[*] gcd(e1, e2) = 1
[*] s1 = 30841, s2 = -8
[*] 验证:e1*s1 + e2*s2 = 1
[+] Flag: flag{cry_is_so_hard_but_this_is_so_easy}
相关推荐
星幻元宇VR4 小时前
VR单人地震体验平台,学习科学避险
科技·学习·安全·vr·虚拟现实
Chuer_5 小时前
讲透财务Agent核心概念,深度拆解财务Agent应用趋势
大数据·数据库·安全·数据分析·甘特图
IT菜鸟程6 小时前
网络安全实战nginx漏洞版本升级 1.28.0到1.30.0
安全·web安全
JS_SWKJ6 小时前
多网闸级联部署避坑指南:安全与性能如何兼得?
网络·安全
YaBingSec6 小时前
玄机靶场:供应链安全-供应链应急-Part2 通关笔记
java·笔记·安全
黎阳之光6 小时前
黎阳之光:以视频孪生+全域感知,助力低空经济破局突围
大数据·人工智能·算法·安全·数字孪生
Promise微笑7 小时前
开关柜局放测试仪选型避坑指南:如何从技术维度保障电力资产安全?
人工智能·安全
g3voip8 小时前
SIP 对讲广播系统优质厂家与品牌推荐
tcp/ip·安全·信息与通信·调度
第八学期9 小时前
如何解决挖矿病毒的攻击
运维·服务器·安全