从零搭建Pikachu本地靶场:手把手实战SQL注入原理与防御

发布时间:2026/7/29 7:24:39

从零搭建Pikachu本地靶场:手把手实战SQL注入原理与防御 1. 项目概述为什么我们需要一个本地靶场如果你是一名刚刚踏入安全领域的新手或者是一名开发人员想要理解那些听起来很酷的“SQL注入”、“XSS攻击”到底是怎么回事你可能会立刻想到去网上找一些在线的靶场平台。这当然可以但很快你就会遇到问题网络延迟、平台不稳定、功能受限甚至有些在线靶场因为安全原因关闭了某些高危漏洞的演示。更关键的是你无法深入底层去查看数据库的实时变化、去修改配置文件、去搭建一个完全受自己控制的实验环境。这种感觉就像学开车你永远坐在副驾驶看别人操作方向盘和油门都不在你手里。所以搭建一个本地测试环境尤其是像Pikachu这样的综合性漏洞靶场就成为了从理论走向实践的关键一步。Pikachu靶场是一个使用PHP/MySQL开发的、专门用于Web安全漏洞学习和测试的开源项目。它覆盖了SQL注入、XSS、CSRF、文件上传、RCE等几乎所有常见的Web漏洞类型并且每种漏洞都提供了多种不同的场景和难度等级。把它搭建在你自己的电脑上意味着你拥有了一个24小时不间断、可以随意“破坏”和研究的私人实验室。我最初接触Pikachu就是为了彻底搞懂SQL注入。光看文章总觉得隔了一层纱那些union select、information_schema的概念很模糊。但当你亲手在靶场的输入框里敲入一段Payload亲眼看到网页返回了数据库的名字、表名、甚至管理员账号密码时那种“原来如此”的顿悟感是无与伦比的。本地环境让你可以大胆尝试错了就重置数据库完全不用担心造成任何实际危害。接下来我就详细拆解如何从零开始搭建Pikachu靶场并以最经典的SQL注入为例带你走一遍完整的手动测试流程分享那些只有亲手操作过才会知道的细节和坑。2. 环境准备与Pikachu靶场部署2.1 核心组件选型为什么是“小皮面板”要运行PHPMySQL的Pikachu靶场我们需要一个Web服务器环境。对于新手而言在Windows或macOS上手动配置Apache、PHP、MySQL这一套组合常被称为WAMP/MAMP步骤繁琐容易出错。因此我强烈推荐使用集成环境软件它能一键安装并管理所有组件。在众多选择中我首选PHPStudy因其Logo常被称为“小皮面板”。理由很充分首先它完全免费且对中文用户友好界面直观。其次它提供了不同版本的PHP、Apache/Nginx、MySQL的自由切换这对于学习兼容性问题和复现特定环境下的漏洞非常有用。最后它的管理功能很完善一键启停服务、修改配置、创建数据库等操作都能在图形化界面中完成极大降低了入门门槛。当然你也可以选择XAMPP、WampServer等原理类似。但根据我的经验PHPStudy在Windows下的兼容性和易用性综合得分最高这也是为什么它在相关热词中频繁出现“pikachu靶场 小皮”。注意请务必从PHPStudy的官方网站下载最新版本避免第三方打包的软件可能携带恶意代码。2.2 分步部署实战部署过程可以概括为“安装环境 - 放置靶场 - 配置数据库”三步。下面我结合可能遇到的坑详细说明。第一步安装与启动PHPStudy下载PHPStudy安装包运行安装程序。建议安装路径不要包含中文或空格例如D:\phpstudy_pro。安装完成后启动PHPStudy。你会看到主界面。首先需要启动Apache和MySQL服务。点击对应的“启动”按钮即可。当按钮变为“停止”状态且旁边显示绿色图标或“运行中”字样表示启动成功。常见问题1端口冲突。如果Apache默认80端口或MySQL默认3306端口启动失败很可能是端口被占用。例如你电脑上已经运行了IIS、Skype或另一个MySQL实例。解决方法在PHPStudy主界面点击“设置”-“端口常规设置”修改Apache端口为8080MySQL端口为3307等未被占用的端口然后重启服务。第二步部署Pikachu源码从GitHub搜索“pikachu”或可信源下载Pikachu的源码压缩包。解压后你会得到一个名为pikachu-master或类似的文件夹。将这个文件夹整体复制到PHPStudy的网站根目录下。这个根目录通常是phpstudy_pro\WWW\。为了访问方便可以将文件夹重命名为简单的pikachu。此时Pikachu的完整路径应该是D:\phpstudy_pro\WWW\pikachu\。第三步初始化数据库这是最关键也最容易出错的一步。打开浏览器访问你的Pikachu靶场。如果Apache运行在80端口地址是http://localhost/pikachu。如果改了端口如8080则是http://localhost:8080/pikachu。首次访问页面很可能会显示一个红色提示“数据库连接错误”或“欢迎使用pikachu漏洞练习平台请先初始化安装”。点击页面上的“初始化安装”或类似链接。Pikachu的安装脚本会自动完成以下工作在MySQL中创建一个名为pikachu的数据库。运行SQL文件创建所有漏洞演示所需的数据表并插入初始数据如用户账号、文章内容等。核心细节与避坑指南数据库账号密码安装脚本默认使用MySQL的root用户和空密码root/进行连接。这正是PHPStudy的默认配置。如果你的PHPStudy修改过MySQL的root密码安装必定失败。解决方法有两种一是在PHPStudy中将MySQL的root密码重置为空二是在Pikachu源码中找到inc/config.inc.php文件手动修改其中的数据库连接信息使其与你的MySQL配置匹配。// 示例修改config.inc.php中的配置 define(DBUSER, root); // 数据库用户名 define(DBPWD, your_password); // 你的MySQL密码 define(DBNAME, pikachu); // 数据库名安装成功标志安装完成后页面会显示“安装成功”的提示并且“初始化安装”按钮会消失。此时刷新页面你就能看到Pikachu漏洞平台的主界面左侧是清晰的漏洞分类菜单。至此你的本地Pikachu靶场就搭建完毕了。整个过程如果顺利10分钟内就能搞定。拥有这个环境后你就可以不受干扰地、反复地进行各种安全测试了。3. SQL注入漏洞原理深度解析在开始“打靶”之前我们必须先弄清楚“子弹”的原理。SQL注入之所以常年位居OWASP Top 10前列根本原因在于它将“用户输入的数据”和“程序要执行的代码SQL语句”混淆在了一起。3.1 从一段代码看漏洞根源想象一个简单的用户登录场景。后端PHP代码可能是这样的$username $_POST[username]; // 用户从表单输入的用户名 $password $_POST[password]; // 用户从表单输入的密码 $sql SELECT * FROM users WHERE username $username AND password $password; $result mysqli_query($conn, $sql);这段代码的意图是构造一条SQL查询在users表中查找用户名和密码都匹配的记录。如果用户老老实实输入admin和123456那么生成的SQL语句是SELECT * FROM users WHERE username admin AND password 123456这完全正确。但问题就出在$username和$password是直接拼接进SQL字符串的。如果一个攻击者在用户名输入框中输入的不是admin而是一个精心构造的字符串admin --注意最后有个空格那么拼接后的SQL语句就变成了SELECT * FROM users WHERE username admin -- AND password xxx在SQL中--是单行注释符它意味着后面的所有内容都会被数据库忽略。于是这条语句的实际效果变成了SELECT * FROM users WHERE username admin它只验证了用户名完全绕过了密码检查如果数据库中恰好存在用户名为admin的记录攻击者就能在不知道密码的情况下成功登录。这就是最经典的基于单引号字符和注释符的注入。3.2 注入类型的分类与危害SQL注入远不止这一种形式根据应用程序处理输入的方式和数据库特性主要可以分为以下几类理解它们对后续测试至关重要基于错误信息的注入应用程序将数据库的报错信息直接显示给用户。攻击者通过故意输入非法参数如单引号诱发数据库报错从而从错误信息中获取数据库结构、字段名等敏感信息。这是初探一个是否存在SQL注入漏洞的常用手段。联合查询注入这是信息获取的主要手段。利用SQL的UNION操作符将恶意查询的结果“附加”到原始查询结果之后从而在网页上显示出来。这通常用于获取其他表的数据如union select 1, database(), user(), 4。布尔盲注页面不会返回具体数据或错误信息只会根据查询结果返回“正常页面”或“错误页面”如404、无数据。攻击者通过构造真/假逻辑的查询像“猜谜”一样一位一位地推断出数据内容。例如and ascii(substr(database(),1,1))100通过页面反应判断猜测是否正确。时间盲注这是布尔盲注的升级版页面无论查询真假返回的界面都一模一样。攻击者利用数据库的延时函数如MySQL的sleep()通过观察页面响应时间的长短来判断注入语句是否执行成功。例如and if(ascii(substr(database(),1,1))100, sleep(5), 0)如果响应延迟了5秒说明猜测正确。堆叠查询注入有些数据库支持一次性执行多条用分号;隔开的SQL语句。攻击者可以利用此特性执行任意SQL命令危害极大。例如; DROP TABLE users; --。但并非所有数据库驱动都支持此功能PHP的mysqli默认不支持多语句查询这是一个重要的安全缓解点。危害不仅仅是绕过登录。通过SQL注入攻击者可以达到以下目的盗取整个数据库的内容用户信息、交易记录篡改网站数据修改文章、转账金额删除数据表甚至整个数据库DROP DATABASE在特定条件下甚至能利用数据库功能读写服务器文件或执行系统命令从而完全控制服务器。4. Pikachu SQL注入靶场实战通关详解Pikachu靶场的SQL注入模块精心设计了多个关卡几乎涵盖了上述所有注入类型。我们挑选几个最具代表性的关卡进行手把手实战。4.1 第一关数字型注入访问SQL-Inject-数字型注入。页面通常是一个简单的查询比如根据用户ID查询信息。漏洞探测首先输入一个正常数字1页面返回ID为1的用户信息。接着我们输入1 and 11。如果页面正常返回再输入1 and 12。如果第一个正常而第二个无返回或报错这就强烈暗示存在数字型注入。因为11永真12永假影响了查询逻辑。判断列数为了后续使用union查询我们需要知道原始查询语句SELECT了多少列。使用order by语句递增猜测1 order by 1正常1 order by 2正常... 直到1 order by N时页面报错说明列数为N-1。假设我们测出列数为3。联合查询获取信息现在可以构造union语句了。首先确定哪些列的位置会在页面上显示出来。输入-1 union select 1,2,3。这里把原ID设为-1一个不存在的ID是为了让原查询结果为空从而页面只显示我们union select的结果。页面可能会在显示数字2和3的位置。利用显示位假设数字2和3的位置被显示在页面上。我们就可以把想要的信息放在这两个位置。例如获取当前数据库名和用户名-1 union select 1, database(), user()获取数据库版本-1 union select 1, version(), 3查询information_schema数据库MySQL的元数据库存储所有表、列信息获取所有表名-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schemadatabase()获取某个表如users的所有列名-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_nameusers and table_schemadatabase()最终拖取数据-1 union select 1,username,password from users实操心得group_concat()函数在这里非常关键它能把多行结果合并成一个字符串返回方便我们一次性查看。否则union每次只能返回一行数据查看大量数据会非常麻烦。4.2 第二关字符型注入访问SQL-Inject-字符型注入。这一关的输入通常被单引号包裹如WHERE name$input。漏洞探测输入一个单引号。如果页面报出数据库语法错误如You have an error in your SQL syntax...则证明存在字符型注入并且输入被带入了SQL语句。构造闭合我们的目标是“修复”这个被我们破坏的SQL语句并在此基础上添加我们自己的恶意代码。原始逻辑可能是...name$input。我们输入 or 11闭合过程如下我们输入 or 11拼接后SQL...name or 11逻辑name为假但11永真。假 OR 真 真因此整个WHERE条件恒成立通常会返回所有数据。注释掉后续语句更常用的方法是使用注释符。输入 or 11 --注意--后有一个空格在URL中空格常编码为或%20。拼接后SQL...name or 11 -- 。--之后的所有内容被注释语句变为...name or 11同样恒真。后续步骤判断列数、联合查询等步骤与数字型注入完全一致只需在构造Payload时注意处理好单引号的闭合与注释即可。例如判断列数 order by 3 --。4.3 第三关搜索型注入访问SQL-Inject-搜索型注入。这类注入常用于搜索功能SQL语句可能使用LIKE关键字如...WHERE title LIKE %$input%。理解结构输入的关键词会被百分号%包裹。%在SQL中代表任意字符。所以如果我们输入test生成的语句是LIKE %test%。探测与闭合输入一个单引号试探。如果报错说明存在注入。我们需要闭合的不仅是单引号还有前后两个%。观察猜测语句结构为LIKE %$input%。构造Payload我们可以这样输入test% and 11 --。拼接后原始LIKE %$input%代入LIKE %test% and 11 -- %解释%test%构成了一个完整的LIKE模式字符串。然后我们用单引号闭合这个字符串后面添加我们的逻辑and 11再用--注释掉后面多余的%。这样我们就成功“逃逸”出了LIKE的括号并附加了新的SQL条件。后续利用同样后续的判断列数、联合查询等操作只需确保Payload能正确闭合前面的LIKE模式即可。例如判断列数test% order by 2 --。4.4 第四关XX型注入Pikachu中可能还有“XX型注入”这通常指输入被其他符号包裹例如括号()。闭合原理是相通的。探测输入单引号或双引号看报错信息。有时会报出)之类的错误提示我们闭合结构可能是($input)。构造如果结构是($input)我们需要先闭合单引号再闭合括号然后注释。Payload示例) or 11 --。拼接后() or 11 -- )逻辑恒真。核心思路无论是什么“型”核心思路都是通过输入闭合掉原有的引号或括号然后插入我们自己的SQL逻辑最后用注释符“清理”掉后续多余的代码。这就像拆解一个锁你要先理解它的锁芯结构如何闭合然后才能用工具Payload打开它。4.5 盲注实战基于布尔与时间的“猜解”访问SQL-Inject-盲注相关关卡。页面通常只有一个“对/错”或“有/无”的反馈。布尔盲注手动测试思路首先用and 11和and 12确认注入点观察页面是否存在差异如“存在”与“不存在”。然后开始“猜”数据。例如猜当前数据库名的第一个字符的ASCII码。Payload: and ascii(substr(database(),1,1))100 --如果页面返回“存在”状态说明ASCII码大于100如果返回“不存在”则小于等于100。我们可以用二分法50, 75...快速缩小范围最终确定准确的ASCII码值再转换为字符如97对应‘a’。接着猜第二个字符substr(database(),2,1)以此类推。整个过程极其繁琐必须借助工具。时间盲注手动测试思路即使页面毫无变化我们也可以通过响应时间来判断。Payload: and if(ascii(substr(database(),1,1))100, sleep(5), 0) --如果页面响应明显延迟约5秒说明条件为真第一个字符ASCII100如果立即返回则为假。同样使用二分法逼近。工具的重要性手动进行盲注是不现实的。在实际学习和测试中我们会使用sqlmap这样的自动化工具。但理解其原理至关重要这能帮助你理解工具在做什么以及如何编写更高效的Payload。对于盲注关卡我建议先手动构造1-2个Payload理解过程然后使用sqlmap验证结果。sqlmap命令示例sqlmap -u http://localhost/pikachu/vul/sqli/sqli_blind.php?id1 --techniqueB --current-db。5. 从攻击到防御SQL注入的修复实践在Pikachu上成功实施注入后我们更应该思考如何修复它。这才是安全学习的终极目的——建设而非破坏。5.1 根本原因与防御原则SQL注入的根本原因是“数据与代码的混淆”。因此防御的核心原则是将用户输入的数据永远视为数据而不是可执行的代码部分。实现这一原则有以下几个层次的方法使用参数化查询这是最有效、最根本的防御手段有时也被称为“预编译语句”。它的原理是将SQL语句的结构代码和传入的值数据分开发送数据库处理。数据库先编译SQL结构知道这是一个查询username和password的语句然后再将用户输入的admin --作为纯粹的“字符串数据”填入username这个位置。即使数据中包含SQL元字符也只会被当作普通字符串处理无法改变查询结构。PHP (PDO) 示例$stmt $pdo-prepare(SELECT * FROM users WHERE username :username AND password :password); $stmt-execute([username $username, password $password]); $user $stmt-fetch();PHP (MySQLi) 示例$stmt $conn-prepare(SELECT * FROM users WHERE username ? AND password ?); $stmt-bind_param(ss, $username, $password); // “ss”表示两个字符串参数 $stmt-execute(); $result $stmt-get_result();输入验证与过滤在参数化查询的基础上进行额外的加固。对输入的数据类型、长度、格式进行严格检查。例如如果期望是数字就用intval()强制转换如果是邮箱就用正则表达式验证格式。但切记过滤不能替代参数化查询因为过滤规则可能被绕过。最小权限原则为Web应用连接数据库的账号分配最小的必要权限。通常只授予SELECT、INSERT、UPDATE、DELETE等基本数据操作权限绝对不要赋予DROP、FILE、EXECUTE等高危权限。这样即使发生注入危害也能被限制在可控范围内。避免动态拼接SQL这是开发中的大忌。正如热词中提到的“mybatis 动态sql 使用${}”在MyBatis框架中#{}是安全的参数占位符会进行预编译而${}是字符串替换直接拼接使用${}就引入了SQL注入风险。在代码审计时搜索${是发现潜在漏洞的快捷方式。5.2 在Pikachu中验证修复效果Pikachu靶场通常也提供了“安全”版本的代码供对比。例如在完成“字符型注入”后你可以查看其“安全”关卡。对比两者的源代码你会发现安全版本无一例外地使用了mysqli_prepare或PDO的预处理语句。亲手在本地修改漏洞代码用安全代码替换然后再次测试你会发现之前所有巧妙的Payload都失效了这正是防御措施生效的直观体现。6. 常见问题排查与进阶技巧在实际搭建和测试过程中你肯定会遇到各种问题。这里我整理了一份速查表涵盖了最常见的情况问题现象可能原因解决方案访问http://localhost/pikachu显示404或目录列表1. Pikachu文件夹未放在WWW目录下。2. Apache未启动或端口错误。3. 文件夹名错误。1. 检查文件路径。2. 确认PHPStudy中Apache服务已启动并尝试用http://localhost:端口号/pikachu访问。3. 确认URL中的文件夹名与实际一致。Pikachu首页显示“数据库连接错误”1. MySQL服务未启动。2. 数据库配置信息错误密码不对。3.pikachu数据库未创建。1. 启动MySQL服务。2. 检查inc/config.inc.php中的用户名、密码或重置MySQL密码为空。3. 访问安装页面如/pikachu/install.php进行初始化。注入测试时输入单引号后页面一片空白或报500错误1. PHP错误显示被关闭。2. 应用程序有全局错误处理屏蔽了SQL错误。1. 在PHPStudy中开启display_errors生产环境切勿开启。2. 尝试布尔盲注或时间盲注的Payload不依赖错误信息。union select时页面只显示一行数据或显示错位1. 原查询语句可能有多行结果干扰显示。2.union前后查询的列数或数据类型不匹配。1. 确保原查询结果为空如使用-1或id99999。2. 仔细核对列数并使用null或数字占位符来匹配数据类型。使用sqlmap扫描本地靶场速度慢或无结果1.sqlmap默认的测试等级和风险等级较低。2. 靶场有简单的防护机制如Token。3. 目标URL格式不对。1. 增加参数--level3 --risk2。2. 使用--random-agent伪装浏览器头或使用--data提交POST数据。3. 确保URL完整如http://localhost/pikachu/vul/sqli/sqli_id.php?id1。时间盲注测试时sleep()函数不生效1. 数据库用户权限可能被限制无法执行sleep()函数。2. 网络延迟导致时间判断不准。1. 尝试使用其他延时方法如MySQL的BENCHMARK(10000000,MD5(1))大量计算消耗时间。2. 在sqlmap中调整--time-sec参数设置更长的延时基准。进阶技巧分享利用sqlmap进行高效学习不要只满足于跑出数据。使用-v 3参数让sqlmap输出它发送的每一个Payload。观察这些Payload是如何构造的特别是如何闭合语句、如何绕过简单的过滤这是学习高级注入技巧的绝佳方式。阅读源码Pikachu每个漏洞点都有对应的前端和后端源码。在测试前后一定要去读一读vul和safe目录下的代码。对比着看你能清晰地看到漏洞是如何产生的以及如何被修复的。这种“上帝视角”能让你理解得更透彻。搭建其他靶场当你通关Pikachu后可以尝试搭建DVWA、SQLi-Labs、Upload-Labs等更专注或更复杂的靶场。每个靶场的设计侧重点不同能帮你巩固和拓展知识。例如SQLi-Labs专注于SQL注入的各种变种而Upload-Labs则聚焦于文件上传漏洞。

相关新闻