前言
本题涉及到的知识点有,Tornado 的 SSTI 漏洞、Tornado 中的 cookie_secret 值 以及 Python3 中的 hashlib 模块。
首先大概的介绍一下 Tornado 的相关概念。
Tornado
Tornado 是使用 Python 开发的全栈式(full-stack)Web 框架和异步网络库。与以前提到过的 Flask 一样都是 Python 的一种 Web 开发框架。
Tornado render 模板注入
Tornado render 是 Python 中的一个渲染函数,也就是一种模板,通过调用的参数不同,生成不同的网页,如果用户对 render 内容可控,不仅可以注入 XSS 代码,而且还可以通过 {{}} 进行传递变量和执行简单的表达式。模板注入的相关概念在以前的文章中已经提到过,在这里就不再赘述。
由于本人对于这些框架的了解几乎为零…. So,关于 SSTI in Tornado 的深入分析可以参考博客 Python中从服务端模板注入到沙盒逃逸的源码探索 (一) 或者 python SSTI tornado render模板注入(PS. 这篇博客写的一点都不通畅….读起来好费力啊….)
接下来具体分析分析题目。
打开题目可以看到有三个文件,逐一访问,内容如下:
- /flag.txt

- /welcome.txt

- /hints.txt

既然第一个文件中的内容提示我们 flag 在 /fllllllllllllag 里面,那么不妨先试着直接访问该文件看看。

可以看到页面返回了错误提示,观察该报错页面的 URL, 注意到 URL 中 msg 的值为 Error,与页面的显示内容相同。故可以尝试修改 msg 的值观察其回显。

此时结合第二个文件中的提示 render,考虑此处存在 Tornado 的服务端模板注入攻击,继续尝试几个例子确认一下。



发现确实存在 SSTI ,但是部分注入被过滤了。
接下来看看第三个文件中的提示,md5(cookie_secret+md5(filename))。观察题目所给三个文件的 URL 的格式,不难发现,该表达式的结果应该是对应着 URL 中的 filehash 参数的值。既然存放 flag 的文件名我们已经知道了,那么我们只需要得到相应的 filehash 参数的值,我们就可以构造 URL 进而得到 flag。
观察该表达式,filename 是我们已知的,所以我们只要知道 cookie_secret 所对应的值,便可以计算出该表达式的结果。
那么 cookie_secret 又是什么东西呢,在 Tornado 中,通过在 tornado.web.Application 中添加 cookie_secret 参数,我们就可以使用 set_secure_cookie() 和 get_secure_cookie() 函数来发送和取得浏览器的 cookies ,借助该参数可以对 cookie 进行简易加密以防止 Cookie 被恶意篡改。关于 Tornado 的 cookie_secret 值 可以参考博客 tornado安全应用之cookie 或者 tornado中的cookie
Tornado 文档中的相关内容如下:

至于要如何获取到 cookie_secret 的值,emmmmm,由于没有学习过相关框架的语法,这里只好直接用大佬的 WP 里面的结论了,参考博客传送门 [护网杯 2018]easy_tornado 及 Tornado小记 – 模板中的Handler,相关摘要如下:

简单总结一下就是,cookie_secret 是存在于 settings 中的,settings 又作为参数传入了 Application 构造函数,因此可以通过 self.application.settings 来获取到 cookie_secret。又因为根据官方文档, RequestHandler.settings 的别名是 self.application.settings,且 handler 指向处理当前这个页面的 RequestHandler 对象,故最终的效果就是可以直接通过 handler.settings 来得到 cookie_secret 的值。

故构造 payload,msg={{handler.settings}} 即可获取到 cookie_secret。

既然 filename 和 cookie_secret 我们都知道了,接下来我们就可以借助 Python 脚本来计算相应表达式的值。
1 | #导入hashlib模块 |
关于 Python 中的 hashlib 模块可以参考博客 Python hashlib模块、python常用模块之hashlib模块 以及 python3 hashlib.md5()_update()_digest()_hexdigest().py
现在 filename 和 filehash 的值我们就都得到了,接下来按照题目的格式构造相应的 URL 即可得到 flag。
