文档网络安全教程【免费下载链接】ctf-wikiCome and join us, we need you!项目地址https://gitcode.com/gh_mirrors/ct/ctf-wiki点击查看免费下载本文以 CTF-Wiki 文档 docs/zh-tw/docs/crypto/blockcipher/mode/ctr.md 为核心系统讲解分组密码 CTR计数器模式的工作原理、并行化特性并以一道 2023 年 CTF 实战题为例完整演示如何利用已知明文 已知密钥 部分密文逆推计数器初始值并最终解密 flag 的全过程。读完本文你将掌握 CTR 模式加解密数学结构能够独立完成此类计数器初值泄露/可逆推型题目的 Exploit 编写。CTR 模式概述CTR 全称为计数器模式Counter mode该模式由 Whitfield Diffie 和 Martin Hellman 设计是一种将分组密码转换为流密码Stream Cipher的工作模式。与 CBC、OFB 等模式通过密文或加密输出反馈来链接各个分组不同CTR 模式的核心思想是对一组随计数器递增而变化的数值执行分组加密生成伪随机密钥流keystream再将密钥流与明文按位异或得到密文。在 CTF-Wiki 的分组模式体系中分组模式简介 指出分组加密会把明文消息划分为固定大小的块每块明文在密钥控制下分别加密为密文当明文长度不是块大小的整数倍时还需要进行填充具体填充规则见 填充方式。CTR 模式是这些模式中最具流密码化特征的一员也是现代密码学与工程实践中使用极为广泛的模式。加密流程CTR 模式的加密流程如下所示从图中可以看出加密过程由三部分构成计数器序列每个分组使用一个由固定前缀Nonce图中示例为c59bcf35...加上递增的Counter值00000000、00000001、00000002……组合而成的输入分组加密将Key与NonceCounter组合送入分组密码加密模块图中为Block Cipher Encryption输出该计数器对应的密钥流块异或运算每个密钥流块与对应的明文块Plaintext做异或输出密文块Ciphertext。其数学表达为$$C_i P_i \oplus E_K(Nonce \parallel Counter_i)$$其中 $C_i$ 为第 $i$ 个密文块$P_i$ 为第 $i$ 个明文块$E_K$ 表示以密钥 $K$ 执行的分组加密如 AES$Counter_i$ 为第 $i$ 个计数器的值。解密流程CTR 模式的解密流程如下可以看到解密过程与加密过程完全对称使用与加密时完全相同的Key、Nonce与递增Counter序列通过分组加密生成完全相同的密钥流再将该密钥流与密文块异或即可还原明文块$$P_i C_i \oplus E_K(Nonce \parallel Counter_i)$$这一特性意味着 CTR 模式的加解密使用的是同一个算法、同一套代码逻辑无需实现分组密码的解密函数这是它在工程实现上的重要优势。特性分析CTR 模式在 CTF-Wiki 原文档 中总结的核心特性如下特性描述加密可并行化 (Encryption parallelizable)是 (Yes)解密可并行化 (Decryption parallelizable)是 (Yes)随机读取访问 (Random read access)是 (Yes)与 OFB 模式的对比优势原文档明确指出CTR 模式比 OFB 模式有一些优势。OFB输出反馈模式的详细介绍可参见 OFB 模式文档其反饋內容是分组加密后的内容需要逐块串行生成密钥流。CTR 模式的一个突出优点是它允许并行加密和解密多个块因为可以为每个块独立计算密钥流。由于每个块的输入NonceCounter互不依赖加密器可以同时处理多个块这可以显著提高加密和解密过程的性能和效率在多核、多线程环境下尤为明显。并行性与随机访问能力加密可并行化每个明文块的密钥流只取决于对应计数器值块与块之间无任何依赖关系天然适合 SIMD 指令集、GPU 或多线程并行加速解密可并行化解密同样只依赖计数器与密钥任何位置的密文块都可以独立、立即解密随机读取访问由于块间无链接关系可以跳过前面所有块直接解密任意位置的密文块这对磁盘加密、网络传输加密等随机访问场景非常关键。与 ECB 模式的区别与 ECB 模式文档 中同样明文块产生相同密文块、不隐藏明文统计规律的缺陷相比CTR 通过引入逐块递增的计数器值使得即使明文块相同所异或的密钥流也不同从而避免了 ECB 的统计规律泄露问题。同时 CTR 又继承了 ECB各块独立计算、可并行的优点并借助异或运算实现了流密码式的加解密对称性。安全使用注意事项从 CTR 的数学结构可以直接推得一个重要的安全结论密钥流的重用是致命的。如果两个不同的消息使用了相同的 Nonce/Counter 初值那么它们生成的密钥流完全相同此时攻击者只需将两段密文异或即可得到两段明文的异或值$$C_1 \oplus C_2 (P_1 \oplus KS) \oplus (P_2 \oplus KS) P_1 \oplus P_2$$因此在实际使用中Nonce/计数器初值必须全局唯一每次加密使用不同的 Nonce且计数器不能发生回绕wrap-around。这是 CTR 模式公认的安全实践要求也是下方 CTF 题目的攻击核心——题目恰恰把用于加密 flag 的密钥泄露成了计数器初始值的 hash等价于密钥流被可控地泄露。2023 某 CTF逆推计数器初始值接下来是原文档收录的一道 2023 年 CTF 实战题目。题目将未知的 AES 密钥 $key1$ 的整数值直接用作 CTR 计数器初始值并给出其 SHA-256 摘要要求攻击者根据已知明文、已知密钥 $key2$ 与部分密文逆推出计数器初始值进而还原出被 $key1$ 加密的 flag。题目源码from Crypto.Util.number import long_to_bytes, bytes_to_long from Crypto.Cipher import AES from Crypto.Util import Counter from hashlib import sha256 import os from secret import flag def padding(msg): return msg os.urandom(16 - len(msg) % 16) msg bwhere is the flag? Key in my Heart/Counter!!!! key bI w0nder how???? assert len(msg) 46 assert len(key) 16 enc_key os.urandom(16) initial_value bytes_to_long(enc_key) hash sha256(str(initial_value).encode()).hexdigest() aes AES.new(enc_key,AES.MODE_ECB) enc_flag aes.encrypt(padding(flag)) ctr Counter.new(AES.block_size * 8, initial_value initial_value) aes AES.new(key, counter ctr, mode AES.MODE_CTR) enc aes.encrypt(msg) print(enc {}.format(enc[-16:])) print(enc_flag {}.format(enc_flag)) print(hash {}.format(hash)) enc_last16 b\xbe\x9bd\xc6\xd4\x8c\xe4\x95bi\xbc\xe01\x0e\xb8 enc_flag b\xb2\x97\x83\x1dB\x13\x9b\xc2\x97\x9a\xa6M\x19\xd74\xd2-\xc0\xb6\xba\xe8ZE\x0b:\x14\xed\xec!\xa1\x92\xdfZ\xb0\xbd\xb4M\xb1\x14\xea\xd8\xee\xbf\x83\x16g\xfa hash efb07225b3f1993113e104757210261083c79de50f577b3f0564368ee7b25eeb 已知条件梳理题目提供的信息可以整理为已知量说明明文msgbwhere is the flag? Key in my Heart/Counter!!!!共 46 字节密钥key$key2$bI w0nder how????16 字节CTR 模式的加密密钥密文最后 16 字节enc_last16CTR 模式加密msg后密文的最后 16 字节hashsha256(str(initial_value).encode())即计数器初始值整数的 SHA-256enc_flag用未知密钥 $key1$即enc_key以 ECB 模式加密的 flag含随机 padding未知量只有计数器初始值initial_value即enc_key的字节序转整数以及 flag 本身。解题思路推导回顾 CTR 模式加密流程$$C_i P_i \oplus E_{key2}(Counter_i)$$即$明文 \oplus E_{key2}(Counter) 密文$。根据异或运算的性质只要同时知道明文与密文就可以恢复出加密器的输出$$E_{key2}(Counter) 明文 \oplus 密文$$这里的关键洞察是不要把思维局限在 CTR 模式的加解密图示上。单独看计数器值被分组加密这一操作它本质上就是 ECB 模式对某个块执行加密$$C_{keystream} E_{key2}(Counter)$$而msg的加密用密钥key$key2$进行$key2$ 已知因此可以直接对 $E_{key2}(Counter)$ 执行 ECB 解密$$Counter D_{key2}(E_{key2}(Counter))$$得到最后一个块所使用的计数器值后再减去加密过程中计数器已经增加的数值即该块对应的计数器增量就还原出了计数器初始值initial_value。得到initial_value后先通过hash校验确认正确性再将initial_value转回字节得到enc_key即 $key1$最后用 ECB 模式解密enc_flag并去掉随机 padding即可得到 flag。Exploit 详解from Crypto.Util.number import long_to_bytes, bytes_to_long from Crypto.Cipher import AES from Crypto.Util import Counter from hashlib import sha256 import os # from secret import flag flag bflag{test} def padding(msg): return msg os.urandom(16 - len(msg) % 16) # 隨機值填充 msg bwhere is the flag? Key in my Heart/Counter!!!! key bI w0nder how???? assert len(msg) 46 assert len(key) 16 enc_key os.urandom(16) # 隨機key initial_value bytes_to_long(enc_key) # key轉爲整數 hash sha256(str(initial_value).encode()).hexdigest() # 字符串(key) 的 sha256 aes AES.new(enc_key,AES.MODE_ECB) enc_flag aes.encrypt(padding(flag)) # 16 * 8 128, # {counter_len: 16, prefix: b, suffix: b, initial_value: 1, little_endian: False} ctr Counter.new(AES.block_size * 8, initial_value initial_value) print(ctr) aes AES.new(key, counter ctr, mode AES.MODE_CTR) # key 已知, 推 counter, CTR mode 不需要 padding enc aes.encrypt(msg) # msg 已知 # print(enc {}.format(len(enc))) # 46 print(enc {}.format(enc[-16:])) # 密文的最後16位, 但並不是最後一個 block print(enc_flag {}.format(enc_flag)) print(hash {}.format(hash)) print(題目數據輸出結束 * * 16) # Data enc_last16 b\xbe\x9bd\xc6\xd4\x8c\xe4\x95bi\xbc\xe01\x0e\xb8 enc_flag b\xb2\x97\x83\x1dB\x13\x9b\xc2\x97\x9a\xa6M\x19\xd74\xd2-\xc0\xb6\xba\xe8ZE\x0b:\x14\xed\xec!\xa1\x92\xdfZ\xb0\xbd\xb4M\xb1\x14\xea\xd8\xee\xbf\x83\x16g\xfa hash efb07225b3f1993113e104757210261083c79de50f577b3f0564368ee7b25eeb # Solution # a msg[32:] # 從明文index 32 開始 a msg[16 * (len(msg) // 16):] # 取最後一個 block b enc_last16[16 - (len(enc) % 16):] # 從密文index 2 開始 | 選最後一個 block # 加密最後步驟 明文 xor enc_{key}(counter) 密文 # 解密最後步驟 enc_{key}(counter) xor 密文 明文 | enc_{key}(counter) 密文 xor 明文 enc_Counter1 bytes(a[i] ^ b[i] for i in range(14)) for i in range(0xff): for j in range(0xff): # ECB mode 要求數據長度與塊長對齊, 而加密後的數據的最後 2 bytes 我們並不清楚, 所以我們需要嘗試所有的可能 enc_Counter2 enc_Counter1 bytes([i]) bytes([j]) aes AES.new(key,AES.MODE_ECB) Counter aes.decrypt(enc_Counter2) # E_{key}(Counter) Counter_enc | Counter D_{key}(Counter_enc) initial_value bytes_to_long(Counter) - (len(msg) // 16) # 經歷兩個 block, 最後一個 block 的 Counter - block 數 初始值 if hash sha256(str(initial_value).encode()).hexdigest(): # type: str print(ffound {initial_value }) enc_key long_to_bytes(initial_value) aes AES.new(enc_key,AES.MODE_ECB) flag aes.decrypt(enc_flag) print(flag) break # flag{9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d}关键坑点解析这个 Exploit 中有几个非常容易被忽略、但对解题至关重要的坑点enc[-16:]并不是最后一个完整块。明文msg长度为 46 字节AES 块大小为 16 字节因此46 16 × 2 14密文由 2 个完整块和 1 个 14 字节的不完整块组成。enc[-16:]取的是密文末尾 16 字节其中前 2 字节属于倒数第二个完整块只有后 14 字节才是最后一个不完整块的密文。按偏移精确切出最后一个块a msg[16 * (len(msg) // 16):]即msg[32:]取出明文最后一个 14 字节块b enc_last16[16 - (len(enc) % 16):]即enc_last16[2:]跳过前 2 字节后取出密文最后一个 14 字节块。E_{key2}(Counter)只有 14 字节确定剩余 2 字节需穷举。由于最后一个明文块只有 14 字节$明文 \oplus 密文$ 只能恢复出密钥流块的前 14 字节而后续要调用 ECB 解密数据长度必须与 16 字节块长对齐因此需要对最后未知的 2 字节做0xff × 0xff的暴力枚举共约 65536 次尝试。计数器增量的扣除。46 字节明文占用了 3 个计数器值第 0 块用initial_value第 1 块用initial_value1最后一个块用initial_value2因此Counter - (len(msg) // 16) Counter - 2才是初始值。从源码中Counter对象的默认打印{counter_len: 16, prefix: b, suffix: b, initial_value: 1, little_endian: False}可以看出Counter.new(AES.block_size * 8, initial_valueinitial_value)创建的是 16 字节、无前后缀、默认大端序little_endian: False的计数器初始值即initial_value这也与bytes_to_long/long_to_bytes默认的大端序约定一致。用 hash 作唯一性校验。对候选initial_value计算sha256(str(initial_value).encode())并与题目给出的 hash 比对命中即为正确答案——这相当于把原本未知的 $key1$ 的指纹用作计数器初值猜测的验证器。最后一步是纯 ECB 解密。enc_key long_to_bytes(initial_value)恢复出 16 字节密钥后直接AES.new(enc_key, AES.MODE_ECB)解密enc_flag得到的明文包含随机填充的尾部字节人工去掉即可看到 flag 本体。总结CTR 计数器模式通过计数器 分组加密 异或三要素把分组密码转化为可并行、可随机访问的流密码。本题目最核心的攻击思路可以概括为一条链路已知明文 已知密钥 → 异或恢复密钥流块 → 视作 ECB 单块解密得到计数器值 → 减去块偏移得到初始值 → hash 校验 → 还原密钥 → ECB 解密 flag。这提醒我们两点实战经验其一计数器初值/Nonce 的保密性与唯一性是 CTR 模式的命脉任何形式的初值泄露哪怕是 hash 摘要都可能被攻破其二CTR 模式下密文异或明文即可还原密钥流凡是已知明文/密文对的场景都应警惕密钥流泄露风险。原文档在文末还给出了两道同类型练习题目可作为延伸训练2017 star ctf ssss2017 star ctf ssss2CTR 模式与其他分组模式ECB、CBC、OFB、CFB、PCBC的原理与攻击专题可继续阅读 分组模式文档目录 及 CBC 字节反转攻击 等相关章节。赞分享文档网络安全教程【免费下载链接】ctf-wikiCome and join us, we need you!项目地址https://gitcode.com/gh_mirrors/ct/ctf-wiki点击查看免费下载相关推荐CTF-Wiki DEX 文件格式深度解析从 Dalvik 可执行文件到逆向实战CTF Wiki DEX 文件格式深度解析从 Dalvik 可执行文件到逆向实战 导读 DEXDalvik eXecutable File是 Androi文档网络安全教程上一篇DeerFlow 开源智能体框架上手指南深度研究、数据分析与图表生成的完整路径下一篇贴个链接一条命令拉完整个主页抖音无水印批量下载实操创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
企业数字化 ERP 产品动态
相关推荐
OpenChamber Control Service 深度解析:类型化控制契约、动作校验与双适配器架构 AI Agent人工智能代码智能体交互助手 【免费下载链接】openchamber Agentic Development Environment based on OpenCode AI agent 项目地址: https://gitcode.com/gh_mirrors/op/openchamber 点击查看 免费下载 OpenChamber Control Service 是 OpenChamber CLI 与… · 2026/9/25 7:10:37
Atlas 300V 24G推理卡部署YOLO实战:模型转换与避坑指南 去年有个项目要把YOLOv5接到华为的AI硬件上,当时我就被一个问题卡住了:Atlas 300V 24G到底是张什么卡?是不是运算加速卡?能不能直接拿来跑目标检测?网上一搜,说法五花八门,有人拿它和GPU比显存&… · 2026/9/25 7:10:25
小红书香港老号自动筛选全流程:量化标准与批量部署实操 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 7:35:16
嵌入式MCU开发全链路:编译、烧录与仿真流程详解 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 7:35:16
钕铁硼磁体原料组成与配方设计关键技术解析 1. 烧结钕铁硼磁体的原料组成概述 烧结钕铁硼(NdFeB)作为第三代稀土永磁材料,其优异的磁性能主要来源于精心设计的化学成分和微观结构。在实际工业生产中,配方设计就像烹饪一道美味佳肴——主料决定基本风味,而辅料和… · 2026/9/25 7:35:16
微信读书下载工具:Python开源方案实现本地备份 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 7:35:16
I2C总线调试实战:开漏输出、上拉电阻与多主仲裁全解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 7:35:16
C# 实现欧姆龙 FINS 协议通信:AMS Net ID、字节序与TCP握手详解 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 7:35:10
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37