首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
CTF对抗
发新帖
0
0
[原创] KCTF 2026 第七题 (AI)
发表于: 2026-8-24 01:34
188
[原创] KCTF 2026 第七题 (AI)
LingYi0110
2026-8-24 01:34
188
## 题目概述 已知数据如下: ~~~text 密钥:121 TLU -> 94AA48550495 Hello -> 34BB405504B5223594B94C53 2026 -> A48844556485223322356483 abcd! -> 547B475584B5223564BB4553 ~~~ 目标是根据样例恢复固定加密流程,再解密 FLAG: ~~~text 14CC4655547594BC475584C5848A43551495448C445584C5D4C9475564C534A84B55A4B574BA4355F495A48844556485648C495534A5548C4F5584A5B4BB405554B522332235A4B3 ~~~ 最终结果是: ~~~text flag{T1u_2026_Kc7f_Crypt0_M4ster!} ~~~ ## 1. 从长度入手 统计样例长度: | 明文 | 明文长度 | 密文长度 | |---|---:|---:| | TLU | 3 字节 | 12 个十六进制字符 | | Hello | 5 字节 | 24 个十六进制字符 | | 2026 | 4 字节 | 24 个十六进制字符 | | abcd! | 5 字节 | 24 个十六进制字符 | 密文总是按 12 个十六进制字符分组。一个 ASCII 字节对应两个十六进制半字节,因此 3 个明文字节对应 6 个有效半字节,最后被扩展成一个 12 字符的密文块。 例如: ~~~text Hello: 34BB405504B5 | 223594B94C53 2026: A48844556485 | 223322356483 ~~~ 这说明算法以 3 个明文字节为一个完整块。最后不足 3 个字节的部分使用长度相关的固定模板。 ## 2. 找出完整块中的固定位置 先只看完整的 3 字节块: ~~~text TLU -> 94AA48550495 Hel -> 34BB405504B5 202 -> A48844556485 abc -> 547B475584B5 ~~~ 按下标展开: ~~~text 下标: 0 1 2 3 4 5 6 7 8 9 10 11 TLU: 9 4 A A 4 8 5 5 0 4 9 5 Hel: 3 4 B B 4 0 5 5 0 4 B 5 202: A 4 8 8 4 4 5 5 6 4 8 5 abc: 5 4 7 B 4 7 5 5 8 4 B 5 ~~~ 固定位置为: ~~~text 1、4、6、7、9、11 ~~~ 它们的值始终是: ~~~text 4、4、5、5、4、5 ~~~ 因此,完整块中的 6 个有效半字节位于: ~~~text 0、2、3、5、8、10 ~~~ 有效半字节还经过了位置重排。结合四组样例,可以确定正确的提取顺序: ~~~python [0, 2, 8, 10, 5, 3] ~~~ 对一个 12 字符密文块 c,依次提取: ~~~text c[0], c[2], c[8], c[10], c[5], c[3] ~~~ 以 TLU 为例: ~~~text 密文:94AA48550495 c[0] = 9 c[2] = A c[8] = 0 c[10] = 9 c[5] = 8 c[3] = A ~~~ 提取结果是: ~~~text 9A098A ~~~ ## 3. 恢复尾块模板 最后一块的固定内容只和剩余明文长度有关。 ### 剩余 2 个明文字节 Hello 和 abcd! 都有一个包含 2 个字节的尾块: ~~~text Hello 的尾块:223594B94C53 abcd! 的尾块:223564BB4553 ~~~ 两者的结构为: ~~~text 2235 r0 4 r1 r3 4 r2 53 ~~~ 有效半字节的提取位置为: ~~~python [4, 6, 9, 7] ~~~ 例如,223594B94C53 按该顺序提取后得到: ~~~text 9BC9 ~~~ ### 剩余 1 个明文字节 2026 的最后一个字符是 6,尾块为: ~~~text 223322356483 ~~~ 模板为: ~~~text 22332235 r0 4 r1 3 ~~~ 提取位置为: ~~~python [8, 10] ~~~ 所以 2026 的尾块提取结果为: ~~~text 68 ~~~ FLAG 的最后一块是: ~~~text 22332235A4B3 ~~~ 它也符合 1 字节尾块模板。因此 FLAG 长度为: ~~~text 11 个完整块 × 3 字节 + 1 字节 = 34 字节 ~~~ ## 4. 找到整体逆序 将 TLU 的完整块按上面的顺序提取: ~~~text 94AA48550495 -> 9A098A ~~~ TLU 的 ASCII 十六进制为: ~~~text T = 54 L = 4C U = 55 TLU = 544C55 ~~~ 将提取结果反转: ~~~text 9A098A -> A890A9 ~~~ 逐个半字节比较: ~~~text 变换结果:A 8 9 0 A 9 明文数值:5 4 4 C 5 5 差值: 5 4 5 4 5 4 ~~~ 差值在模 16 下交替出现 5 和 4。因此: ~~~text 高半字节:+5 mod 16 低半字节:+4 mod 16 ~~~ 解密时执行相反操作: ~~~text 高半字节:-5 mod 16 低半字节:-4 mod 16 ~~~ 例如: ~~~text C + 4 = 10(hex) -> 0 ~~~ 这是模 16 循环偏移。 ## 5. 密钥 121 的作用 密钥 121 按三个数字使用,而不是作为一个十进制整数整体参与运算: ~~~text 1、2、1 ~~~ 三轮模 16 偏移的基础总量为: ~~~text 1 + 2 + 1 = 4 ~~~ 低半字节使用基础偏移 4。高半字节再使用首位 1 进行额外扰动,因此偏移量为 5: ~~~text base = 1 + 2 + 1 低半字节:value + base = value + 4 高半字节:value + base + 1 = value + 5 ~~~ 三个数字的作用可以概括为: | 数字 | 作用 | |---|---| | 第一个 1 | 第一轮偏移,并提供高半字节的额外偏移 | | 中间的 2 | 第二轮模 16 偏移 | | 最后的 1 | 第三轮模 16 偏移 | ## 6. 完整加密流程 加密流程如下: 1. 将明文转为 ASCII 字节。 2. 将每个字节转换为两个十六进制半字节。 3. 高半字节加 5,低半字节加 4,结果对 16 取模。 4. 将整个半字节序列反转。 5. 每 6 个半字节分成一块。 6. 根据剩余半字节数量,写入对应的固定模板。 7. 输出大写十六进制字符串。 三个模板为: ~~~text 完整块,6 个有效半字节: r0 4 r1 r5 4 r4 5 5 r2 4 r3 5 尾块,4 个有效半字节: 2235 r0 4 r1 r3 4 r2 53 尾块,2 个有效半字节: 22332235 r0 4 r1 3 ~~~ ## 7. 手工验证 Hello Hello 的 ASCII 十六进制为: ~~~text 48656C6C6F ~~~ 逐个半字节偏移: ~~~text 4 + 5 = 9 8 + 4 = C 6 + 5 = B 5 + 4 = 9 6 + 5 = B C + 4 = 0 6 + 5 = B C + 4 = 0 6 + 5 = B F + 4 = 3 ~~~ 得到: ~~~text 9CB9B0B0B3 ~~~ 整体反转: ~~~text 3B0B0B9BC9 ~~~ 分块: ~~~text 3B0B0B | 9BC9 ~~~ 第一块使用完整块模板,得到: ~~~text 34BB405504B5 ~~~ 第二块使用 4 半字节尾模板,得到: ~~~text 223594B94C53 ~~~ 拼接后为: ~~~text 34BB405504B5223594B94C53 ~~~ 这与题目给出的 Hello 密文一致。 ## 8. 解密 FLAG 按照完整块和尾块的提取规则处理 FLAG,得到处于逆序状态的数据流: ~~~text 1C576C9B8C7C88193A488C4CDC6C793AABB87BF93AA86848683A9C588AFCBB5B0BAB ~~~ 反转后: ~~~text BAB0B5BBCFA885C9A38684868AA39FB78BBAA397C6CDC4C884A39188C7C8B9C675C1 ~~~ 交替减去 5 和 4,得到明文十六进制: ~~~text 666C61677B5431755F323032365F4B6337665F4372797074305F4D3473746572217D ~~~ 按每两个十六进制字符转换为 ASCII: ~~~text flag{T1u_2026_Kc7f_Crypt0_M4ster!} ~~~ ## 9. Python 解密脚本 ~~~python FLAG_CIPHER = ( "14CC4655547594BC475584C5848A43551495448C445584C5" "D4C9475564C534A84B55A4B574BA4355F495A48844556485" "648C495534A5548C4F5584A5B4BB405554B522332235A4B3" ) def extract_block(block, nibble_count): if nibble_count == 6: # 完整块:r0 4 r1 r5 4 r4 5 5 r2 4 r3 5 positions = [0, 2, 8, 10, 5, 3] elif nibble_count == 4: # 2 字节尾块:2235 r0 4 r1 r3 4 r2 53 positions = [4, 6, 9, 7] elif nibble_count == 2: # 1 字节尾块:22332235 r0 4 r1 3 positions = [8, 10] else: raise ValueError("invalid nibble count") return "".join(block[i] for i in positions) def detect_tail_size(block): if block.startswith("22332235"): return 2 if block.startswith("2235"): return 4 return 6 def decrypt(ciphertext, key="121"): if len(ciphertext) % 12 != 0: raise ValueError("ciphertext length must be a multiple of 12") blocks = [ ciphertext[i:i + 12] for i in range(0, len(ciphertext), 12) ] # 恢复偏移后的逆序半字节流。 reversed_stream = [] for block in blocks[:-1]: reversed_stream.append(extract_block(block, 6)) tail_size = detect_tail_size(blocks[-1]) reversed_stream.append( extract_block(blocks[-1], tail_size) ) shifted_stream = "".join(reversed_stream)[::-1] digits = [int(x) for x in key] base_shift = sum(digits) # 4 high_extra = digits[0] # 1 plain_nibbles = [] for i, char in enumerate(shifted_stream): value = int(char, 16) if i % 2 == 0: value = (value - base_shift - high_extra) % 16 else: value = (value - base_shift) % 16 plain_nibbles.append(format(value, "X")) plain_hex = "".join(plain_nibbles) return bytes.fromhex(plain_hex).decode("ascii") print(decrypt(FLAG_CIPHER)) ~~~ 输出: ~~~text flag{T1u_2026_Kc7f_Crypt0_M4ster!} ~~~ ## 10. 正向验证 把恢复出的明文重新加密,可以验证算法不仅能解出 FLAG,也能重现所有已知样例: ~~~python def encrypt(plaintext, key="121"): plain_hex = plaintext.encode("ascii").hex().upper() digits = [int(x) for x in key] base_shift = sum(digits) high_extra = digits[0] shifted = [] for i, char in enumerate(plain_hex): value = int(char, 16) if i % 2 == 0: value = (value + base_shift + high_extra) % 16 else: value = (value + base_shift) % 16 shifted.append(format(value, "X")) reversed_stream = "".join(shifted)[::-1] result = [] for i in range(0, len(reversed_stream), 6): r = reversed_stream[i:i + 6] if len(r) == 6: block = ( f"{r[0]}4{r[1]}{r[5]}4{r[4]}" f"55{r[2]}4{r[3]}5" ) elif len(r) == 4: block = ( f"2235{r[0]}4{r[1]}{r[3]}" f"4{r[2]}53" ) elif len(r) == 2: block = f"22332235{r[0]}4{r[1]}3" else: raise ValueError("invalid block length") result.append(block) return "".join(result) tests = { "TLU": "94AA48550495", "Hello": "34BB405504B5223594B94C53", "2026": "A48844556485223322356483", "abcd!": "547B475584B5223564BB4553", } for plaintext, expected in tests.items(): calculated = encrypt(plaintext) print(plaintext, calculated == expected) flag = "flag{T1u_2026_Kc7f_Crypt0_M4ster!}" print(encrypt(flag)) ~~~ 四组样例和 FLAG 都能重新生成题目中的密文,说明恢复出的流程完整一致。
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
收藏
・
0
点赞
・
0
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
0
)
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
LingYi0110
6
发帖
1
回帖
10
RANK
关注
私信
他的文章
[原创] KCTF 2026 第八题 (AI)
368
[原创] KCTF 2026 第七题 (AI)
188
[原创] KCTF 2026 第五题
256
[原创] KCTF 2026 第九题
39
[原创] KCTF 2026 第二题
160
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部