Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2023-41048: Merge pull request from GHSA-jj7c-jrv4-c65x · plone/plone.namedfile@ff5269f

plone.namedfile allows users to handle File and Image fields targeting, but not depending on, Plone Dexterity content. Prior to versions 5.6.1, 6.0.3, 6.1.3, and 6.2.1, there is a stored cross site scripting vulnerability for SVG images. A security hotfix from 2021 already partially fixed this by making sure SVG images are always downloaded instead of shown inline. But the same problem still exists for scales of SVG images. Note that an image tag with an SVG image as source is not vulnerable, even when the SVG image contains malicious code. To exploit the vulnerability, an attacker would first need to upload an image, and then trick a user into following a specially crafted link. Patches are available in versions 5.6.1 (for Plone 5.2), 6.0.3 (for Plone 6.0.0-6.0.4), 6.1.3 (for Plone 6.0.5-6.0.6), and 6.2.1 (for Plone 6.0.7). There are no known workarounds.

CVE
#xss#vulnerability

Expand Up @@ -4,12 +4,15 @@ from plone.app.testing import SITE_OWNER_PASSWORD from plone.namedfile import field from plone.namedfile import file from plone.namedfile.interfaces import IAvailableSizes from plone.namedfile.interfaces import IImageScaleTraversable from plone.namedfile.testing import PLONE_NAMEDFILE_FUNCTIONAL_TESTING from plone.namedfile.tests import getFile from plone.testing.zope import Browser from Products.CMFPlone.utils import safe_unicode from zope.annotation import IAnnotations from zope.annotation import IAttributeAnnotatable from zope.component import getSiteManager from zope.interface import implementer
import os Expand Down Expand Up @@ -48,6 +51,11 @@ def get_disposition_header(browser): return browser.headers.get(name, None)

def custom_available_sizes(): # Define available image scales. return {"custom": (10, 10)}

class TestAttackVectorNamedImage(unittest.TestCase): layer = PLONE_NAMEDFILE_FUNCTIONAL_TESTING field_class = file.NamedImage Expand All @@ -58,6 +66,12 @@ def setUp(self): item = DummyContent() self.layer[“app”]._setOb("item", item) self.item = self.layer[“app”].item sm = getSiteManager() sm.registerUtility(component=custom_available_sizes, provided=IAvailableSizes)
def tearDown(self): sm = getSiteManager() sm.unregisterUtility(provided=IAvailableSizes)
def get_admin_browser(self): browser = Browser(self.layer[“app”]) Expand Down Expand Up @@ -100,30 +114,73 @@ def assert_display_inline_is_download(self, base_url): self.assertIn("attachment", header) self.assertIn("filename", header)
def assert_scale_view_works(self, base_url): # Test that accessing a scale view shows the image inline. browser = self.get_anon_browser() browser.open(base_url + "/@@images/{}".format(self.field_name)) self.assertIsNone(get_disposition_header(browser))
# Note: the ‘custom’ scale is defined in an adapter above. browser.open(base_url + "/@@images/{}/custom".format(self.field_name)) self.assertIsNone(get_disposition_header(browser))
unique_scale_id = list(IAnnotations(self.item)[“plone.scale”].keys())[0] browser.open(base_url + "/@@images/{}".format(unique_scale_id)) self.assertIsNone(get_disposition_header(browser))
def assert_scale_view_is_download(self, base_url): # Test that accessing a scale view turns into a download. browser = self.get_anon_browser() browser.open(base_url + "/@@images/{}".format(self.field_name)) header = get_disposition_header(browser) self.assertIsNotNone(header) self.assertIn("attachment", header) self.assertIn("filename", header)
browser.open(base_url + "/@@images/{}/custom".format(self.field_name)) header = get_disposition_header(browser) self.assertIsNotNone(header) self.assertIn("attachment", header) self.assertIn("filename", header)
unique_scale_id = list(IAnnotations(self.item)[“plone.scale”].keys())[0] browser.open(base_url + "/@@images/{}".format(unique_scale_id)) header = get_disposition_header(browser) self.assertIsNotNone(header) self.assertIn("attachment", header) self.assertIn("filename", header)
def test_png_image(self): setattr(self.item, self.field_name, self._named_file(“image.png”)) transaction.commit() base_url = self.item.absolute_url() self.assert_download_works(base_url) self.assert_display_inline_works(base_url) if self.field_name == "image": self.assert_scale_view_works(base_url)
def test_svg_image(self): setattr(self.item, self.field_name, self._named_file(“image.svg”)) transaction.commit() base_url = self.item.absolute_url() self.assert_download_works(base_url) self.assert_display_inline_is_download(base_url) if self.field_name == "image": self.assert_scale_view_is_download(base_url)
def test_filename_none(self): # A ‘None’ filename None probably does not happen during normal upload, # but if an attacker manages this, even @@download will show inline. # A ‘None’ filename probably does not happen during normal upload, # but if an attacker manages this, even @@download would show inline. # We prevent this. data = self._named_file(“image.svg”) data.filename = None setattr(self.item, self.field_name, data) transaction.commit() base_url = self.item.absolute_url() self.assert_download_works(base_url) self.assert_display_inline_is_download(base_url) if self.field_name == "image": self.assert_scale_view_is_download(base_url)
def test_filename_empty(self): # An empty filename is probably no problem, but let’s check. Expand All @@ -134,6 +191,8 @@ def test_filename_empty(self): base_url = self.item.absolute_url() self.assert_download_works(base_url) self.assert_display_inline_is_download(base_url) if self.field_name == "image": self.assert_scale_view_is_download(base_url)

class TestAttackVectorNamedBlobImage(TestAttackVectorNamedImage): Expand Down

Related news

GHSA-jj7c-jrv4-c65x: plone.namedfile vulnerable to Stored Cross Site Scripting with SVG images

### Impact There is a stored cross site scripting vulnerability for SVG images. A [security hotfix from 2021](https://github.com/plone/Products.PloneHotfix20210518) already partially fixed this, by making sure SVG images are always downloaded instead of shown inline. But the same problem still exists for scales of SVG images. Note that an image tag with an SVG image as source is not vulnerable, even when the SVG image contains malicious code. To exploit the vulnerability, an attacker would first need to upload an image, and then trick a user into following a specially crafted link. All versions of `plone.namedfile` are impacted. ### Patches Patches will be released in various `plone.namedfile` releases: * 5.6.1 (for Plone 5.2) * 6.0.3 (for Plone 6.0.0-6.0.4) * 6.1.3 (for Plone 6.0.5-6.0.6) * 6.2.1 (for Plone 6.0.7) ### Workarounds There is no workaround.

CVE: Latest News

CVE-2023-50976: Transactions API Authorization by oleiman · Pull Request #14969 · redpanda-data/redpanda