BUUCTF[护网杯 2018]easy_tornado1-write up
知识点:
SSTI(模板注入),render函数,Tornado框架(知识点在最后)
打开靶机,看到三个链接:
分别点进去:
注意每个链接的url:
/file?filename=/xxx&filehash=32位MD5
根据flag.txt和hint.txt提示url的形式为:文件名+32位MD5
再看hint.txt的提示:md5(cookie_secret+md5(filename))。明显我们要先找到cookie_secret进行MD5加密,再和进行MD5加密后输入的文件名进行连接,再次加密。
可知想得到flag也就是需要构造:
/file?filename=/fllllllllllllag&filehash=md5(cookie_secret+md5(/fllllllllllllag))
那么如何得到cookie_secret?
先尝试输入一个相同格式的/fllllllllllllag:
/file?filename= /fllllllllllllag&filehash=fc5e038d38a57032085441e7fe7010b0(这里随便构造了一个MD5进行测试,不是正确答案)
查看有没有信息:
输入后就会跳转到/error?msg=Error
提示有签名错误,发现/error?msg=签名错误。根据提示网页提示:render(渲染)函数,联想到ssti注入(模板注入)
SSTI注入:服务端模板注入
当前使用的一些框架,比如python的flask,php的tp,java的spring等一般都采用成熟的的MVC的模式,用户的输入先进入Controller控制器,然后根据请求类型和请求的指令发送给对应Model业务模型进行业务逻辑判断,数据库存取,最后把结果返回给View视图层,经过模板渲染展示给用户。
简单来说就是,用户输入的东西在进行分析后被传出来视图层进行模板渲染(render),让用户看到结果。模板是不同的,因此会有不同的传输结果。
php常见的模板:twig,smarty,blade。而render函数就存在于模板twig中:
比如:
<?php
require_once dirname(__FILE__).'\twig\lib\Twig\Autoloader.php';
Twig_Autoloader::register(true);
$twig = new Twig_Environment(new Twig_Loader_String());
$output = $twig->render("Hello {{name}}", array("name" => $_GET["name"])); // 将用户输入作为模版变量的值
echo $output;
?>
Twig使用一个加载器 loader(Twig_Loader_Array) 来定位模板,以及一个环境变量 environment(Twig_Environment) 来存储配置信息。其中,render() 方法通过其第一个参数载入模板,并通过第二个参数中的变量来渲染模板。使用 Twig 模版引擎渲染页面,其中模版含有 {{name}} 变量,其模版变量值来自于GET请求参数$_GET["name"] ,
而用户可以控制输入的变量,像这样:
render("Hello {$_GET['name']}")
用户输入1则输出“Hello 1”
如果服务端将用户的输入作为了模板的一部分,那么在页面渲染时也必定会将用户输入的内容进行模版编译和解析最后输出。
除此之外,在Twig模板引擎里,,{{var}} 除了可以输出传递的变量以外,还能执行一些基本的表达式然后将其结果作为该模板变量的值。这里用户输入name={{6+10}},则输出“Hello 16”
基于此,我们尝试输入:/error?msg={{1}},输出1,可以确定是模板注入。
至于要寻找cookie_secret,要先知道tornado框架:
Tornado全称Tornado Web Server,是一个用Python语言写成的Web服务器兼Web应用框架
tornado官方文档详情Templates and UI — Tornado 6.2.dev1 documentation (tornadoweb.org)
在搜索框输入cookie_secret:
出来好几个有关内容点进"get",利用ctrl+f搜索cookie_secret,跟settings有关:
输入以上两个命令却找不到cookie_secert,已知Tornado提供了一些对象别名来快速访问对象。Handler指向的处理当前这个页面的RequestHandler对象,handler是RequestHandler的别名,而上面又提到RequestHandler.settings是self.application.settings的别名。所以handler.settings就等于RequestHandler.application.settings。这个self是oneself的意思,哪个对象用就是哪个对象的.application.settings。
所以用handler.settings访问RequestHandler.application.settings:
即输入:/error?msg={{handler.settings}}
得到cookie_secert后就简单了,像前文所言,将cookie_secert和/fllllllllllllag分别MD5加密后拼接再次加密输入得到flag构造payload:
file?filename=/fllllllllllllag&filehash=正确MD5值