手把手复现BUUCTF Ezsql漏洞环境:在Docker里搭建你的第一个PHP+MySQL靶场

手把手复现BUUCTF Ezsql漏洞环境:在Docker里搭建你的第一个PHP+MySQL靶场 从零构建PHPMySQL漏洞靶场Docker实战BUUCTF Ezsql环境复现在网络安全学习过程中理论知识的吸收固然重要但真正能让你快速成长的往往是动手实践。今天我将带你从零开始用Docker完整复现BUUCTF Ezsql这道经典的SQL注入题目环境。不同于简单的WriteUp阅读我们将亲手搭建一个可攻击、可修复的实战环境让你在安全可控的环境中体验从漏洞发现到修复的全过程。1. 环境准备与Docker基础配置在开始之前我们需要准备好基础环境。Docker作为轻量级的容器技术非常适合用来构建这种一次性的实验环境。如果你还没有安装Docker可以参考官方文档进行安装这里我们假设你已经具备了基本的Docker使用知识。首先创建一个项目目录我们将所有相关文件都放在这个目录下mkdir buuctf-ezsql cd buuctf-ezsql接下来我们需要准备三个核心文件Dockerfile定义我们的容器环境docker-compose.yml编排PHP和MySQL服务漏洞PHP文件模拟题目中的有漏洞登录页面2. 编写Docker配置文件我们先从Dockerfile开始这个文件定义了PHP环境的构建规则FROM php:7.4-apache RUN docker-php-ext-install mysqli docker-php-ext-enable mysqli RUN apt-get update apt-get install -y \ libzip-dev \ rm -rf /var/lib/apt/lists/* COPY src/ /var/www/html/这个配置做了以下几件事基于官方PHP 7.4镜像构建安装mysqli扩展用于PHP连接MySQL复制我们的PHP源代码到容器中接下来是docker-compose.yml它将协调PHP和MySQL两个服务version: 3 services: web: build: . ports: - 8080:80 depends_on: - db environment: - MYSQL_HOSTdb - MYSQL_USERroot - MYSQL_PASSWORDroot - MYSQL_DATABASEctf db: image: mysql:5.7 environment: - MYSQL_ROOT_PASSWORDroot - MYSQL_DATABASEctf volumes: - mysql_data:/var/lib/mysql volumes: mysql_data:这个配置定义了两个服务web我们的PHP应用映射到宿主机的8080端口dbMySQL数据库服务使用MySQL 5.7版本3. 构建有漏洞的PHP应用现在我们来创建漏洞页面。在src目录下创建index.php?php error_reporting(0); $conn new mysqli(getenv(MYSQL_HOST), getenv(MYSQL_USER), getenv(MYSQL_PASSWORD), getenv(MYSQL_DATABASE)); if ($conn-connect_error) { die(连接失败: . $conn-connect_error); } $username $_GET[username] ?? ; $password $_GET[password] ?? ; if (!empty($username) !empty($password)) { $sql SELECT * FROM users WHERE username $username AND password $password; $result $conn-query($sql); if (!$result) { die(查询错误: . $conn-error); } if ($result-num_rows 0) { echo h2登录成功/h2; } else { echo h2用户名或密码错误/h2; } } ? !DOCTYPE html html head title登录系统 - Ezsql漏洞演示/title style body { font-family: Arial, sans-serif; max-width: 500px; margin: 0 auto; padding: 20px; } .login-form { margin-top: 30px; } input, button { padding: 8px; margin: 5px 0; width: 100%; } /style /head body h1登录系统/h1 div classlogin-form form methodGET input typetext nameusername placeholder用户名 required input typepassword namepassword placeholder密码 required button typesubmit登录/button /form /div /body /html这个文件的关键漏洞在于直接拼接用户输入到SQL查询中没有进行任何过滤或参数化处理。4. 初始化数据库我们需要创建一个数据库初始化脚本src/init.sqlCREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL, password VARCHAR(50) NOT NULL ); INSERT INTO users (username, password) VALUES (admin, admin123), (test, test123);为了让这个脚本在容器启动时自动执行我们需要修改docker-compose.yml中的db服务部分db: image: mysql:5.7 environment: - MYSQL_ROOT_PASSWORDroot - MYSQL_DATABASEctf volumes: - mysql_data:/var/lib/mysql - ./src/init.sql:/docker-entrypoint-initdb.d/init.sql5. 启动环境并测试漏洞现在我们可以启动整个环境了docker-compose up --build等待所有服务启动完成后访问http://localhost:8080你应该能看到登录页面。漏洞测试 尝试使用经典的SQL注入payload用户名admin OR 11密码任意值如123你应该会看到登录成功的提示这说明我们的漏洞环境搭建成功了。6. 漏洞分析与修复让我们分析一下这个漏洞。问题出在PHP代码中的这一行$sql SELECT * FROM users WHERE username $username AND password $password;当用户输入admin OR 11时实际执行的SQL语句变成了SELECT * FROM users WHERE username admin OR 11 AND password 123由于11永远为真这个查询会返回users表中的所有记录导致绕过认证。修复方案有几种方法可以修复这个漏洞使用预处理语句推荐$stmt $conn-prepare(SELECT * FROM users WHERE username ? AND password ?); $stmt-bind_param(ss, $username, $password); $stmt-execute(); $result $stmt-get_result();使用过滤函数$username $conn-real_escape_string($username); $password $conn-real_escape_string($password);使用PHP的PDO扩展$stmt $pdo-prepare(SELECT * FROM users WHERE username :username AND password :password); $stmt-execute([username $username, password $password]);7. 加固后的完整代码让我们实现第一个方案预处理语句这是最安全的方式?php error_reporting(0); $conn new mysqli(getenv(MYSQL_HOST), getenv(MYSQL_USER), getenv(MYSQL_PASSWORD), getenv(MYSQL_DATABASE)); if ($conn-connect_error) { die(连接失败: . $conn-connect_error); } $username $_GET[username] ?? ; $password $_GET[password] ?? ; if (!empty($username) !empty($password)) { $stmt $conn-prepare(SELECT * FROM users WHERE username ? AND password ?); $stmt-bind_param(ss, $username, $password); $stmt-execute(); $result $stmt-get_result(); if ($result-num_rows 0) { echo h2登录成功/h2; } else { echo h2用户名或密码错误/h2; } } ? !DOCTYPE html html !-- 保持原有的HTML部分不变 -- /html8. 验证修复效果修改代码后我们需要重启服务使更改生效docker-compose down docker-compose up再次尝试之前的SQL注入payload现在应该会看到用户名或密码错误的提示说明我们的修复生效了。额外测试用例正常登录admin/admin123错误的登录admin/wrongpassword再次尝试SQL注入admin OR 11/任意密码9. 环境扩展与进阶学习现在你已经成功复现并修复了这个SQL注入漏洞可以进一步扩展这个环境添加更多漏洞类型XSS漏洞文件包含漏洞命令注入漏洞实现自动化测试 使用Python脚本自动化测试漏洞import requests url http://localhost:8080 payload {username: admin OR 11, password: 123} response requests.get(url, paramspayload) print(Vulnerable! if 成功 in response.text else Fixed!)集成到CTF比赛中 你可以把这个环境打包成一个CTF题目添加flag机制和重置功能。10. 最佳实践与安全建议在开发实际的PHP应用时除了修复这个特定的SQL注入漏洞外还应该遵循以下安全实践始终使用预处理语句处理所有数据库查询最小权限原则数据库用户只应拥有必要的最小权限错误处理生产环境中不要显示详细的错误信息输入验证对所有用户输入进行严格的验证定期更新保持PHP和MySQL等组件的更新安全防护措施对比表防护方法安全性易用性适用场景预处理语句★★★★★★★★★所有数据库操作转义函数★★★★★★★遗留代码快速修复存储过程★★★★★★复杂业务逻辑ORM框架★★★★★★★★★现代应用开发