当前位置: 首页 > news >正文

XWiki Platform 路径遍历漏洞分析 | CVE-2025-55747CVE-2025-55748

0x0 背景介绍

XWiki Platform是一个通用的wiki平台,为构建在其之上的应用程序提供运行时服务XWiki Platform在特定版本中存在安全漏洞:
CVE-2025-55747->攻击者可以通过构造特定的URL,利用webjars API访问并读取系统中的配置文件,泄露敏感信息。
CVE-2025-55748->配置文件可以通过jsxsx 端点访问,可以访问和读取配置文件。

0x1 环境搭建

1. Ubuntu24+Docker环境复现

  • 另存为install.sh并赋予执行权限chmod +x install.sh
#!/bin/bash
echo "部署 Xwiki CVE-2025-55747&CVE-2025-55748"
echo -e "\n======= 重建阶段 ======="
echo "[*] 创建新目录..."
mkdir -p /root/xwiki-Dir && cd /root/xwiki-Dir || exit# 检查端口占用
if lsof -i :8080 >/dev/null; thenecho "[-] 错误:8080端口已被占用"exit 1
fi
# 创建 docker-compose.yml
cat > docker-compose.yml << 'EOF'
version: '2'
networks:bridge:driver: bridge
services:web:image: "xwiki:17.2.2-postgres-tomcat"container_name: xwiki-postgres-tomcat-webdepends_on:- dbports:- "8080:8080"environment:- XWIKI_VERSION=17.2.2- DB_USER=xwiki- DB_PASSWORD=xwiki- DB_HOST=xwiki-postgres-dbvolumes:- xwiki-data:/usr/local/xwikinetworks:- bridgedb:image: "postgres:17"container_name: xwiki-postgres-dbvolumes:- postgres-data:/var/lib/postgresql/dataenvironment:- POSTGRES_ROOT_PASSWORD=xwiki- POSTGRES_PASSWORD=xwiki- POSTGRES_USER=xwiki- POSTGRES_DB=xwiki- POSTGRES_INITDB_ARGS=--encoding=UTF8 --locale-provider=builtin --locale=C.UTF-8networks:- bridge
volumes:postgres-data: {}xwiki-data: {}
EOFecho "[+] 启动环境..."
docker compose up -d
sleep 20
echo -e "\n[√] 服务已就绪"echo -e "\n======= 完成 ======="
echo "访问地址: http://localhost:8080"
echo "默认安装即可,我这装了一个默认主题(比较慢)"

2. 成功页面

搭建成功页面

0x2 漏洞复现

1) CVE-2025-55747

  • YML检测
https://gitee.com/songKl992/cve-series-update/blob/master/CVE/2025/Xwiki-CVE-2025-55747.yml

55747截图

2) CVE-2025-55748

  • YML检测
https://gitee.com/songKl992/cve-series-update/blob/master/CVE/2025/Xwiki-CVE-2025-55748.yml

55748截图
3) 信息收集脚本

  • 同理,对于我这个脚本小子来说,更喜欢一些自动化收集,如下:
https://github.com/Kai-One001/Xwiki--/blob/main/Scan-Xwiki.py

脚本截图

PS:一些解答和遇到的疑惑:

  • Q1:为什么不能读取系统文件呢?例如etc/这些
    答:因为漏洞用的是 ClassLoader.getResourceAsStream(),它只能读 classpath 内的资源(如 WEB-INF/classes/
  • Q2:为什么公开是xwiki读取,而这里是直接webjars
    答:部署路径不同,部署在/xwiki 路径下 和docker中部署在/根下是不同路径

2、复现流量特征 (PACP)

1) CVE-2025-55747

  • 读取xwiki.cfg文件
    xwiki.cfg

2) CVE-2025-55748

  • 读取xwiki.cfg文件
    xwiki.cfg

0x3 漏洞原理分析

1、CVE-2025-55747分析

1) URL 入口

  • 文件位置:org.xwiki.url.ExtendedURL.java
  • 关键代码位置:ExtendedURL.java (第236行):URL解码处理
  • 找到路径分割与解码方法:extractPathSegments(String rawPath)
private List<String> extractPathSegments(String rawPath)
{List<String> urlSegments = new ArrayList<>();if (StringUtils.isEmpty(rawPath)) {return urlSegments;}for (String pathSegment : rawPath.split(URL_SEPARATOR, -1)) {// 去除路径参数,如 ;jsessionid=...String normalizedPathSegment = pathSegment.split(";", 2)[0];// 现在开始解码String decodedPathSegment;try {decodedPathSegment = URLDecoder.decode(normalizedPathSegment, UTF8);} catch (UnsupportedEncodingException e) {throw new RuntimeException("Failed to URL decode...", e);}urlSegments.add(decodedPathSegment);}WebJarsResourceReference.javareturn urlSegments;
}

2) 路径拼接

  • 文件位置:org.xwiki.webjars.internal.WebJarsResourceReference.java
  • 作用:把解析后的路径段,包装成一个“资源引用”对象,用于后续查找WebJar文件
  • 关键代码段分析 构造函数:接收 namespaceresourceSegments
public WebJarsResourceReference(String namespace, List<String> resourceSegments)
{setType(TYPE); // 设置资源类型为 "webjars"this.namespace = namespace;this.resourceSegments = new ArrayList<>(resourceSegments); // ❌ 直接复制,无检查
}
方法 getResourceName():拼接路径public String getResourceName(){return StringUtils.join(getResourceSegments(), RESOURCE_PATH_SEPARATOR);}

3) 资源访问

  • 文件位置:org.xwiki.webjars.internal.WebJarsResourceReferenceHandler.java
  • 作用:根据 WebJarsResourceReference 提供的路径,从类加载器中读取文件并返回给用户
@Override
protected InputStream getResourceStream(WebJarsResourceReference resourceReference)
{String resourcePath = String.format("%s%s", WEBJARS_RESOURCE_PREFIX, getResourceName(resourceReference));return getClassLoader(resourceReference.getNamespace()).getResourceAsStream(resourcePath);
}

4) 漏洞流程

URL解析阶段:%2F被解码为/,%3A被解码为 ↓
资源引用创建:解码后的段被直接使用,没有安全检查 ↓
路径组装:使用StringUtils.join()直接拼接,没有重新编码 👇
资源访问:ClassLoader解析相对路径,允许访问WebJars目录之外的文件

2、CVE-2025-55748分析

1) SsxAction.java(入口)

  • 文件位置:com.xpn.xwiki.web.SsxAction
  • 作用:处理 /bin/ssx/ 请求的入口类
  • 关键点:继承 AbstractSxAction
@Component
@Named("ssx")
@Singleton
public class SsxAction extends AbstractSxAction
{/** The extension type of this action. */public static final CssExtension CSSX = new CssExtension();/** Logging helper. */private static final Logger LOGGER = LoggerFactory.getLogger(SsxAction.class);@Overridepublic Extension getExtensionType(){return CSSX;}@Overrideprotected Logger getLogger(){return LOGGER;}
}
  • @Named("ssx") → 对应 URL 路由/bin/ssx/
  • 继承自 AbstractSxAction→ 使用其通用渲染逻辑
  • getExtensionType() 返回 CssExtension → 表示这是用于加载.css类型的资源扩展
    2) AbstractSxAction.java(参数获取)
  • 文件位置:com.xpn.xwiki.web.sx.AbstractSxAction
  • 作用:处理.css 或 .js资源的通用逻辑
  • 关键方法:render()
    @Overridepublic String render(XWikiContext context) throws XWikiException{SxSource sxSource;if (context.getRequest().getParameter(JAR_RESOURCE_REQUEST_PARAMETER) != null) {sxSource = new SxResourceSource(context.getRequest().getParameter(JAR_RESOURCE_REQUEST_PARAMETER));} else {if (context.getDoc().isNew()) {context.getResponse().setStatus(HttpServletResponse.SC_NOT_FOUND);return "docdoesnotexist";}sxSource = new SxDocumentSource(context, getExtensionType());}try {renderExtension(sxSource, getExtensionType(), context);} catch (IllegalArgumentException e) {// Simply set a 404 status code and return null, so that no unneeded bytes are transferedcontext.getResponse().setStatus(HttpServletResponse.SC_NOT_FOUND);}return null;}
  • JAR_RESOURCE_REQUEST_PARAMETER 是什么?通常定义为:
public static final String JAR_RESOURCE_REQUEST_PARAMETER = "resource";
  • 所以resourceName 直接来自用户输入
  • 没有任何校验:没检查 ..、没解码、没白名单
  • 直接传给 SxResourceSource

3) SxResourceSource.java(最终文件读)

  • 文件位置:com.xpn.xwiki.web.sx.SxResourceSource
  • 作用:根据路径从 ClassLoader 中读取资源
  • 关键方法:getContent()
    @Overridepublic String getContent(){try {// Load from the current context class loader to allow extensions to contribute skin extensions.ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();try (InputStream in = contextClassLoader.getResourceAsStream(this.resourceName)) {return IOUtils.toString(in, StandardCharsets.UTF_8);}} catch (NullPointerException e) {// This happens when the file was not found. Forward an IAE so that the sx action returns 404throw new IllegalArgumentException(e);} catch (IOException e) {return "";}}

1、this.resourceName 就是用户传的…/…/WEB-INF/xwiki.cfg

  • getResourceAsStream 会解析 ../
  • this.resourceName 是用户输入的 resource 参数
  • ClassLoader.getResourceAsStream() 会解析相对路径:
  • ../../WEB-INF/xwiki.cfg → 跳出 jar 包,读取 webapp 目录下的文件(但也不会抛出异常,只是返回 null

2、IOUtils.toString(in, UTF_8)

  • 使用 Apache Commons IO InputStream 转为字符串
  • 假设文件是文本格式(如 .cfg, .xml, .properties
  • 如果文件是二进制(如 .jar, .class),可能会乱码,但依然能传输

0x4 修复建议

修复方案

  1. 升级到最新版本:目前厂商已发布升级补丁以修复漏洞,补丁获取链接
  2. 临时缓解措施
    • Tomcat 拦截 :使用security-constraint通过 web.xml 添加安全约束,禁止对敏感路径的 GET 请求;
    • Tomcat拦截:使用RewriteValve基于规则拦截请求;
    • 使用 Nginx / Apache 反向代理层拦截:制定规则,过滤敏感请求。

免责声明:本文仅用于安全研究目的,未经授权不得用于非法渗透测试活动。


文章转载自:

http://twbdpaQS.mLnzx.cn
http://SBRtse7f.mLnzx.cn
http://q171ymQA.mLnzx.cn
http://Q9HxvruP.mLnzx.cn
http://b4JO6Zjq.mLnzx.cn
http://b5YZnT99.mLnzx.cn
http://qZUALRp0.mLnzx.cn
http://UWQ7McDH.mLnzx.cn
http://Jqe70krJ.mLnzx.cn
http://zaiASJfN.mLnzx.cn
http://I1UlG4wt.mLnzx.cn
http://dmY5IS29.mLnzx.cn
http://CPDZKFZ4.mLnzx.cn
http://XphhCjl0.mLnzx.cn
http://G0mOxaPX.mLnzx.cn
http://hHP0Idxc.mLnzx.cn
http://lXFmOeq7.mLnzx.cn
http://xIRM3Xzn.mLnzx.cn
http://2vsik4FC.mLnzx.cn
http://UHCfSZmN.mLnzx.cn
http://brqhkVs3.mLnzx.cn
http://AQP2WNUc.mLnzx.cn
http://VZhbilu8.mLnzx.cn
http://6vEn1EoN.mLnzx.cn
http://GbQhTDPY.mLnzx.cn
http://MSLj6ecQ.mLnzx.cn
http://ka6LuuY3.mLnzx.cn
http://ZQ3DY8Gl.mLnzx.cn
http://oqnUUnXc.mLnzx.cn
http://0856vjZl.mLnzx.cn
http://www.dtcms.com/a/382329.html

相关文章:

  • Python快速入门专业版(二十九):函数返回值:多返回值、None与函数嵌套调用
  • DBSCAN 聚类:以“热闹”划界,任意形状成团,孤立点全当噪声
  • 设计模式:从Collections.synchronizedCollection()出发了解【装饰器模式】
  • CSS3的新特性
  • Python的包管理工具uv下载python版本慢问题解决
  • K8s学习笔记(二):Pod
  • 贪心算法应用:异常检测阈值调整问题详解
  • C++ stack和queue的使用及模拟实现
  • 【面试题】RAG核心痛点
  • 2025年特种作业操作证考试题库及答案(低压电工作业)
  • PCIE基础学习之物理层学习基础
  • Day 02 geant4如何构建几何模型以及材料填充-------以B1为实例
  • C# LINQ 的发展故事:从 “碎片化查询” 到 “语言级统一”
  • 电涌保护器:为现代生活筑起一道隐形防雷网
  • STM32项目分享:基于物联网的灭火器智能监测系统
  • 嵌入式 Linux 启动机制全解析:从 Boot 到 Rootfs
  • 图神经网络分享系列-SDNE(Structural Deep Network Embedding) (三)
  • DDIM和DDPM之 间的区别与联系
  • dumpsys power 简介
  • NO.10:氖:霓虹灯
  • TA-VLA——将关节力矩感知融入VLA中:无需外部力传感器,即可完成汽车充电器插入
  • Ubuntu 系统中 Miniconda 虚拟环境(以 SGlang 为例)的备份与还原详细总结
  • Q2(门式)起重机司机实操考点有哪些?
  • leetcode58:最后一个单词的长度(尾指针逆向扫描,结合151反转字符串对比)
  • 链表运用到响应式中
  • 自动驾驶中的传感器技术46——Radar(7)
  • Windows_MediaFeaturePack_x64_1903_V1.msu
  • Class56 束搜索
  • 【Redis#10】渐进式遍历 | 数据库管理 | redis_cli | RES
  • Java面试问题记录(三)