1. 项目概述:一次从CVE编号到完整漏洞复现的深度剖析

最近在安全社区里,CVE-2025-12598这个编号引起了我的注意。它关联到一个名为“Best house rental management system V1.0”的房屋租赁管理系统。作为一名长期从事应用安全研究的从业者,我深知这类中小型、特定行业的Web系统往往是安全风险的“重灾区”。厂商在快速迭代功能时,安全往往被置于次要位置,而“白盒审计”正是我们深入其内部,系统性发现和验证这类风险最直接、最有效的手段。这个CVE编号背后,很可能隐藏着一个或多个典型的Web漏洞,比如文件上传、SQL注入或是逻辑缺陷。我决定以这个系统为样本,进行一次完整的白盒代码审计与漏洞复现,并将整个过程详细记录下来。这不仅是为了复现一个已知漏洞,更是希望通过这个案例,分享一套可复用的、从信息收集到漏洞验证的实战方法论,无论你是刚入门的安全爱好者,还是想提升代码审计效率的工程师,都能从中获得直接的参考价值。

2. 环境准备与目标系统初探

2.1 目标系统定位与源码获取

进行白盒审计的第一步,永远是获取目标代码。对于公开披露的CVE,通常可以在漏洞公告平台、GitHub的漏洞PoC仓库或安全研究者的博客中找到线索。经过一番搜索,我定位到了“Best house rental management system V1.0”的官方或第三方下载源。这里有一个关键点: 务必确保获取的源码版本与漏洞影响版本一致 。CVE-2025-12598影响的是V1.0,那么下载时就要确认文件名和内部版本标识,避免因版本差异导致审计路径失效。我下载到的是一个ZIP压缩包,解压后是一个典型的PHP+MySQL架构的Web项目目录。

解压后,我首先快速浏览了项目结构。目录中通常包含 admin/ (后台)、 include/ (公共函数和配置)、 uploads/ (上传目录)以及大量以功能命名的PHP文件,如 login.php property.php tenant.php 等。这种结构非常清晰,有利于我们快速定位功能模块。同时,我特别注意了 config.php database.php 这类配置文件,里面通常含有数据库连接信息,这在后续搭建本地环境时必不可少。

注意:在测试环境中,务必使用虚拟机和隔离的网络环境。永远不要在连接公网或包含敏感数据的主机上直接运行来源未知的代码,以防其中包含后门或造成意外损害。

2.2 本地测试环境搭建

为了动态调试和验证漏洞,我们需要在本地完整运行这套系统。我选择了最通用的组合:XAMPP集成环境(包含Apache、MySQL、PHP)。步骤如下:

  1. 部署源码 :将解压后的整个项目文件夹,复制到XAMPP的 htdocs 目录下,例如 htdocs/rental_system
  2. 创建数据库 :启动XAMPP的Apache和MySQL服务,通过phpMyAdmin(通常访问 http://localhost/phpmyadmin )新建一个数据库,比如命名为 house_rental
  3. 导入数据 :在项目文件夹内寻找SQL文件,通常是 database.sql install.sql sql/ 目录下的文件。在phpMyAdmin中选中新建的 house_rental 数据库,使用“导入”功能执行这个SQL文件,创建所有数据表。
  4. 修改配置 :找到项目的配置文件(如 config.php ),将其中的数据库连接参数(主机名、用户名、密码、数据库名)修改为本地环境的值。XAMPP默认MySQL用户是 root ,密码为空。
  5. 访问系统 :在浏览器中访问 http://localhost/rental_system ,如果看到登录页面或安装引导页面,说明环境搭建成功。根据系统提示完成安装或使用默认账号(常在 readme 或SQL文件中)登录。

搭建过程中,我遇到了一个小坑:原SQL文件可能使用了较新的MySQL语法,而本地PHP版本较低,导致导入失败。我的解决方法是,在phpMyAdmin导入时,将“SQL兼容模式”改为 MYSQL40 NONE ,并确保没有启用严格模式。这提醒我们, 环境兼容性是成功复现的第一步

3. 白盒代码审计核心思路与入口点分析

3.1 审计策略:由外而内,功能追踪

面对一个完整的项目代码,逐行阅读效率极低。我采用的策略是“由外而内,功能追踪”。首先,从外部攻击面(User Interface)入手,即用户能直接交互的功能点,然后追踪其对应的后端代码处理流程。

我通常会从以下几个高危功能入口开始筛查:

  • 文件上传点 :任何允许用户上传文件的功能,如房源图片上传、租客证件上传、管理员导入数据等。对应的代码会出现在 property_upload.php admin/upload.php 等文件中。
  • 用户输入点 :所有表单提交、URL参数( $_GET $_POST $_REQUEST )、HTTP头部(如 $_SERVER[‘HTTP_USER_AGENT’] )接收数据的地方。登录、搜索、数据筛选是重灾区。
  • 身份验证与权限校验 :检查登录逻辑( login.php )、会话管理( session 处理)、以及每个需要权限的页面(如 admin/*.php )开头是否有统一的、有效的权限检查代码。
  • 数据库操作 :全局搜索 mysql_ mysqli_ PDO 相关的函数调用,特别是那些将用户输入直接拼接进SQL语句的地方。

对于“Best house rental management system”,我首先尝试以租客或管理员身份进行常规操作,同时用Burp Suite这类代理工具拦截所有HTTP请求,观察参数传递,从而快速定位到后端处理文件。

3.2 关键入口定位与敏感函数回溯

根据经验,我直接使用代码编辑器(如VS Code)的全局搜索功能,搜索高危函数。在PHP中,这些函数是漏洞的“信号灯”:

  • 文件包含 include , require , include_once , require_once 。如果其参数用户可控,可能导致本地文件包含(LFI)或远程文件包含(RFI)。我搜索发现多处包含,但参数多为固定路径(如 include(‘header.php’); ),初步看风险较低。
  • 命令/代码执行 exec() , system() , passthru() , shell_exec() , eval() , assert() 。全局搜索 eval assert 未发现,但在一个名为 backup.php 的文件中发现了 system() 调用,这需要重点关注。
  • 文件操作 move_uploaded_file() , copy() , file_put_contents() , fopen() / fwrite() 。这是文件上传漏洞的核心。我搜索 move_uploaded_file ,在 upload_tenant_doc.php admin/property_image_upload.php 中找到了它。
  • 数据库操作 mysql_query() , mysqli_query() 。我搜索 mysql_query ,发现项目大量使用了老的 mysql_* 扩展(这本身就是一个风险点,该扩展在PHP 5.5+已废弃),并且很多查询语句直接使用字符串拼接。

例如,在 search_property.php 中发现了这样的代码:

$location = $_POST['location'];
$sql = "SELECT * FROM properties WHERE location LIKE '%$location%'";
$result = mysql_query($sql);

这里,用户输入的 $location 直接拼接进了SQL语句,没有任何过滤或转义,这是一个非常明显的 SQL注入漏洞 潜在点。我立即将其标记为第一个待验证的漏洞点。

4. 漏洞挖掘与原理深度解析

4.1 漏洞一:SQL注入漏洞(以搜索功能为例)

漏洞定位 :如上所述,位于 search_property.php ,参数 location (来自POST请求)直接拼接。

漏洞原理 :SQL注入的本质是“数据”被当成了“代码”执行。当攻击者在 location 参数中输入 ‘ OR ‘1’=‘1 时,拼接后的SQL语句变为:

SELECT * FROM properties WHERE location LIKE '%' OR '1'='1%'

由于 ‘1’=‘1‘ 永远为真,这条语句将返回 properties 表中的所有记录,绕过了基于地点的搜索限制。更危险的payload可以是 ‘; DROP TABLE users; -- ,试图删除用户表(具体能否执行取决于数据库权限和PHP配置)。

审计与验证过程

  1. 静态确认 :在代码中确认是否存在过滤函数。我检查了 search_property.php 及其包含的文件,没有发现对 $_POST[‘location’] 使用 mysql_real_escape_string() addslashes() 或预处理语句(PDO prepare)的痕迹。
  2. 动态验证 :使用浏览器或Burp Suite重放请求。我打开系统的房源搜索页面,拦截搜索请求,将 location 参数修改为 test’ AND ‘1’=‘2 。如果存在注入且页面返回结果异常(如无结果),初步证明注入点存在。为了进一步确认,我使用经典的延时注入payload: test’ AND SLEEP(5) -- 。如果页面响应延迟了大约5秒,则100%确认存在基于时间的盲注。
  3. 信息获取 :通过联合查询(UNION SELECT)尝试获取数据库信息。例如,构造 location 参数为: ‘ UNION SELECT 1, database(), user(), version() -- 。这需要先判断查询的列数,通过 ORDER BY 子句递增测试即可。

实操心得:在老旧的 mysql_* 扩展中, magic_quotes_gpc 配置可能会自动转义单引号,但在PHP 5.4+该配置已被移除。在我们的测试环境(PHP 5.6+)中,此漏洞是可利用的。这提醒我们, 了解目标环境的PHP配置对于判断漏洞可利用性至关重要

4.2 漏洞二:任意文件上传漏洞(以租客文档上传为例)

漏洞定位 :位于 upload_tenant_doc.php 。这是租客上传身份证、合同等文档的功能。

漏洞原理 :文件上传漏洞的产生,通常源于服务端对上传文件的 类型、内容、后缀、存储路径 校验不严。攻击者可以上传一个包含恶意代码(如PHP webshell)的文件,并使其在服务器上以可执行脚本的方式被解析,从而获取服务器控制权。

代码审计关键点 :我仔细阅读了 upload_tenant_doc.php 的核心逻辑。

// 伪代码示意
$target_dir = “uploads/tenant_docs/”;
$target_file = $target_dir . basename($_FILES[“doc”][“name”]);
$uploadOk = 1;
// 检查1: 文件是否已存在 (无关紧要)
if (file_exists($target_file)) { $uploadOk = 0; }
// 检查2: 限制文件大小 (无关类型)
if ($_FILES[“doc”][“size”] > 500000) { $uploadOk = 0; }
// 缺少关键的文件类型检查!!!
if ($uploadOk == 0) {
    echo “Sorry, your file was not uploaded.”;
} else {
    if (move_uploaded_file($_FILES[“doc”][“tmp_name”], $target_file)) {
        echo “The file “. htmlspecialchars(basename($_FILES[“doc”][“name”])). “ has been uploaded.”;
    }
}

这段代码的问题非常明显:

  1. 没有检查文件扩展名 :它只检查了文件大小和是否存在,没有对 $_FILES[“doc”][“name”] 的后缀(如 .php , .phtml , .jsp )进行黑名单或白名单过滤。
  2. 没有检查文件内容类型(MIME Type) $_FILES[“doc”][“type”] 是由浏览器提供的,可以被轻易篡改,不可信。代码中甚至没有参考这个值。
  3. 没有对文件名进行重命名 :直接使用用户上传的文件名,可能导致覆盖已有文件或上传特殊文件名(如 ../../../shell.php 进行路径遍历)。

漏洞复现

  1. 我编写了一个简单的PHP webshell文件 shell.php ,内容为``。
  2. 在租客文档上传页面,选择这个 shell.php 文件进行上传。
  3. 上传成功后,页面返回了文件路径,例如 uploads/tenant_docs/shell.php
  4. 在浏览器中访问 http://localhost/rental_system/uploads/tenant_docs/shell.php?cmd=whoami
  5. 页面成功显示了Web服务器进程的运行用户(如 www-data apache ),证明恶意文件已被上传并成功执行。

这个漏洞危害极大,因为它提供了直接的服务器命令执行能力。在实际攻击中,攻击者会上传更强大的webshell,进而窃取数据、植入后门、进行内网渗透等。

4.3 漏洞三:后台权限绕过与垂直越权

漏洞定位 :在审计后台管理目录 admin/ 下的文件时,我检查每个文件的头部。发现了一个模式:大部分文件开头都有一行类似 include(‘../check_admin.php’); 的代码。我立刻去查看 check_admin.php 的内容。

漏洞原理 :权限校验的逻辑通常放在一个公共文件中,其他受保护页面通过包含(include)它来验证用户会话。如果某个本该受保护的页面忘记包含这个校验文件,或者校验逻辑存在缺陷,就会导致未授权访问,即权限绕过。

代码审计 :打开 check_admin.php ,其核心逻辑如下:

session_start();
if(!isset($_SESSION[‘admin_id’]) || $_SESSION[‘admin_id’] == “”){
    header(“Location: login.php”);
    exit();
}

逻辑看起来是标准的:检查会话中是否存在 admin_id ,不存在则跳转到登录页。这本身没问题。但关键在于, 所有后台页面是否都包含了这个文件 ?我使用全局搜索,在 admin/ 目录下查找不包含 check_admin.php 或类似校验语句的PHP文件。

很快,我发现 admin/export_tenants.php (一个导出租客列表的功能)和 admin/system_log.php (查看系统日志的功能)这两个文件, 开头直接是业务代码,没有任何 include(‘check_admin.php’) 或会话检查语句

漏洞复现

  1. 我首先正常登录一个普通用户(租客)账号,获取一个有效的非管理员会话。
  2. 在不登录管理员账号的情况下,直接在浏览器中访问 http://localhost/rental_system/admin/export_tenants.php
  3. 页面没有跳转到登录页,而是直接显示了数据导出选项或执行了导出操作。同样,访问 system_log.php 也能直接查看日志。
  4. 这意味着,任何知道这些URL的已认证用户(甚至在某些配置下,未认证用户)都可以执行管理员功能。如果 export_tenants.php 功能涉及敏感数据导出,就会造成数据泄露。

这种漏洞非常隐蔽,源于开发者的疏忽。在审计时, 逐一核对受保护目录下每个文件的权限校验逻辑是必不可少的步骤

5. 漏洞利用链构建与深度利用演示

5.1 从注入到GetShell:一条可能的攻击路径

单独看每个漏洞危害已经不小,但攻击者往往不会满足于此。他们会尝试将多个漏洞串联起来,形成一条杀伤力更大的攻击链。基于我们发现的漏洞,可以构建这样一条路径:

  1. 信息搜集(利用SQL注入) :攻击者首先通过 search_property.php 的SQL注入点,使用 UNION SELECT 查询数据库中的敏感信息。他们不仅想偷数据,更想获取网站绝对路径、管理员密码哈希等。

    • Payload示例 ‘ UNION SELECT 1,2,3,@@basedir,5,6,7,8 FROM properties --
    • 目的 :获取MySQL的安装基础目录 @@basedir ,有时能推断出Web根目录。
    • 进一步 :查询 information_schema.tables columns ,找到管理员表(可能叫 admin users ),然后爆出用户名和密码哈希字段。
  2. 破解或绕过认证(利用权限绕过) :如果从数据库中获得的管理员密码哈希强度较弱(如MD5),可能被快速破解。如果无法破解,攻击者可以直接利用发现的 后台权限绕过漏洞 。他们无需密码,直接访问 admin/export_tenants.php 等未授权页面,相当于进入了后台。

  3. 立足与持久化(利用文件上传) :进入后台后,攻击者会寻找任何文件上传功能。在房屋租赁系统中,上传房源图片、LOGO、公告附件等功能很可能存在。即使这个功能有前端校验,攻击者也可以通过Burp Suite拦截修改请求,将 shell.php 的文件名改为 shell.php.jpg ,同时修改 Content-Type image/jpeg ,并尝试绕过可能存在的后缀检查。如果后台的上传功能同样缺乏严格校验(这在很多系统中很常见),攻击者就能成功上传webshell。

  4. 获取服务器控制权 :通过访问上传的webshell,攻击者获得了在Web服务器上执行命令的能力。他们可以进一步提权、嗅探内网、植入持久化后门,完全控制这台服务器。

这条攻击链演示了“外部注入 -> 信息泄露 -> 权限提升 -> 代码执行”的经典渗透流程。它告诉我们, 中低危漏洞的组合可能产生高危甚至严重的影响

5.2 漏洞修复方案与安全开发建议

针对以上发现的漏洞,修复措施必须立即跟进:

1. SQL注入修复:

  • 根本方案 :将废弃的 mysql_* 扩展升级为 MySQLi PDO ,并使用 参数化查询(预处理语句)
    // 使用PDO示例
    $pdo = new PDO(“mysql:host=localhost;dbname=house_rental”, “username”, “password”);
    $stmt = $pdo->prepare(“SELECT * FROM properties WHERE location LIKE :location”);
    $stmt->execute([‘:location’ => ‘%’ . $_POST[‘location’] . ‘%’]);
    $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
  • 临时方案 :如果无法立即重构代码,必须对所有用户输入进行严格的转义和过滤。使用 mysqli_real_escape_string() (对应MySQLi连接)或 addslashes() (需注意字符集),并对输入类型进行强制转换(如数字型参数用 intval() )。

2. 文件上传漏洞修复:

  • 白名单校验 :只允许指定的、安全的文件扩展名(如 .jpg , .png , .pdf , .docx )。
    $allowed_ext = array(‘jpg’, ‘jpeg’, ‘png’, ‘gif’, ‘pdf’, ‘doc’, ‘docx’);
    $file_ext = strtolower(pathinfo($target_file, PATHINFO_EXTENSION));
    if(!in_array($file_ext, $allowed_ext)) { die(“Invalid file type.”); }
    
  • 文件内容检查 :使用 finfo_file() 函数(Fileinfo扩展)根据文件内容(而非扩展名或MIME)判断真实类型。
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mime = finfo_file($finfo, $_FILES[“doc”][“tmp_name”]);
    if(!in_array($mime, array(‘image/jpeg’, ‘application/pdf’))) { die(“Invalid file content.”); }
    
  • 重命名文件 :使用随机生成的文件名(如 md5(uniqid()) . ‘.’ . $file_ext )存储,避免覆盖和路径遍历。
  • 设置目录权限 :将上传目录(如 uploads/ )的脚本执行权限移除(在Apache中可通过 .htaccess 文件设置 php_flag engine off )。

3. 权限绕过修复:

  • 统一入口与强制包含 :在后台目录( admin/ )的 .htaccess 中设置重写规则,强制所有请求通过一个统一的入口文件(如 index.php ),在该入口文件中进行权限校验。
  • 代码审查与自动化检查 :对所有 admin/ 下的文件进行人工复查,确保每个文件开头都包含了权限检查代码。可以编写一个简单的脚本,扫描目录下所有PHP文件,检查是否包含特定的校验语句。
  • 使用中间件或框架 :如果可能,引入一个简单的MVC框架或自定义路由,在控制器层面统一进行身份认证和授权校验,避免在单个文件中遗漏。

6. 白盒审计工具辅助与自动化思路

6.1 静态代码分析工具(SAST)的运用

人工审计虽然深入,但面对大型项目效率较低。我们可以借助静态应用程序安全测试(SAST)工具进行初步的、全局性的漏洞模式扫描。对于PHP项目,一些优秀的开源工具包括:

  • RIPS :一款经典的PHP静态代码分析工具,能有效检测SQLi、XSS、文件包含、命令执行等漏洞。将项目路径提供给RIPS,它能生成一份详细的漏洞报告,并高亮显示有问题的代码行。
  • SonarQube (with PHP Plugin) :这是一个更强大的代码质量与安全平台。通过配置SonarQube服务器和扫描器,可以对代码进行持续检测,它不仅发现安全漏洞,还能发现代码坏味道、潜在bug等。
  • Semgrep :一款快速、轻量级的静态分析工具,支持自定义规则。我们可以编写或使用现成的规则集来匹配PHP中的不安全模式,例如“未过滤的用户输入直接用于数据库查询”。

在我的审计过程中,我使用了RIPS对“Best house rental management system”进行了扫描。它成功地标记出了我们人工发现的 search_property.php 中的SQL注入点,以及几处我们尚未注意到的潜在XSS(跨站脚本)点,例如在 property_details.php 中直接输出用户评论时未使用 htmlspecialchars() 函数。 工具的价值在于查漏补缺和提升效率,但它不能替代人工对业务逻辑漏洞(如权限绕过)的深度分析。

6.2 自动化审计脚本与自定义规则

对于重复性的检查工作,可以编写简单的脚本进行自动化。例如:

  • 查找未经验证的直接输入使用 :用 grep awk 命令搜索所有将 $_GET $_POST $_REQUEST 直接用于 echo print 或SQL查询的代码行。
    grep -r “echo.*\$_GET\|echo.*\$_POST\|echo.*\$_REQUEST” –include=“*.php” .
    grep -r “mysql_query.*\$_GET\|mysql_query.*\$_POST” –include=“*.php” .
    
  • 检查文件包含漏洞 :搜索包含函数,并检查其参数是否包含变量。
    grep -r “include.*\$” –include=“*.php” .
    grep -r “require.*\$” –include=“*.php” .
    
  • 检查危险函数 :一键列出所有使用了 eval() , assert() , system() , exec() 等函数的文件。
    grep -r “eval(” –include=“*.php” .
    grep -r “system(” –include=“*.php” .
    

将这些命令整合成一个Shell脚本或Python脚本,可以在新项目审计开始时快速生成一份“可疑点”清单,极大提高初始排查速度。

7. 总结与反思:从一次审计中学到的

这次对“Best house rental management system V1.0”的审计,虽然源于一个具体的CVE,但整个过程反映了许多中小型Web应用,尤其是传统PHP项目的通病:依赖过时且不安全的扩展( mysql_* )、对用户输入缺乏敬畏之心、权限校验存在遗漏、对文件上传的风险认知不足。

从技术细节回到方法论,一次成功的白盒审计离不开以下几点: 扎实的环境搭建 是基础,它让你能动态验证猜想; 清晰的审计策略 是地图,让你不至于在代码海洋中迷失; 对危险函数的敏感度 是探雷器,能快速定位高风险区域;而 对业务逻辑的理解 则是X光机,能发现那些静态扫描工具无法察觉的深层漏洞(如权限绕过)。

最后,修复漏洞永远比发现漏洞更重要。作为开发者,应当将“安全由设计”、“最小权限”、“输入验证与输出编码”等原则融入开发习惯。作为安全人员,我们的价值不仅在于找出问题,更在于提供清晰、可落地的修复方案,帮助提升整个产品的安全水位。这次审计报告,连同详细的修复建议,就是交付给开发团队最好的“药方”。

更多推荐