本篇仅代表个人观点。
ctfshow web571
先看题—ctfshow web571
提示:黑客建立了控制器后门
所以直接从控制器中找利用点,发现/Home/Controller/indexController.class.php
中控制器可传参n,并且在最后会被渲染
public function index($n=''){
$this->show('<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "微软雅黑"; color: #333;font-size:24px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.8em; font-size: 36px } a,a:hover{color:blue;}</style><div style="padding: 24px 48px;"> <h1>CTFshow</h1><p>thinkphp 专项训练</p><p>hello,'.$n.'黑客建立了控制器后门,你能找到吗</p>','utf-8');
}
所以利用点很明显了直接传参
/index.php/home/index/index/n/{php}system('cat /fl*');{/php}
但传参后并没有执行,主要是两个问题
1、标签问题,我这里用的是tp5的标签{}
,但后来经过查看手册tp3的标签是<>
2、就是关于URL解析方式的问题,如果执行语句是常规的echo $n;
,是肯定没有问题的,但这里利用点是show方法,传参方式就需要是?n=
这种常规的方式,之前的方式传入标签后是不会被解析的,本地测试了一下确实是这样:
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller{
public function index($name=''){
$this->show($name);
}
}
所以最后的payload:
/index.php/home/index/index/?n=<php>system('cat /fl*');</php>
本地调试
本地有环境顺便debug了一波:
跟进show()->display()->fetch()
,之前的$content
一直是<php>echo 'Sentiment';</php>
,经过129行后变成了Sentiment
就很疑惑跟了进去,发现这个地方进行了赋值
看了一下ob_get_contents
用法——返回输出缓冲区的内容
这里echo没执行的原因:
ob_start()把输出内容输出到缓冲区,而不是到浏览器。然后用ob_get_contents得到缓冲区的数据。
思考
看完后就更蒙蔽了,这里为什么会在缓冲区进行解析?
后来看了师傅的文章,他说本题由于TMPL_ENGINE_TYPE
我们默认的值是Think
,而ctfshow设置的是php,所以可以进入117行的if,从而执行eval,执行了我们输入的语句
后来我把TMPL_ENGINE_TYPE
改成了php进行debug,但无法正常解析执行。而且还有个问题就是即使我们不使用php
而是使用默认的Think
,模板也照样会渲染输出最后的Sentiment
所以我认为这里跟eval语句无关,还是一个缓冲区的问题。(仅个人观点)
于是在本地进行了ob_get_contents()
的测试
<?php
ob_start();
echo "<br>echo '12'</br>";
$out1 = ob_get_contents();
ob_end_clean();
var_dump($out1);
?>
发现渲染了<br>
标签,发生了换行,但里边的语句并没有执行
所以在tp源码这里,由于tp内置标签<php>
会执行php语句,所以在渲染<php>echo 'Sentiment';</php>
标签时,就相当于是执行了php语句echo 'Sentiment';
,而由于ob_start()
的存在这里的echo并不会直接输出出来,而是先放到缓存区,之后再通过ob_get_contents()
得到缓冲区数据,就得到了最后的Sentiment
值,
所以在tp的内部就相当于执行了:
<?php
ob_start();
echo 'Sentiment';
$contents = ob_get_contents();
if ($contents !== false) ob_end_clean();
return $contents;
?>
再经过retrun 返回就得到了129行的$content=Sentiment
后来查资料看师傅说:
大部分使用 php模板引擎技术 外部可以控制的情况下,都容易存在这种问题跟tp框架无关但并没有做过多的解释。