文章总结: 文档记录了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 GCC optimize("O3,unroll-loops")
#include<iostream>
#include<vector>
#include<string>
#include<iomanip>
#include<cstdint>
#include<array>
// 配置常量
constuint32_t CFG_DELTA = 0x23251156;
constint CFG_ROUNDS = 30;
// 待解密的密文块 (从 .data 提取)
const std::vector<uint32_t> TARGETS = {
0xDEA5D6E9, 0x9384AE5B, 0x2FB88B3B,
0x0378255E, 0x33D33EF7, 0x217A011C
};
// 预计算 acc 表,避免重复计算
uint32_t ACC_TABLE[CFG_ROUNDS];
voidprecompute_acc(){
uint32_t acc = 0;
uint32_t delta = CFG_DELTA;
for(int i = 0; i < CFG_ROUNDS; i++) {
acc += delta;
ACC_TABLE[i] = acc;
}
}
// 核心加密函数 (内联以提速)
inlineuint32_tencrypt_core(uint32_t v){
for (int i = 0; i < CFG_ROUNDS; i++) {
// v = v + ((v << 5) ^ acc ^ (v >> 4))
v += ((v << 5) ^ ACC_TABLE[i] ^ (v >> 4));
}
return v;
}
// 辅助:将整数转为字符串(自动处理字节序)
std::string u32_to_str(uint32_t val, bool little_endian){
std::string s(4, ' ');
if (little_endian) {
s[0] = (val >> 0) & 0xFF;
s[1] = (val >> 8) & 0xFF;
s[2] = (val >> 16) & 0xFF;
s[3] = (val >> 24) & 0xFF;
} else {
s[0] = (val >> 24) & 0xFF;
s[1] = (val >> 16) & 0xFF;
s[2] = (val >> 8) & 0xFF;
s[3] = (val >> 0) & 0xFF;
}
return s;
}
intmain(){
std::cout << "[*] Initializing tables..." << std::endl;
precompute_acc();
// 1. 快速验证:检查 "flag" 是否匹配第一个块
// "flag" -> 0x67616C66 (Little Endian) 或 0x666C6167 (Big Endian)
uint32_t test_le = 0x67616C66;
uint32_t test_be = 0x666C6167;
std::cout << "[?] Check logic: 'flag' encrypts to:" << std::endl;
std::cout << " LE input -> " << std::hex << encrypt_core(test_le) << std::endl;
std::cout << " BE input -> " << std::hex << encrypt_core(test_be) << std::endl;
std::cout << " Target 0 -> " << std::hex << TARGETS[0] << std::endl;
std::cout << "------------------------------------------------" << std::endl;
std::cout << "[*] Starting brute force (Space: ~81M)..." << std::endl;
// 存储结果:key 是 target 索引, value 是解出的字符串
std::string results[6];
int found_count = 0;
// 4层循环穷举 (c0 c1 c2 c3)
// 假设输入是 "ABCD",我们构建两个整数:
// LE_VAL = 0x44434241 (x86常用)
// BE_VAL = 0x41424344 (网络序/Z3脚本常用)
// 优化:并行计算 (如果编译器支持 OpenMP)
#pragma omp parallel for collapse(2)
for (int c0 = 32; c0 <= 126; c0++) {
for (int c1 = 32; c1 <= 126; c1++) {
for (int c2 = 32; c2 <= 126; c2++) {
for (int c3 = 32; c3 <= 126; c3++) {
// 构建两种字节序的整数
uint32_t val_be = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3;
uint32_t val_le = (c3 << 24) | (c2 << 16) | (c1 << 8) | c0;
// 计算加密
uint32_t enc_be = encrypt_core(val_be);
uint32_t enc_le = encrypt_core(val_le);
// 检查是否命中目标
for (int i = 0; i < 6; i++) {
if (enc_le == TARGETS[i]) {
// 命中 LE 模式
char buf[5] = {(char)c0, (char)c1, (char)c2, (char)c3, 0};
#pragma omp critical
{
std::cout << "[+] Found Chunk [" << i << "] (LE Mode): " << buf << std::endl;
results[i] = buf;
found_count++;
}
}
elseif (enc_be == TARGETS[i]) {
// 命中 BE 模式
char buf[5] = {(char)c0, (char)c1, (char)c2, (char)c3, 0};
#pragma omp critical
{
std::cout << "[+] Found Chunk [" << i << "] (BE Mode): " << buf << std::endl;
results[i] = buf;
found_count++;
}
}
}
}
}
}
}
std::cout << "[*] Done." << std::endl;
std::cout << "Final Flag: ";
for(int i=0; i<6; i++) std::cout << (results[i].empty() ? "????" : results[i]);
std::cout << std::endl;
return0;
}
运行完就是flag{Fl0weRTeAVM15E3}
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:蚁景网络安全 《记2025鹏城杯CTF线上赛部分题目》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论