强网杯:Python is the best language-writeup
题目
Python is the best language 1/2
Python is the best language 1
源码下载下来后,由于是基于flask框架,因此先看了看路由文件routes.py
,大概如下:
这些功能大部分是基于登陆的,因此从注册和登陆相关的代码入手。
跟进RegistrationForm
,定义在 forms.py
的第20行:
在这里可以很明显的看到两个验证函数有差别,validate_username
在进行mysql.One
前进行了正则匹配的过滤和审核,而validate_email
仅仅通过validators=[DataRequired(), Email()]
来匹配。
Email
定义在wtforms.validators
中,相关源码如下:
其正则规则为^.+@([^.@][^@]+)$
,也就是说对email而言,即使提交如'"#a@q.com
包含单引号,双引号,注释符等敏感字符的形式也是能通过的。
回到validate_email
验证函数中:
跟入mysql.One
,定义在others.py:
最后拼接出来的sql语句如下:
结合前面所说的对输入邮箱email形式的验证,这里存在sql注入漏洞。我们设置邮箱为test'/**/or/**/1=1#@test.com
,则拼接后的sql语句为:
可以看到成功注入。由于此处不能回显数据,因此采用盲注。回到validate_username
当查询为真时也即user != 0
会出现信息Please use a different username.
,结合这点构造出最后的exp.py:
由于在注册部分有csrf_token,因此在每次submit时要记得带上,同时在每次返回的页面中取得下一次的csrf_token。
最后的flag:
Python is the best language 2
分析
接着进行代码审计。在others.py
的最后有这样的内容:
我把这部分内容分为两部分;反序列化漏洞以及基本的沙箱逃逸问题。
先忽略unpkler.dispatch[REDUCE]
这一行的内容。
|
|
这里对file
进行了反序列化,因此如果file
可控即可造成危险。
用下面的脚本(exp4.py)进行序列化payload的生成:
测试反序列化漏洞(exp5.py):
不过没那么简单,源码还设置了沙箱/黑名单来防止某些函数的执行,比如前面的os.system就被禁用了,我们修改exp5.py为进一步的测试:
此时如果简单地想通过前一步生成的test来执行系统命令,会报错。
考虑其他方法。python中除了os和sys模块有提供命令执行的函数外,还有其他第三方模块,比如commands模块:
因此改写生成序列化文件的exp4.py如下:
同时为了进一步利用,我们尝试反弹shell。过程如下,先运行exp4.py生成新的test序列化文件,接着nc监听本地端口,接着运行exp5.py触发序列化漏洞并完成利用
不过该怎么控制源代码中的load(file)
的file呢?通过全局搜索关键字,在Mycache.py
的FileSystemCache类
中有多次引用,比如定义在第137行的get方法:
可以看到将传入的字符串key进行MD5,并将其返回。不过这个key
在哪里定义?通过全局搜索,不难发现在Mysession.py
的open_session
中进行了调用:
其中self.key_prefix
即为bdwsessions
,因此假设cookie中的sesssion值为675b6ec7-95bd-411f-a59d-4c3dbchybeta
,则self.key_prefix + sid
即为bdwsessions675b6ec7-95bd-411f-a59d-4c3dbchybeta
,然后这串字符串进行MD5得到的结果78f634977cbacf167dfd9656fe9dd5f3
即为675b6ec7-95bd-411f-a59d-4c3dbchybeta
对应的session文件名。
同时根据config.py
:
可以知道session文件的保存路径在/tmp/ffff
,以及用户为root,因此具有文件导出的权限的可能性很大。
流程
结合Python is the best language 1
中的sql注入漏洞,我们梳理出如下的攻击流程:
- 本地生成序列化文件,并且进行十六进制编码
- 通过sql注入漏洞outfile出session文件
- 访问index,同时带上session文件对应的session值,触发
open_session
中的self.cache.get
,进行反序列化攻击
假设前面生成的序列化文件存在于/tmp/ffff/chybeta
,建议使用mysql的hex转码来进行十六进制的转换:
以使用675b6ec7-95bd-411f-a59d-4c3dbchybeta
作为cookie为例,则其session文件存在于/tmp/ffff/78f634977cbacf167dfd9656fe9dd5f3
在十六进制的序列化串前面添加0x
,构造邮箱处的注入点:
也即在注册的邮箱处填入:
点击submit后出现Please use a different email address.
。
接着在burp中抓取访问index的包,并修改cookie为675b6ec7-95bd-411f-a59d-4c3dbchybeta
,在自己的vps上监听对应的端口:
flag:
总结
- wtforms.validators的Email类验证不完善
- flask的session处理机制
- python沙箱逃逸
- python反序列化漏洞
- 一点“小小”的脑洞