Wagtail CMS 6.4.1 is vulnerable to a Stored Cross-Site Scripting (XSS) in the document upload functionality. Attackers can inject malicious code inside a PDF file. When a user clicks the document in the CMS interface, the payload executes. NOTE: this is disputed by the Supplier because "It has been well documented that when serving uploaded files using a method outside of Wagtail (which admittedly is the default), it requires additional configuration from the developer, because Wagtail cannot control how these are served. ... For example, if a Wagtail instance is configured to upload files into AWS S3, Wagtail cannot control the permissions on how they're served, nor any headers used when serving them (a limitation of S3)."
End of life: 5/6/2025, End of support: 5/6/2025, Latest version: 5.2.8
End of life: 5/6/2025, End of support: 5/6/2025, Latest version: 5.2.8
End of life: 11/1/2024, End of support: 8/1/2024, Latest version: 6.1.3
End of life: 11/1/2024, End of support: 8/1/2024, Latest version: 6.1.3
End of life: 8/1/2024, End of support: 5/1/2024, Latest version: 6.0.6
End of life: 8/1/2024, End of support: 5/1/2024, Latest version: 6.0.6
Impact
A user with a limited-permission editor account for the Wagtail admin can make a direct URL request to the admin view that handles bulk actions on user accounts. While authentication rules prevent the user from making any changes, the error message discloses the display names of user accounts, and by modifying URL parameters, the user can retrieve the display name for any user. The vulnerability is not exploitable by an ordinary site visitor without access to the Wagtail admin.
Patches Patched versions have been released as Wagtail 4.1.9 (LTS), 5.0.5 and 5.1.3. The fix is also included in Release Candidate 1 of the forthcoming Wagtail 5.2 release.
Workarounds None.
Acknowledgements Many thanks to @quyenheu for reporting this issue.
For more information If you have any questions or comments about this advisory:
Visit Wagtail's support channels Email us at security@wagtail.org (view our security policy for more information).
End of life: 2/1/2024, End of support: 11/1/2023, Latest version: 5.1.3
End of life: 2/1/2024, End of support: 11/1/2023, Latest version: 5.1.3
End of life: 11/1/2023, End of support: 8/1/2023, Latest version: 5.0.5
End of life: 11/1/2023, End of support: 8/1/2023, Latest version: 5.0.5
Impact
A memory exhaustion bug exists in Wagtail's handling of uploaded images and documents. For both images and documents, files are loaded into memory during upload for additional processing. A user with access to upload images or documents through the Wagtail admin interface could upload a file so large that it results in a crash or denial of service.
The vulnerability is not exploitable by an ordinary site visitor without access to the Wagtail admin. It can only be exploited by admin users with permission to upload images or documents.
Image uploads are restricted to 10MB by default, however this validation only happens on the frontend and on the backend after the vulnerable code.
Patches
Patched versions have been released as Wagtail 4.1.4 (for the LTS 4.1 branch) and Wagtail 4.2.2 (for the current 4.2 branch).
Workarounds
Site owners who are unable to upgrade to the new versions are encouraged to add extra protections outside of Wagtail to limit the size of uploaded files. Exactly how this is done will vary based on your hosting environment, but here are a few references for common setups:
- Nginx: clientmaxbodysize - Apache: LimitRequestBody - Cloudflare: Already imposes a limit of 100MB - 500MB depending on plan - CloudFront: SizeConstraint - Traefik: traefik.http.middlewares.limit.buffering.maxRequestBodyBytes
The changes themselves are deep inside Wagtail, making patching incredibly difficult.
Impact
A stored cross-site scripting (XSS) vulnerability exists on ModelAdmin views within the Wagtail admin interface. A user with a limited-permission editor account for the Wagtail admin could potentially craft pages and documents that, when viewed by a user with higher privileges, could perform actions with that user's credentials. The vulnerability is not exploitable by an ordinary site visitor without access to the Wagtail admin, and only affects sites with ModelAdmin enabled.
- For page, the vulnerability is in the "Choose a parent page" ModelAdmin view (ChooseParentView), available when managing pages via ModelAdmin. - For documents, the vulnerability is in the ModelAdmin Inspect view (InspectView) when displaying document fields.
Patches
Patched versions have been released as Wagtail 4.1.4 (for the LTS 4.1 branch) and Wagtail 4.2.2 (for the current 4.2 branch).
Workarounds
Site owners who are unable to upgrade to the new versions can disable or override the corresponding functionality.
ChooseParentView
For ChooseParentView:
- Disable ModelAdmin for all page models. - Or provide a custom view via chooseparentviewclass, with the custom view overriding the getform method.
One of those steps need to be applied for every ModelAdmin class hooked into Wagtail where the model is a Wagtail Page or sub-class. Here is an example of implementing the custom ChooseParentView with patched HTML escaping:
python from django import forms from django.utils.translation import gettext as from wagtail.contrib.modeladmin.views import ChooseParentView from wagtail.contrib.modeladmin.forms import ParentChooserForm
class PatchedPageChoiceField(forms.ModelChoiceField): """PageChoiceField with plain-text breadcrumbs to patch stored XSS.""" def labelfrominstance(self, obj): bits = [] for ancestor in ( obj.getancestors(inclusive=True).exclude(depth=1).specific(defer=True) ): bits.append(ancestor.getadmindisplaytitle()) return ' | '.join(bits)
class PatchedParentChooserForm(ParentChooserForm): """ParentChooserForm with custom parentpage to patch stored XSS.""" parentpage = PatchedPageChoiceField( label=("Parent page"), required=True, emptylabel=None, queryset=Page.objects.none(), widget=forms.RadioSelect(), )
class PatchedChooseParentView(ChooseParentView): """ChooseParentView with custom getform patch stored XSS.""" def getform(self, request): parents = self.permissionhelper.getvalidparentpages(request.user) return PatchedParentChooserForm(parents, request.POST or None)
InspectView
For InspectView:
- Remove inspectviewenabled=True or set it to False to disable the view. - Or use inspectviewfields or inspectviewfieldsexclude to prevent displaying document fields in the views. - Or provide a custom view via inspectviewclass, with the custom view overriding the getdocumentfielddisplay method.
One of those steps need to be applied for every ModelAdmin class hooked into Wagtail where inspectviewenabled=True. Here is an example of implementing the custom InspectView with patched HTML escaping:
python from django.template.defaultfilters import filesizeformat from django.utils.html import formathtml from wagtail.contrib.modeladmin.views import InspectView
class PatchedInspectView(InspectView): """InspectView with override to patch stored XSS vulnerability.""" def getdocumentfielddisplay(self, fieldname, field): """Render a link to a document""" document = getattr(self.instance, fieldname) if document: return formathtml( '<a href="{}">{} <span class="meta">({}, {})</span></a>', document.url, document.title, document.fileextension.upper(), filesizeformat(document.file.size), ) return self.modeladmin.getemptyvaluedisplay(fieldname)
End of life: 8/1/2023, End of support: 5/2/2023, Latest version: 4.2.4
End of life: 8/1/2023, End of support: 5/2/2023, Latest version: 4.2.4
End of life: 2/1/2024, End of support: 2/1/2024, Latest version: 4.1.9
End of life: 2/1/2024, End of support: 2/1/2024, Latest version: 4.1.9
End of life: 2/1/2023, End of support: 11/1/2022, Latest version: 4.0.4
End of life: 2/1/2023, End of support: 11/1/2022, Latest version: 4.0.4
End of life: 11/1/2022, End of support: 8/31/2022, Latest version: 3.0.3
End of life: 11/1/2022, End of support: 8/31/2022, Latest version: 3.0.3
End of life: 8/1/2022, End of support: 5/1/2022, Latest version: 2.16.3
End of life: 8/1/2022, End of support: 5/1/2022, Latest version: 2.16.3
Impact When notifications for new replies in comment threads are sent, they are sent to all users who have replied or commented anywhere on the site, rather than only in the relevant threads. This means that a user could listen in to new comment replies on pages they have not had editing access to, as long as they have left a comment or reply somewhere on the site.
Patches A patched version has been released as Wagtail 2.15.2 (for the current LTS), which restores the intended behaviour - to send notifications for new replies to the participants in the active thread only (editing permissions are not considered).
Workarounds New comments can be disabled by setting WAGTAILADMINCOMMENTSENABLED = False in the Django settings file.
Acknowledgements
Many thanks to Ihor Marhitych for identifying this issue.
For more information If you have any questions or comments about this advisory:
Visit Wagtail's support channels Email us at security@wagtail.io (if you wish to send encrypted email, the public key ID is 0x6ba1e1a86e0f8ce8)
End of life: 2/1/2023, End of support: 2/1/2023, Latest version: 2.15.6
End of life: 2/1/2023, End of support: 2/1/2023, Latest version: 2.15.6
End of life: 2/7/2022, End of support: 11/4/2021, Latest version: 2.14.2
End of life: 2/7/2022, End of support: 11/4/2021, Latest version: 2.14.2
Impact When the {% includeblock %} template tag is used to output the value of a plain-text StreamField block (CharBlock, TextBlock or a similar user-defined block derived from FieldBlock), and that block does not specify a template for rendering, the tag output is not properly escaped as HTML. This could allow users to insert arbitrary HTML or scripting. This vulnerability is only exploitable by users with the ability to author StreamField content (i.e. users with 'editor' access to the Wagtail admin).
Patches Patched versions have been released as Wagtail 2.11.8 (for the LTS 2.11 branch), Wagtail 2.12.5, and Wagtail 2.13.2 (for the current 2.13 branch).
Site implementors who wish to retain the existing behaviour of allowing editors to insert HTML content in these blocks (and are willing to accept the risk of untrusted editors inserting arbitrary code) may disable the escaping by surrounding the relevant {% includeblock %} tag in {% autoescape off %}...{% endautoescape %}.
Workarounds Site implementors who are unable to upgrade to a current supported version should audit their use of {% includeblock %} to ensure it is not used to output CharBlock / TextBlock values with no associated template. Note that this only applies where {% includeblock %} is used directly on that block (uses of includeblock on a block containing a CharBlock / TextBlock, such as a StructBlock, are unaffected). In these cases, the tag can be replaced with Django's {{ ... }} syntax - e.g. {% includeblock mytitleblock %} becomes {{ mytitleblock }}.
Acknowledgements Many thanks to Karen Tracey for reporting this issue.
For more information If you have any questions or comments about this advisory:
Visit Wagtail's support channels Email us at security@wagtail.io (if you wish to send encrypted email, the public key ID is 0x6ba1e1a86e0f8ce8)