# SpringBlade XXE 漏洞总结 ## 漏洞概述 SpringBlade 的 `blade-report` 模块(UReport2 集成)存在 XML 外部实体注入(XXE)漏洞。攻击者可通过 `POST /ureport/designer/saveReportFile` 接口上传包含恶意 `DOCTYPE` 声明的 XML 文件,再通过 `GET /ureport/designer/loadReport` 接口触发解析,从而读取服务器本地文件或发起 SSRF 请求。 **受影响版本:** ≤ 4.8.0 **受影响组件:** `blade-report` 模块 **触发端点:** - 注入点:`POST /ureport/designer/saveReportFile` - 触发点:`GET /ureport/designer/loadReport` --- ## 影响范围 1. **任意文件读取**:通过注入 ``,可读取服务器权限范围内的任意文件(如 `win.ini`、配置文件、密钥等)。 2. **服务端请求伪造(SSRF)**:通过注入 ``,可强制服务器访问内网或外部主机。 3. **拒绝服务(DoS)**:通过递归实体定义(Billion Laughs 攻击),可耗尽服务器内存和 CPU。 --- ## 修复方案 ### 立即缓解措施 - 若生产环境无需报表设计功能,应在网关或 Web 过滤器层阻止对所有 `/ureport/designer/*` 端点的访问。 ### 代码级修复 1. **禁用外部实体解析**:在创建 `SAXParserFactory` 时配置以下安全特性: ```java SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); factory.setFeature("http://xml.org/sax/features/external-general-entities", false); factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); factory.setXIncludeAware(false); ``` 2. **保存前校验 XML 内容**:在存储报表前,验证 XML 不包含 `DOCTYPE`、` ]> ``` **保存后数据库中的 XML(经 MySQLProvider.saveReport() 存储):** ```xml ]> &xxe; &xxe; &xxe; ``` ### 步骤2:触发解析(loadReport 接口) 请求示例: ```http GET http://localhost:8080/ureport/designer/loadReport?file=xxx HTTP/1.1 Host: localhost:8080 ... ``` ### 步骤3:验证结果 响应体中返回的 XML 包含被读取的文件内容(如 `win.ini` 内容嵌入在 `` 单元格中)。 --- ## 根因分析 漏洞存在于 UReport2 的 `ReportParser` 类中,其创建 `SAXParserFactory` 时未禁用外部实体解析和 DOCTYPE 声明: ```java public class ReportParser { public ReportDefinition parse(InputStream inputStream) { try { // VULNERABILITY: SAXParserFactory created with default settings // External entities and DOCTYPE declarations are enabled by default SAXParserFactory factory = SAXParserFactory.newInstance(); // Missing security hardening: // factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); // factory.setFeature("http://xml.org/sax/features/external-general-entities", false); // factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); SAXParser parser = factory.newSAXParser(); ReportParserHandler handler = new ReportParserHandler(); // XML is parsed here - external entities are resolved automatically // If the XML contains , // the parser will read the file and substitute its contents into the document parser.parse(inputStream, handler); return handler.getReportDefinition(); } catch (Exception e) { throw new ReportException(e); } } } ``` **攻击链:** 1. **注入**:攻击者调用 `saveReportFile`,传入含恶意 DOCTYPE 的 XML,未经校验直接存入数据库。 2. **触发**:攻击者调用 `loadReport`,从数据库取回 XML 并传入 `ReportParser.parse()`,解析时触发外部实体解析。 3. **外泄**:解析后的实体内容(如文件内容)嵌入到报表结构中,随响应返回给攻击者。