遇到一個有趣的邏輯漏洞
遇到個有趣的邏輯漏洞,和大家分享一下。
某系統資料庫是mysql。user表有個code欄位,型別是int(11),這個欄位是儲存一個隨機數,用來找回密碼的時候做驗證,預設值是0。
找回密碼時候的步驟是,首先填寫自己郵箱,接收重置密碼的郵件,點選連結,訪問如下程式碼:
if (!empty($_GET['email']) && !empty($_GET['code'])) { if (!$db->count('user',"email='{$_GET['email']}' AND code='{$_GET['code']}'")) die('error'); $_SESSION['email'] = $_GET['email']; ... }
在資料庫中查詢email=$_GET['email']
並且code=$_GET['code']
的行數,如果行數為0則die出去,否則設定$_SESSION['email'] = $_GET['email'];
最後就以$_SESSION['email']
記憶體儲的郵箱重置密碼。
看似似乎沒問題,只有當email為你的email,並且你知道他的隨機code的時候,才能不die,才能獲得$_SESSION['email']
。
但關鍵問題就是:code的預設值是0,也就是說所有使用者只要沒有重置過密碼,他的code就是0,所以等於說我知道了所有使用者的code,那我不就可以重置所有使用者的密碼了嗎?
不不,等下,我們看到這行程式碼:
if (!empty($_GET['email']) && !empty($_GET['code']))
必須要!empty($_GET['code'])
的時候,才可能進入這個if語句。熟悉php的人都知道,empty(0)是返回真的。所以說,如果$_GET['code']=0
的話,根本進不來這個if語句。
那怎麼辦?
又涉及到mysql一個tip,很容易犯錯的點。
我之前說了,code這個欄位的型別是整型int(11)。而在mysql裡面,當欄位型別為整型,而where語句中的值不為整型的時候,會被轉換成整型才放入查詢。也就是說,如果where code='xxx'
也就是說,如果我們傳入的字串為0aaa,則會轉換成0,再執行。
我們可以來做個試驗,
上圖大家可以看到,select count(*) from `user` where `id`='0a';
和select count(*) from `user` where `id`='0';
得到的結果都是1。
所以通過這個tip,就可以繞過if (!empty($_GET['email']) && !empty($_GET['code']))
,只要我們傳入的$_GET['code']=0xxx
,就可以進入if語句,並且讓select count(*)
語句返回1,最後找回任意使用者密碼,不需要爆破。