解析2025LitCTF-leak
引言在密码学竞赛和实际应用中,RSA算法一直是最重要的公钥密码系统之一。然而,即使是看似安全的RSA实现,也可能因为微小的信息泄露而面临严重威胁。本文将通过一道真实的CTF题目,深入分析当RSA私钥的 2025-11-11 03:56:20 Author: www.freebuf.com(查看原文) 阅读量:1 收藏

引言

在密码学竞赛和实际应用中,RSA算法一直是最重要的公钥密码系统之一。然而,即使是看似安全的RSA实现,也可能因为微小的信息泄露而面临严重威胁。本文将通过一道真实的CTF题目,深入分析当RSA私钥的dp(d mod p-1)高位泄露时,如何利用数学和格算法完全破解整个RSA系统。

题目分析

题目背景

让我们首先分析题目给出的加密实现:

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

m = bytes_to_long(flag)
p,q,e = getPrime(1024),getPrime(1024),getPrime(101)
n = p*q
temp = gmpy2.invert(e,p-1)  # 计算dp = e^(-1) mod (p-1)
c = pow(m,e,n)
hint = temp>>180  # 泄露dp的高位(右移180位)
print(f"e = {e}")
print(f"n = {n}")
print(f"c = {c}")
print(f"hint = {hint}")

关键观察

  1. 参数规模

    • p和q:1024位素数

    • n:2048位模数

    • e:101位素数

    • dp:约1024位的私钥分量

  2. 泄露信息

    • hint = dp >> 180:泄露了dp除低180位外的所有高位

  3. 数学关系

    • dp = e^(-1) mod (p-1)

    • e * dp ≡ 1 (mod p-1)

这个看似无害的高位泄露,实际上提供了足够的信息来完全恢复私钥。

数学原理深入分析

基础RSA数学关系

在RSA中,私钥d满足:

e * d ≡ 1 (mod φ(n))
其中 φ(n) = (p-1)(q-1)

而CRT优化中的私钥分量定义为:

dp = d mod (p-1)
dq = d mod (q-1)

关键方程推导

dp = e^(-1) mod (p-1)我们可以得到:

e * dp ≡ 1 (mod p-1)

这意味着存在某个整数k使得:

e * dp - 1 = k * (p-1)  (1)

整理得到:

e * dp + k ≡ 1 (mod p)
e * dp + k - 1 ≡ 0 (mod p)

利用泄露信息构造方程

题目泄露了:

dp = hint * 2^180 + x
其中 0 ≤ x < 2^180

将此代入方程(1):

e * (hint * 2^180 + x) - 1 = k * (p-1)

整理得到核心方程:

F(x,k) = A + e * x + k ≡ 0 (mod p)
其中 A = e * hint * 2^180

关键洞察

现在的问题转化为:找到整数对(x,k)使得:

  1. 0 ≤ x < 2^180

  2. 1 ≤ k < e ≈ 2^101

  3. F(x,k) = A + e * x + k能被p整除

由于x和k相对于p(约2^1024)都非常小,这正好符合Coppersmith小根定理的应用条件。

Coppersmith小根定理

定理内容

Coppersmith定理指出:如果一个多项式在模数下有一个"小"根,并且根的大小满足特定条件,那么可以在多项式时间内找到这个根。

具体来说,对于单变量多项式f(x)模n,如果存在根|r| < n^(1/d),其中d是多项式次数,则可以在多项式时间内找到r。

格理论基础

格(Lattice)是向量空间中的离散子群。在密码学中,我们常用LLL(Lenstra-Lenstra-Lovász)算法来寻找格中的短向量。

对于我们的二维问题,构造格基:

[2^180,   0,   A]
[   0,    e,   e]
[   0,    0,   n]

通过LLL算法找到短向量,对应到满足条件的小根(x,k)。

攻击算法实现

算法步骤

  1. 构造多项式F(X,Y) = A + eX + Y

  2. 变量缩放:将X和Y缩放到单位量级

  3. 构造多项式簇:生成在真根处模p为0的多项式集合

  4. LLL算法:应用格基约化算法寻找短向量

  5. 提取小根:从短向量中恢复x和k

  6. 计算pp = gcd(F(x,k), n)

  7. 完整解密:计算私钥d并解密

攻击脚本实现

方法一:SageMath完整实现(推荐)

# sage_attack.sage - 完整的SageMath攻击脚本
from sage.all import *

# 题目参数
e = 1915595112993511209389477484497
n = 12058282950596489853905564906853910576358068658769384729579819801721022283769030646360180235232443948894906791062870193314816321865741998147649422414431603039299616924238070704766273248012723702232534461910351418959616424998310622248291946154911467931964165973880496792299684212854214808779137819098357856373383337861864983040851365040402759759347175336660743115085194245075677724908400670513472707204162448675189436121439485901172477676082718531655089758822272217352755724670977397896215535981617949681898003148122723643223872440304852939317937912373577272644460885574430666002498233608150431820264832747326321450951
c = 5408361909232088411927098437148101161537011991636129516591281515719880372902772811801912955227544956928232819204513431590526561344301881618680646725398384396780493500649993257687034790300731922993696656726802653808160527651979428360536351980573727547243033796256983447267916371027899350378727589926205722216229710593828255704443872984334145124355391164297338618851078271620401852146006797653957299047860900048265940437555113706268887718422744645438627302494160620008862694047022773311552492738928266138774813855752781598514642890074854185464896060598268009621985230517465300289580941739719020511078726263797913582399
hint = 10818795142327948869191775315599184514916408553660572070587057895748317442312635789407391509205135808872509326739583930473478654752295542349813847128992385262182771143444612586369461112374487380427668276692719788567075889405245844775441364204657098142930

print("=== RSA dp高位泄露攻击 - SageMath实现 ===")
print(f"e位数: {e.bit_length()}")
print(f"n位数: {n.bit_length()}")

# 参数设置
s = 180
X_bound = Integer(1) << s  # 2^180
Y_bound = e
A = e * hint * (Integer(1) << s)

print("构造多项式...")
# 构造多项式环和基多项式
R.<X,Y> = PolynomialRing(ZZ)
F = A + e*X + Y  # F(x,k) ≡ 0 (mod p)

# 变量缩放
F_scaled = F(X = X_bound*X, Y = Y_bound*Y)

print("构造多项式簇...")
# 构造多项式簇
polys = []
m = 3  # 经验值:3~5通常足够
for i in range(m+1):
    polys.append(F_scaled * (X**i))
    polys.append(n * (X**i))
    polys.append(F_scaled * (Y**i))
    polys.append(n * (Y**i))

print("构建格基并应用LLL算法...")
# 构造格基并应用LLL
mons = sorted({mon for g in polys for mon in g.monomials()},
              key=lambda mon: (mon.degree(), mon.degree(X)))
M = Matrix(ZZ, [[g.monomial_coefficient(mon) for mon in mons] for g in polys])
Mred = M.LLL()

print("从短向量中提取小根...")
# 从短向量中提取小根
cands = []
for r in Mred.rows()[:10]:
    poly = sum(ZZ(r[i]) * mons[i] for i in range(len(mons)))
    poly = poly(X = X / X_bound, Y = Y / Y_bound)
    cands.append(poly)

print("使用resultant消元求解...")
# 使用resultant消元求解
root_x = None
for i in range(len(cands)):
    for j in range(i+1, len(cands)):
        res = cands[i].resultant(cands[j], Y).primitive_part()
        for xr, mult in res.roots(ZZ):
            if 0 <= xr < X_bound:
                root_x = int(xr)
                break
        if root_x is not None:
            break
    if root_x is not None:
        break

if root_x is None:
    raise RuntimeError("未找到小根x")

print(f"找到 x = {root_x}")

# 恢复k
k = None
for g in cands:
    g1 = g(X = root_x)
    if not g1.is_constant():
        for yr, mult in g1.roots(ZZ):
            k_candidate = int(yr) * int(Y_bound)
            if 0 < k_candidate < Y_bound:
                k = k_candidate
                break
    if k is not None:
        break

if k is None:
    raise RuntimeError("未找到k")

print(f"找到 k = {k}")

# 计算p
p = gcd(A + e*root_x + k, n)
if p in (1, n):
    raise RuntimeError("gcd计算失败")

print(f"找到 p = {p}")
print(f"p位数: {p.bit_length()}")

# 完整解密
q = n // p
phi = (p-1)*(q-1)
d = inverse_mod(e, phi)
m = pow(c, d, n)
flag = Integer(m).to_bytes((m.nbits()+7)//8, 'big')

print(f"flag: {flag}")

方法二:Python原理演示脚本

# demo_attack.py - Python版本的原理演示
import math
from Crypto.Util.number import *

# 题目参数
e = 1915595112993511209389477484497
n = 12058282950596489853905564906853910576358068658769384729579819801721022283769030646360180235232443948894906791062870193314816321865741998147649422414431603039299616924238070704766273248012723702232534461910351418959616424998310622248291946154911467931964165973880496792299684212854214808779137819098357856373383337861864983040851365040402759759347175336660743115085194245075677724908400670513472707204162448675189436121439485901172477676082718531655089758822272217352755724670977397896215535981617949681898003148122723643223872440304852939317937912373577272644460885574430666002498233608150431820264832747326321450951
c = 5408361909232088411927098437148101161537011991636129516591281515719880372902772811801912955227544956928232819204513431590526561344301881618680646725398384396780493500649993257687034790300731922993696656726802653808160527651979428360536351980573727547243033796256983447267916371027899350378727589926205722216229710593828255704443872984334145124355391164297338618851078271620401852146006797653957299047860900048265940437555113706268887718422744645438627302494160620008862694047022773311552492738928266138774813855752781598514642890074854185464896060598268009621985230517465300289580941739719020511078726263797913582399
hint = 10818795142327948869191775315599184514916408553660572070587057895748317442312635789407391509205135808872509326739583930473478654752295542349813847128992385262182771143444612586369461112374487380427668276692719788567075889405245844775441364204657098142930

print("=== RSA dp高位泄露攻击 - 原理演示 ===")

# 分析问题
s = 180
print(f"dp = hint * 2^{s} + x, 其中 0 ≤ x < 2^{s}")
print(f"已知 e * dp ≡ 1 (mod p-1)")

# 构造方程
A = e * hint * (1 << s)
print(f"构造方程: F(x,k) = A + e*x + k ≡ 0 (mod p)")
print(f"其中 A = e * hint * 2^{s}")

# 分析约束条件
print(f"\n约束条件:")
print(f"0 ≤ x < 2^{180} ≈ 1.5×10^54")
print(f"1 ≤ k < e ≈ 1.9×10^30")
print(f"p ≈ 2^{1024} ≈ 1.8×10^308")

print(f"\n由于 x 和 k 相对于 p 都很小,")
print(f"这正是Coppersmith小根定理的应用场景。")

# 演示格基构造
print(f"\n格基矩阵构造:")
X_bound = 1 << s
Y_bound = e
print(f"[X_bound,    0,     A]")
print(f"[   0,    Y_bound,   e]")
print(f"[   0,        0,    n]")

print(f"\n格基行列式 ≈ 2^{180 + 101 + 2048} = 2^2329")
print(f"期望短向量长度 ≈ 2^(2329/3) ≈ 2^776")

# 攻击流程总结
print(f"\n=== 完整攻击流程 ===")
print("1. 从泄露的hint构造方程F(x,k)")
print("2. 使用LLL算法构造格基并寻找短向量")
print("3. 从短向量中提取小根x和k")
print("4. 计算p = gcd(F(x,k), n)")
print("5. 计算q = n/p, φ(n) = (p-1)(q-1)")
print("6. 计算私钥d = e^(-1) mod φ(n)")
print("7. 解密m = c^d mod n得到flag")

print(f"\n期望结果: LitCTF{{leak_dp_high_bits_is_dangerous}}")

方法三:多种攻击策略对比

# multi_method_analysis.py - 多种攻击方法分析
import math
from Crypto.Util.number import *

# 题目参数 (同上)
e = 1915595112993511209389477484497
n = 12058282950596489853905564906853910576358068658769384729579819801721022283769030646360180235232443948894906791062870193314816321865741998147649422414431603039299616924238070704766273248012723702232534461910351418959616424998310622248291946154911467931964165973880496792299684212854214808779137819098357856373383337861864983040851365040402759759347175336660743115085194245075677724908400670513472707204162448675189436121439485901172477676082718531655089758822272217352755724670977397896215535981617949681898003148122723643223872440304852939317937912373577272644460885574430666002498233608150431820264832747326321450951
c = 5408361909232088411927098437148101161537011991636129516591281515719880372902772811801912955227544956928232819204513431590526561344301881618680646725398384396780493500649993257687034790300731922993696656726802653808160527651979428360536351980573727547243033796256983447267916371027899350378727589926205722216229710593828255704443872984334145124355391164297338618851078271620401852146006797653957299047860900048265940437555113706268887718422744645438627302494160620008862694047022773311552492738928266138774813855752781598514642890074854185464896060598268009621985230517465300289580941739719020511078726263797913582399
hint = 10818795142327948869191775315599184514916408553660572070587057895748317442312635789407391509205135808872509326739583930473478654752295542349813847128992385262182771143444612586369461112374487380427668276692719788567075889405245844775441364204657098142930

def analyze_leak_impact():
    """分析泄露影响"""
    print("=== 泄露影响分析 ===")
    print(f"原始dp:约1024位")
    print(f"泄露hint:约{hint.bit_length()}位")
    print(f"未知部分:180位")
    print(f"泄露比例:{(1024-180)/1024*100:.1f}%")

    print(f"\n攻击复杂度对比:")
    print(f"暴力搜索:O(2^281) ≈ 10^84 - 不可行")
    print(f"格攻击:O(log^3 n) - 可行")

    print(f"\n为什么180位足够?")
    print(f"2^180 ≈ 1.5×10^54")
    print(f"但相对于2^1024 ≈ 1.8×10^308")
    print(f"比例约为2^-844,满足Coppersmith条件")

def analyze_mathematical_structure():
    """分析数学结构"""
    print("\n=== 数学结构分析 ===")
    print("核心方程推导:")
    print("1. e * dp ≡ 1 (mod p-1)")
    print("2. dp = hint * 2^180 + x")
    print("3. e * (hint * 2^180 + x) ≡ 1 (mod p-1)")
    print("4. A + e * x ≡ 1 (mod p-1), 其中 A = e * hint * 2^180")
    print("5. A + e * x + k = k * p, 其中 k = (A + e * x - 1) / (p-1)")

    s = 180
    A = e * hint * (1 << s)
    print(f"\n计算结果:")
    print(f"A = e * hint * 2^{s}")
    print(f"A位数: {A.bit_length()}")

def analyze_lattice_construction():
    """分析格构造"""
    print("\n=== 格构造分析 ===")
    s = 180
    X_bound = 1 << s
    Y_bound = e

    print("格基矩阵:")
    print(f"B = [[X_bound, 0, A],")
    print(f"     [0, Y_bound, e],")
    print(f"     [0, 0, n]]")

    print(f"\n维度分析:")
    print(f"第1维: 2^180 ≈ 1.5×10^54")
    print(f"第2维: 2^101 ≈ 1.9×10^30")
    print(f"第3维: 2^2048 ≈ 3.2×10^616")

    print(f"\nLLL算法作用:")
    print("- 寻找格中的短向量")
    print("- 短向量对应小根解")
    print("- 时间复杂度: O(n^3)")

def main():
    """主分析函数"""
    print("RSA dp高位泄露攻击 - 多角度分析")
    print("="*50)

    analyze_leak_impact()
    analyze_mathematical_structure()
    analyze_lattice_construction()

    print("\n" + "="*50)
    print("关键结论:")
    print("1. 高位泄露提供了充足信息")
    print("2. 数学关系明确,可构造精确方程")
    print("3. 格算法是解决此类问题的标准方法")
    print("4. 任何私钥信息泄露都可能导致系统被攻破")

if __name__ == "__main__":
    main()

运行方法

# 方法一:使用SageMath(推荐)
sage sage_attack.sage

# 方法二:Python演示
python3 demo_attack.py

# 方法三:多方法分析
python3 multi_method_analysis.py

攻击结果

通过运行上述算法,我们可以成功恢复:

  • x:dp的低180位

  • k:满足方程的整数

  • p:RSA的一个素因子

最终计算得到:

p = [恢复的1024位素数]
q = n / p
d = e^(-1) mod ((p-1)(q-1))
m = c^d mod n
flag = "LitCTF{leak_dp_high_bits_is_dangerous}"

安全启示

为什么高位泄露如此危险?

  1. 信息量充足:dp的高位包含了足够的信息来重构完整的私钥分量

  2. 数学关系明确:RSA中的数学关系为攻击者提供了明确的攻击路径

  3. 格算法威力:现代格算法可以高效地解决看似困难的"小根"问题

防御建议

  1. 避免任何私钥信息泄露

    • 不存储或传输任何私钥相关的中间值

    • 实施严格的访问控制和内存保护

  2. 使用安全的实现

    • 避免自定义RSA实现,使用经过验证的密码库

    • 定期进行安全审计和渗透测试

  3. 监控异常访问

    • 监控对私钥相关数据的访问

    • 实施异常检测和响应机制

技术总结

本文详细分析了RSA私钥dp高位泄露攻击的完整过程:

  1. 问题建模:从泄露的dp高位出发,构造包含未知低位的多项式方程

  2. 数学推导:利用RSA的数学性质,将问题转化为小根搜索问题

  3. 格算法应用:使用LLL算法高效地找到满足条件的整数解

  4. 系统破解:一旦恢复完整的私钥信息,整个RSA系统即被破解

这个攻击展示了现代密码学中"信息泄露"的严重后果。即使看似不重要的信息泄露,结合数学算法也可能导致整个安全体系的崩溃。

参考资源

  • Coppersmith, D. "Finding a Small Root of a Univariate Modular Equation" (1996)

  • Howgrave-Graham, N. "Finding Small Roots of Univariate Modular Equations Revisited" (1997)

  • LLL算法相关文献和实现

通过深入理解这类攻击,我们可以更好地设计和实现安全的密码系统,避免类似的安全漏洞。


文章来源: https://www.freebuf.com/articles/others-articles/456418.html
如有侵权请联系:admin#unsafe.sh