☰
轩禹CTF_RSA工具:集成多类攻击模块,CTF密码题秒解利器
2026/10/20 6:51:32 网站建设 项目流程

简介:面向CTF竞赛选手与密码学初学者的RSA综合工具包,聚焦常见RSA攻击题型与自动化解题脚本,可在比赛或练习中快速定位解题思路并验证结果。压缩包共32个文件,以18个Python脚本为主体,覆盖维纳攻击、低加密指数攻击、共模攻击、dp/dq泄露、Rabin算法、Boneh Durfee、有限域开方、模不互素、低加密广播攻击等经典场景,另含3个exe命令行程序及yafu因子分解组件,用于大整数分解、日志记录与配置管理,整体大小26.83MB。压缩包内置素数表与ctfprimes等数据文件,便于构造测试样例和随机参数。已有2077人学习/下载,适用于CTF入门选手快速上手,也适合有经验者赛前查漏补缺。工具集还提供AMM算法与特殊题型处理方法,覆盖e与phi不互素、p高位泄露、m高位泄露等进阶场景,脚本按攻击类型分类、结构清晰,能够帮助读者系统梳理RSA题型的攻击脉络,提升解题效率。

1. 轩禹CTF_RSA工具:比赛里最缺的不是RSA原理,是解题速度

一场CTF比赛留给密码题的时间往往不到半小时,RSA又是出现频率最高、套路最多的一类。低加密指数、共模、维纳、费马分解,每种攻击单独写脚本都不难,难的是在有限时间里判断该用哪一种,并且不把参数格式搞错。轩禹CTF_RSA工具3.6.1这类集成工具解决的正是这个问题:它把常见RSA攻击收拢成统一的参数入口,你只需要提供n、e、c,工具按规则尝试还原明文。适合刚入门CTF、想省下反复写脚本时间的新手,也适合需要批量验证RSA题思路的进阶选手。下面从解压运行讲起,拆开它的攻击分类、参数边界,再列出我实际踩过的几个坑。

2. 摸清RSA攻击套路:工具内置的分类与适用边界

工具不是黑匣子。它能在几秒内给出一串明文,是因为背后按固定顺序尝试了若干种经典攻击,这个顺序通常是「先分解模数,再看指数异常,再看多组参数关系,最后看私钥位数泄露」。理解这个顺序,才知道为什么某些参数要单独勾选,也才知道自动检测失败后该手动切到哪个模式。

RSA题目里真正变化的只有五个量:模数n、公钥指数e、密文c,有时还有私钥指数d或它的部分片段dp、dq。工具拿到参数后会把每个攻击都当成一个独立模块,模块之间用输入条件做闸门。例如低加密指数攻击要求e小于某个阈值,共模攻击要求两组数据共享同一个n,Wiener攻击要求e的位数接近n的位数。闸门不满足,模块直接跳过。这也是为什么同一个工具在A题上秒出结果,在B题上却什么都给不出来——不是工具坏了,是题目根本不满足当前模块的前提。

2.1 从n、e、c三个入口判断攻击方向

拿到RSA题先别急着打开工具,先看一眼三个核心数字的特征。判断方向可以依赖几条经验:

  • n的位数低于512位,或者n能被小质数整除,优先走模数分解。
  • e等于3、5、7这类小值,且c的位数明显低于n的位数,优先走低加密指数。
  • 同一份题目里给了两组(e,c),n完全相同,优先共模攻击。
  • e非常大,位数接近n,同时n在1024到2048位之间,优先尝试Wiener攻击,它针对的是d偏小的场景。
  • n是p²的结构,或者p、q是相邻质数,费马分解会在几秒内生效。

工具界面上通常把这些判断自动化了,但手动过一遍仍然有用。因为自动检测是按固定顺序跑的,如果第一优先级的攻击不满足时它直接跳过还好,怕的是它误判成另一种攻击然后跑很久。第5章会讲这类误判的具体案例。

2.2 工具内置的攻击类型与参数格式

绝大多数同类RSA工具都包含下面几张表里的模块,输入格式基本统一:n、e、c用十进制或十六进制整数,dp、dq这类泄露参数单独提供输入槽。

攻击类型需要的输入典型成功条件常见失败原因
模数分解n, e, cn存在小因子或p、q十分接近n是正常生成的大素数乘积
低加密指数e, cm^e < n,允许直接开e次方e太大,或c经过模运算后失真
共模攻击n, e1, e2, c1, c2gcd(e1, e2) == 1,且是同一明文两组密文来自不同明文
Wienern, e, cd < n^0.25d偏大,或n太小导致连分数过短
dp/dq泄露n, e, c, dp, dqdp、dq确实由同一私钥生成dp、dq是伪造值

要注意一个细节:工具的输入槽大多数接收的是整数int,不是bytes。同一个十六进制字符串,直接填进去和转成int再填进去,结果完全不同。我见过不少人在这一步把进制搞错,后面所有输出都是乱的。统一的处理办法是:不管题目给的是hex、base64还是PEM文件,先在脚本里转成int,再用这个int填工具。

2.3 拿到题目先做的三道工序:分类、提取、备份

第一步是分类,判断题目给的是单组公钥还是多文件结构,有没有提示词,比如flag格式前缀。第二步是提取,写个小脚本把公钥PEM拆成n和e,把密文统一成整数。第三步是备份,原始数据无论如何先存一份,转换过程丢一个前缀0整个题就做不出来了。

提取PEM最快的方式是openssl:

openssl rsa -pubin -in public.pem -text -noout | head -20

这条命令会把PEM公钥里的模数和指数直接打印成可读文本,方便肉眼核对。逻辑说明:-pubin表示输入是公钥,-text要求显示内部结构,-noout避免把密钥本体再输出一遍。参数说明:如果文件是私钥格式,去掉-pubin即可;如果报错说不是PEM格式,说明文件可能是DER,先执行openssl rsa -pubin -inform DER -in public.der -text -noout再看。

备份习惯在连续刷题时尤其重要。某次我把题目给的十六进制密文手工转成了十进制,结果少复制了一位,后面的明文怎么解都是乱码,最后回看原始文件才发现问题。原始文件就是后悔药,别把转换后的结果覆盖回去。

3. 从zip解压到跑通第一道RSA题:完整的操作流

这一章把第一次使用这个工具的全过程走一遍。假设你刚拿到轩禹CTF_RSA工具3.6.1.zip,还没看过帮助文档,照着下面的操作流可以跑通一道典型题目。整体流程是:解压、检查依赖、准备输入参数、执行攻击、处理输出。

3.1 解压、运行环境与快速自检

unzip 轩禹CTF_RSA工具3.6.1.zip -d ~/tools/rsa-3.6.1 cd ~/tools/rsa-3.6.1 file *

逻辑说明:第一行把zip解压到固定目录,第二行进入目录,第三行用file命令检查每个文件的真实类型。这里有个经验:file比直接看扩展名可靠得多,它能区分出这是Linux下的ELF可执行文件、Windows下的PE文件,还是一个需要解释器运行的Python脚本。

参数说明:如果你的系统上unzip提示需要装包,常见做法是apt install unzip或yum install unzip,这个看发行版选一个。如果解压出来的文件名是乱码,大概率是zip内编码问题,可以尝试unzip -O UTF-8 轩禹CTF_RSA工具3.6.1.zip -d ~/tools/rsa-3.6.1。文件名带中文确实容易踩这个坑,我一般先复制一份改成全英文名再解压,省得终端编码和系统区域设置打架。

解压后先看依赖。工具如果依赖Python库,通常会缺gmpy2和pycryptodome。gmpy2提供高精度大整数运算,Wiener攻击和开方操作都依赖它;pycryptodome负责解析PEM密钥文件。缺哪个装哪个:

pip install gmpy2 pycryptodome

逻辑说明:这两行把工具最常调用的两个第三方库装好。参数说明:如果你的Python环境是3.9以下或3.12以上的特定版本,gmpy2可能没有预编译包,这时需要先装编译工具链build-essential再用pip install gmpy2源码编译。这个坑比较隐蔽,装不上时先看pip输出里是不是在编译而不是在下载。

3.2 从PEM和十六进制文本里抽取n、e、c

题目给一个PEM公钥文件和一个hex密文,第一步是把它们转成工具能用的整数。我一般用下面这段脚本:

from Crypto.PublicKey import RSA import re with open("public.pem", "rb") as f: pub_data = f.read() try: key = RSA.import_key(pub_data) n = key.n e = key.e print("n =", n) print("e =", e) except ValueError: print("不是标准PEM,尝试从文本中提取十六进制") text = pub_data.decode(errors="ignore") hex_n = re.findall(r"[0-9a-fA-F]{64,}", text) print(hex_n[:1])

逻辑说明:脚本优先用RSA.import_key解析PEM,解析成功就能直接拿到n和e。解析失败则把文件当文本读,用正则抓取长度不少于64个字符的十六进制片段,这段通常就是模数n。参数说明:errors="ignore"很关键,PEM文件里可能混入不可见字符,直接decode会抛异常,加上这个参数能保证走到正则分支。

密文的转换更简单:

c = int("0x9f2c...", 0) # 密文是hex,用0x前缀 n = int("00c1a3...", 16) # 模数没带前缀,手动指定16

逻辑说明:int的第二个参数如果是0,Python会根据字符串前缀自动判断进制;如果手动写16,则强制按十六进制解析。参数说明:题目里经常出现十六进制字符串前带\x或者没有0x的情况,这两种写法都覆盖到了。我建议所有从题目原始材料里转换出来的值都打印一遍,和题目预期值核对,再填进工具。

3.3 按提示输入参数并拿到明文

把上一步得到的n、e、c填进工具的输入槽,选择自动检测模式,执行后会看到输出。命令行版本通常是这个形式:

python3 xuan_yu_rsa.py --n 0x00c1a3... --e 65537 --c 0x9f2c... --mode auto

逻辑说明:--n、--e、--c分别接收模数、公钥指数和密文,--mode指定攻击模式。参数说明:--mode auto表示自动检测,其他常见值包括factor、low_e、common_n、wiener,分别对应模数分解、低加密指数、共模攻击和Wiener攻击。如果auto没有输出明文,就按第2章的判断手动指定模式重跑。我在实际使用中,auto模式的成功率在六成左右,剩余四成是因为题目混合了多种攻击条件,需要手动打开第二个攻击模块。

3.4 输出格式与字节转字符串的处理

工具的输出有时候是一个大整数,有时候是一段hex。flag通常藏在转成字节后的字符串里:

from Crypto.Util.number import long_to_bytes flag_int = 1234567890 # 替换成工具输出的明文整数 flag_bytes = long_to_bytes(flag_int) try: print(flag_bytes.decode()) except UnicodeDecodeError: print(flag_bytes.hex())

逻辑说明:long_to_bytes把大整数按字节切分,得到原始字节串。先尝试decode()转成可打印字符,失败则输出hex再人工查看。参数说明:decode()默认用utf-8,而flag内容一般只有可打印ASCII,所以一旦出现UnicodeDecodeError,说明明文中含有超出ASCII范围的字节,这时候看hex反而更清楚,不强制转utf-8。

4. 高频攻击的单独调优:四个常用功能的参数与边界

自动检测能覆盖大部分简单题,但比赛里常出现需要手动选模式的情况。这一章把四个高频攻击的原理、参数边界和调优方式单独拆开讲,重点说清楚每个模块在什么条件下有效、什么条件下会误判。

4.1 模数分解:费马分解与Pollard rho的参数范围

模数分解是RSA题的第一优先级,因为一旦n被分解,私钥立刻还原。费马分解针对的是p和q距离很近的情况,数学依据是n可以写成两个平方数的差。工具里通常给你一个「分解精度」参数,实际控制的是循环次数。手动判断适用性的脚本如下:

import gmpy2 a = gmpy2.isqrt(n) + 1 b2 = a * a - n while not gmpy2.is_square(b2): a += 1 b2 = a * a - n p = a - gmpy2.isqrt(b2) q = a + gmpy2.isqrt(b2) print("p =", p) print("q =", q)

逻辑说明:从sqrt(n)向上取整开始,不断调整a,直到a² - n成为完全平方数,此时n的因子立即分解出来。参数说明:这个循环的耗时完全取决于p和q的差值。差值在几千以内时是秒出,差值到百万级别就要几分钟,差值再大就不适合硬跑,应该换Pollard rho或者直接用更重的工具。我一般只看一个数:gmpy2.isqrt(n) - p的大小,这个差值超过一万就果断放弃费马分解。

Pollard rho在工具里适合处理n在512位以下、含有较小因子但不容易试除的情况。它本质是随机寻找碰撞,参数上有迭代上限,通常默认设成10^6,跑超过上限自动放弃。如果你的题目n有不止两个因子,分解出第一个因子后需要把n除以该因子再继续,工具一般会自动处理这部分。

4.2 低加密指数攻击:从爆破到字节填充

e很小是RSA里最明显的送分信号。攻击成立的数学前提是:加密时c = m^e mod n,如果m^e本来就小于n,模运算没有起任何作用,直接对c开e次方就能得到m。

from gmpy2 import iroot e = 3 c = 832978... # 替换成题目给的密文 m, exact = iroot(c, e) if exact: print("明文整数:", m) else: print("e次方不是整数,密文经过了模运算")

逻辑说明:iroot(c, e)返回两个值,第一个是c的e次方根向下取整,第二个是布尔值,表示开方结果是否完全精确。参数说明:如果exact为False,说明m^e已经超过了n,不能直接开方,这时要换思路,比如检查m是否只经过了某种填充,或者题目是否用了同一e加密多组消息。

这类攻击在工具里的参数是「小明文爆破上限」。有些题目虽然e与n不满足直接开方条件,但明文本身很短,比如只有几字节。工具会尝试从0到一个上限值逐一遍历,拼上常见前缀再加密比较。这个上限默认是10^6,题目如果明示flag格式为固定前缀,可以把这个上限调低,加快爆破速度。

4.3 共模攻击:在拿不到私钥的情况下恢复明文

共模攻击的成立条件很苛刻但很常见:同一个n,两条不同的e都加密了同一条明文。数学上的解法是利用扩展欧几里得找到一组整数a、b,使得a*e1 + b*e2 = 1,然后用c1^a * c2^b mod n还原明文。

from gmpy2 import gcdext from Crypto.Util.number import long_to_bytes e1 = 65537 e2 = 65539 c1 = 123456... c2 = 654321... n = 987654... g, a, b = gcdext(e1, e2) if g != 1: print("gcd(e1, e2) != 1,不满足共模前提") else: m = (pow(c1, a, n) * pow(c2, b, n)) % n print(long_to_bytes(m))

逻辑说明:gcdext返回最大公约数和两组系数。如果最大公约数是1,说明两个指数互质,表达式成立。pow(c1, a, n)里的a可能是负数,Python的三参pow能自动处理模逆运算,不需要手动转换。参数说明:这里最关键的是确认两条密文确实对应同一条明文,题目里如果出现两个不同的c但n相同,基本可以断定是共模。如果gcd不是1,最常见原因是两个指数成倍数关系,比如3和6,这时候题目设计有误或者不是共模套路。

工具里这个模块的输入槽比别的多两个,填的时候容易对错位。我习惯先把e1、e2填上再填c1、c2,然后在心里默念一遍对应关系,填反了输出就是乱码。这里还容易和「共享素数」搞混,后者是两个不同的n共用同一个因子p,走的是模数分解路线,输入方式完全不同。

4.4 Wiener攻击:连分数展开的终止条件

d偏小时,可以从公钥n、e的连分数展开中还原k/d的关系,这是Wiener攻击的核心。它的适用边界有明确定论:d < n^0.25。工具在实现上就是做连续的连分数展开,每一步算出一个候选d,然后验证(e*d-1)能否被phi(n)整除。

手动判断该不该跑Wiener前,我会先看一眼位的分布:

from math import log2 n_bits = int(log2(n)) e_bits = int(log2(e)) print("n bits:", n_bits) print("e bits:", e_bits)

逻辑说明:如果e的位数接近n的位数,比如n是1024位、e也接近1024位,同时题目没有提供其他线索,d很可能是小的,值得跑Wiener。参数说明:这只是个粗略预判,不能替代实际验证。判断这个的目的是避免在d根本不小时白等,因为连分数展开在d较大时会展开几十上百项,每项都要做大数验算,耗时随展开深度指数上升。

工具里通常有「连分数深度」参数,默认40项。遇到没跑出来的情况可以调到80,但如果到了80还是没结果,大概率题目根本不是Wiener攻击,硬跑不如回到第2章的判断流程重新归类。这个模块还经常跟低加密指数混淆,因为它们的适用场景都可以是e偏大,区别在于明文和密文的相对大小完全不一样。我的经验是:题目直接把n、e、c三个大整数文本丢给你的时候,优先试Wiener;题目给了多个文件、明显有冗余结构的时候,优先找共模或共享素数。

5. RSA工具避坑手册:五类翻车现场与排查顺序

工具本身不会帮你判断思路对不对,它只负责执行你选择的攻击。这一章列出的五个坑我都实际踩过,每一条按「现象、原因、解决」的顺序讲清,方便你在翻了车之后按这个顺序排查。

5.1 现象:n、e、c都填对了,输出却一直是乱码

最诡异的翻车现场是参数肉眼检查完全没问题,明文整数也对,转成字符串却是乱码。原因多半不在工具,而在输入的进制格式上。题目给的十六进制字符串以0x开头时,如果工具的输入框默认按十进制解析,0x9f...会被当成十进制的整数,整个c的值差了数量级。

解决:进工具前统一完成进制转换。我习惯在填参数前先跑一段脚本强制打印三个值的十六进制和十进制两个形态,再核对题目原始串。还有一个隐藏点:PEM文件里解析出来的n,有些工具会把前导00字节去掉,而密文解码时又要求补回前导字节。如果明文开头少了一个字节,把long_to_bytes(m)的结果补上\x00再转字符串就对了。这一点在公钥带前缀、密文长度刚好等于n的字节长度时特别容易踩到。

5.2 现象:自动检测把它当成Wiener攻击,跑了十分钟不结束

工具自动检测的顺序如果先跑Wiener,题目又不是这个套路,运行时间会非常难看。原因在于Wiener攻击的连分数展开没有前置校验,只要e比n小一点,它就会一路展开下去,每一段都尝试验算。验算本质上要做模幂运算,次数多了以后慢得离谱。

解决:在自动检测跑超过30秒时直接中断,切到手动分类。先看n能不能试除,再看e是不是过小,这两个判断都是一眼能定的事。只有e位数接近n位数时,才让Wiener跑完整流程。另外一个实操技巧:把--mode auto改成--mode factor先跑分解类攻击,这类攻击通常会先做小质数试除和费马分解,一旦n有结构性弱点,十几秒内就能给出结论,远比干等Wiener划算。

5.3 现象:十六进制串带0x前缀,工具却按十六进制解析失败

命令行工具和图形工具对0x前缀的宽容度不一样。某些版本解析参数时刻不认0x,只认纯十六进制数字,导致填进去少了一个字母解析错位。原因在于参数解析库里用的是int(x, 16)而不是int(x, 0),前者遇到0x前缀直接抛异常。

解决:把所有带0x的字符串在脚本里做一次清理:

raw = "0x9f2c..." c = int(raw.replace("0x", ""), 16)

逻辑说明:先把前缀去掉,再强制按十六进制转整数。参数说明:这里不要用int(raw, 0),因为如果raw是纯数字串不带前缀,int(raw, 0)会默认当十进制,仍然出错。统一改成先替换前缀再指定进制,和工具的解析逻辑保持一致。批量处理时也顺手把大写A-F全部转成小写,避免个别解析器对小写字母敏感。

5.4 现象:公钥文件解析报错,提示不是PEM格式

PEM公钥以-----BEGIN PUBLIC KEY-----开头,但很多时候题目给的不是标准PEM,而是DER二进制格式,或者PEM文件被base64换行符破坏过。RSA.import_key遇到DER时直接抛出异常。

解决:先判断真实格式,再决定走哪条解析路径:

file public.key xxd public.key | head -10

逻辑说明:file输出会标明是PEM文本还是DER数据。xxd查看头部字节,DER二进制通常以30 82开头,PEM文本则是可读的ASCII字符串。参数说明:如果确定是DER,解析方式改为RSA.import_key(open("public.key", "rb").read()),大多数密码学库会自动识别DER;如果还是失败,可能是文件头部有额外字节,先tail -c +2跳过多余字节再试。我见过一种特殊情况:工具把私钥文件的注释行误判成公钥内容,因为文件开头是-----BEGIN RSA PRIVATE KEY-----,工具直接拒绝执行。这时要用openssl先单独提取公钥部分再喂给工具。

5.5 现象:攻击成功却被杀软或系统安全策略拦截

工具目录下如果包含可执行文件,有些平台的安全策略会自动隔离,表现是双击没反应或运行到一半进程消失。原因不是工具本身有问题,而是压缩包里的exe没有签名,被系统当成了未受信程序。

解决:在解压目录里右键查看文件属性,如果有「解除锁定」按钮,勾选确认后再运行。Linux环境则检查文件执行权限,补上chmod +x再启动。这一步属于环境问题,不代表工具功能有问题。我在某公司训练机上也遇到过类似情况,当时顺手把目录加入了杀软白名单才跑通,后来比赛用的电脑没有这么严格的限制,直接解压就能用。优先确认依赖库齐全、参数格式正确,最后再考虑系统拦截的因素。

6. 进阶技巧:把工具挂进自己的批处理流水线

工具单次执行只能处理一道题,但比赛训练经常需要批量验证几十道历史题目。我的做法是写一层薄薄的批处理脚本,把工具当成一个命令行子进程来调用,按目录组织每道题的参数。

#!/usr/bin/env bash for d in ./cases/*/; do id=$(basename "$d") n=$(python3 parse_params.py "$d" n) e=$(python3 parse_params.py "$d" e) c=$(python3 parse_params.py "$d" c) echo "正在处理 $id" python3 xuan_yu_rsa.py -n "$n" -e "$e" -c "$c" --mode auto > "$d/result.txt" done

逻辑说明:外层循环遍历cases目录下每个子目录,子目录里放着一道题目的原始文件和参数。parse_params.py是从第3章的提取脚本演化出来的,按题目目录自动读PEM或hex生成参数。每次执行结果写入该题目录的result.txt,方便对照人工复盘。参数说明:--mode auto可以在大规模扫描时统一用;遇到失败目录,我会再单拎出来手动指定模式重跑,不在批量脚本里做二次尝试。

另外我常用它配合SageMath做二次验证。工具跑出d或者分解出的p、q之后,我会把这些值丢进SageMath里重算一遍私钥推导和签名验证,确认工具结果不是偶然碰撞。这一步在自动检测成功时尤其值得做,因为自动模式偶尔会在边界条件下给出错误候选解。比如费马分解循环里如果p、q差距过大,它可能返回的是一组平方因子,而不是真实质数分解。验证方法很简单:把p和q乘回去,看是否严格等于n,再把解密结果加密回来,看是否等于c。这两步能过滤掉九成以上的误报。

回到开头那句话——CTF打RSA考的是识别套路的速度,不是手写脚本的耐心。把这个工具当作第一手探测器,快速扫完常规攻击面,再针对未命中题目用符号计算工具做深挖,是我现在习惯的节奏。工具解决80%的常规题,剩下20%交给更自由的数学工具,两者配合工作效率高得多。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询