### 简要描述: 通常用于诈骗! ### 详细说明: 官方的demo已经是4.0,可惜下不到源码 翻来看看ThinkSNS 3.0的源码,发现一处严重的设计缺陷 apps\public\Lib\Action\AccountAction.class.php 下doSaveProfile方法为保存用户信息操作 ``` /** * 保存基本信息操作 * @return json 返回操作后的JSON信息数据 */ public function doSaveProfile() { $res = true; // 保存用户表信息 if(!empty($_POST['sex'])) { $save['sex'] = 1 == intval($_POST['sex']) ? 1 : 2; //$save['lang'] = t($_POST['lang']); $save['intro'] = t($_POST['intro']); // 添加地区信息 $save['location'] = t($_POST['city_names']); $cityIds = t($_POST['city_ids']); $cityIds = explode(',', $cityIds); if(!$cityIds[0] || !$cityIds[1] || !$cityIds[2]) $this->error('请选择完整地区'); isset($cityIds[0]) && $save['province'] = intval($cityIds[0]); isset($cityIds[1]) && $save['city'] = intval($cityIds[1]); isset($cityIds[2]) && $save['area'] = intval($cityIds[2]); // 修改用户昵称 $uname = t($_POST['uname']); $oldName = t($_POST['old_name']); $save['uname'] = filter_keyword($uname); $res = model('Register')->isValidName($uname, $oldName);//这里跟进 if(!$res) { $error =...
### 简要描述: 通常用于诈骗! ### 详细说明: 官方的demo已经是4.0,可惜下不到源码 翻来看看ThinkSNS 3.0的源码,发现一处严重的设计缺陷 apps\public\Lib\Action\AccountAction.class.php 下doSaveProfile方法为保存用户信息操作 ``` /** * 保存基本信息操作 * @return json 返回操作后的JSON信息数据 */ public function doSaveProfile() { $res = true; // 保存用户表信息 if(!empty($_POST['sex'])) { $save['sex'] = 1 == intval($_POST['sex']) ? 1 : 2; //$save['lang'] = t($_POST['lang']); $save['intro'] = t($_POST['intro']); // 添加地区信息 $save['location'] = t($_POST['city_names']); $cityIds = t($_POST['city_ids']); $cityIds = explode(',', $cityIds); if(!$cityIds[0] || !$cityIds[1] || !$cityIds[2]) $this->error('请选择完整地区'); isset($cityIds[0]) && $save['province'] = intval($cityIds[0]); isset($cityIds[1]) && $save['city'] = intval($cityIds[1]); isset($cityIds[2]) && $save['area'] = intval($cityIds[2]); // 修改用户昵称 $uname = t($_POST['uname']); $oldName = t($_POST['old_name']); $save['uname'] = filter_keyword($uname); $res = model('Register')->isValidName($uname, $oldName);//这里跟进 if(!$res) { $error = model('Register')->getLastError(); return $this->ajaxReturn(null, model('Register')->getLastError(), $res); } //如果包含中文将中文翻译成拼音 if ( preg_match('/[\x7f-\xff]+/', $save['uname'] ) ){ //昵称和呢称拼音保存到搜索字段 $save['search_key'] = $save['uname'].' '.model('PinYin')->Pinyin( $save['uname'] ); } else { $save['search_key'] = $save['uname']; } $res = model('User')->where("`uid`={$this->mid}")->save($save); $res && model('User')->cleanCache($this->mid); $user_feeds = model('Feed')->where('uid='.$this->mid)->field('feed_id')->findAll(); if($user_feeds){ $feed_ids = getSubByKey($user_feeds, 'feed_id'); model('Feed')->cleanCache($feed_ids,$this->mid); } } ``` 修改用户昵称这里$uname 是你想修改的昵称(你是A,你想修改成B)$oldName是你以前的昵称,我们跟进$res = model('Register')->isValidName($uname, $oldName);这个函数 ``` /** * 验证昵称内容的正确性 * @param string $name 输入昵称的信息 * @param string $old_name 原始昵称的信息 * @return boolean 是否验证成功 */ public function isValidName($name, $old_name = null) { // 默认不准使用的昵称 $protected_name = array('name', 'uname', 'admin', 'profile', 'space'); $site_config = model('Xdata')->get('admin_Config:site'); !empty($site_config['sys_nickname']) && $protected_name = array_merge($protected_name, explode(',', $site_config['sys_nickname'])); $res = preg_match($this->_name_reg, $name) !== 0; if($res) { $length = get_str_length($name); $res = ($length >= 2 && $length <= 10); } else { $this->_error = '仅支持中英文,数字,下划线'; $res = false; return $res; } // 预保留昵称 if(in_array($name, $protected_name)) { $this->_error = L('PUBLIC_NICKNAME_RESERVED');// 抱歉,该昵称不允许被使用 $res = false; return $res; } if(!$res) { $this->_error = L('PUBLIC_NICKNAME_LIMIT', array('nums'=>'2-10'));// 昵称长度必须在2-10个汉字之间 return $res; } if(($name != $old_name) && $this->_user_model->where('`uname`="'.mysql_escape_string($name).'"')->find()) { $this->_error = L('PUBLIC_ACCOUNT_USED');// 该用户名已被使用 $res = false; } // 敏感词 if (filter_keyword($name) !== $name) { $this->_error = '抱歉,该昵称包含敏感词不允许被使用'; return false; } return $res; } ``` 这里主要经过三次审核: 1.是否使用预保留的名称array('name', 'uname', 'admin', 'profile', 'space'); 2.判断昵称长度是否是2-10个汉字之间; 3.判断用户昵称是否被使用。 我们直接看看第三处的判断逻辑:用户昵称是否被使用 if(($name != $old_name) && $this->_user_model->where('`uname`="'.mysql_escape_string($name).'"')->find()) { 如果$name != $old_name,则进入if。但是old_name可被参数传入,我直接更改old_name和name为任意的同一个人就绕过该if逻辑了! 同时我们看看安装好ThinkSNS 3后数据库人员信息: [<img src="https://images.seebug.org/upload/201506/05104714273cab9d60890e9e45da97175ad4f8cc.jpg" alt="11111.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201506/05104714273cab9d60890e9e45da97175ad4f8cc.jpg) 管理员的昵称是“管理员”,不在保留的名称array('name', 'uname', 'admin', 'profile', 'space')里,所以也可以更改为管理员。。。 ### 漏洞证明: 现在官方demo是ThinkSNS 4版本的,注册一个账户,编辑我的个人信息 http://demo.thinksns.com/ts4/index.php?app=public&mod=Account&act=index [<img src="https://images.seebug.org/upload/201506/05110414940a527b2b2e0e51792616667ad76820.jpg" alt="1.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201506/05110414940a527b2b2e0e51792616667ad76820.jpg) 在朋友圈里看到一个人Clay http://demo.thinksns.com/ts4/index.php?app=public&mod=Profile&act=index&uid=32323 [<img src="https://images.seebug.org/upload/201506/05110520e337b2181b9120078da97da60a1a24e6.jpg" alt="2.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201506/05110520e337b2181b9120078da97da60a1a24e6.jpg) 貌似是个技术总监,那我们抓包更改我的个人信息试下: [<img src="https://images.seebug.org/upload/201506/05110728ac09ca1ba41afab99b18bbb67fd3894e.jpg" alt="3.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201506/05110728ac09ca1ba41afab99b18bbb67fd3894e.jpg) 发现我也是技术总监了! [<img src="https://images.seebug.org/upload/201506/05110807a237d80db00ed77d1acd085bb554c0f0.jpg" alt="4.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201506/05110807a237d80db00ed77d1acd085bb554c0f0.jpg)