CVE-2026-107850
Executive Summary
CVE-2026-107850 is a broken access control vulnerability (CWE-639 — Authorization Bypass Through User-Controlled Key) in Contao, an open-source CMS. From version 5.7.1 through 5.7.12, a misconfiguration in core-bundle/config/services.yaml registers the preview access voter under the class name Contao\CoreBundle\Security\Voter\DataContainer\PreviewAccessVoter, while the shipped class is actually PreviewVoter. Symfony consequently omits voter autoconfiguration and removes the private service, so PreviewVoter::hasAccess() is never invoked and ownership checks on preview links are not enforced.
The impact: a non-admin backend user holding the preview_link module permission can enumerate every tl_preview_link record, obtain signed share URLs created by other users, and use those URLs to view unpublished pages via showUnpublished, despite lacking the corresponding page permission. The vulnerability is rated CVSS 4.3 (Moderate), requiring network access and low privileges. It is fixed in 5.7.12.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Technical Analysis
Contao is a PHP-based content management system built on the Symfony framework. Symfony's security subsystem uses "voters" — classes implementing VoterInterface — to decide whether a given user is granted access to a resource or attribute. Voters must be registered as services and tagged (security.voter) so the framework's access decision manager knows to consult them. Symfony's autoconfiguration normally applies this tag automatically when a class implements the voter interface, provided the service definition references the correct class name.
The defect in this CVE is a class-name mismatch in service registration. The file core-bundle/config/services.yaml registers the preview access voter as:
Contao\CoreBundle\Security\Voter\DataContainer\PreviewAccessVoter
However, the class actually shipped in the codebase is Contao\CoreBundle\Security\Voter\DataContainer\PreviewVoter (the difference being the Access infix and the associated namespace path). Because the service definition points to a non-existent class identifier:
- Symfony cannot autoconfigure the service (autoconfiguration inspects the real class' implemented interfaces, and the referenced class name does not resolve).
- The
security.votertag is not applied, so the access decision manager never registers the voter. - If the definition is marked private and is never referenced by another service, Symfony's container compilation removes the private, unused service entirely.
The net result is that the ownership-enforcement logic inside PreviewVoter::hasAccess() is dead code — it is never called during an access decision. The voter is intended to verify that the backend user requesting access to a tl_preview_link record actually owns (or is otherwise authorized for) that record. With the voter absent, that ownership check degrades to whatever default access policy applies, which — for a user possessing the preview_link module — permits listing all records rather than only their own.
Two resources are implicated:
tl_preview_link— the database table storing preview link records. Each record contains a signed share URL. Without the voter, a user with module access can enumerate all records, including those created by other users.showUnpublished— the front-end parameter/behavior that controls whether unpublished pages are rendered. Signed preview URLs are the mechanism by which unpublished content is intended to be previewed. Because share URLs created by other users are signed with the site's secret, they remain valid and can be presented by anyone who obtains them.
The vulnerability class is CWE-639, an authorization bypass through a user-controlled key: the user-controlled key here is the record identifier (or the set of records) exposed via the tl_preview_link listing, and the missing voter is what would normally bind that key to the requesting user's identity.
Notably, the advisory explicitly states that editing or deletion of foreign links is not established. The confirmed impact is read-only unauthorized exposure of preview links and the unpublished content they unlock.
The relevant root-cause pattern — a security control (voter) silently failing to load due to a class-name/reference mismatch — is a class of "fails-open" configuration error. There is no indication of attacker-triggered memory corruption, injection, or remote code execution.
Exploitation Scenarios
No public PoC or exploit code is available for CVE-2026-107850. The attack chain below is inferred from the vulnerability class (CWE-639, missing authorization voter) and the specific behaviors described in the NVD entry. It relies only on components named in the description: the preview_link module, the tl_preview_link table, signed share URLs, and the showUnpublished behavior.
1) Reconnaissance / target identification.
The attacker must already hold a low-privileged backend account — a non-admin user. The victim target is a Contao 5.7.1–5.7.12 installation. The attacker confirms that the backend account has the preview_link module permission assigned. No front-end interaction or authentication bypass is needed to reach the backend; the attacker logs in with legitimate low-privilege credentials (or credentials obtained through separate means).
2) Triggering the flaw (missing voter).
Because the registered voter class name does not resolve, the PreviewVoter is not autoconfigured and (as a private service) is dropped at container compile time. The attacker navigates to the preview-link management interface exposed by the preview_link module. The intended access decision — "does this user own this tl_preview_link record?" — is never evaluated because the voter that would perform it is absent. The access check therefore falls through to a permissive default, and the list of preview links is returned in full, including records created by other backend users.
3) Payload / execution method (vulnerability-class level).
There is no injection payload in the classic sense. The "payload" is the abuse of a legitimate feature: the attacker reads the signed share URLs embedded in the tl_preview_link records belonging to other users. These URLs are cryptographically signed (by the site's signing secret) and therefore remain valid regardless of which user presents them. The attacker simply opens each signed URL in a browser, appending or relying on the showUnpublished behavior so that unpublished pages are rendered rather than suppressed. Because the signature is intact and no ownership voter intervenes, the request is served.
4) Post-exploitation impact. The attacker views unpublished pages — content not yet publicly released — without holding the corresponding page permission. This exposes pre-publication information: draft content, embargoed announcements, unreleased pricing, internal notes rendered on preview pages, and similar material. The advisory does not establish that the attacker can edit or delete foreign preview links or the underlying content; the confirmed impact boundary is unauthorized read/view access.
It is acceptable to state plainly: no public PoC is available; the above is reconstructed from the vulnerability class and the description. No specific function, endpoint, or parameter beyond those in the NVD description (PreviewVoter::hasAccess(), tl_preview_link, showUnpublished, preview_link) should be assumed.
Impact Assessment
The CVSS v3.1 base score is 4.3 (Moderate) with vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N. Decomposed:
- Attack Vector (Network): the attacker interacts with the CMS over the web.
- Attack Complexity (Low): no special conditions beyond holding the
preview_linkmodule permission are required; the missing voter fails open automatically. - Privileges Required (Low): a non-admin authenticated backend account is needed. This is the principal constraint.
- User Interaction (None): the attacker does not need a victim to click anything.
- Scope (Unchanged): the failure stays within the CMS's own security authority.
- Confidentiality (Low): exposure is limited to preview links and the unpublished pages they reveal — sensitive but bounded.
- Integrity / Availability (None): the advisory establishes no write or denial-of-service impact.
Business impact. Organizations running Contao 5.7.1–5.7.12 with multiple backend editors are affected. Any editor account provisioned with the preview_link module — including contractors, agencies, or compromised low-privilege accounts — can read unpublished content created by other editors. For sites using Contao for staging press releases, product launches, legal notices, or other embargoed material, this creates a pre-publication information-disclosure risk and potential business/reputational harm.
Blast radius. The vulnerability is not remotely exploitable without credentials; it is an authenticated, low-privilege horizontal privilege escalation. The blast radius is therefore limited to the pool of authored preview links and unpublished pages on the affected instance. It does not, per the advisory, extend to content modification or deletion. Because the flaw is a fail-open authorization control, every affected installation with the module enabled and shared among users should be considered exposed until patched.
Detection Guidance
Because this vulnerability involves a legitimate feature being used without an authorization check, detection focuses on anomalous access to preview-link records and signed preview URLs by low-privilege accounts.
Sigma Rule
title: Contao PreviewAccessVoter Missing - Unauthorized Preview Link Access
id: 3f8a1c62-7b4d-4e9a-9d21-cve2026107850
status: experimental
description: Detects access to the tl_preview_link management area or showUnpublished preview URLs by backend users, consistent with CVE-2026-107850 authorization bypass.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-107850
author: Threat Intelligence
date: 2026/01/01
logsource:
product: contao
service: backend
detection:
selection_preview_module:
request_uri|contains:
- 'preview_link'
- 'tl_preview_link'
selection_show_unpublished:
request_uri|contains:
- 'showUnpublished'
condition: selection_preview_module or selection_show_unpublished
fields:
- user
- request_uri
- src_ip
falsepositives:
- Legitimate editors accessing their own preview links
level: medium
YARA Rule
rule CVE_2026_107850_Contao_PreviewVoter_Misconfig
{
meta:
description = "Detects the contao services.yaml voter registration mismatch for CVE-2026-107850"
author = "Threat Intelligence"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2026-107850"
date = "2026-01-01"
strings:
$voter_reg = "PreviewAccessVoter" ascii
$voter_real = "PreviewVoter" ascii
$svc_file = "services.yaml" ascii
$dc_ns = "Security\\Voter\\DataContainer" ascii
condition:
($svc_file and $voter_reg and $dc_ns) or ($voter_reg and $voter_real)
}
Suricata IDS Rule
alert http any any -> any any (msg:"CVE-2026-107850 Contao Preview Link Authorization Bypass Attempt"; flow:established,to_server; http.method; content:"GET"; http.uri; content:"preview_link"; nocase; http.uri; content:"showUnpublished"; nocase; classtype:attempted-recon; reference:cve,2026-107850; sid:2026107850; rev:1;)
Elastic Detection
sequence by user.name
[ authentication where event.action == "user_login" and event.outcome == "success" ]
[ http where url.path : "*preview_link*" or url.query : "*showUnpublished*" ]
Splunk SPL Query
index=web sourcetype=contao_access
| search uri="*preview_link*" OR uri="*showUnpublished*"
| stats count min(_time) as first_seen max(_time) as last_seen
values(uri) as uris by src_ip, user
| where count > 5
| sort - count
Wazuh OSSEC Rule
<group name="web,contao,">
<rule id="107850" level="8">
<decoded_as>web-accesslog</decoded_as>
<url>preview_link</url>
<description>CVE-2026-107850: Contao preview_link access - possible authorization bypass</description>
</rule>
<rule id="107851" level="8">
<decoded_as>web-accesslog</decoded_as>
<url>showUnpublished</url>
<description>CVE-2026-107850: Contao showUnpublished preview access detected</description>
</rule>
</group>
Mitigation & Patch Guidance
Patch confidence: 2/4 (CLAIMED — a single source, the GHSA advisory, claims a fix; not independently confirmed).
Immediate mitigations. Until the patch is verified in your environment, reduce exposure by removing the
preview_linkmodule permission from any backend user who does not strictly require it. Where the module is not needed for operational workflows, disable it entirely to eliminate the attack surface. Restrict backend access (/contao) to trusted networks or VPN, and audittl_preview_linkrecords for stale or broadly shared links. Note: the advisory does not list alternative workarounds, so these are compensating controls rather than vendor-endorsed fixes.Patch / upgrade steps. Upgrade Contao to version 5.7.12, which the advisory states fixes the issue. After upgrading, verify that the service definition in
core-bundle/config/services.yamlreferences the actual shipped class name so that Symfony autoconfigures the voter. Confirm thesecurity.votertag is present on the service and that the private service is retained at container compile time; test by confirming that a non-admin user withpreview_linkcan only see their owntl_preview_linkrecords. Because patch confidence is only 2/4, validate the fix behaviorally rather than relying solely on the version number.Long-term hardening. Adopt a least-privilege model for backend module permissions. Add automated tests that assert security voters are actually registered in the compiled Symfony container — a "fails-open" configuration test would have caught this class-name mismatch. Monitor
services.yamlfor references to non-existent classes as part of CI (static analysis / container-lint). Apply the principle of deny-by-default to access decision logic so that a missing voter results in denial, not permissive fall-through. Maintain prompt patching for CMS platforms, which are frequent targets, and review embargoed-content handling for additional exposure controls.
References
- NVD — CVE-2026-107850: https://nvd.nist.gov/vuln/detail/CVE-2026-107850
- GitHub Security Advisory (GHSA) — Contao core-bundle preview voter misconfiguration (source of the patch claim); reference the advisory linked from the CVE record.
- Contao CMS official release notes / security advisories: https://contao.org/en/news.html
- CWE-639 — Authorization Bypass Through User-Controlled Key: https://cwe.mitre.org/data/definitions/639.html
- CISA Known Exploited Vulnerabilities catalog — status: NOT listed (no known exploitation in the wild as of this report). No public PoC, exploit code, or Nuclei template was identified. No threat actor campaign is associated with this CVE in the available data.
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.