• PHP审计之WeEngine审计


    PHP审计之WeEngine审计

    前言

    审计该CMS加深一下对于MVC架构的php审计流程

    梳理路由

    打开代码看到index.php文件

    if($_W['os'] == 'mobile' && (!empty($_GPC['i']) || !empty($_SERVER['QUERY_STRING']))) {
    	header('Location: ./app/index.php?' . $_SERVER['QUERY_STRING']);
    } else {
    	header('Location: ./web/index.php?' . $_SERVER['QUERY_STRING']);
    }
    

    web端会跳转到/web/index.php,来看到web/index.php

    if (($_W['setting']['copyright']['status'] == 1) && empty($_W['isfounder']) && $controller != 'cloud' && $controller != 'utility' && $controller != 'account') {
    	$_W['siteclose'] = true;
    	if ($controller == 'account' && $action == 'welcome') {
    		template('account/welcome');
    		exit;
    	}
    	if ($controller == 'user' && $action == 'login') {
    		if (checksubmit()) {
    			require _forward($controller, $action);
    		}
    		template('user/login');
    		exit;
    	}
    	isetcookie('__session', '', -10000);
    	message('站点已关闭,关闭原因:' . $_W['setting']['copyright']['reason'], url('account/welcome'), 'info');
    }
    

    这里$controller$action接收是从bootstrap.inc.php

    $controller = $_GPC['c'];
    $action = $_GPC['a'];
    $do = $_GPC['do'];
    

    上面接收$controller$actionaccountwelcome

    调用template('account/welcome');

    function template($filename, $flag = TEMPLATE_DISPLAY) {
    	global $_W;
    	$source = IA_ROOT . "/web/themes/{$_W['template']}/{$filename}.html";
    	$compile = IA_ROOT . "/data/tpl/web/{$_W['template']}/{$filename}.tpl.php";
    	if(!is_file($source)) {
    		$source = IA_ROOT . "/web/themes/default/{$filename}.html";
    		$compile = IA_ROOT . "/data/tpl/web/default/{$filename}.tpl.php";
    	}
        ...
    

    这段代码实际上就是加载一个模板渲染。

    继续往下看

    $controllers = array();
    $handle = opendir(IA_ROOT . '/web/source/');
    if(!empty($handle)) {
    	while($dir = readdir($handle)) {
    		if($dir != '.' && $dir != '..') {
    			$controllers[] = $dir;
    		}
    	}
    }
    if(!in_array($controller, $controllers)) {
    	$controller = 'account';
    }
    $init = IA_ROOT . "/web/source/{$controller}/__init.php";
    if(is_file($init)) {
    	require $init;
    }
    
    $actions = array();
    $handle = opendir(IA_ROOT . '/web/source/' . $controller);
    if(!empty($handle)) {
    	while($dir = readdir($handle)) {
    		if($dir != '.' && $dir != '..' && strexists($dir, '.ctrl.php')) {
    			$dir = str_replace('.ctrl.php', '', $dir);
    			$actions[] = $dir;
    		}
    	}
    }
    if(empty($actions)) {
    	header('location: ?refresh');
    }
    if(!in_array($action, $actions)) {
    	$action = $acl[$controller]['default'];
    }
    if(!in_array($action, $actions)) {
    	$action = $actions[0];
    }
    
    

    遍历读取/web/source/,所有内容。

    遍历读取/web/source/' . $controller,并且把内容中的.ctrl.php去掉。

    if(is_array($acl[$controller]['direct']) && in_array($action, $acl[$controller]['direct'])) {
    		require _forward($controller, $action);
    	exit;
    

    判断是否为数组并且判断$acl[$controller]['direct'],并且查看$action是否在$acl截取对应$controllerdirect

    逻辑其实就是$acl中定义了大量的数组,如

    'account' => array(
    		'default' => 'welcome',
    		'direct' => array(
    			'welcome',
    			'auth'
    		)
    

    $controller=account

    direct=array('welcome','auth')

    direct这个数组对应的是account下面的路由

    访问welcome的路由的访问策略即

    /web/index.php?c=account&a=welcome
    

    漏洞审计

    定位到漏洞位置web/source/site/category.ctrl.php

    定位到176行

    if (!empty($navs)) {
    		foreach ($navs as $row) {
    			file_delete($row['icon']);
    		}
    		pdo_query("DELETE FROM ".tablename('site_nav')." WHERE id IN (".implode(',', array_keys($navs)).")");
    	}
    

    file_delete($row['icon']);,这里的$row['icon']是通过遍历$navs

    $navs是通过一下这个sql语句查询得来的

    $navs = pdo_fetchall("SELECT icon, id FROM ".tablename('site_nav')." WHERE id IN (SELECT nid FROM ".tablename('site_category')." WHERE id = {$id} OR parentid = '$id')", array(), 'id');
    

    找看数据库中的这两个字段是否可控

    site_nav数据库表中对应的数据是$nav变量内容,发现 $nav['icon'] 变量是从$_GPC['iconfile']来的,即参数可控。这里的 $nav['icon']变量,其实就是我们文章开头分析的传入 file_delete函数的参数

    			if(!empty($nav_exist)) {
    				pdo_update('site_nav', $nav, array('id' => $category['nid'], 'uniacid' => $_W['uniacid']));
    			} else {
    				pdo_insert('site_nav', $nav);
    

    这里需要先把文件名插入到数据库中,然后调用该功能将文件上传,代码会从数据库从查找该文件名,然后删除对应文件。

    参考

    https://github.com/hongriSec/PHP-Audit-Labs/tree/master/Part1/Day6/files

  • 相关阅读:
    mysql 实战 or、in与union all 的查询效率
    转:手机流畅的决定性因素
    合批只是对CPU的优化,与GPU没有任何关系
    UNITY 打包时提示sdk tools 或 sdk build tools版本低时可以直接点update 按钮进行更新
    RGB ECT 4BIT 压缩后质量远高于RGB ETC2 4BIT
    Adreno GPU Profiler
    UNITY2018.3 在editor下运行时new memoryprofiler显示 shader占用内存很大的问题在安卓上并没有看到
    VS2017断点调试UNITY2018.3 经常卡住的问题
    一次UNITY闪退问题的定位心得
    UNITY2018 真机开启deepprofiling的操作
  • 原文地址:https://www.cnblogs.com/nice0e3/p/15388045.html
Copyright © 2020-2023  润新知