[1.10.x] Fixed CVE-2017-12794 -- Fixed XSS possibility in traceback section of technical 500 debug page.

This is a security fix.
This commit is contained in:
Tim Graham 2017-08-02 16:22:35 -04:00
parent fba3c96a74
commit 58e08e80e3
3 changed files with 25 additions and 17 deletions

View File

@ -775,38 +775,37 @@ TECHNICAL_500_TEMPLATE = ("""
<h2>Traceback <span class="commands">{% if not is_email %}<a href="#" onclick="return switchPastebinFriendly(this);"> <h2>Traceback <span class="commands">{% if not is_email %}<a href="#" onclick="return switchPastebinFriendly(this);">
Switch to copy-and-paste view</a></span>{% endif %} Switch to copy-and-paste view</a></span>{% endif %}
</h2> </h2>
{% autoescape off %}
<div id="browserTraceback"> <div id="browserTraceback">
<ul class="traceback"> <ul class="traceback">
{% for frame in frames %} {% for frame in frames %}
{% ifchanged frame.exc_cause %}{% if frame.exc_cause %} {% ifchanged frame.exc_cause %}{% if frame.exc_cause %}
<li><h3> <li><h3>
{% if frame.exc_cause_explicit %} {% if frame.exc_cause_explicit %}
The above exception ({{ frame.exc_cause }}) was the direct cause of the following exception: The above exception ({{ frame.exc_cause|force_escape }}) was the direct cause of the following exception:
{% else %} {% else %}
During handling of the above exception ({{ frame.exc_cause }}), another exception occurred: During handling of the above exception ({{ frame.exc_cause|force_escape }}), another exception occurred:
{% endif %} {% endif %}
</h3></li> </h3></li>
{% endif %}{% endifchanged %} {% endif %}{% endifchanged %}
<li class="frame {{ frame.type }}"> <li class="frame {{ frame.type }}">
<code>{{ frame.filename|escape }}</code> in <code>{{ frame.function|escape }}</code> <code>{{ frame.filename }}</code> in <code>{{ frame.function }}</code>
{% if frame.context_line %} {% if frame.context_line %}
<div class="context" id="c{{ frame.id }}"> <div class="context" id="c{{ frame.id }}">
{% if frame.pre_context and not is_email %} {% if frame.pre_context and not is_email %}
<ol start="{{ frame.pre_context_lineno }}" class="pre-context" id="pre{{ frame.id }}"> <ol start="{{ frame.pre_context_lineno }}" class="pre-context" id="pre{{ frame.id }}">
{% for line in frame.pre_context %} {% for line in frame.pre_context %}
<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ line|escape }}</pre></li> <li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ line }}</pre></li>
{% endfor %} {% endfor %}
</ol> </ol>
{% endif %} {% endif %}
<ol start="{{ frame.lineno }}" class="context-line"> <ol start="{{ frame.lineno }}" class="context-line">
<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre> <li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>
""" """{{ frame.context_line|escape }}</pre>{% if not is_email %} <span>...</span>{% endif %}</li></ol> """ """{{ frame.context_line }}</pre>{% if not is_email %} <span>...</span>{% endif %}</li></ol>
{% if frame.post_context and not is_email %} {% if frame.post_context and not is_email %}
<ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}"> <ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}">
{% for line in frame.post_context %} {% for line in frame.post_context %}
<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ line|escape }}</pre></li> <li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ line }}</pre></li>
{% endfor %} {% endfor %}
</ol> </ol>
{% endif %} {% endif %}
@ -831,7 +830,7 @@ TECHNICAL_500_TEMPLATE = ("""
<tbody> <tbody>
{% for var in frame.vars|dictsort:0 %} {% for var in frame.vars|dictsort:0 %}
<tr> <tr>
<td>{{ var.0|force_escape }}</td> <td>{{ var.0 }}</td>
<td class="code"><pre>{{ var.1 }}</pre></td> <td class="code"><pre>{{ var.1 }}</pre></td>
</tr> </tr>
{% endfor %} {% endfor %}
@ -842,7 +841,6 @@ TECHNICAL_500_TEMPLATE = ("""
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>
{% endautoescape %}
<form action="http://dpaste.com/" name="pasteform" id="pasteform" method="post"> <form action="http://dpaste.com/" name="pasteform" id="pasteform" method="post">
{% if not is_email %} {% if not is_email %}
<div id="pastebinTraceback" class="pastebin"> <div id="pastebinTraceback" class="pastebin">
@ -888,9 +886,9 @@ In template {{ template_info.name }}, error at line {{ template_info.line }}
Traceback:{% for frame in frames %} Traceback:{% for frame in frames %}
{% ifchanged frame.exc_cause %}{% if frame.exc_cause %}{% if frame.exc_cause_explicit %} {% ifchanged frame.exc_cause %}{% if frame.exc_cause %}{% if frame.exc_cause_explicit %}
The above exception ({{ frame.exc_cause }}) was the direct cause of the following exception: The above exception ({{ frame.exc_cause|force_escape }}) was the direct cause of the following exception:
{% else %} {% else %}
During handling of the above exception ({{ frame.exc_cause }}), another exception occurred: During handling of the above exception ({{ frame.exc_cause|force_escape }}), another exception occurred:
{% endif %}{% endif %}{% endifchanged %} {% endif %}{% endif %}{% endifchanged %}
File "{{ frame.filename|escape }}" in {{ frame.function|escape }} File "{{ frame.filename|escape }}" in {{ frame.function|escape }}
{% if frame.context_line %} {{ frame.lineno }}. {{ frame.context_line|escape }}{% endif %}{% endfor %} {% if frame.context_line %} {{ frame.lineno }}. {{ frame.context_line|escape }}{% endif %}{% endfor %}

View File

@ -5,3 +5,12 @@ Django 1.10.8 release notes
*September 5, 2017* *September 5, 2017*
Django 1.10.8 fixes a security issue in 1.10.7. Django 1.10.8 fixes a security issue in 1.10.7.
CVE-2017-12794: Possible XSS in traceback section of technical 500 debug page
=============================================================================
In older versions, HTML autoescaping was disabled in a portion of the template
for the technical 500 debug page. Given the right circumstances, this allowed
a cross-site scripting attack. This vulnerability shouldn't affect most
production sites since you shouldn't run with ``DEBUG = True`` (which makes
this page accessible) in your production settings.

View File

@ -9,6 +9,7 @@ error (raise ... from ...) can't be silenced using NOQA.
import sys import sys
from django.test import RequestFactory, TestCase from django.test import RequestFactory, TestCase
from django.utils.safestring import mark_safe
from django.views.debug import ExceptionReporter from django.views.debug import ExceptionReporter
@ -20,10 +21,10 @@ class Py3ExceptionReporterTests(TestCase):
request = self.rf.get('/test_view/') request = self.rf.get('/test_view/')
try: try:
try: try:
raise AttributeError('Top level') raise AttributeError(mark_safe('<p>Top level</p>'))
except AttributeError as explicit: except AttributeError as explicit:
try: try:
raise ValueError('Second exception') from explicit raise ValueError('<p>Second exception</p>') from explicit
except ValueError: except ValueError:
raise IndexError('Final exception') raise IndexError('Final exception')
except Exception: except Exception:
@ -37,9 +38,9 @@ class Py3ExceptionReporterTests(TestCase):
html = reporter.get_traceback_html() html = reporter.get_traceback_html()
# Both messages are twice on page -- one rendered as html, # Both messages are twice on page -- one rendered as html,
# one as plain text (for pastebin) # one as plain text (for pastebin)
self.assertEqual(2, html.count(explicit_exc.format("Top level"))) self.assertEqual(2, html.count(explicit_exc.format('&lt;p&gt;Top level&lt;/p&gt;')))
self.assertEqual(2, html.count(implicit_exc.format("Second exception"))) self.assertEqual(2, html.count(implicit_exc.format('&lt;p&gt;Second exception&lt;/p&gt;')))
text = reporter.get_traceback_text() text = reporter.get_traceback_text()
self.assertIn(explicit_exc.format("Top level"), text) self.assertIn(explicit_exc.format('<p>Top level</p>'), text)
self.assertIn(implicit_exc.format("Second exception"), text) self.assertIn(implicit_exc.format('<p>Second exception</p>'), text)