Calibre-web 0.6.21 - Stored XSS
# Exploit Title: Stored XSS in Calibre-web
# Date: 07/05/2024
# Exploit Authors: Pentest-Tools.com (Catalin Iovita & Alexandru Postolache)
# Vendor Homepage: (https://github.com/janeczku/calibre-web/)
# Version: 0.6.21 - Romesa
# Tested on: Linux 5.15.0-107, Python 3.10.12, lxml4.9.4
# CVE: CVE-2024-39123
## Vulnerability Description
Calibre-web 0.6.21 is vulnerable to a Stored Cross-Site Scripting (XSS) vulnerability. This vulnerability allows an attacker to inject malicious scripts that get stored on the server and executed in the context of another user's session.
## Steps to Reproduce
1. Log in to the application.
2. Upload a new book.
3. Access the Books List functionality from the `/table?data=list&sort_param=stored` endpoint.
4. In the `Comments` field, input the following payload:
<a href=javas%1Bcript:alert()>Hello there!</a>
4. Save the changes.
5. Upon clicking the description on the book that was created, in the Book Details, the payload was successfully injected in the Description field. By clicking on the message, an alert box will appear, indicating the execution of the injected script. Calibre-web 0.6.21 — Stored Cross‑Site Scripting (CVE‑2024‑39123) — Analysis, Impact, and Mitigation
Calibre‑web is a popular web frontend for managing ebook libraries. In version 0.6.21 a stored Cross‑Site Scripting (XSS) vulnerability (CVE‑2024‑39123) was reported that allows an attacker with the ability to create or modify certain metadata fields to store HTML/JavaScript which can later execute in another user's browser when viewing the affected UI. This article explains the vulnerability class, likely impact, safe detection strategies, and practical mitigation and remediation measures for operators and developers.
What is Stored XSS (brief)
Stored XSS occurs when an application accepts untrusted input, persists it on the server (database, files), and later renders it into a web page without proper output encoding or sanitization. When a victim's browser renders that stored content, any embedded script runs in the victim's origin and can perform actions with the victim's privileges (session theft, UI manipulation, CSRF amplification, etc.).
Vulnerability Summary (high level)
- Type: Stored Cross‑Site Scripting (persistent XSS).
- Affected component: content fields used to display book metadata or descriptions in the web UI.
- Affected version: Calibre‑web 0.6.21 (as reported).
- CVE: CVE‑2024‑39123.
- Root cause: user‑supplied data rendered in HTML contexts without robust sanitization or proper output encoding.
Why it matters — possible impacts
- Account compromise: an attacker can retrieve session cookies or tokens if the application does not use HttpOnly or secure cookie attributes.
- Privilege escalation inside the application UI: malicious scripts can perform actions on behalf of the user (e.g., change settings, trigger uploads) if UI flows rely on client‑side logic.
- Malware delivery / phishing: an attacker can present deceptive UI elements or redirect users to malicious content.
- Supply‑chain or multi‑tenant risk: if the server is used by multiple users, one compromised account could affect many users.
Safe detection and verification (responsible testing)
When verifying a stored XSS, use non‑destructive techniques and coordinate with asset owners. Avoid publishing or sharing actual exploit payloads. Recommended safe checks:
- Search persisted metadata for unexpected markup or attribute characters (for example, payloads often include angle brackets or event attributes).
- Use a low‑impact note such as a simple benign marker string (e.g., "XSS_TEST_MARKER") and confirm that it appears verbatim when viewing the UI; if it is rendered as HTML rather than escaped text, that indicates output encoding is missing.
- Use automated scanners that report potential XSS without executing arbitrary scripts in production, or test in isolated staging environments.
- Review server‑side templating and rendering code paths that output user‑provided fields into HTML pages.
Recommended remediation steps
- Apply vendor fixes and updates as the primary remediation. If an official patch or newer version is available, upgrade the deployment to the patched release immediately.
- If an immediate upgrade is not possible, implement temporary hardening layers: output encoding, input sanitization, Content Security Policy (CSP), and strengthen cookie flags.
- Audit persisted content for malicious markup and remove or neutralize entries that contain unexpected HTML. Use automated sanitization before rendering.
| Item | Recommendation |
|---|---|
| Primary fix | Upgrade Calibre‑web to the vendor‑released patched version |
| Short‑term mitigation | Sanitize outputs, enforce CSP, set secure cookie attributes, and audit stored metadata |
| Long‑term | Adopt output encoding libraries, automated security tests, and review templating contexts |
Defensive code examples (server‑side sanitization)
Below is a safe pattern for cleaning user‑supplied HTML in a Python web application using the bleach library. The code demonstrates restricting allowed tags and attributes so stored content cannot include scripts or dangerous attributes.
import bleach
# Define a minimal allowed set of tags and attributes for description fields
ALLOWED_TAGS = ['b', 'i', 'em', 'strong', 'p', 'br', 'ul', 'ol', 'li', 'a']
ALLOWED_ATTRIBUTES = {'a': ['href', 'title', 'rel', 'target']}
def sanitize_description(raw_html):
cleaned = bleach.clean(
raw_html,
tags=ALLOWED_TAGS,
attributes=ALLOWED_ATTRIBUTES,
protocols=['http', 'https', 'mailto'],
strip=True
)
# Optionally, force safe rel and target on links to avoid tabnabbing
cleaned = bleach.linkify(cleaned, parse_email=True, callbacks=[bleach.callbacks.nofollow])
return cleaned
Explanation: This function uses bleach.clean to strip or escape any tags and attributes not in the explicitly allowed lists. Linkification converts plaintext URLs into safe anchor tags and enforces rel="nofollow" to reduce risks like tabnabbing. Use this when storing or before rendering user‑provided HTML.
Template‑level escaping and safe rendering
Most templating engines (Jinja2, Django templates) escape variables by default unless explicitly marked safe. Ensure that templates do not disable autoescaping for user data and that only sanitized HTML is ever marked safe.
{# Jinja2 template example #}
{# BAD: directly rendering user input (if input may contain markup) #}
{{ book.description }}
{# GOOD: ensure book.description has been sanitized server-side or is escaped #}
{{ book.description | e }}
{# If book.description is intentionally sanitized and safe, mark it explicitly #}
{{ book.description | safe }}
Explanation: The first example risks rendering raw markup. The second forces escaping so any HTML is shown as text. The third is acceptable only if the application has sanitized book.description with a trusted sanitizer like bleach before storing or rendering.
Content Security Policy (CSP) as a mitigation layer
While CSP is not a replacement for proper input/output handling, it reduces risk by restricting where scripts can run and where resources can be loaded from.
# Example HTTP header to add to responses (recommended CSP baseline)
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none';
Explanation: This CSP restricts script execution to the same origin and blocks plugin objects. For applications that need third‑party scripts, adjust the policy carefully. CSP helps mitigate exploitation even if stored HTML reaches a page, but it should be part of layered defenses, not the only control.
Cookie hardening and additional controls
- Ensure session cookies are set with HttpOnly, Secure, and SameSite attributes to reduce risk of theft via script.
- Use server‑side authorization checks for actions initiated from the UI so that a malicious script cannot escalate privileges without valid credentials.
- Rate‑limit and monitor modification endpoints to make mass injection harder.
Detection, logging, and cleanup guidance
Operators should search stored metadata for HTML or event attributes and remove suspicious entries. A benign discovery marker test can reveal whether user input is being escaped or rendered as HTML. Maintain logs of metadata changes (who changed what and when) and consider rolling back or sanitizing entries created by untrusted accounts.
Incident response checklist (if you detect exploitation)
- Isolate affected accounts and reset sessions/tokens for users who may have been targeted.
- Remove malicious stored content from the database and ensure sanitizers are applied going forward.
- Patch the application to the vendor’s fixed version; if unavailable, apply the mitigations described above.
- Review access logs for unusual activity, and notify impacted users if sensitive data may have been exposed.
Developer best practices to prevent stored XSS
- Apply output encoding appropriate to the context (HTML, attribute, JavaScript, CSS, URL).
- Sanitize or whitelist user input where HTML is allowed; prefer a whitelist approach.
- Keep frameworks, dependencies, and libraries up to date.
- Add automated security tests that detect unescaped user content in rendered HTML.
- Adopt a secure deployment baseline: CSP, cookie flags, secure headers, and a WAF tuned to reduce common injection patterns.
Final notes
CVE‑2024‑39123 highlights the importance of treating all user‑supplied content as untrusted and ensuring robust output encoding and sanitization. Operators should upgrade Calibre‑web to a patched release as soon as possible and apply the layered mitigations described above to reduce exposure while performing remediation.