范文健康探索娱乐情感热点
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

简记一次Tp3框架审计之旅

  前言
  MVC框架是代码审计必需学习的知识,这里以TpV3.2.3框架为例,进行一次对MVC框架代码的漏洞审计,简单学一下MVC的相关知识,希望对正在学习MVC框架的师傅有所帮助。 框架
  我们这里首先需要了解一下什么是MVC架构, M:Model(模型),其负责业务数据的处理和与数据库的交互 V:View(视图),提供了展示数据的各种方式 C:Controller(控制器),负责用户请求的调度和处理业务逻辑
  具体如下图
  如果想对MVC框架进行进一步了解,可以参考https://www.kancloud.cn/manual/thinkphp/1698
  接下来我们需要了解一下ThinkPHP框架。 TP3
  如果想了解Tp3常见的操作,可以看一下这篇文章
  https://blog.csdn.net/spc007spc/article/details/103489711 目录文件
  Tp3的目录如下所示
  www WEB部署目录(或者子目录)
  index.php 入口文件
  README.md README文件
  Application 应用目录
  Public 资源文件目录
  ThinkPHP 框架目录
  通俗的说的话,这里的index.php就是提供一个对外的接口,Public就是存放一些公共资源的地方,ThinkPHP是我们的核心框架,其内容如下:
  ThinkPHP 框架系统目录(可以部署在非web目录下面)
  Common 核心公共函数目录
  Conf 核心配置目录
  Lang 核心语言包目录
  Library 框架类库目录
  Think 核心Think类库包目录
  Behavior 行为类库目录
  Org Org类库包目录
  Vendor 第三方类库目录
  ... 更多类库目录
  Mode 框架应用模式目录
  Tpl 系统模板目录
  LICENSE.txt 框架授权协议文件
  logo.png 框架LOGO文件
  README.txt 框架README文件
  ThinkPHP.php 框架入口文件  路由格式
  Tp3提供了多种路由格式,这里的话对其进行简单介绍。 pathinfo模式
  pathinfo 模式,是ThinkPHP的默认模式,其规范格式如下: http://网址/index.php/模块/控制器/操作方法/参数/参数值
  示例如下 http://127.0.0.1:8080/index.php/Home/Index/index/id/2
  它的优点显而易见, 简化了URL地址。 普通模式
  普通模式的规范格式如下 http://网址/index.php?m=模块名称&c=控制器&a=方法&参数=参数值
  示例如下 http://127.0.0.1:8080/index.php?m=Home&c=index&a=index&id=1
  兼容模式
  兼容模式的规范格式如下 http://网址/index.php?s=/模块名称/控制器/方法/参数/参数值
  示例如下 http://127.0.0.1:8080/index.php?s=Home/index/index/id/33
  rewrite 模式
  这个的话首先需要说一下配置,这个想要使用首先需要Apache开启rewrite拓展,我这里的环境是phpstudy集成环境,具体操作如下。
  首先打开"phpStudyPHPTutorialApacheconfhttpd.conf"文件,搜索rewrite_module将其前面的#删去
  而后去TP的根目录,写一个.htaccess文件,内容如下   RewriteEngine on  RewriteCond %{REQUEST_FILENAME} !-d  RewriteCond %{REQUEST_FILENAME} !-f  RewriteRule ^(.*)$ index.php?s=$1 [QSA,PT,L] 
  接下来重启phpstudy
  此时就配置好了。(如若是linux环境,可参考此文https://blog.csdn.net/zhazhaji/article/details/80493513)
  rewrite的规范格式如下 http://网址/模块/控制器/操作方法/参数/参数值
  其实相比于默认模式就是少了个入口文件,看着更简洁了一些。
  示例如下 http://127.0.0.1:8080/Home/index/index/id/2
  特殊方法
  ThinkPHP将一些经常使用的方法进行了封装,也就是我们这里的特殊方法,其目的在于使程序更加安全。
  接下来对几个相对较常用的方法进行简单介绍。
  如果想进行仔细了解,可以访问如下链接
  https://www.cnblogs.com/kenshinobiy/p/9165662.html
  https://www.thinkphp.cn/info/tag I方法
  I方法是ThinkPHP用于更加方便和安全的获取系统输入变量,可以用于任何地方,用法格式如下: I("变量类型.变量名/修饰符",["默认值"],["过滤方法或正则"],["额外数据源"])
  示例如下 echo I("get.id"); //等同于$_GET["id"] echo I("get.id",0); // 如果不存在$_GET["id"] 则返回0 echo I("get.name",""); // 如果不存在$_GET["name"] 则返回空字符串 echo I("get.name","","htmlspecialchars"); // 采用htmlspecialchars方法对$_GET["name"] 进行过滤,如果不存在则返回空字符串 M方法
  M方法用于实例化一个基础模型类,M方法的调用格式: M("[基础模型名:]模型名","数据表前缀","数据库连接信息")
  示例如下 $User = M("User"); #等效于$User = new Model("User"); C方法
  C方法是ThinkPHP用于设置、获取,以及保存配置参数的方法。 $model = C("db_name","thinkphp");; //读取当前的URL模式配置参数 $userType = C("USER_TYPE"); //获取USER_TYPE参数的值 漏洞分析SQL注入环境搭建
  首先我们需要做一些配置。
  我们需要在本地Mysql中新建一个thinkphp数据库用于测试,然后在其中新建一个users数据表,包括id、username、passwd三个字段
  接下来我们需要让Tp与Mysql中的数据库进行对接。
  打开ApplicationHomeConfconfig.php,写入以下内容 <?php return array(  "DB_TYPE" => "mysql", // 数据库类型  "DB_HOST" => "localhost", // 服务器地址  "DB_NAME" => "thinkphp", // 数据库名  "DB_USER" => "root", // 用户名  "DB_PWD" => "root", // 密码  "DB_PORT" => 3306, // 端口  "DB_PREFIX" => "", // 数据库表前缀   "DB_CHARSET"=> "utf8", // 字符集  "DB_DEBUG" => TRUE, // 数据库调试模式 开启后可以记录SQL日志 3.2.3新增 );
  此时环境便搭建好了。 where注入
  打开/Application/Home/Controller/IndexController.class.php,添加内容如下 public function select() {         $id = I("get.id");         $user = M("users");         $data = $user->find($id);         var_dump($data);         }
  此时先在I方法处添加断点
  接下来我们写入我们的语句 http://127.0.0.1:8080/index.php/home/index/select?id[where]=1 and 1=updatexml(1,concat(0x7e,user(),0x7e),1)-- -
  访问之
  然后开始单步调试
  一开始都是赋值这种,后面看到这里
  可以发现有一个htmlspecialchars函数过滤,不过这个主要是针对XSS的,所以对SQL注入影响不大,接着看,到最后
  可以发现这里的value就是我们写入的语句,即1 and 1=updatexml(1,concat(0x7e,user(),0x7e),1)-- -
  然后这里的话他过滤的关键词是 if(preg_match("/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i",$value)){         $value .= " ";     }
  显而易见,过滤的很少,报错注入的updatexml和extractvalue,以及联合查询的union也未被过滤,所以这里也可以使用其他语句,例如 http://127.0.0.1:8080/index.php/home/index/select?id[where]=1 and 1=extractvalue(1,concat(0x7e,user(),0x7e))-- -
  亦可使用联合查询 http://127.0.0.1:8080/index.php/home/index/select?id[where]=0 union select user(),2,3
  接下来调整断点,将断点放在find处,进行单步调试
  跟进
  可以看到这里是判断数值是否为数字字符串串或字符串,满足的话就走if条件下的函数,不过看明显可以看出我们这里是数组(看旁边的options的值为array(1)也可以发现),不满足条件,所以直接走下面。
  走到这个 $pk = $this->getPk();
  跟进
  在上面发现$pk=id
  接下来继续往下走
  这里检验了$pk是否为数组,因不满足条件(此处$pk="id"),所以直接走下面
  接下来是添加limit=1,然后这个用了_parseOptions函数进行处理,跟进此自定义函数
  这里的话可以看到有一个过滤的,但我们这里的话简单看一下就会发现,这里条件并不满足,我们这里的where的值是 0 union select user(),2,3
  所以这里的话就直接pass,接下来继续走
  这些就是一些查询语句,然后将结果返回,接下来到最后
  这里进入parseWhere方法
  我们这里的$where是字符串,所以走if语句,将$where的值赋给$whereStr
  可以看到这里是直接返回了$whereStr,没有用过滤函数什么的处理,因此最终返回的仍是我们传入的
  具体SQL内容如下 SELECT * FROM `users` WHERE 0 union select user(),2,3 LIMIT 1 exp注入
  测试这个的话需要简单修改一下我们的select()方法,修改后内容如下 public function select() {         $map =array("id"=>$_GET["id"]);         $user = M("users");         $data = $user->where($map)->find();         var_dump($data);         }
  这里需要说明一下,之所以不用I方法,是因为I方法中存在过滤,即think_filter函数,其内容如下 if(preg_match("/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i",$value)){         $value .= " ";     }
  可以看出这里过滤了exp,所以不能直接用I方法来写,接下来我们访问网页,写入如下payload http://127.0.0.1:8080/index.php/home/index/select?id[0]=exp&id[1]==1 and updatexml(1,concat(0x7e,user(),0x7e),1)
  同时开启调试,单步跟进
  这里的话我们的where是数组,所以条件不满足,直接pass,第二个if同理,然后第三个的话,我们这里并未对optinos[where]进行赋值,所以就会走else,把$where的值赋给他,接着看
  到find方法这里,第一个if语句,我们这里$options为数组,所以直接pass,第二个if语句,我们的$pk值为id,而非数组,所以也pass,到下面跟之前一样,参数被函数_parseOptions包裹,接下来跟进这个函数
  这里有一个字段类型验证的,我们可以看到这个是满足条件的,所以他会进入下面这个函数,我们首先这个语句 if(is_scalar($val)) {
  这里的is_scalar是检验变量是否为标量,什么是标量,官方文档如下
  我们这里的$val变量值如下
  是Array,因此不会进入这个if语句,即不会进入_parseType方法,这里直接pass,接着看下面
  第一个是执行查询语句返回结果的,第二个是返回预编译的语句,继续跟进
  我们这里用了where,跟进parseWhere方法
  可以看到这里是拼接,继续往下看
  最终返回的是拼接的结果,接下来走到最后,得到SQL语句
  如下 SELECT * FROM `users` WHERE `id` =1 and updatexml(1,concat(0x7e,user(),0x7e),1) LIMIT 1 bind注入
  更改内容如下 public function select() {     $User = M("users");     $user["id"] = I("id");     $data["passwd"] = I("passwd");     $valu = $User->where($user)->save($data);     var_dump($valu);         }
  payload如下 http://127.0.0.1:8080/index.php/home/index/select?id[0]=bind&id[1]=0 and updatexml(1,concat(0x7e,user(),0x7e),1)&passwd=1
  接下来开启xdebug,然后访问
  这里与之前相同,因为是数组,所以让前三个if语句,直接到 if(isset($this->options["where"]))
  这里,因为没有设置这个options["where"],所以走下面,将$where的值赋给他,然后将值返回,接下来继续看
  我们这里的data是一个数组,是有值的,所以 if(empty($data))
  这个不满足条件,直接看下面,可以看到这里有_facade对data进行了处理,跟进这个函数
  可以看到这里先是检验了是否有fields是否为空,然后进入 if(!empty($this->options["field"])) {
  因为这里并不存在options["field"],所以直接pass,走else那里,即将fields值赋给这里的fields,接下来是foreach语句
  因为$data中的passwd在fields中,所以走下面,即elseif那里
  这里的$var为1是标量,所以满足条件,接下来data被_parseType函数处理,跟进这个函数
  可以发现这里就是对内容进行了intval处理,没什么影响,继续跟进
  这里的话是使用了filter函数对内容进行了一次过滤,跟进
  接下来到_parseOptions函数
  接下来这里以为$val是数组,使用不会进入_parseType方法,出来该方法后,到这里
  跟进update方法
  发现有parseSet方法,跟进此方法
  可以看到这里拼接了=:,此时的SQL语句为 UPDATE `users` SET `passwd`=:0
  接下来进入parseWhereItem方法
  这里可以看出当$exp=bind时,$whereStr是可控的,而后得到拼接后的语句,此时的SQL "UPDATE `users` SET `passwd`=:0 WHERE `id` = :0 and updatexml(1,concat(0x7e,user(),0x7e),1)"
  接下来到execute执行函数这里
  重点在于这里 if(!empty($this->bind)){             $that   =   $this;             $this->queryStr =   ($this->queryStr,array_map(function($val) use($that){ return """.$that->escapeString($val)."""; },$this->bind));         }
  这个strtr函数在这里的话就是起到替换作用,比如我们这里,我们传入的是0(payload中的id[1]=0),那么这里就会拼接变成:0,而这个strtr函数将其替换为1
  此时也就得到了我们最终的SQL语句 "UPDATE `users` SET `passwd`="1" WHERE `id` = "1" and updatexml(1,concat(0x7e,user(),0x7e),1)" 命令执行环境搭建
  环境配置,我们首先需要在ApplicationHomeController新建一个文件,用之前SQL注入的亦可,这里就用之前的了,修改文件内容如下 <?php namespace HomeController; use ThinkController; class IndexController extends Controller {     public function  index($value=""){         $this->assign($value);         $this->display();     } }
  因为该漏洞利用的assign函数需要模板渲染,所以需要新建一个模板文件,模板文件位置如下 ThinkPHPApplicationHomeViewIndexindex.html #内容随意
  这里还需要说明的是,日志和debug的关系
  若开启debug模式日志会到:ApplicationRuntimeLogsHome下
  若未开启debug模式日志会到:ApplicationRuntimeLogsCommon下
  接下来开始复现一下,首先我们创建log文件 /ThinkPHP/index.php?m=--><?=phpinfo();?>接下来去包含log文件(这里log的文件名与年月日相关)http://127.0.0.1:8080/ThinkPHP/index.php?m=Home&c=Index&a=index&value[_filename]=./Application/Runtime/Logs/Common/23_01_18.log
  漏洞分析
  这里之所以存在漏洞,其原因是
  由于在业务代码中如果对模板赋值方法assign的第一个参数可控,则导致模板路径变量被覆盖为携带攻击代码路径,造成文件包含,代码执行等危害。
  接下来我们在函数处打上断点
  而后访问 http://127.0.0.1:8080/ThinkPHP/index.php?m=Home&c=Index&a=index&value[_filename]=./Application/Runtime/Logs/Common/23_01_18.log
  开始单步调试
  首先来到这个assign函数,这里的name是数组,其内容为我们的日志文件,可以看到这个函数里用了另一个assign函数来处理变量,跟进
  这里判断$name是否为数组,我们的$name为数组,所以进入if语句,这里的array_merge是合并数组的,但这里$this->tvar
  为空,所以这里的话其实就是$this->tVar=$name,即将name变量的值赋给了$tVar
  继续跟进
  接下来到display函数
  同上个相似,这里是用了另一个同名函数来处理变量,跟进
  我们这里模板内容为空,看到这里有fetch函数,跟进
  首先判断了模板文件是否存在
  而后检验使用的是否是PHP原生模板,系统配置的默认引擎是Think,所以这里走else
  这里可以看到将$this->tVar的值赋给了$params,而后进入了listen函数,跟进此函数
  发现这里经过一些判断后进入了exec函数,跟进此函数
  可以发现这里是调用BehaviorParseTemplateBehavior类中的run方法处理$params,我们跟进run方法,寻找哪里对日志文件路径进行了处理,最终找到ThinkPHPLibraryThinkTemplate.class.php文件下的fetch方法
  loadTemplate函数是读取文件路径的,而后这里用load函数对其进行了处理,我们跟进此函数
  $_filename是之前获取到的的缓存文件路径,$vars是带有变量_filename的数组,这里的$vars不为空,因此使用extract方法的EXTR_OVERWRITE默认描述对变量值进行覆盖。
  最后include日志文件路径,造成文件包含,最终导致包含文件造成RCE。 参考链接
  https://www.freebuf.com/articles/web/345544.html
  https://www.freebuf.com/vuls/282906.html
  https://mp.weixin.qq.com/s/OqfruwHf9CAt--2dQQfNJw
  https://forum.butian.net/share/546
  from https://www.freebuf.com/articles/web/355571.html

故事还没领证未婚夫去世,我决定认婆婆当妈,一起养大腹中孩子上午的妇产科门诊一片繁忙,产检的孕妇或坐或立地等候着,药欣萍在办公室忙得不亦乐乎,忽然等候区一阵骚乱,有人喊道有孕妇晕倒了。医生和护士们涌了过去,几个男家属帮着她们把晕倒的孕妇先搬老人的凄惨晚年,儿子不让进家门,被逼住在面包车,磕头下跪求助在的家长都比较重视孩子的教育问题,对于孩子的教育问题,最好就是从娃娃抓起,因为如果孩子长大了,他们的某些观念也就已经根深蒂固,不是那么容易纠正回来的,这时候家长再后悔,也没有多大的儿童跳绳注意事项家长越早掌握越好现在父母也会有意识地培养孩子技能的学习。在学校老师教,在家里自己督促在我们的线下教学中也经常有家长问到孩子学不会跳绳?跳绳速度太慢了应该怎么办?这里还是要强调一下!欲速而不达,跳绳那些不会告诉你的关于生孩子的二三事1。孕期要多吃水果补充维生素不假,但吃的太多后期容易得妊娠糖尿病。2。孕期要补充营养,是营养不是碳水,所以不需要强迫自己吃饭,如果胃口不好,不吃就不吃吧,少这一顿不会饿着肚里的孩子胎儿有手有脚,堕胎就是杀人!看到这些图片你就懂了当一个生命被孕育的时候也被称为生命的奇迹。不少第一次当妈妈的女生都感慨没想到自己生出了一个人!不少人因为好奇去搜索过刚怀孕,肚子的宝宝能看见吗?刚怀孕,肚子里的宝宝长什么样?网上跳初为人母我们需要准备好什么我们这代人接受了更高等的知识和文化,认识了家庭教育的重要性,很多很多的父母确实会考虑,如何生出养育出更好的孩子憨笑,跟他们更好的生活?采访了很多经验丰富的宝妈以下是我们整理出来的干看完北大学霸李雪琴的家教模式,我决定换个思路养娃赢得孩子的心最好的方式,不是靠拳头,也不是靠嘴巴。而是以身作则,跟孩子并肩长成两棵树,一起历经风霜雨雪,迎来朝霞晚霞,最终,让孩子明白尊重和陪伴的含义。作者可乐妈前段时间,无意间看抄书打卡第36天,心理成长的秘诀认知疗法只因在人群中多看了一眼,有人找到了心灵契合的姻缘,而有人造成了心理的抑郁。抄书打卡第36天,今天继续读到维尼老师的内心的重建,学习到什么是认知疗法?这就是今天摘抄的内容什么是认知疗小儿养护不得不煲的羊肚菌山药排骨汤很多宝妈呢,每天都想给小孩子补这个补那个。其实不需要那么复杂,今天呢我教大家一个既好吃又不麻烦的靓汤,包你小孩子喜欢喝!羊肚菌的好处非常多性平,味甘寒,无毒有益肠胃助消化化痰理气补我教孙子弟子规(二)天雅居小区内有个活动场所,小区里的人都把它叫做水泥地。到了晚上8点左右水泥地就成了孩子们的游乐场。围墙外是龙步西路,临路是一些商铺。这天晚上,一伙河南人和一伙湖南人不知因何在韵达快PC用户赶快升级Win11Moment1发布,大量功能更新之前微软已经正式发布了Windows年度更新补丁,也就是22H2的版本。这虽然是微软今年在系统上最重要的一次更新,但微软并没有将所有的新功能都放在一个版本上推送。就像微软说的一样,
全球首个AI象棋机器人出道,竟能给郭晶晶家当私人教练编辑好困桃子新智元导读可能连你都想不到,奥运冠军郭晶晶家的私人象棋教练竟是一个机器人!整个京城,只要有它在场,都引来大街小巷的人前来围观。它能和你对弈,不论小白,还是象棋大师,都会吸尘器哪个牌子好,手持无线吸尘器什么牌子好介绍冰尊吸尘器是世界吸尘器十大排名龙头品牌。在居家健康意识普遍增强的当下,吸尘器能够很好的清除尘螨和灰尘,因而受到人们的喜爱。特别是手持无线吸尘器以更加轻便实用而大受追捧。那么手持无线干净治愈的温柔句子,直抵心灵生活总是两难,就算有再多的执着,再多的不情愿,最终人们也不得不学会接受,从哭着控诉,到笑着对待,结果到头来,不过是一场随遇而安罢了。未曾清贫过便难做人,不经过打击便会永远天真。成熟有哪些一眼就会让你爱上的句子?1。不知所措的年纪,什么都不尽人意。2。我有两大爱好反省,不知悔改。3。每一个决定转身的人,都在风里站了很久。4。执迷不悟Q是最荒唐的浪漫与忠诚5。所有的诋毁都来源于嫉妒和自愧不如朋友圈晚安心语句子沉默不是没情绪,只是觉得说出来没意义一辈子和你在一起才叫将来,换了别人那叫将就。晚安我不想输就算辛苦。晚安别贪心,你不可能什么都有。也别灰心,因为你不可能什么都没有。晚安人各有心,心各有见,很多事都是你以为而已。晚安能让你成长的句子句句有厚度,都是人生大智慧小时候不理解老人晒太阳,一坐就是半天,长大了才明白,目之所及,皆是回忆,心之所想,皆是过往,眼之所看,皆是遗憾。要做一件事,就不宜把它拿来瞎想,不然想来想去,越想越有味,做事的雄心改变穷忙的人生,要戒掉这3种习惯文舍予图来源网络,与文无关,侵删看过一句话人生之路有两种,择大门走险路,过窄门而宽途。社会越来越自由,选择越来越多,一个人贫穷,已不再是运气的问题了。生活中,有的人一直在走上坡路,2022。08。10早安心语,正能量美好的句子新的一天问候语录图片早上好,今天是2022年08月10日,星期三,农历七月十三,壬寅年虎年戊申月乙未日。每个人都有觉得自己不够好的时候,羡慕别人闪闪发光的时候,但其实大多人都是普通的,那些闪闪发光的背那些神仙句子,建议收藏(你携远山来,风也琳琅)1。不要难过,她说,每家每户都有废物,我们家的废物就是我。2。要继续调整心性,安静读书,一步一步按计划完成工作,然后就可以更加幸福。这个道理很简单,但是我从没看见谁做到过。3。玫瑰再读这12句子,我有了新的理解和获得导读再读这12句子,我有了新的理解和获得。1无生命的东西,是对生命的支持,无论我们多聪明,都要感谢一丝一缕的针织,一餐一汤的饱食。2。自由和孤独是一个伪命题。3坚持自己不是一句话,朋友圈发泄情绪的伤感句子一大概这辈子不会再爱上谁了,因为没有了一见钟情的皮囊,缺少了日久生情的耐心。二这世间怎么会有我喜欢你这种人间疾苦。三错把陈醋当成墨,写尽半生纸上酸,错把陈墨当成醋,喝进半生苦,醋酸