
1. 从零到一我的CTF密码学实战入门心路第一次接触CTFCapture The Flag里的Crypto密码学题目时我对着一段看似天书的密文和几行描述发呆了半小时。那感觉就像拿到了一把锁却连钥匙孔都找不到。但几年下来从最初的懵懂到后来能独立解决一些中等难度的题目我深刻体会到Crypto并非高不可攀的数学玄学而是一套有迹可循的“破译游戏”。它考验的不仅是密码学知识更是信息检索、逻辑推理和脚本编写的能力。如果你也对那些神秘的加密信息感到好奇或者想在CTF比赛中多拿几分那么这篇从实战中总结出的Crypto练习指南或许能帮你少走些弯路。简单来说CTF中的Crypto题目就是出题人利用各种古典、现代密码算法或自定义的加密逻辑将flag一串有格式的字符串如flag{this_is_flag}隐藏起来。解题者的任务就是逆向这个过程从给定的密文、代码或描述中恢复出原始信息。这个过程很像侦探破案需要你仔细观察“现场”题目描述、附件识别“手法”加密算法并找到关键的“证据”漏洞或特征来还原真相。无论你是计算机专业的学生还是对安全技术感兴趣的爱好者掌握基本的Crypto解题思路都能极大地提升你在CTF中的竞争力与乐趣。2. 解题工具箱必备的知识与软件准备工欲善其事必先利其器。在开始“砸锁”之前你得先认识各种“锁”的类型并准备好顺手的“工具”。2.1 核心密码学知识图谱不需要你成为密码学博士但以下几类算法你必须能一眼认出其典型特征古典密码这类密码通常只涉及字符的替换或移位安全性低但却是入门的最佳选择。凯撒密码最简单的移位密码。特征密文看起来像乱序的英文单词或句子。例如khoor可能是hello移位3。仿射密码凯撒密码的升级版使用一个数学公式E(x) (ax b) mod m进行加密。特征需要找到a和b两个密钥。简单替换密码每个明文字母被唯一地替换为另一个字母。特征密文的字母频率分布与自然语言相似如英文中E、T出现频率高但单词无意义。维吉尼亚密码使用一个关键词进行多表替换。特征密文周期性出现重复模式破解的关键在于寻找密钥长度。栅栏密码将明文按一定规则重新排列。例如2栏栅栏hello-hloel。现代密码对称/非对称这类题目往往需要结合数论知识和编程。RSA这是CTF Crypto的绝对主角。特征题目通常会给出n大整数模数、e公钥指数有时还有c密文。你需要利用n的弱分解如p、q很接近或n可以被某些数据库查询到、e与φ(n)不互素、或选择密文攻击等手段来求解。AES/DES对称加密算法。在CTF中很少让你直接暴力破解AES更多的是考察你对加密模式如ECB、CBC的理解或利用诸如Padding Oracle Attack等侧信道攻击。椭圆曲线密码难度较高通常涉及离散对数问题。编码与杂项这不算严格意义上的加密但却是信息隐藏的常见手段。Base64/32/16用于将二进制数据编码成可打印字符。特征密文字符集有限Base64结尾常有Base32字符集为大写字母和数字2-7。摩斯电码由.和-或0和1组成单词间用/分隔。二维码图片附件需要扫码或使用工具解析。文件隐写有时密码学题目会与Misc杂项结合flag可能藏在图片的二进制数据、音频的频谱图或文档的属性里。2.2 软件与在线工具推荐纯手算在CTF里是不现实的善用工具能事半功倍。Python 关键库这是你的主力军。pycryptodome/cryptography库提供了几乎所有加密算法的实现。gmpy2库用于大数运算RSA必备。sympy库用于解方程或进行数论计算。CyberChef一个“网络瑞士军刀”在线工具。它集成了编码解码Base64、Hex、古典密码加解密、哈希计算、正则表达式等上百种操作。它的“魔方”功能甚至能自动识别并尝试破解一些简单编码。对于快速测试和灵感启发极其有用。CTFcrackTools、John the Ripper、Hashcat用于暴力破解或字典攻击哈希值。RSACTFTool、RsaCtfTool专门针对RSA攻击的Python脚本集合集成了数十种已知的攻击方法如小公钥指数攻击、共模攻击、维纳攻击等。拿到RSA题先用它跑一遍常见攻击往往有奇效。QuipQuip一个优秀的在线工具用于破解替换密码和维吉尼亚密码它会根据语言频率进行智能分析。注意工具虽好但不能替代思考。工具的输出需要你结合题目上下文进行解读。盲目运行工具而不理解其原理在遇到变种或自定义算法时会束手无策。3. 通用解题四步法从拿到题目到提交Flag我总结了一套适用于大多数Crypto题目的通用流程可以帮你理清思路避免在错误的方向上浪费时间。3.1 第一步审题与信息收集这是最关键的一步决定了你后续90%的努力方向。仔细阅读题目描述出题人可能在描述里埋下重要提示。比如“老师用了一个简单的加密”、“密钥是我的生日”这些都在暗示加密方式或密钥范围。分析所有附件通常是一个.txt文本文件或一个.py脚本。文本文件观察密文特征。是全大写字母有没有等号有没有括号、空格等特殊符号长度是多少尝试判断它是编码还是加密。Python脚本这是黄金信息源。仔细阅读加密代码。重点关注用了什么库函数Crypto.Cipher.AESRSA.construct密钥是如何生成的是随机数还是固定值加密流程中有没有可疑的操作如异或了一个固定字符串把代码复制下来在自己的环境里运行一下看看输出是否与题目给的密文一致这是验证理解是否正确的最快方法。识别算法类型根据以上信息初步判断题目属于古典密码、现代密码还是编码。3.2 第二步尝试与特征匹配根据第一步的猜测进行快速尝试。如果是编码直接使用CyberChef将密文放入输入框尝试常见的解码操作如“From Base64”、“From Hex”、“URL Decode”等。观察输出是否有可读字符串或flag{字样。如果是古典密码密文像乱序英文先试凯撒密码CyberChef有“ROT13 Brute Force”功能可以遍历所有移位。无效则考虑简单替换或维吉尼亚。密文只有数字可能是ASCII码十进制/十六进制表示也可能是字母序A1, B2...。密文只有0和1可能是二进制转文本也可能是摩斯电码需确认分隔符。如果是现代密码如RSA检查给出的n是否能在 factordb.com 这类因数分解网站上直接查到p和q。检查e是否特别大可能对应d很小适用维纳攻击或特别小如e3可能适用低加密指数攻击。检查是否给了一组以上的(n, c)对可能适用共模攻击或广播攻击。3.3 第三步深入分析与脚本编写当快速尝试无效时就需要深入分析并动手写脚本了。这是提升能力的关键环节。代码审计如果给了加密脚本逐行分析。寻找逻辑漏洞。例如弱随机数密钥生成使用了random.randint(0, 100)这样的小范围随机数可以直接暴力遍历。加密模式误用AES的ECB模式相同的明文块会产生相同的密文块通过观察密文块重复模式可能推断出部分信息。自定义加密函数自己实现的加密函数往往存在缺陷比如异或操作时密钥复用、线性同余生成器LCG参数过小等。数学推导尤其是RSA题。你需要根据已知条件尝试推导出私钥d或直接计算明文m。常见的数学关系有φ(n) (p-1)*(q-1)d e^(-1) mod φ(n)如果知道dp d mod (p-1)也可以结合其他条件进行攻击。编写Python脚本将你的攻击思路转化为代码。例如写一个循环遍历所有可能的凯撒移位写脚本利用已知的p和q计算RSA私钥并解密写脚本实现Padding Oracle攻击等。3.4 第四步验证与提交得到一串疑似flag的字符串后不要急于提交。检查格式CTF的flag通常有固定格式如flag{...}、CTF{...}、SECCON{...}等。确保你提取的字符串符合比赛规定的格式。提交测试在比赛平台上提交看是否正确。如果错误回顾第三步的分析检查是否有边界条件没处理好或者算法识别是否有误。复盘总结无论是否解出事后一定要复盘。这道题用了什么知识点我是怎么想到的有没有更优雅的解法把解题脚本和思路记录下来积累成自己的知识库。4. 经典题型实战拆解手把手带你闯关光说不练假把式下面我们通过几个虚拟但非常典型的例子来具体感受一下解题过程。4.1 案例一隐藏的栅栏题目描述密文hloolelwrd。附件无。解题过程审题只有一串密文无其他信息。密文长度10看起来像乱序的英文单词。特征匹配首先排除Base64无字符集不符。尝试凯撒密码用CyberChef暴力破解输出均无可读单词。考虑可能是换位密码如栅栏密码。分析与尝试栅栏密码需要试栏数。栏数为2时将密文分成两栏h l o o l和e l w r d然后按行读取得到hello world。成功验证明文hello world符合常见测试语句特征。虽然这不是标准flag格式但很多入门题会以此作为答案或下一步的提示。实操心得对于短密文古典密码的可能性很大。栅栏密码的栏数一般不会超过密文长度的一半可以手动或写个小脚本快速遍历。hello world是常见的测试字符串见到它基本就意味着方向对了。4.2 案例二孱弱的RSA题目描述n145906768007583323230186939349070635292401872375357164399581871019873438799005358938369571402670149802121818086292467422828157022922076746906543401224889672472407926969987100581290103199317858753663710862357656510507883714297115637342788911463535102712032765166518411726859837988672111837205085526346618740053, e65537, c83208298995174604174773590298203639360540024871256126892889661345742403314929861939100492666605647316646576486526217457006376842280869728581726746401583705899941768214138742259689334840735633553053887641847651173776251820293087212885670180367406807406765923638973161375817392737747832762751690104423869019034。 请解密。解题过程审题典型的RSA参数n, e, c。目标是求m。特征匹配与快速尝试将n提交到factordb.com网站查询。幸运地这个n已经被分解过网站直接给出了p和q。p 1098382268985762240184333...q 13284562957208247589...编写脚本有了p和q一切就简单了。编写Python脚本计算私钥d并解密。import gmpy2 from Crypto.Util.number import long_to_bytes n 145906768007583323230186939349070635292401872375357164399581871019873438799005358938369571402670149802121818086292467422828157022922076746906543401224889672472407926969987100581290103199317858753663710862357656510507883714297115637342788911463535102712032765166518411726859837988672111837205085526346618740053 e 65537 c 83208298995174604174773590298203639360540024871256126892889661345742403314929861939100492666605647316646576486526217457006376842280869728581726746401583705899941768214138742259689334840735633553053887641847651173776251820293087212885670180367406807406765923638973161375817392737747832762751690104423869019034 p 1098382268985762240184333... # 从factordb获取的实际p值 q 13284562957208247589... # 从factordb获取的实际q值 # 计算私钥d phi (p-1)*(q-1) d gmpy2.invert(e, phi) # 求e关于phi的模逆元 # 解密 m pow(c, d, n) # 计算 c^d mod n print(long_to_bytes(m))运行与提交运行脚本输出b‘flag{weak_rsa_is_easy}’。提交该flag即可。注意事项不是所有n都能在factordb上查到。如果查不到就要考虑其他攻击方式比如检查n是否过小可以用yafu工具本地分解或者p和q很接近利用费马分解法。4.3 案例三自定义加密的漏洞题目描述附件为一个encrypt.py文件内容如下import os flag os.environ.get(FLAG, flag{test_flag}) key os.urandom(8) def encrypt(data): encrypted b‘’ for i in range(len(data)): encrypted bytes([data[i] ^ key[i % len(key)]]) return encrypted.hex() print(encrypt(flag.encode()))运行后输出密文2b3f1c0a534a0a53471d0752解题过程审题与代码审计加密脚本逻辑清晰。flag与一个8字节的随机密钥key进行逐字节循环异或加密然后输出十六进制密文。这里的关键漏洞是密钥虽然随机但长度固定为8且加密时循环使用。分析漏洞在已知明文部分内容的情况下循环异或非常脆弱。我们虽然不知道完整的flag但知道flag的标准格式以flag{开头。密文是十六进制我们先将其转换为字节流。密文2b3f1c0a534a0a53471d0752- 字节:[0x2b, 0x3f, 0x1c, 0x0a, 0x53, 0x4a, 0x0a, 0x53, 0x47, 0x1d, 0x07, 0x52]明文开头flag{- 字节:[0x66, 0x6c, 0x61, 0x67, 0x7b]利用已知明文恢复部分密钥异或运算有一个重要性质A ^ B C那么A ^ C BB ^ C A。密文前5字节C1 [0x2b, 0x3f, 0x1c, 0x0a, 0x53]明文前5字节M1 [0x66, 0x6c, 0x61, 0x67, 0x7b]根据Key Cipher ^ Plaintext可以计算出密钥的前5位K_part [0x2b^0x66, 0x3f^0x6c, 0x1c^0x61, 0x0a^0x67, 0x53^0x7b] [0x4d, 0x53, 0x7d, 0x6d, 0x28]推测并验证完整密钥密钥长度是8我们已有前5位。我们需要猜测后3位。一个合理的推测是flag的内容可能是常见的单词或短语。我们可以用已知的部分密钥前5位去解密密文的第6-8位对应密钥的第6-8位看解密出的明文是否合理。密文第6-8位[0x4a, 0x0a, 0x53]假设它们是用密钥第6-8位[k5, k6, k7]加密的。但我们没有k5,k6,k7。换个思路因为密钥循环使用密文第9-11位[0x47, 0x1d, 0x07]应该也是用密钥的前三位[0x4d, 0x53, 0x7d]加密的。我们可以先解密这部分看看能得到什么。解密第9-11位0x47^0x4d0x0a(\n),0x1d^0x530x4e(N),0x07^0x7d0x7a(z)。得到\nNz这看起来不合理flag里通常不会有换行符。编写爆破脚本更可靠的方法是由于密钥只有8字节64位且我们已经知道了前5字节剩下的3字节24位的搜索空间是2^241600万这对于计算机来说是完全可以在短时间内暴力破解的。我们编写脚本遍历所有可能的3字节密钥用完整的8字节密钥去解密整个密文寻找解密结果中包含flag{和}的可读字符串。import itertools cipher_hex “2b3f1c0a534a0a53471d0752” cipher_bytes bytes.fromhex(cipher_hex) known_key_part bytes([0x4d, 0x53, 0x7d, 0x6d, 0x28]) # 从‘flag{’计算得来 for k5 in range(256): for k6 in range(256): for k7 in range(256): full_key known_key_part bytes([k5, k6, k7]) decrypted b‘’ for i in range(len(cipher_bytes)): decrypted bytes([cipher_bytes[i] ^ full_key[i % 8]]) try: text decrypted.decode(‘utf-8’) if ‘flag{’ in text and ‘}’ in text: print(f“Key: {full_key.hex()}”) print(f“Flag: {text}”) break except UnicodeDecodeError: pass获取结果运行脚本很快就能找到正确的密钥和flag。实操心得遇到自定义加密函数一定要寻找其确定性或可预测性。循环异或、弱随机数、固定IV等都是常见的突破口。已知明文攻击Known Plaintext Attack在CTF中非常常见flag{这个前缀就是最强的已知明文。5. 进阶挑战与技巧当标准方法失效时随着水平提升你会遇到更多变种和复合型题目。下面分享一些应对复杂情况的思路。5.1 面对未知或自定义算法有时题目会使用一个完全陌生的算法名称或一段令人费解的加密代码。搜索是第一生产力将算法名称、代码中的关键函数或特征字符串如常数0x9e3779b9直接复制到搜索引擎中。很可能这是一个已知但不太常见的算法如TEAXXTEA或者是一道经典老题的变种。动态调试与逆向如果给了可执行文件.exe,.elf而不是Python脚本就需要用到逆向工程工具如GDBLinux、x64dbgWindows、IDA Pro或Ghidra来静态分析或动态跟踪程序的加密逻辑。侧信道与错误利用例如Padding Oracle Attack它不直接攻击加密算法本身而是利用服务器在解密后对填充Padding正确性的不同反馈如返回错误信息或响应时间差异来逐步推导出明文。这类题目需要你对加密模式如CBC有深入理解。5.2 数学知识的应用Crypto尤其是非对称密码本质上是数学。数论基础理解模运算、欧拉定理、中国剩余定理CRT、连分数展开等是必须的。例如维纳攻击就基于连分数展开。脚本能力学会使用sympy解方程用gmpy2处理大整数运算。当题目给出多个数学关系式时将其转化为代码求解是最高效的方式。识别特殊结构比如当RSA的n可以由多个素数组成多素数RSAφ(n)的计算方式会变化。当e和φ(n)不互素时需要用到AMM算法等开根号方法。5.3 信息隐写与复合题型Flag可能不直接存在于解密后的文本中。多重加密/编码解密出一段Base64解码后可能又是一段摩斯电码再解码才是flag。要有“剥洋葱”的耐心对每一步的输出都保持警惕观察其特征。文件格式分析解密后得到一个文件用file命令查看其类型。可能是图片、压缩包、PDF等。用binwalk检查文件内是否隐藏了其他文件用steghide需要密码或zsteg检查图片中是否隐写了数据。协议与分析有时会给出一段网络流量包.pcap你需要用Wireshark打开分析其中的SSL/TLS握手或加密通信可能密钥就藏在某个数据包里。6. 我的实战避坑指南与资源推荐最后分享一些只有踩过坑才能得到的经验以及持续学习的资源。6.1 常见失误与排查清单遇到死活解不出的题可以按这个清单检查问题现象可能原因排查方向解密结果乱码1. 算法识别错误2. 密钥/参数用错3. 字符编码问题1. 重新审题确认密文和算法2. 检查脚本中的n、e、c、p、q等值是否复制正确3. 尝试decode(‘utf-8’)、decode(‘latin-1’)或直接print(bytes)查看运行脚本报数学错误1. 模逆元不存在e和φ(n)不互素2. 大数运算溢出或类型错误1. 检查p和q是否为质数计算φ(n)是否正确2. 确保使用gmpy2.mpz()处理大整数在线工具解密结果不对1. 输入格式错误如Hex vs Base642. 工具默认参数与题目不符1. 确认密文是否需要先unhexlify或b64decode2. 仔细对照工具选项如AES的模式、填充方式始终找不到flag格式1. flag可能被进一步处理如哈希、反转2. 需要提交的不是最终字符串而是中间结果的某种形式1. 对解密出的明文尝试MD5、SHA1等哈希或反转字符串2. 再次阅读题目描述看是否有特殊提交要求6.2 持续学习与练习平台练习平台CTFlearn题目分类清晰非常适合入门社区活跃。HackTheBox虽然侧重渗透但其Crypto挑战也很有质量环境逼真。PicoCTF面向完全新手的平台引导性极强游戏化体验。Cryptohack专精于密码学的练习平台从易到难每道题都有清晰的提示和讨论区是系统学习Crypto的绝佳场所。知识扩展《深入浅出密码学》一本很好的入门理论书。CTF Wiki中文社区维护的宝贵知识库涵盖了几乎所有CTF考点遇到不懂的概念先去这里查。赛后Writeup参加完任何比赛一定要去搜别人的解题报告Writeup。学习别人的思路尤其是那些你没想到的“神来之笔”。从我个人的经验来看Crypto能力的提升是一个“见识”积累的过程。初期你会觉得无从下手但当你见识过几十种不同类型的题目后再看到新题大脑会自动将其与你见过的模式进行匹配。所以多练、多总结、多复现把每一次解题都当成一次经验的积累。不要害怕复杂的数学公式很多时候工具和脚本已经帮你封装好了底层实现你的核心任务是理解攻击的逻辑并将其组合运用。记住在CTF的世界里好奇心与耐心是你最强大的武器。