Flask源码学习之意外在Debugger上发现通用XSS【转】

来源:猫头鹰

前言

1

前段时间发现较为鸡肋的洞和当时写的文章。

一个存在于Flask框架Debugger页面上的通用XSS,Werkzeug0.10.10之前版本受影响,已经报告给Flask官方并提交修复代码。官方在确认之后,及时发布了0.10.11。

记下了发现的过程。

正文

看了一段时间Flask的源码,想学习一下项目架构和一些感兴趣的实现,其中就包括Flask功能强大的Debugger页面。用过Flask的人都知道,Flask的Debug模式能帮助我们在开发Web应用时跟踪异常信息,调试代码,解决问题。

在使用Debugger的时候,我就在想这个页面难道也是使用render_template或其他Flask模板调用函数生成的吗?稍微翻了一下代码,发现这个功能既没有使用Jinja2模板,甚至它的主要代码写在Flask的另一个基础库werkzeug上面。

Werkzeug是Flask官方开发的一个WSGI工具箱,可以作为一个Web框架的底层库。事实上Flask就是基于Werkzeug和Jinja2开发的一个Web框架。

我以前曾经在记一下PythonWeb代码审计应该注意的地方提及PythonWeb开发中容易容易产生XSS的几种情况,其中提到:

如果webapp没有使用模板语言的话,又没有对用户输入进行过滤直接返回给客户端的话,就容易产生XSS。

lask的程序员显然不会犯这样的错误,我可以看一下werkzeug/debug/tbtools.py这个文件。

2

可以发现Debugger页面是以字符串拼接和字符串格式化的形式构成的。这个字符串可以传进五个变量,分别是exception_type, exception, summary, plaintext, plaintext_cs。看变量名就可以知道,应该是一些异常信息。这些信息由render_full函数填充进字符串。

3

代码里使用了escape函数过滤了异常信息和异常类型,但是这两行代码引起了我注意。

4

self.plaintext依然包含着异常信息:

5

plaintext_cs是放在html注释内的完整异常信息,为了避免异常内出现–>闭合之前的注释符,这里会把重复的-替换为一个-,但是plaintext没有经过任何处理,plaintext放在一个textarea里,显然我们的Flask程序员们没有想到异常信息会闭合textarea而造成问题。

这样思路就清晰了:

  1. 我们需要在Flask的WebApp产生一个异常,以至于它能返回Debugger页面。
  2. 我们需要一个异常信息内包含着我们所构造数据的异常,以便我们构造payload字符串

很巧,把字符串转换成数字的常用函数int()就拥有这样的特性。

于是我们写了一个小Demo:

6

接下来的活就是普通的XSS构造payload了。只要闭合前面的textarea标签即可。

例如:

2-37 3-33 4-27 5-27 6-25 7-22

8

写了一个脚本稍微跑了一下,int()的这个异常最多含有200个字符串值,如果传入的字符串长度大于200,就会截取前200位。然而200位的payload已经足够我们构造所有xss利用代码了。

修复

我自己提交的修复代码,官方也采用了,只是加了过滤而已。现在的话,只要运行pip install -U werkzeug,把werkzeug更新到0.10.11版本就没事了。当然更重要的是,任何框架的debug模式都不要放到生产环境。

待续

我当时还有几个小想法,一个就是想思考如何通过一个简单的脚本获取看啊可能那些常见的字符串处理函数其产生的异常,是带有字符串值的,另一个就是经P牛提醒如果可以构造Payload的去获取pin码的话,就可直接代码执行了。

但是一来是Debug模式在线上确实不多见,所以这个洞影响也不是特别大。二来也要忙着审计php和找工作就没再花时间在这上面了。等有时间再好好弄弄。

参考

https://github.com/pallets/werkzeug/commits?author=neargle

此条目发表在未分类, 经验技术分类目录。将固定链接加入收藏夹。