Django debug page XSS漏洞(CVE-2017-12794)
利用vulhub上的环境
复现
编译及启动环境:1
2docker-compose build
docker-compose up -d
访问http://your-ip:8000/create_user/?username=<script>alert(1)</script>
创建一个用户,成功
再次访问http://your-ip:8000/create_user/?username=<script>alert(1)</script>
,触发异常:
Postgres抛出的异常为:1
2duplicate key value violates unique constraint "xss_user_username_key"
DETAIL: Key (username)=(<script>alert(1)</script>) already exists.
这个异常被拼接进1
The above exception ({{ frame.exc_cause }}) was the direct cause of the following exception
最后触发XSS。
分析
在官方库中有如下描述:
In Django 1.10.x before 1.10.8 and 1.11.x before 1.11.5, 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.
在调试模板中关闭了html的自动转义,但官方认定危害不大,因为在生产环境中不应该设置:DEBUG = True
查看官方的django/views/debug.py
修改记录,发现增加了强制转义:
如果要触发这两个输出点,就必须进入这个if语句:1
{% ifchanged frame.exc_cause %}{% if frame.exc_cause %}
注意到The above exception was the direct cause of the following exception
这句话,一般是在出现数据库异常的时候,会抛出这样的错误语句。
查看django/db/utils.py
的__exit__
函数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25def __exit__(self, exc_type, exc_value, traceback):
if exc_type is None:
return
for dj_exc_type in (
DataError,
OperationalError,
IntegrityError,
InternalError,
ProgrammingError,
NotSupportedError,
DatabaseError,
InterfaceError,
Error,
):
db_exc_type = getattr(self.wrapper.Database, dj_exc_type.__name__)
if issubclass(exc_type, db_exc_type):
dj_exc_value = dj_exc_type(*exc_value.args)
dj_exc_value.__cause__ = exc_value
if not hasattr(exc_value, '__traceback__'):
exc_value.__traceback__ = traceback
# Only set the 'errors_occurred' flag for errors that may make
# the connection unusable.
if dj_exc_type not in (DataError, IntegrityError):
self.wrapper.errors_occurred = True
six.reraise(dj_exc_type, dj_exc_value, traceback)
其中exc_type
是异常,如果其类型是DataError,OperationalError,IntegrityError,InternalError,ProgrammingError,NotSupportedError,DatabaseError,InterfaceError,Error
之一,则抛出一个同类型的新异常,并设置其__cause__
和__traceback__
为此时上下文的exc_value
和traceback
。
exc_value
是上一个异常的说明,traceback
是上一个异常的回溯栈。这个函数其实就是关联了上一个异常和当前的新异常。
最后,在500页面中,__cause__
被输出。
使用Postgres
数据库并触发异常的时候,psycopg2
会将字段名和字段值全部抛出。那么,如果字段值中包含我们可控的字符串,又由于之前说到的,这个字符串其实就会被设置成__cause__
,最后被显示在页面中。
Author: damn1t
Link: http://microvorld.com/2020/01/16/cve/CVE-2017-12794/
Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.