CVE-2026-29038: changedetection.io: Reflected XSS in RSS Tag Error Response

Published Mar 4, 2026
·
Updated

A reflected cross-site scripting (XSS) vulnerability was identified in the /rss/tag/ endpoint of changedetection.io. The taguuid path parameter is reflected directly in the HTTP response body without HTML escaping. Since Flask returns text/html by default for plain string responses, the browser parses and executes injected JavaScript.

This vulnerability persists in version 0.54.1, which patched the related XSS in /rss/watch/ (CVE-2026-27645 / GHSA-mw8m-398g-h89w) but did not address the identical pattern in the tag RSS endpoint.

Package

- Ecosystem: pip - Package: changedetection.io - Affected versions: <= 0.54.1 - Patched versions: (none yet)

Severity Moderate - CVSS 6.1 CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

Details File: changedetectionio/blueprint/rss/tag.py Line: 36 Source: tag.py @ 1d72716

The taguuid parameter from the URL path is interpolated into the response body using an f-string with no escaping:

python tag = datastore.data['settings']['application'].get('tags', {}).get(taguuid) if not tag: return f"Tag with UUID {taguuid} not found", 404 # ← No escaping, Content-Type: text/html

Flask's default Content-Type for plain string responses is text/html; charset=utf-8, so any HTML/JavaScript injected via {taguuid} is rendered and executed by the browser.

Relationship to CVE-2026-27645

CVE-2026-27645 (GHSA-mw8m-398g-h89w) addressed the identical vulnerability pattern in /rss/watch/ (singlewatch.py). The fix applied in v0.54.1 patched that endpoint but did not fix the same pattern in /rss/tag/ (tag.py). Testing confirms:

- /rss/watch/ on v0.54.1 — Returns generic 404 page, XSS no longer triggers ✅ - /rss/tag/ on v0.54.1 — XSS payload still fires, vulnerability confirmed ❌

Attack Vector

The attack requires a valid RSS access token, which is a 32-character hex string exposed in the <link> HTML tag on the homepage without authentication:

1. Attacker visits the target's homepage (if unauthenticated) and extracts the RSS token from the <link> tag 2. Crafts a malicious URL: http://target:5000/rss/tag/<img src=x onerror=alert(document.cookie)>?token=EXTRACTEDTOKEN 3. Sends the link to a victim who has an active session on the changedetection.io instance 4. When the victim clicks the link, the server responds with: Tag with UUID <img src=x onerror=alert(document.cookie)> not found 5. The browser renders the <img> tag, the onerror fires, and JavaScript executes in the victim's session context

Proof of Concept

Request

http GET /rss/tag/%3Cimg%20src%3Dx%20onerror%3Dalert(document.domain)%3E?token=60b83b06df98b24c66367bc3d233105b HTTP/1.1 Host: localhost:5000

Response

http HTTP/1.1 404 NOT FOUND Content-Type: text/html; charset=utf-8

Tag with UUID <img src=x onerror=alert(document.domain)> not found

The XSS payload is reflected unescaped in an HTML response. The browser executes alert(document.domain) and displays "localhost", confirming JavaScript execution.

Tested on: changedetection.io v0.54.1 (Docker, localhost, Feb 25, 2026)

https://github.com/user-attachments/assets/6db07f6a-6df8-48a7-a597-9f39dfa1bb29

Impact

- Session cookie theft via document.cookie exfiltration - Account takeover if session cookies lack the HttpOnly flag - Phishing via crafted links that appear to originate from a trusted changedetection.io instance - Low exploitation barrier - the RSS token is obtainable without authentication from the homepage <link> tag - Widespread exposure - prior scanning of internet-facing instances (during CVE-2026-27645 research) identified 500+ publicly accessible deployments

Suggested Fix

Escape the taguuid parameter before reflecting it in the response, or set the Content-Type to text/plain:

Option A: HTML Escape (Recommended)

python from markupsafe import escape

if not tag: return f"Tag with UUID {escape(taguuid)} not found", 404

Option B: Set Content-Type to text/plain

python from flask import makeresponse

if not tag: resp = makeresponse(f"Tag with UUID {taguuid} not found", 404) resp.headers['Content-Type'] = 'text/plain; charset=utf-8' return resp

Credits

- Roberto Nunes (@Akokonunes) - Reporter - neo-ai-engineer (@neo-ai-engineer) - Reporter

References - Related advisory: GHSA-mw8m-398g-h89w (CVE-2026-27645) - Vulnerable source: tag.py @ 1d72716

Other sources

changedetection.io is a free open source web page change detection tool. Prior to version 0.54.4, there is a reflected cross-site scripting (XSS) vulnerability identified in the /rss/tag/ endpoint of changedetection.io. The taguuid path parameter is reflected directly in the HTTP response body without HTML escaping. Since Flask returns text/html by default for plain string responses, the browser parses and executes injected JavaScript. This issue has been patched in version 0.54.4.

MITRE

Affected Software

2 affected componentsFixes available
pip/changedetection.io<0.54.4
0.54.4
Webtechnologies Changedetection<0.54.4

Event History

Mar 4, 2026
Advisory Published
via GitHub·08:58 PM
Data Sourced
via GitHub·08:58 PM
DescriptionSeverityWeaknessAffected Software
Mar 6, 2026
CVE Published
via MITRE·06:53 AM
Data Sourced
via MITRE·06:53 AM
DescriptionSeverityWeakness
Data Sourced
via NVD·07:16 AM
DescriptionSeverityWeakness
Data Sourced
via NVD·07:16 AM
RemedyAffected Software
May 22, 58164
Event
via FIRST·07:05 PM
Free Weekly Intel

Don't miss critical vulnerabilities

Join thousands of security professionals who receive our weekly digest of trending CVEs, zero-days, and exploited vulnerabilities.

No spam. Unsubscribe anytime.

Frequently Asked Questions

1

What is the severity of CVE-2026-29038?

CVE-2026-29038 is classified as a high severity vulnerability due to the potential for reflected cross-site scripting attacks.

2

How do I fix CVE-2026-29038?

To fix CVE-2026-29038, you should upgrade to version 0.54.4 or later of the changedetection.io package.

3

What type of vulnerability is CVE-2026-29038?

CVE-2026-29038 is a reflected cross-site scripting (XSS) vulnerability.

4

Which endpoint is affected by CVE-2026-29038?

The /rss/tag/ endpoint is the specific endpoint affected by CVE-2026-29038.

5

What impact does CVE-2026-29038 have on users?

CVE-2026-29038 can allow attackers to execute scripts in the context of a user's browser, potentially leading to data theft or session hijacking.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203