AI辅助RSA题Writeup实战:从低加密指数广播攻击到完整解题流程

AI辅助RSA题Writeup实战:从低加密指数广播攻击到完整解题流程 把一道RSA题交给AI来写Writeup这个想法放在两年前可能显得有点偷懒但放到现在反而是CTF新手绕过大量基础障碍、快速建立解题手感的高效路径。我最近在整理一套密码学入门题的时候就专门试了一次这个流程拿一道中等偏易的RSA题目不直接给答案而是让AI分步骤推理、生成攻击脚本、甚至输出解题报告然后我再针对它给出的结果做校验和修正。整个过程跑下来最大的感触是AI在RSA这类数学结构清晰的题目上确实能当半个队友用但它给出的东西永远需要你保留最后的判断力。这篇Writeup适合三类人刚开始打CTF、看到RSA只知道ne c的纯新手已经会调库但容易卡在“题目变种”上的选手以及想搞清楚“AI辅助解题的边界在哪里”的工具党。我会把一道典型的低加密指数广播攻击题从读题到出flag的完整链路展开包含提示词设计、参数还原、脚本调试和翻车修复最后再聊几个AI在密码题里最容易“一本正经胡说八道”的地方。1. 解题思路为什么RSA题最适合AI辅助以及它到底能帮你做到哪一步1.1 RSA题型的可结构化程度高天然适合交给模型推理CTF里的RSA题和Pwn、逆向不太一样它的核心从来不是“猜”或“硬调”而是“识别攻击场景套用对应数学方法”。低加密指数、共模攻击、维纳攻击、Fermat分解、已知高位攻击……这些攻击模式都有非常固定的特征判据比如e3且有多组明文相同的密文那就大概率是广播攻击e特别大且d很小那就是Wiener两个n共享同一个素数直接求公约数就能分解。这种“特征→方案→代码”的映射关系恰好是大语言模型最擅长的联想和生成任务。相比之下逆向题的代码审计依赖上下文追踪Web题的漏洞利用依赖环境交互AI在这些场景里容易跑偏但RSA题通常只需要给它几个整数它就能根据数值特征判断攻击方向。我实测下来把题目里的n、e、c原样丢给AI让它“先判断可能的攻击类型再写脚本”大部分时候它给出的方向是对的这就能帮你省掉翻资料和猜套路的时间。1.2 AI能完成的四类工作以及它替代不了的那部分我用一轮完整实验给AI的“岗位职责”做了个划分简单说就是脏活、累活它能干但拍板的事得你来。第一类是参数提取与预处理。很多RSA题目的数据是塞在文本文件或者图片里的格式千奇百怪可能有多余的换行、十进制和十六进制混排、甚至有n 0x...这种带前缀的写法。AI能根据上下文自动判断进制并帮你把参数整理成干净的Python字典这个步骤虽然不难但手动搞容易眼瞎。第二类是攻击模式识别。你把e3、三组n,c给它它基本能想到中国剩余定理CRT你只需要在最后验证这个方向是否成立。第三类是脚本生成。这一项目前最实用AI能直接给出可运行的Python代码省去你记忆各种函数名和库用法的负担。第四类是Writeup草稿生成。它可以帮你把解题过程整理成叙述性的报告结构完整、逻辑通顺但里面的数学推导和脚本输出需要你逐项核对。它替代不了的部分也很明确确认flag格式和编码、处理非标准变种题、判断攻击条件是否真正满足。AI经常只看到“e3”就急急忙忙写广播攻击完全忽略了三组密文是否对应同一明文、模数是否两两互素这些前置条件而这类判断直接决定了脚本能不能跑通。2. 实操前置RSA题解题环境的搭建与工具箱清单2.1 Python库与基础函数的选择我习惯用一个独立虚拟环境专门跑密码学脚本避免和日常项目互相污染依赖。创建环境后需要安装的库其实很固定pip install pycryptodome gmpy2 libnum sympy四个库的分工很明确。pycryptodome提供RSA对象、Crypto.Util.number里的bytes_to_long和long_to_bytesgmpy2提供高精度整数运算尤其是iroot——在不知道确切次方数时做整数开方这是广播攻击和低加密指数攻击里最核心的函数libnum是CTF选手的老朋友n2s和s2n用来在数字和字节串之间互转虽然pycryptodome也能做但libnum在异常处理上更宽容sympy里的crt可以直接解中国剩余定理省去手写扩展欧几里得的功夫。如果不想装那么多库也可以只用pycryptodome和gmpy2crt自己用大约十行代码实现但对新手来说没必要重复造轮子。这里有个小建议所有依赖库尽量装在虚拟环境里因为gmpy2在部分环境下需要编译如果遇到装不上的情况直接换Windows的预编译wheel包别跟编译环境较劲。2.2 从题目附件里稳定提取RSA参数题目给的参数文件通常长这样n 0x... e 3 c 0x...但也有不讲究的出题人会把十进制数和十六进制数混在一起甚至参数名用全角空格分隔或者把n写成N。这时候我会先把AI当成实习生来用把原始文件内容原样粘贴给它要求“提取n、e、c统一转为十进制整数给出Python字典格式”并附上原始文件。AI通常能处理好格式问题但你必须自己核对一遍每个数字的长度——n一般至少有256位十六进制对应1024位二进制如果AI提取出的数字明显短了一截那说明提取过程出错了。对于图片里的参数最简单的方法是先用OCR工具转成文本。手机上随便一个扫描类App或者电脑上的截图OCR都能胜任识别出来的结果再交给AI整理。实测下来OCR对字母O和数字0、小写l和数字1的混淆率偏高而RSA参数里恰好全是这种字符所以务必在整理后抽样检查几段。2.3 环境验证用一组自造参数跑通全链路正式解题前我建议先跑一遍“自产自销”的验证流程确保环境没问题。生成一组正常RSA密文然后用私钥解回来如果这步能通说明crypto的基本功能正常from Crypto.PublicKey import RSA from Crypto.Util.number import bytes_to_long, long_to_bytes key RSA.generate(1024) n, e, d key.n, key.e, key.d m bhello rsa c pow(bytes_to_long(m), e, n) m2 pow(c, d, n) print(long_to_bytes(m2))这段脚本能输出bhello rsa说明pow运算和字节转换链路都正常。这一步看起来很基础但能排除很多低级问题——比如pow的第三个参数忘写导致结果爆炸、long_to_bytes后忘记去掉0x前缀导致解码失败等。整个环境从零到能跑通熟练的话五分钟内就能搞定。3. 实战拆解一道低加密指数广播攻击题的AI辅助全流程3.1 AI给出攻击方向后如何验证它的判断先交代题目背景。假设附件里有一份task.py内容是生成三组RSA参数并加密同一句话from Crypto.Util.number import * import gzip flag bflag{example_broadcast_attack} m bytes_to_long(flag) ps [getPrime(512) for _ in range(3)] ns [p*q for p, q in zip(ps, ps[1:] ps[:1])] e 3 cs [pow(m, e, n) for n in ns] for i, (n, c) in enumerate(zip(ns, cs)): print(fn{i} {n}) print(fc{i} {c})这里有个典型的考点三组密文是同一明文用**同一个指数e3**分别加密得到的而三个模数两两互素。这种场景叫低加密指数广播攻击数学基础是中国剩余定理已知同一个m^3在三个不同模数下的余数可以通过CRT恢复m^3在Nn1*n2*n3下的值再对结果开三次方即可还原m。我把参数和数据喂给AI之后它给出的第一条回复是“检测到e较小且存在多个(c,n)对可能是低加密指数广播攻击使用中国剩余定理恢复明文。”这个方向是对的。但关键一步是验证我需要确认三个n两两互素同时三组c确实是对同一m的加密结果。前者用math.gcd检查即可后者在这个题里假设成立但在真实赛题里可能会有坑——比如不同组的m其实被填充过、或者明文不同这时候CRT就完全不适用。这个例子也说明AI给出的方向性判断不应该被无条件信任但它的价值在于它帮你把“这题考什么”从模棱两可变成了明确的“下一步去验证什么”这就足够节省大量查资料时间了。3.2 提示词设计的两种示范菜鸟版与老手版如果你直接把题目文件丢给AI说“帮我解出来”有时候它会给你一段“看起来很对”但细节全错的代码。我在实验里试过两种提问方式效果差别很大。菜鸟版提问是这一段RSA怎么解nxxx, e3, cxxxAI的回复大概率是“看起来是低加密指数攻击以下是脚本”然后给出用gmpy2.iroot解密的代码。方向对但缺少对参数意义的解释和对边角情况的处理而且在三个模数非互素的变种里会直接翻车。老手版提问更值得推荐以下为RSA题目参数三项均为同一明文m使用相同指数e3、不同模数n加密的密文。 1) 请先分析可能的攻击类型并说明判断依据 2) 再给出完整Python求解脚本要求使用中国剩余定理 3) 最后对脚本输出结果做简要说明。 数据如下 n0... c0... n1... c1... n2... c2...这样提问的好处是你给AI划定了输出框架它不容易“自由发挥”写出一堆没用的推导而且你明确要求它“先分析再写码”它就会在代码之前给出攻击依据方便你判断思路是否正确。实测中这个版本的AI回答质量明显更高生成脚本直接可用不再需要大改。3.3 从CRT到明文核心代码的逐段解读AI输出的脚本万变不离其宗核心就三步CRT求m^3、开三次方、转字节串。我用最清晰的版本示范一遍from sympy.ntheory.modular import crt from gmpy2 import iroot from Crypto.Util.number import long_to_bytes ns [n0, n1, n2] cs [c0, c1, c2] # 第一步中国剩余定理求一个数x使得 x ≡ ci (mod ni) # crt返回第一个满足条件的余数x以及最小公倍数mod x, mod crt(ns, cs) # 第二步x就是m^3在模n1*n2*n3意义下的值对它开三次方 m, exact iroot(x, 3) if exact: print(m , m) print(flag , long_to_bytes(m).decode()) else: print(开方结果不是整数可能攻击条件不满足)这段代码的细节足够看出RSA题的一些通用规律。crt函数把三个同余方程合并为一个得到的x满足x ≡ c_i (mod n_i)而因为m^3 n1*n2*n3所以m^3 x在模意义下也恰好等于真实值不是多解。iroot返回两个值第一个是整数部分的根第二个是布尔值表示是否“开尽了”。这个布尔值非常关键——如果攻击条件凑得不对x可能压根不是完全立方数exact为False此时就不能直接long_to_bytes否则会得到一串乱码甚至异常。long_to_bytes之前需要确保m是一个正整数的十进制表示如果AI输出的m带了0x前缀或者末尾有L后缀记得先做类型规整。这些都是经验里最常见的坑算法没写错但数据格式没洗干净导致最后一步decode失败。3.4 完整求解脚本与“自造测试数据”验证法上面的代码块里ns和cs需要手动填充为了能在本地完整跑通我给你一个可以自行生成演示数据的版本这样在没有真实靶机的时候也能练手from Crypto.Util.number import getPrime, bytes_to_long, long_to_bytes from sympy.ntheory.modular import crt from gmpy2 import iroot flag bflag{test_broadcast_attack} m bytes_to_long(flag) p_list [getPrime(512) for _ in range(3)] q_list [getPrime(512) for _ in range(3)] ns [p*q for p, q in zip(p_list, q_list)] e 3 cs [pow(m, e, n) for n in ns] print(n0 , ns[0]) print(c0 , cs[0]) print(n1 , ns[1]) print(c1 , cs[1]) print(n2 , ns[2]) print(c2 , cs[2]) x, mod crt(ns, cs) m_recovered, exact iroot(x, 3) print(exact , exact) print(long_to_bytes(m_recovered).decode())把这段脚本跑一遍你会看到终端里打印出的参数然后脚本自动解出flag{test_broadcast_attack}。这个过程完整复现了正规解题流程也方便你核对AI给的脚本是不是正确。我通常把这种“自造数据验证法”当作所有RSA脚本的第一道测试——先保证解法在自己可控的数据上成立再拿真实题目参数替换。4. 踩坑实录与排查技巧AI辅助RSA题最常见的翻车现场4.1 AI计算出超大整数后“假报成功”以及iroot的坑有一次我让AI处理一道公钥指数较大的题目它生成了一段脚本开头是“使用Wiener攻击”然后输出了一串十进制数字还非常自信地写了句“flag已还原”。我拿那个数字转字节得到的是b\x15\x8c...这样的不可读内容。后来一查问题出在它调用了某个开方函数没有检查结果是否为整数就强制转换成了int导致后面输出了一个无意义的大数它却自以为成功了。这个问题的根源就是前面提到的iroot返回的exact参数被忽略了。AI生成的代码在遇到“开方不开尽”的场景时会默认取整数部分继续跑而你只要不检查exact脚本就会“安静地”输出错误结果。所以我的习惯是所有AI生成的求解脚本必须手动加上exact校验否则一律视为不可信输出。4.2 十六进制、十进制与字节串之间的混乱RSA参数在传输过程中经常出现进制混乱。AI生成的代码有时会把十六进制字符串直接传给int()而忘了指定base16导致int(0x...)直接报错有时又会把十进制数当成十六进制解析数字瞬间膨胀好几倍后续所有运算全部失效。排查这种问题时我通常先打印n的比特长度。一个正常的1024位RSA模数二进制长度应该在1024左右如果打印出来的长度是4096或者128那基本可以断定进制处理出错了。libnum里的s2n默认按十进制处理用之前先确认参数是十进制字符串如果是从0x开头的十六进制字符串转换用int(x, 16)或者libnum.s2n(bytes.fromhex(x[2:]))。4.3 明文填充与decode失败在广播攻击里三组加密如果使用了不同的填充方案那么“同一明文”这个前提就不成立CRT根本不能用。AI不一定能察觉到这一点它只会机械套用“e3就上CRT”的模板。这时如果脚本算出exactFalse或者long_to_bytes后得到一段开头带着随机填充字节的乱码就要回头检查题目到底有没有做PKCS#1 v1.5之类的填充。另一个高频问题出现在decode()上。long_to_bytes(m)得到的是字节串但里面可能包含不可打印字符直接.decode(utf-8)会抛UnicodeDecodeError。这种情况并不代表解题失败——flag可能以其他编码形式存在或者中间还夹着填充数据。我常用的临时处理是data long_to_bytes(m) print(data) # 如果能看到明显的flag文本只是开头或结尾有乱码直接截取 print(data[-50:])很多赛题flag其实就在最后几十个字节里直接切片就能看到。4.4 常见问题速查表现象可能原因排查方向int()转换报错十六进制字符串未指定base16检查参数是否带0x前缀统一进制crt返回的x与实际值不符三个模数不两两互素用math.gcd两两检查公约数iroot(x, 3)返回False明文可能被填充或e不等于3检查攻击前提是否成立解密后是乱码进制转换或字节序错误打印中间变量检查long_to_bytes输入值脚本运行极慢大数运算库没装Python原生int撑不住安装gmpy2并导入其powmod等函数AI输出的m带L后缀旧版Python产生的长整型字面量用int(str(m).replace(L,))清洗三组数据CRT结果不对密文c顺序与模数n顺序不对应核对题目源文件重建对应关系这张表是我实际踩坑后整理的遇到类似问题可以按图索骥能省不少时间。5. AI辅助RSA题的能力边界与更高效的协作方式5.1 AI稳定胜任的题型和容易翻车的场景在RSA这个大分类下AI在不同题型上的表现差异很大。低加密指数攻击、共模攻击、已知明文高位攻击、基础Wiener攻击这几类有非常成熟的模板代码AI基本能直接给对因为它们的攻击条件清晰、数学流程固定、训练语料里大量覆盖。但到了需要“组合创新”的题目AI就有点勉强了。比如同一道题里既要做部分明文泄露恢复又要结合LSB Oracle反复查询或者出题人恶意构造了非标准的指数选择让Wiener攻击的连分数条件被隐藏再或者需要你自己编写格基约减脚本的Coppersmith类题型AI通常只能给你“用small_roots”这样一句空话而不会帮你把多项式构建和边界参数调好。这种时候AI的作用更多是提供思路线索而不是直接给答案。5.2 如何设计提问让AI的代码更接近“可运行”经过多轮实验我发现让AI生成的RSA解题脚本“一次跑通”的概率和提示词的详细程度高度相关。一个可复现的高质量提示词模板大概是你是CTF密码学方向的解题助手。以下是一道RSA题目请完成三步 1) 根据提供的n/e/c特征判断最可能的攻击类型并给出判断依据 2) 写出完整的Python求解脚本要求 - 采用整数开方并检查是否开尽 - 统一使用十进制整数不混用进制 - 最终输出long_to_bytes的结果 3) 如果脚本无法解出明文请说明可能原因不要强行输出结果。 题目数据 N... e... c...这段提示词的巧妙之处在于它明确要求AI“不强行输出结果”这能有效压制AI在条件不足时编造flag的冲动同时要求“统一十进制”和“检查开尽”等于提前把最容易翻车的两个点给堵上了。实测加了这两条约束后AI生成脚本的有效率提升非常明显。5.3 让AI帮忙写Writeup草稿时的二次加工技巧写完脚本拿到flag之后让AI帮你整理Writeup也很顺手。我会给它一个简短的结果描述比如“已通过CRT恢复m^3开三次方得到m转字节串得到flag”让它把过程扩展成结构化的叙述。它生成的草稿通常会有清晰的章节题目分析、解题思路、脚本展示、结果验证。这份草稿能覆盖八成以上的排版工作。但你千万别直接发出去。AI写的Writeup有个通病它会把“推导过程”写得特别顺滑导致读者误以为所有步骤都是一眼就能看出来的而真实解题过程中那些失败的尝试、误入歧途的方向它都自动隐去了。作为有经验的博主我会刻意把实际走过的弯路补回去比如“一开始我还考虑过共模攻击后来检查发现两个n并不共享素因子于是转向广播攻击”这种真实感反而让Writeup更有参考价值。同时要核对脚本和输出是否与题目参数一致避免AI“脑补”了不存在的题目细节。6. AI生成脚本的测试规范与最终检查清单6.1 一个稳健的RSA求解流程应该怎么设计把前面所有经验压缩成一套可复用的流程我的习惯是这样拿到题目参数先自己用眼睛扫一遍n、e、c的格式确定进制然后让AI做攻击类型判断并把判断依据写出来拿到AI回复后先核对它说的特征参数是否和题目一致再运行它给的脚本但运行前必须人工加上exact校验如果脚本失败把报错信息原样贴回给AI要求它根据报错修改而不是自己从头看代码——这一点尤其重要AI基于报错信息修代码的成功率远高于“重新分析题目”。最后拿到输出后不要立即写入Writeup先尝试把字节串转成可读文本确认flag格式是否连贯。如果结果是乱码多数情况下不是攻击失败而是编码或截取问题这时可以尝试对结果做切片、去除填充、反转字节序等操作。6.2 发给AI前自己先确认的五件事为了保证协作效率每次把题目喂给AI之前我都会过一遍这个清单参数是否完整n、e、c有没有缺失或明显截断——尤其是比赛给的压缩包解压后可能有多个文件别漏看。进制是否统一如果题目里混用了0x前缀和纯十进制先自己统一成十进制再发。e的值是多少e3、e65537、非常大的e对应的攻击思路完全不同先心里有数。是否有多组(n,c)多组同指数数据往往暗示广播或共模攻击。明文是否有特殊格式提示有些题目为了让选手验证结果会专门保留flag头和花括号这类信息是有用的线索务必让AI知道。这五项确认完AI给出的答案质量会明显高一个档次。6.3 团队协作场景下的AI使用建议如果你不是单打独斗而是有队友一起解题我建议每个人用同一个AI工具、甚至同一份提示词模板去各自解题然后对照结果。因为AI在给定相同提示词时输出具有很强的“偏好性”——同一个模型两次回答可能高度相似如果两个人独立提问反而可能因为上下文不同而得到差异化的思路。我在组队打线上赛时常用做法是一个人拿AI生成的攻击方向去验证脚本另一个人拿同一方向手写数学推导互相校验。这样既利用了AI的速度又保留了人类对数学正确性的把控。7. 从一道Writeup延伸出去的训练方法如果你看了上面的流程也想把“AIRSA”这套组合练熟我建议按这个顺序做几个小练习。第一个练习拿一道你已经会做的RSA题把参数喂给AI让它生成解题脚本然后故意在提示词里去掉“检查开尽”这个约束观察它会不会出错。这个练习能让你直观感受到AI的乐观偏差也让你学会在每个环节加校验。第二个练习找一道没有现成攻击模板的变种题先不让AI写脚本只让它分析攻击思路。然后你自己按照这个思路手写代码写完再让AI评价你的代码。这个反向用法能让你的编码能力得到针对性反馈比让AI直接给答案有用得多。第三个练习用AI生成的三份不同Writeup把其中数学推导部分全部隐去自己重新补全。这个过程能逼着你把RSA的每一步数学原理彻底搞懂——毕竟AI的推导是“事后合理”的你的推导才是真正属于自己的。我在实际使用中的体会是AI不是帮你绕过学习的工具而是帮你把“低价值劳动”压缩到最小让你把精力集中在真正重要的判断上。RSA题尤其适合这种协作方式因为它的攻击模式高度标准化AI能覆盖大部分重复工作但最后那步“我确信这个攻击成立、这个flag是对的”的信心永远只能来自你自己对数学和代码的双重确认。最后再分享一个小技巧当你拿AI生成的脚本跑通一道题之后别忘了把这道题的参数和脚本存进自己的笔记下次遇到类似的题直接翻笔记比重新问AI要快得多而这份笔记其实就是你真正沉淀下来的功力。