Thinkphp 3.2.3 parseWhere設計缺陷導致update/delete注入 分析

  • 2019 年 10 月 3 日
  • 筆記

目錄

分析

首先看一下控制器,功能是根據用戶傳來的id,修改對應用戶的密碼。

13行把用戶傳來的id參數送入where()作為SQL語句中的WHERE語句,將pwd參數送入save()作為UPDATE語句。
這裡我們假設請求id參數為array("bind","aaa"),pwd參數為bbb。

其中11行12行的意思是獲取id、pwd參數,並通過I函數進行過濾。我們跟進一下I()

可以看到,這裡首先對參數進行htmlspecialchars過濾,然後在最後調用think_filter()函數進行過濾,跟進一下這個函數

可以看到,這裡通過匹配參數中的一些關鍵字,並在其後加上空格。
到這裡I函數就結束了。

回到控制器繼續往下走,首先進入where()

可以看到由於$where是數組、$parser是null,所以三個if都不滿足,直接到1813行,接下來就是將$this->options[‘where’]與$where拼接到一起,用於最終拼湊成一條完整的SQL語句。

繼續跟進就到save函數了。

由於$data非空,到409行。這裡主要對比$data與資料庫中的表中的各個欄位的數據類型是否一致,如果不一致則會進行一些強制轉換或是直接報錯【TODO】。
然後到416行調用了_parseOptions(),這裡用於解析出表名、where中的欄位名等【TODO】。

下面直接跳到最後,調用update函數準備執行SQL語句

跟進update,直接進入899行

跟進parseWhere

其中在536行調用了parseWhereItem,跟進

可以看到,這裡直接拼接了$key和$val[1]到$whereStr中

最後可以看到,$whereStr="`id` = :aaa",其中aaa就是我們一開始傳入的id=array(‘bind’, ‘aaa’)數組中的第二項。由於後來直接被拼接到SQL語句中,因此這個裡存在注入。

http://127.0.0.1/thinkphp/thinkphp_3.2.3_full/index.php/home/index/sqli1?id[0]=bind&id[1]=0%20and%20(updatexml(1,concat(0x7e,user(),0x7e),1))&pwd=bbb

總結

本質原因是框架處理pdo時,將用戶可控的字元串作為了佔位符,導致sql注入在預處理之前就已經形成了,而所謂的過濾bind只是治標不治本而已(沒做過開發,也有可能這本來就是一種寫法?)。也有可能就是本來入口也就不多吧【TODO】。

哎,好菜。
01點46分