
PoC: changedetection.io settings blind-merge mass assignment (CVE-2026-71204, Medium 6.3)
Product: dgtlmoon/changedetection.io — v0.55.7
Files: changedetectionio/blueprint/settings/__init__.py, changedetectionio/forms.py
CWE: CWE-284 — Improper Access Control
CVSS 3.1: AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:L — 6.3 (Medium)
CNA: Turan Security · CVE record
changedetection.io's /settings save handler builds an update dict from
form.data['application'] and blind-merges it into the stored application settings via
.update(). Because HTML checkboxes only appear in submitted form data when checked, a
crafted POST that simply omits a security-relevant checkbox field (such as the one
enforcing API key requirement) is indistinguishable, at the handler level, from the user
having unchecked it in the UI — the blind .update() silently flips that setting off.
A request that never explicitly sets api_access_token_enabled (or equivalent) to false —
it just leaves the field out of the POST body — results in the application settings being
merged with that protection effectively disabled, without any explicit confirmation step or
audit signal distinguishing "user unchecked this" from "field wasn't sent."
/settings page normally./settings with the
api_access_token_enabled field (or whichever protected boolean setting is being tested)
omitted entirely from the body, while including only unrelated fields:
POST /settings HTTP/1.1
Content-Type: application/x-www-form-urlencoded
application-some_other_field=value
form.data['application'] → .update() merge in the settings save handler
treats the missing field the same as an explicit "off," silently disabling the protection
without the user (or an auditor reviewing the request) seeing an explicit toggle-off signal.Using a blind dict .update() from raw form data conflates "field absent from POST" with
"field explicitly set to false" for checkbox-backed settings — HTML forms never submit
unchecked checkboxes, so this pattern is unsafe for any settings field with security
implications.
Explicitly enumerate expected boolean settings fields and default absent ones to their
current stored value (not False), or use a form library that distinguishes "not submitted"
from "submitted as unchecked" and validates security-relevant toggles explicitly rather than
via blind dict merge.