刚好CTFSHOW做到ThinkPHP代码审计的部分了,以前也没尝试审计过这种完整的框架,只会照着成品Payload瞎打,这里就审计下ThinkPHP3.2.3的那些利用点,总结一下。
分析
show方法在ThinkPHP/Library/Think/Controller.class.php中被定义
此处参数$content可控即可使得show方法实现代码执行。
跟进display方法。
跟进display方法中的72行,在此参数$content会被解析。
在117-127行参数$content被具体处理,根据配置TMPL_ENGINE_TYPE的不同会使用不同的引擎,最终在129获取到解析后结果,在133行被返回输出。
先看ctfshow题目中对应的TMPL_ENGINE_TYPE='php'配置的情况。
以参数$content值为hello为例,122行处eval对应输出为
所以构造参数$content值为<?php Payload_Code?>样式的代码即可实现show方法导致的命令执行。
再看TMPL_ENGINE_TYPE为其他值的情况(实际上划分为了think和第三方两种)。
参数$content被组合进数组$params,并在126行进行进一步处理。
跟进,注意到在在89行数组params被调用。
119行处数组params被进一步调用,跟进。
引擎在这里被进一步细分为think和第三方两种,这里不讨论第三方。
根据缓存的是否有效分别会加载缓存或模板文件,而缓存是否有效与show方法传入的参数$content的值有关,第一次传入时因为不存在对应缓存(缓存无效)会加载模板并生成对应的缓存接着再加载,在第二次传入时由于已存在对应缓存便会直接加载缓存。
从结果上来看两者是等效的,两种情况最终会调用28行的load方法(不代表传入的参数的情况完全如28行中所示)来加载缓存文件。
令show方法传入参数$content的值为<?php phpinfo();?>为例并进入缓存无效的情况,审计生成缓存过程中涉及传入内容处理的相关代码。
直接跟进到解析的部分,此处的$tmplContent即为传入的内容,126行-134行简略来说对传入内容进行了PHP代码优化、Lirertal标签替换等,但不影响PHP代码解析(但是会增加可解析的形式,这一点会在给出的Payload中体现)。
最终解析完成并在load方法中include的内容如下(通常来说并不会触发exit)。
Payload
TMPL_ENGINE_TYPE=Think/php
<?php Payload_Code ?> <?= Payload_Code ?>
TMPL_ENGINE_TYPE=Think
<php>Payload_Code</php> <php>Payload_Code<php>
未完待续