ASISCTFFinals2017–Gracias密码学挑战题详解

admin 2026-01-09 23:33:27 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详解ASISCTF密码题Gracias,该题结合三素数RSA与ElGamal加密。核心漏洞在于使用了过小的私钥d,使得d约等于n的0.167次方。作者通过将Boneh-Durfee攻击适配至三素数RSA场景,利用格基规约算法成功恢复私钥,进而解密获取Flag。文章涵盖了漏洞分析、攻击原理、算法适配及完整的解题脚本,展示了针对小私钥RSA的攻击技术。 综合评分: 95 文章分类: CTF,漏洞分析,代码审计


cover_image

ASIS CTF Finals 2017 – Gracias 密码学挑战题详解

原创

破镜安全

破镜安全

2026年1月9日 08:00 四川

ASIS CTF Finals 2017 – Gracias 密码学挑战题详解

一、题目背景

本题是ASIS CTF Finals 2017中的一道密码学挑战题,名为”Gracias”。题目实现了一个基于多素数RSA的混合加密系统,结合了RSA加密和ElGamal加密的特点。本文将从零开始,详细分析题目的加密机制、漏洞所在,以及完整的攻击过程。

二、题目文件分析

题目提供了一个压缩包,解压后包含以下文件:

2.1 加密源代码 (gracias.py)

首先,我们来看题目提供的加密算法源代码:

#!/usr/bin/python

from gmpy import *
from Crypto.Util.number import *
import gensafeprime
from flag import FLAG

def make_pubpri(nbit):
    p, q, r = [ getPrime(nbit) for _ in xrange(3)]
    n = p * q * r
    phi = (p-1)*(q-1)*(r-1)
    l = min([p, q, r])
&nbsp; &nbsp; d = getPrime(1&nbsp;<<&nbsp;8)
&nbsp; &nbsp; e = inverse(d, phi)
&nbsp; &nbsp; a = gensafeprime.generate(2*nbit)
&nbsp; &nbsp;&nbsp;while&nbsp;True:
&nbsp; &nbsp; &nbsp; &nbsp; g = getRandomRange(2, a)
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;pow(g,&nbsp;2, a) !=&nbsp;1&nbsp;and&nbsp;pow(g, a/2, a) !=&nbsp;1:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;break
&nbsp; &nbsp;&nbsp;return&nbsp;(n, e, a, g), (n, d, a, g)

def&nbsp;encrypt(m, pub):
&nbsp; &nbsp; n, e, a, g = pub
&nbsp; &nbsp; k = getRandomRange(2, a)
&nbsp; &nbsp; K = pow(g, k, a)
&nbsp; &nbsp; c1, c2 = pow(k, e, n), (m * K) % a
&nbsp; &nbsp;&nbsp;return&nbsp;c1, c2

nbit =&nbsp;512
pubkey, privkey = make_pubpri(nbit)
m = bytes_to_long(FLAG)
c = encrypt(m, pubkey)
print&nbsp;'c =', c
print&nbsp;'pubkey =', pubkey

2.2 密文和公钥 (enc_pubkey.txt)

题目还提供了加密后的密文和公钥:

c = (1569733526826523065259704222721381245770313117205865099913421859731162526943498524936251685846967970606251353344665893442015804015671457823645874503670136308040791285744658847419176471348768113798503897694020110157476679833746227801224812046930570487233225157924912272791212802495997329083436189937249314855532400635293522270501567950040825794060896420481676398789310029592608176167251882124182145471818654414925639589921023176070657483148482403065241178276749773,&nbsp;139537660044872985880471632333334179976891152860359271230202507995985566816703080930428310461057387079799847266510420206696052591677854190150642820963140050439023069266243433278700748622126726137374130247097863526461696642750021196138340072411724739383716017406022211953417323065831672315854246554523225039827)

pubkey = (n, e, a, g)

其中:

  • n = 1696852658826990842058316561963467335977986730245296081842693913454799128341723605666024757923000936875008280288574503060506225324560725525210728761064310034604441130912702077320696660565727540525259413564999213382434231194132697630244074950529107794905761549606578049632101483460345878198682237227139704889943489709170676301481918176902970896183163611197618458670928730764124354693594769219086662173889094843054787693685403229558143793832013288487194871165461567
  • e = 814161885590044357190593282132583612817366020133424034468187008267919006610450334193936389251944312061685926620628676079561886595567219325737685515818965422518820810326234612624290774570873983198113409686391355443155606621049101005048872030700143084978689888823664771959905075795440800042648923901406744546140059930315752131296763893979780940230041254506456283030727953969468933552050776243515721233426119581636614777596169466339421956338478341355508343072697451
  • a = 171012227587318507773834753911094468358648971527111097308935888531930900156798659257578479378777764146070352809723708236353390208094909385240006920137781562826981091183813955039359863361624869703055918575613667858215532572602435432258750639197322091887713402631456113333645709142822182724397962837201266977523
  • g = 96969753191136466007366303619618019752521508403657426306543836447071659732926802256183021740376016065813234292694535879838415771865207311953800116203362150588941093508091412441933752168889516206420588410478242229762908362637083786338280959547015086176046206126019992386890758970740552952647510652431386064722

三、加密算法深度分析

3.1 密钥生成过程

让我们逐行分析密钥生成函数 make_pubpri:

p, q, r = [ getPrime(nbit)&nbsp;for&nbsp;_&nbsp;in&nbsp;xrange(3)]
n = p * q * r

这里生成了三个512位的素数p、q、r,然后计算模数 n = p * q * r。这是一个三素数RSA系统,不同于传统的两素数RSA。

phi = (p-1)*(q-1)*(r-1)

计算欧拉函数 φ(n)。对于三素数RSA,欧拉函数为 φ(n) = (p-1)(q-1)(r-1)。

d = getPrime(1&nbsp;<<&nbsp;8)
e = inverse(d, phi)

这是关键的漏洞所在:

  • 1 << 8 等于 256,表示私钥d是一个256位的素数
  • 然后计算 e = d^(-1) mod φ(n),即e是d的模逆元

这种做法与标准RSA完全相反。标准RSA是先选择一个小的公钥e(如65537),然后计算大的私钥d。而这里先选择了一个非常小的私钥d,导致公钥e变得非常大。

a = gensafeprime.generate(2*nbit)
while&nbsp;True:
&nbsp; &nbsp; g = getRandomRange(2, a)
&nbsp; &nbsp;&nbsp;if&nbsp;pow(g,&nbsp;2, a) !=&nbsp;1&nbsp;and&nbsp;pow(g, a/2, a) !=&nbsp;1:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;break

这部分生成ElGamal加密所需的参数:

  • a是一个1024位的安全素数
  • g是a的一个生成元

3.2 加密过程分析

加密函数 encrypt 实现了一个混合加密方案:

def&nbsp;encrypt(m, pub):
&nbsp; &nbsp; n, e, a, g = pub
&nbsp; &nbsp; k = getRandomRange(2, a)
&nbsp; &nbsp; K = pow(g, k, a)
&nbsp; &nbsp; c1, c2 = pow(k, e, n), (m * K) % a
&nbsp; &nbsp;&nbsp;return&nbsp;c1, c2

加密过程分为以下步骤:

步骤1: 随机生成一个数k,范围在[2, a)之间

步骤2: 计算共享密钥 K = g^k mod a (这是ElGamal加密的核心)

步骤3: 生成两个密文:

  • c1 = k^e mod n (使用RSA加密随机数k)
  • c2 = m * K mod a (使用共享密钥K加密明文m)

这是一个典型的混合加密方案:RSA用于加密会话密钥k,ElGamal用于加密实际消息。

四、漏洞识别与分析

4.1 小私钥问题

通过分析密钥生成代码,我们发现了一个严重的安全漏洞:

d = getPrime(1&nbsp;<<&nbsp;8) &nbsp;# d是一个256位的素数

让我们计算一下私钥d相对于模数n的大小:

  • 模数n由三个512位素数相乘,约为 512 * 3 = 1536位
  • 私钥d只有256位
  • 因此 d ≈ n^(256/1536) ≈ n^0.167

4.2 为什么小私钥是危险的

在RSA加密系统中,私钥d和公钥e满足以下关系:

e * d ≡ 1 (mod φ(n))

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

e * d = k * φ(n) + 1

当d很小时,这个等式可以转化为一个多项式小根问题,使用格基规约算法(LLL)可以求解。针对小私钥RSA的攻击主要有两种:

  1. Wiener攻击: 适用于 d < n^0.25
  2. Boneh-Durfee攻击: 适用于 d < n^0.292

本题中 d ≈ n^0.167,完全在Boneh-Durfee攻击的范围内。

五、Boneh-Durfee攻击原理

5.1 标准RSA的Boneh-Durfee攻击

Boneh-Durfee攻击是一种针对小私钥RSA的强大攻击方法。其核心思想是将RSA问题转化为多项式小根问题。

对于标准的两素数RSA (n = p * q):

  1. 欧拉函数: φ(n) = (p-1)(q-1) = n – (p+q) + 1
  2. 设 A = (n+1)/2,构造多项式: f(x,y) = x(A+y) + 1
  3. 该多项式在模e下有小根 (x,y) = (k, -(p+q-1)/2)
  4. 使用Coppersmith方法和LLL算法求解小根

5.2 适配到三素数RSA

本题使用的是三素数RSA (n = p * q * r),需要对攻击方法进行调整:

对于三素数RSA:

  • φ(n) = (p-1)(q-1)(r-1)
  • 展开: φ(n) = pqr – (pq+pr+qr) + (p+q+r) – 1
  • 即: φ(n) = n – (pq+pr+qr) + (p+q+r) – 1

因此,我们需要修改多项式构造:

  • 设 A = (n-1)/2
  • 设 y = -(pq+pr+qr) + (p+q+r)
  • 多项式: f(x,y) = x(A+y) + 1

关键的修改在于y的界限:

  • 对于两素数RSA: y ≈ n^0.5
  • 对于三素数RSA: y ≈ n^(2/3)

六、攻击实施

6.1 修改Boneh-Durfee攻击脚本

我们使用David Wong实现的Boneh-Durfee攻击脚本作为基础,针对三素数RSA进行修改。关键修改在boneh_durfee函数中:

def&nbsp;boneh_durfee(n, e):
&nbsp; &nbsp; delta = RR(0.167) &nbsp;# d ~ n^0.167
&nbsp; &nbsp; m =&nbsp;5
&nbsp; &nbsp; t = round((1-2*delta) * m)
&nbsp; &nbsp; X = ZZ(2*floor(n^delta))
&nbsp; &nbsp;&nbsp;# 关键修改:将Y的界限设为n^(2/3)
&nbsp; &nbsp; Y = ZZ(floor(n^(2/3)))
&nbsp; &nbsp; P.<x,y> = PolynomialRing(ZZ)
&nbsp; &nbsp; A = ZZ((n-1)/2)
&nbsp; &nbsp; pol =&nbsp;1&nbsp;+ x * (A + y)
&nbsp; &nbsp; solx, soly = boneh_durfee_small_roots(pol, e, m, t, X, Y)
&nbsp; &nbsp;&nbsp;if&nbsp;solx >&nbsp;0:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;int(pol(solx, soly) / e)
&nbsp; &nbsp;&nbsp;return&nbsp;0

参数说明:

  • delta = 0.167: 表示 d ≈ n^0.167
  • m = 5: 格的维度参数
  • t: 控制y方向的多项式数量
  • X = 2 * n^delta: x的上界
  • Y = n^(2/3): y的上界(这是针对三素数RSA的关键修改)

6.2 运行攻击脚本

将题目给出的n和e代入脚本,运行攻击:

sage boneh_durfee.sage

攻击成功后,输出结果:

96495049525709737646237784043989949922984947124527440290534285859764556084120
-213794805403874041582980303133544562301251683427479249295482840787267897208520588155734823678603567894373005996132986582548573443974113270682593599108980563590099083306897483648896074029256401888893482421279582317475577712512180341863233891668553849278499685187092538645494649114612591891435800686745132070463
100556095937036905102538523179832446199526507742826168666218687736467897968451

输出的三个值分别是:

  • 第一行: solx (k的值)
  • 第二行: soly (y的值)
  • 第三行: 私钥d = 100556095937036905102538523179832446199526507742826168666218687736467897968451

攻击成功!我们成功恢复了256位的私钥d。

七、解密密文

7.1 验证私钥正确性

在使用恢复的私钥d之前,我们需要验证它是否正确。RSA的基本性质是:

对于任意消息m: (m^e)^d ≡ m (mod n)

我们使用m=2进行验证:

d =&nbsp;100556095937036905102538523179832446199526507742826168666218687736467897968451
print(pow(pow(2, e, n), d, n) ==&nbsp;2) &nbsp;# 输出: True

验证通过!这证明我们恢复的私钥d是完全正确的。

7.2 解密过程

回顾加密过程:

  • c1 = k^e mod n
  • c2 = m * K mod a,其中 K = g^k mod a

解密需要三个步骤:

步骤1: 使用私钥d解密c1,得到随机数k

k = pow(c1, d, n)

由于 c1 = k^e mod n,而 d 是 e 的模逆元,因此 c1^d ≡ k (mod n)。

步骤2: 使用k计算共享密钥K

K = pow(g, k, a)

这一步重现了加密时的共享密钥计算过程。

步骤3: 使用K解密c2,得到明文m

m = (c2 * modinv(K, a)) % a

由于 c2 = m * K mod a,我们需要计算 m ≡ c2 * K^(-1) (mod a)。

7.3 完整解密脚本

from&nbsp;Crypto.Util.number&nbsp;import&nbsp;long_to_bytes

def&nbsp;modinv(a, m):
&nbsp; &nbsp;&nbsp;"""扩展欧几里得算法计算模逆元"""
&nbsp; &nbsp;&nbsp;def&nbsp;extended_gcd(a, b):
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;a ==&nbsp;0:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;b,&nbsp;0,&nbsp;1
&nbsp; &nbsp; &nbsp; &nbsp; gcd, x1, y1 = extended_gcd(b % a, a)
&nbsp; &nbsp; &nbsp; &nbsp; x = y1 - (b // a) * x1
&nbsp; &nbsp; &nbsp; &nbsp; y = x1
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;gcd, x, y

&nbsp; &nbsp; gcd, x, _ = extended_gcd(a % m, m)
&nbsp; &nbsp;&nbsp;if&nbsp;gcd !=&nbsp;1:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;raise&nbsp;Exception('Modular inverse does not exist')
&nbsp; &nbsp;&nbsp;return&nbsp;(x % m + m) % m

# 公钥和密文
n =&nbsp;1696852658826990842058316561963467335977986730245296081842693913454799128341723605666024757923000936875008280288574503060506225324560725525210728761064310034604441130912702077320696660565727540525259413564999213382434231194132697630244074950529107794905761549606578049632101483460345878198682237227139704889943489709170676301481918176902970896183163611197618458670928730764124354693594769219086662173889094843054787693685403229558143793832013288487194871165461567
e =&nbsp;814161885590044357190593282132583612817366020133424034468187008267919006610450334193936389251944312061685926620628676079561886595567219325737685515818965422518820810326234612624290774570873983198113409686391355443155606621049101005048872030700143084978689888823664771959905075795440800042648923901406744546140059930315752131296763893979780940230041254506456283030727953969468933552050776243515721233426119581636614777596169466339421956338478341355508343072697451
a =&nbsp;171012227587318507773834753911094468358648971527111097308935888531930900156798659257578479378777764146070352809723708236353390208094909385240006920137781562826981091183813955039359863361624869703055918575613667858215532572602435432258750639197322091887713402631456113333645709142822182724397962837201266977523
g =&nbsp;96969753191136466007366303619618019752521508403657426306543836447071659732926802256183021740376016065813234292694535879838415771865207311953800116203362150588941093508091412441933752168889516206420588410478242229762908362637083786338280959547015086176046206126019992386890758970740552952647510652431386064722

c1 =&nbsp;1569733526826523065259704222721381245770313117205865099913421859731162526943498524936251685846967970606251353344665893442015804015671457823645874503670136308040791285744658847419176471348768113798503897694020110157476679833746227801224812046930570487233225157924912272791212802495997329083436189937249314855532400635293522270501567950040825794060896420481676398789310029592608176167251882124182145471818654414925639589921023176070657483148482403065241178276749773
c2 =&nbsp;139537660044872985880471632333334179976891152860359271230202507995985566816703080930428310461057387079799847266510420206696052591677854190150642820963140050439023069266243433278700748622126726137374130247097863526461696642750021196138340072411724739383716017406022211953417323065831672315854246554523225039827

# 通过Boneh-Durfee攻击恢复的私钥
d =&nbsp;100556095937036905102538523179832446199526507742826168666218687736467897968451

# 解密过程
k = pow(c1, d, n)
K = pow(g, k, a)
m = (c2 * modinv(K, a)) % a

# 转换为字符串
flag = long_to_bytes(m)
print(flag.decode())

运行脚本后,成功得到flag:

ASIS{Wiener_at7ack_iN_mUlt1_Prim3_RSA_iZ_f34sible_t0O!}

解密成功!

八、技术总结

8.1 攻击链回顾

本题的完整攻击链可以总结为以下步骤:

  1. 代码审计: 分析加密源代码,识别密钥生成和加密流程
  2. 漏洞发现: 发现私钥d只有256位,远小于模数n
  3. 攻击选择: 确定使用Boneh-Durfee攻击恢复小私钥
  4. 算法适配: 将标准攻击适配到三素数RSA,修改Y的界限
  5. 私钥恢复: 运行攻击脚本,成功恢复256位私钥d
  6. 密文解密: 使用私钥解密RSA部分,再解密ElGamal部分

8.2 关键技术点

1. 多素数RSA

多素数RSA使用多个素数的乘积作为模数:

  • 标准RSA: n = p * q
  • 多素数RSA: n = p * q * r (或更多素数)
  • 欧拉函数: φ(n) = (p-1)(q-1)(r-1)

2. 小私钥攻击

当RSA私钥d过小时,可以使用以下攻击方法:

  • Wiener攻击: d < n^0.25
  • Boneh-Durfee攻击: d < n^0.292
  • 本题: d ≈ n^0.167,完全可以被攻击

3. 格基规约(LLL算法)

Boneh-Durfee攻击的核心是LLL算法:

  • 用于在格中找到短向量
  • 将RSA小根问题转化为格中的短向量问题
  • 通过LLL算法求解多项式的小根

4. 混合加密系统

本题使用了RSA和ElGamal的混合加密:

  • RSA部分: 加密会话密钥k
  • ElGamal部分: 使用共享密钥K加密消息
  • 弱点: RSA部分的漏洞导致整个系统崩溃

8.3 安全建议

1. 私钥大小要求

  • 私钥d应该足够大,建议 d > n^0.292
  • 不要为了提高解密速度而使用小私钥
  • 标准做法: 先选择小的公钥e,再计算大的私钥d

2. 密钥生成方式

错误的做法(本题):

d = getPrime(256) &nbsp;# 先生成小的d
e = inverse(d, phi) &nbsp;# 再计算e

正确的做法:

e =&nbsp;65537&nbsp;&nbsp;# 先选择标准公钥
d = inverse(e, phi) &nbsp;# 再计算d

3. 混合加密系统设计

  • 确保每个组件都是安全的
  • 一个组件的弱点会导致整个系统失效
  • 使用经过验证的标准方案

九、学习要点

9.1 代码审计能力

在密码学CTF题目中,源代码分析至关重要:

  • 仔细分析密钥生成过程
  • 识别不安全的参数选择
  • 理解加密算法的实现细节

9.2 攻击方法的适配

标准攻击方法需要根据具体场景调整:

  • 理解攻击的数学原理
  • 识别需要修改的参数
  • 正确设置边界条件

9.3 工具使用能力

本题涉及的工具:

  • SageMath: 运行Boneh-Durfee攻击脚本
  • Python: 编写解密脚本
  • 密码学库: pycrypto, gmpy等

9.4 数学基础

解决本题需要的数学知识:

  • 数论: 模运算、欧拉函数、模逆元
  • RSA原理: 公钥私钥的数学关系
  • 多项式理论: Coppersmith方法

十、总结

本题”Gracias”是一道综合性很强的密码学挑战题,涉及多个知识点的综合运用。题目的核心漏洞在于使用了过小的私钥d(256位),这使得Boneh-Durfee攻击成为可能。

通过本题的分析和求解,我们完成了以下工作:

  1. 分析了三素数RSA的密钥生成和加密流程
  2. 识别出小私钥漏洞(d ≈ n^0.167)
  3. 将Boneh-Durfee攻击适配到三素数RSA场景
  4. 成功恢复256位私钥d
  5. 解密混合加密系统,获得flag

本题充分展示了密码学攻击的完整流程,从漏洞发现到攻击实施,再到最终的密文解密。这对于学习密码学攻击技术和理解RSA安全性具有重要的教育意义。

参考资料

  1. Dan Boneh and Glenn Durfee, “Cryptanalysis of RSA with Private Key d Less than N^0.292”
  2. “Fast Variants of RSA”, New Mexico State University
  3. David Wong’s Boneh-Durfee Implementation: https://github.com/mimoo/RSA-and-LLL-attacks
  4. Coppersmith’s Method for Finding Small Roots of Modular Polynomial Equations

最终FlagASIS{Wiener_at7ack_iN_mUlt1_Prim3_RSA_iZ_f34sible_t0O!}


免责声明:

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

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

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

本文转载自:破镜安全 破镜安全《ASIS CTF Finals 2017 – Gracias 密码学挑战题详解》

评论:0   参与:  0