TL;DR
I got CVE-2026-54256 in Winter CMS.
This one lived in the backend FileUpload widget.
The bug was not about file contents. It was about what happened after the widget trusted an attacker-controlled file_id and resolved it globally instead of scoping it to the current relation.
So any authenticated backend user who could reach a form containing a file upload field could target another attachment record and modify metadata that did not belong to them.
The built-in My Account avatar form was enough to reach the vulnerable widget path, which made the attack surface much wider than “admins editing content.”
The bug
The vulnerable lookup sat in FileUpload::getFileRecord().
According to the advisory, the old code effectively did this:
$this->getRelationModel()->find(post('file_id'))That is the whole problem.
The posted file_id was resolved against the global system_files table without checking whether the file actually belonged to:
- the widget’s own relation
- the current parent record
- the current deferred-binding session
So once an attacker could send requests to that widget, the only thing they really needed was a valid attachment ID.
And those IDs were easy to enumerate because they were sequential integers.
What an attacker could do
The advisory confirmed two concrete write impacts.
An authenticated backend user could:
- modify another attachment’s title and description via
onSaveAttachmentConfig - change another attachment’s
sort_orderviaonSortAttachments
The second bug was especially clean because the posted IDs were passed straight into an unscoped sortable update.
The same unscoped lookup path was also reachable from:
onLoadAttachmentConfigonSaveAttachmentConfigonRemoveAttachment
So even though the confirmed impact was framed around unauthorized integrity changes, the unsafe object resolution itself was shared across multiple widget actions.
Why I liked it
I like bugs like this because they come from a very normal developer assumption:
the widget is already attached to the right model, so the posted file ID must also belong there
That assumption is fine right up until somebody stops using the UI honestly.
Once the request is attacker-controlled, a global find(post('file_id')) is just an IDOR waiting to happen.
And because Winter stores all these attachments in the shared System\Models\File model backed by system_files, the bug was not limited to avatars. One widget path could reach attachments tied to completely different records.
Why the My Account path mattered
The advisory called out a detail I liked a lot: the built-in My Account avatar field was enough.
That means the issue did not depend on some unusual plugin or privileged editorial flow.
A backend account with basically any level of access could reach a form containing the widget and start testing file_id tampering from there.
The attack still needed:
- an authenticated backend session
- valid CSRF token handling
But it did not need site-admin level access.
That keeps the bug firmly in low-privileged authenticated territory, which is the interesting place for this kind of issue.
The patch
The fixed version changed the widget to scope lookups through the actual relation, including deferred-bound files in the current session.
The advisory described the fix like this:
$this->getRelationObject()->withDeferred($this->sessionKey)->find(post('file_id'))That is the right shape.
Instead of treating the posted ID as a global file record reference, the widget now asks:
does this file ID belong to this relation in this widget context?
The sort path was fixed the same way in spirit: posted IDs are filtered down to IDs that actually belong to the relation before setSortableOrder() is called.
That is exactly the boundary the old code was missing.
Versions and severity
The advisory data you shared lists:
- CVE:
CVE-2026-54256 - Severity:
Moderate - CVSS:
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N - Weaknesses:
CWE-284,CWE-639 - Affected:
<= 1.2.12 - Patched:
1.2.13
The fix landed in:
- commit
9cb0ae5f9d837db141ab111c6a7de8eed9603d25
The boundary I kept
I kept the write-up aligned with the confirmed impact from the advisory.
This post is about:
- authenticated backend access
- attacker-controlled
file_id - cross-user or cross-record attachment targeting
- unauthorized metadata and ordering changes
I did not stretch it into arbitrary file read or arbitrary file overwrite, because that is not what the advisory confirmed.
The useful bug here is already clear enough without inflating it.
Reference