记2025鹏城杯CTF线上赛部分题目

admin 2025-12-23 16:03:34 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 文档记录了2025年鹏城杯CTF赛题解法,涵盖密码学、杂项及逆向方向。重点包括利用高精度浮点泄露恢复RSA私钥、结合佩尔方程与LLL算法解密、图像隐写与盲水印提取、CAN总线流量分析、SMB协议逆向、SDR信号解调摩斯密码及自定义VM保护的ARX算法爆破。文章提供了详细的攻击思路与代码脚本,实战参考价值高。 综合评分: 88 文章分类: CTF,逆向分析,二进制安全,网络安全


得到的xor1.png与xor.png类似,盲水印解,解完就可以得到flag了

获得一段音频,一听就知道是摩斯密码

提取一下 .—- .—- ….- ….. .—- ….- …– ….. —– ..— …– ….- .—- .—- ….- ….. .—- ….-

翻译过来就是114514350234114514

解压flag.zip,其中flag.txt是头尾已知的部分明文,flag.zip是包含flag.txt的加密压缩包

明文攻击

bkcrack.exe -C flag.zip -c flag.txt -x 0 666c61677b593075 -x 25 2121217d

得到三个key 33b19021 93c4a78d 9ceed931

拿ARCHPR去跑,就可以得到flag

3.re

3.1 more_more_flower

Windows 32-bit PE 可执行文件Console 程序

给的flagSHA256.txt:给了一个 flag 的 SHA256,用来最后校验结果

flagSHA256.txt 内容类似:

flag SHA256 Encrypted:3dbe89f66cb189f9cac1fb5ec23fac941df69119792aad4b6d61d63b98ddb527

IDA里面跟flag有关的就是这个函数

sub_401000这个函数很长,大概就是 全局变量每轮从 .data 里取 opcode 还有dispatch jump table,opcode -> handler 地址

还有全局寄存器R0C、R10、R14、R18、R28…

最后还在在 .data 里开了一段空间 + SP 指针,就是用来验证flag

输入长度固定为 0x18(24)字节,处理时按 dword对齐读取,因此总共会跑6 个 block

每次取 4 字节时,先按高字节在前拼成 32-bit 值:

– v = (b0<<24) + (b1<<16) + (b2<<8) + b3

完成该 block 的运算后,结果不会按原顺序写回,而是把 dword 拆成 小端序的 4 个字节压入 VM 栈

并没有单独的 loop 变量,而是把计数放在 栈底第 0 字节 启动阶段先 PUSH 0x06,每处理完一组就对 STACK[0] 做 -1,再用 JNZ STACK[0] 来决定要不要继续下一组

每个 4 字节 block 内部会进入一个固定轮数的 ARX 更新流程,风格接近 TEA 那类“sum 逐轮叠加 delta”的写法

– sum 初始清零(VM 里对应 R18

  • 轮数硬编码为 0x1e(即 30

每轮的顺序是先累加:

– sum += delta

随后再更新数据本体 v

– v += ((v<<5) ^ sum ^ (v>>4))

delta 不是直接出现的立即数,而是由字节码“拼装”出来:每轮都会 push 四个字节 56 11 25 23,再 POP 成 dword,因此得到常量:

– delta = 0x23251156

6 组数据全部处理完后,VM 栈里会累计得到 24 个变换后的输出字节;随后 bytecode 进入固定 24 次的校验循环,每次都从栈顶 POP 1 字节并与 .data 段中的常量数组 DATA[i] 通过 SUB+JNZ 逐一比对,一旦不相等就直接走失败分支 RET 0。由于校验是“从栈顶往下弹”,实际比较顺序与生成顺序相反,因此整体等价于检查 DATA == reverse(out)。从 .data 中提取的 24 字节常量为 21 7a 01 1c 33 d3 3e f7 03 78 25 5e 2f b8 8b 3b 93 84 ae 5b de a5 d6 e9,将其反序后再按每 4 字节以小端拼回 6 个 32-bit 密文块,分别是 0xDEA5D6E9, 0x9384AE5B, 0x2FB88B3B, 0x0378255E, 0x33D33EF7, 0x217A011C,于是问题就转化为:在已知这 6 个 32-bit 输出的情况下,求对应的 6 个由 4 个可打印字符组成的 32-bit 输入

用python会跑得很慢,所以直接改为用C++好了

#pragma&nbsp;GCC optimize("O3,unroll-loops")
#include<iostream>
#include<vector>
#include<string>
#include<iomanip>
#include<cstdint>
#include<array>

// 配置常量
constuint32_t&nbsp;CFG_DELTA =&nbsp;0x23251156;
constint&nbsp;CFG_ROUNDS =&nbsp;30;

// 待解密的密文块 (从 .data 提取)
const&nbsp;std::vector<uint32_t> TARGETS = {
&nbsp; &nbsp;&nbsp;0xDEA5D6E9,&nbsp;0x9384AE5B,&nbsp;0x2FB88B3B,
&nbsp; &nbsp;&nbsp;0x0378255E,&nbsp;0x33D33EF7,&nbsp;0x217A011C
};

// 预计算 acc 表,避免重复计算
uint32_t&nbsp;ACC_TABLE[CFG_ROUNDS];

voidprecompute_acc(){
&nbsp; &nbsp;&nbsp;uint32_t&nbsp;acc =&nbsp;0;
&nbsp; &nbsp;&nbsp;uint32_t&nbsp;delta = CFG_DELTA;
&nbsp; &nbsp;&nbsp;for(int&nbsp;i =&nbsp;0; i < CFG_ROUNDS; i++) {
&nbsp; &nbsp; &nbsp; &nbsp; acc += delta;
&nbsp; &nbsp; &nbsp; &nbsp; ACC_TABLE[i] = acc;
&nbsp; &nbsp; }
}

// 核心加密函数 (内联以提速)
inlineuint32_tencrypt_core(uint32_t&nbsp;v){
&nbsp; &nbsp;&nbsp;for&nbsp;(int&nbsp;i =&nbsp;0; i < CFG_ROUNDS; i++) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// v = v + ((v << 5) ^ acc ^ (v >> 4))
&nbsp; &nbsp; &nbsp; &nbsp; v += ((v <<&nbsp;5) ^ ACC_TABLE[i] ^ (v >>&nbsp;4));
&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;return&nbsp;v;
}

// 辅助:将整数转为字符串(自动处理字节序)
std::string&nbsp;u32_to_str(uint32_t&nbsp;val,&nbsp;bool&nbsp;little_endian){
&nbsp; &nbsp;&nbsp;std::string&nbsp;s(4,&nbsp;' ');
&nbsp; &nbsp;&nbsp;if&nbsp;(little_endian) {
&nbsp; &nbsp; &nbsp; &nbsp; s[0] = (val >>&nbsp;0) &&nbsp;0xFF;
&nbsp; &nbsp; &nbsp; &nbsp; s[1] = (val >>&nbsp;8) &&nbsp;0xFF;
&nbsp; &nbsp; &nbsp; &nbsp; s[2] = (val >>&nbsp;16) &&nbsp;0xFF;
&nbsp; &nbsp; &nbsp; &nbsp; s[3] = (val >>&nbsp;24) &&nbsp;0xFF;
&nbsp; &nbsp; }&nbsp;else&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp; s[0] = (val >>&nbsp;24) &&nbsp;0xFF;
&nbsp; &nbsp; &nbsp; &nbsp; s[1] = (val >>&nbsp;16) &&nbsp;0xFF;
&nbsp; &nbsp; &nbsp; &nbsp; s[2] = (val >>&nbsp;8) &&nbsp;0xFF;
&nbsp; &nbsp; &nbsp; &nbsp; s[3] = (val >>&nbsp;0) &&nbsp;0xFF;
&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;return&nbsp;s;
}

intmain(){
&nbsp; &nbsp; std::cout <<&nbsp;"[*] Initializing tables..."&nbsp;<< std::endl;
&nbsp; &nbsp;&nbsp;precompute_acc();

&nbsp; &nbsp;&nbsp;// 1. 快速验证:检查 "flag" 是否匹配第一个块
&nbsp; &nbsp;&nbsp;// "flag" -> 0x67616C66 (Little Endian) 或 0x666C6167 (Big Endian)
&nbsp; &nbsp;&nbsp;uint32_t&nbsp;test_le =&nbsp;0x67616C66;
&nbsp; &nbsp;&nbsp;uint32_t&nbsp;test_be =&nbsp;0x666C6167;
&nbsp; &nbsp; std::cout <<&nbsp;"[?] Check logic: 'flag' encrypts to:"&nbsp;<< std::endl;
&nbsp; &nbsp; std::cout <<&nbsp;" &nbsp; &nbsp;LE input -> "&nbsp;<< std::hex <<&nbsp;encrypt_core(test_le) << std::endl;
&nbsp; &nbsp; std::cout <<&nbsp;" &nbsp; &nbsp;BE input -> "&nbsp;<< std::hex <<&nbsp;encrypt_core(test_be) << std::endl;
&nbsp; &nbsp; std::cout <<&nbsp;" &nbsp; &nbsp;Target 0 -> "&nbsp;<< std::hex << TARGETS[0] << std::endl;
&nbsp; &nbsp; std::cout <<&nbsp;"------------------------------------------------"&nbsp;<< std::endl;

&nbsp; &nbsp; std::cout <<&nbsp;"[*] Starting brute force (Space: ~81M)..."&nbsp;<< std::endl;

&nbsp; &nbsp;&nbsp;// 存储结果:key 是 target 索引, value 是解出的字符串
&nbsp; &nbsp; std::string results[6];
&nbsp; &nbsp;&nbsp;int&nbsp;found_count =&nbsp;0;

&nbsp; &nbsp;&nbsp;// 4层循环穷举 (c0 c1 c2 c3)
&nbsp; &nbsp;&nbsp;// 假设输入是 "ABCD",我们构建两个整数:
&nbsp; &nbsp;&nbsp;// LE_VAL = 0x44434241 (x86常用)
&nbsp; &nbsp;&nbsp;// BE_VAL = 0x41424344 (网络序/Z3脚本常用)

&nbsp; &nbsp;&nbsp;// 优化:并行计算 (如果编译器支持 OpenMP)
&nbsp; &nbsp;&nbsp;#pragma&nbsp;omp parallel for collapse(2)
&nbsp; &nbsp;&nbsp;for&nbsp;(int&nbsp;c0 =&nbsp;32; c0 <=&nbsp;126; c0++) {
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;(int&nbsp;c1 =&nbsp;32; c1 <=&nbsp;126; c1++) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;(int&nbsp;c2 =&nbsp;32; c2 <=&nbsp;126; c2++) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;(int&nbsp;c3 =&nbsp;32; c3 <=&nbsp;126; c3++) {

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 构建两种字节序的整数
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;uint32_t&nbsp;val_be = (c0 <<&nbsp;24) | (c1 <<&nbsp;16) | (c2 <<&nbsp;8) | c3;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;uint32_t&nbsp;val_le = (c3 <<&nbsp;24) | (c2 <<&nbsp;16) | (c1 <<&nbsp;8) | c0;

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 计算加密
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;uint32_t&nbsp;enc_be =&nbsp;encrypt_core(val_be);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;uint32_t&nbsp;enc_le =&nbsp;encrypt_core(val_le);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 检查是否命中目标
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;(int&nbsp;i =&nbsp;0; i <&nbsp;6; i++) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(enc_le == TARGETS[i]) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 命中 LE 模式
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;char&nbsp;buf[5] = {(char)c0, (char)c1, (char)c2, (char)c3,&nbsp;0};
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;#pragma&nbsp;omp critical
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; std::cout <<&nbsp;"[+] Found Chunk ["&nbsp;<< i <<&nbsp;"] (LE Mode): "&nbsp;<< buf << std::endl;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; results[i] = buf;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; found_count++;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;elseif&nbsp;(enc_be == TARGETS[i]) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 命中 BE 模式
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;char&nbsp;buf[5] = {(char)c0, (char)c1, (char)c2, (char)c3,&nbsp;0};
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;#pragma&nbsp;omp critical
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; std::cout <<&nbsp;"[+] Found Chunk ["&nbsp;<< i <<&nbsp;"] (BE Mode): "&nbsp;<< buf << std::endl;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; results[i] = buf;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; found_count++;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }

&nbsp; &nbsp; std::cout <<&nbsp;"[*] Done."&nbsp;<< std::endl;
&nbsp; &nbsp; std::cout <<&nbsp;"Final Flag: ";
&nbsp; &nbsp;&nbsp;for(int&nbsp;i=0; i<6; i++) std::cout << (results[i].empty() ?&nbsp;"????"&nbsp;: results[i]);
&nbsp; &nbsp; std::cout << std::endl;

&nbsp; &nbsp;&nbsp;return0;
}

运行完就是flag{Fl0weRTeAVM15E3}


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:蚁景网络安全 《记2025鹏城杯CTF线上赛部分题目》

评论:0   参与:  3