分析
首先看一下控制器,功能是根據用戶傳來的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分