当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2014-082108

漏洞标题:WeiPHP微信开发框架SQL注入漏洞

相关厂商:WeiPHP

漏洞作者: L.N.

提交时间:2014-11-07 12:33

修复时间:2015-02-05 12:34

公开时间:2015-02-05 12:34

漏洞类型:设计错误/逻辑缺陷

危害等级:高

自评Rank:20

漏洞状态:未联系到厂商或者厂商积极忽略

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2014-11-07: 积极联系厂商并且等待厂商认领中,细节不对外公开
2015-02-05: 厂商已经主动忽略漏洞,细节向公众公开

简要描述:

程序简介:
weiphp是一个开源,高效,简洁的微信开发平台,它是基于oneThink这个简单而强大的内容管理框架实现的。旨在帮助开发者快速实现微信公众账号的个性化功能。开源产品WeiPHP下载量10万多,被众多开发者安装使用。
危害简介:
无任何限制,通杀sql注入。

详细说明:

一个漏洞个人认为需要两个必要因数:
1.数据,你所能控制的数据(直接控制、间接控制)。
2.方法,使用到数据的方法(此处的数据是你能控制的数据)。
下面我们找我们能控制的数据:
\Application\Home\Controller\WeixinController.class.php

public function index() {
// 删除微信传递的token干扰
unset ( $_REQUEST ['token'] );
$weixin = D ( 'Weixin' );
// 获取数据
$data = $weixin->getData ();//此处有数据传入,我们具体看下它传入的方式
//var_dump($data);exit;
$this->data = $data;
if (! empty ( $data ['ToUserName'] )) {
get_token ( $data ['ToUserName'] );
}
if (! empty ( $data ['FromUserName'] )) {
session ( 'openid', $data ['FromUserName'] );
}

$this->token = $data ['ToUserName'];//赋值给了token

$this->initFollow ( $weixin );

// 记录日志
addWeixinLog ( $data, $GLOBALS ['HTTP_RAW_POST_DATA'] );

// 回复数据
$this->reply ( $data, $weixin );

// 结束程序。防止oneThink框架的调试信息输出
exit ();
}


\Application\Home\Model\WeixinModel.class.php

public function __construct() {
if ($_REQUEST ['doNotInit'])
return true;
$content = file_get_contents ( 'php://input' );//相当霸气的数据传入
! empty ( $content ) || die ( '这是微信请求的接口地址,直接在浏览器里无效' );
$data = new \SimpleXMLElement ( $content );//xml解析
$data || die ( '参数获取失败' );
foreach ( $data as $key => $value ) {//数据赋值
$this->data [$key] = strval ( $value );
}
}
/* 获取微信平台请求的信息 */
public function getData() {
return $this->data;
}


有了可控制的数据,然后我们寻找数据进入过的方法(数据在流动的过程中,会赋值,会被处理而改变,过程繁复复杂,动态调试是一个让我们不至于被程序绕晕的好方法),最后我动态调试,找到了一个利用点:
通过第一段代码,我们知道我们的数据有被赋值给this->token:
\Application\Home\Controller\WeixinController.class.php

private function reply($data, $weixin) {
$key = $data ['Content'];
$keywordArr = array ();

````````
代码省略

````````
if (! empty ( $forbit_addon )) {
$like ['addon'] = array (
'not in',
$forbit_addon
);
}
$like ['token'] = array (
'exp',
"='0' or token='{$this->token}'" //此处被赋值给$like
);
if (! isset ( $addons [$key] )) {
$like ['keyword'] = $key;
$like ['keyword_type'] = 0;
$keywordArr = M ( 'keyword' )->where ( $like )->order ( 'id desc' )->find ();//$like被加入数据库中查询

if (! empty ( $keywordArr ['addon'] )) {
$addons [$key] = $keywordArr ['addon'];
$this->request_count ( $keywordArr );
}
}
````````
代码省略

````````
}
}


最后经过层层数据库的处理最后query
\ThinkPHP\Library\Think\Model.class.php
此时的

$str="SELECT * FROM `wp_keyword` WHERE (  (`token` ='0' or token='222'')  ) AND ( `keyword` = '33333' ) AND ( `keyword_type` = 0 ) ORDER BY id desc LIMIT 1  "
public function query($str) {
if(0===stripos($str, 'call')){ // 存储过程查询支持
$this->close();
$this->connected = false;
}
$this->initConnect(false);
if ( !$this->_linkID ) return false;
$this->queryStr = $str;
//释放前次的查询结果
if ( $this->queryID ) { $this->free(); }
N('db_query',1);
// 记录开始执行时间
G('queryStartTime');
$this->queryID = mysql_query($str, $this->_linkID);//注入进去了
$this->debug();
if ( false === $this->queryID ) {
$this->error();
return false;
} else {
$this->numRows = mysql_num_rows($this->queryID);
return $this->getAll();
}
}


通过上面分析,我们可以看出我们有两个参数可控,一个是token,一个是keyword,对应的数据传入的变量ToUserName和Content
------------------------------------------------------
通过上面的分析,其实我们挖一般sql注入有个非常好的方法是:
记录所有的mysql_error()-->写个工具fuzz数据输入点-->查看错误日志反向寻找注入

漏洞证明:

构造poc:

<?xml version="1.0" encoding="utf-8"?>
<xml>
<ToUserName>请看测试代码</ToUserName>
<FromUserName>111</FromUserName>
</xml>


RQ6_`3{%HS6JB}T%~V$PCJX.jpg


BG%H]NQ%2(DHGAL5V@15L[8.jpg

修复方案:

输入过滤。

版权声明:转载请注明来源 L.N.@乌云


漏洞回应

厂商回应:

未能联系到厂商或者厂商积极拒绝