Pikachu靶场实战:从原理到防御,手把手复现SQL注入漏洞

发布时间:2026/7/9 10:07:30
Pikachu靶场实战:从原理到防御,手把手复现SQL注入漏洞 1. 项目概述为什么选择Pikachu作为DVWA的平替如果你刚开始接触Web安全或者想找一个环境来复现SQL注入漏洞你大概率听说过DVWA。DVWADamn Vulnerable Web Application确实经典但它的安装过程尤其是在新版本的PHP环境下常常伴随着各种依赖冲突和配置报错让很多新手在“环境搭建”这一步就卡住了安全知识还没学先跟PHP版本和MySQL扩展较上了劲。这正是我推荐Pikachu靶场的原因。你可以把它看作是DVWA的一个优秀“平替”——功能相似但上手更友好。Pikachu是一个同样用PHP/MySQL开发的、专门为Web安全学习设计的漏洞演示平台。它最大的优点就是“开箱即用”。官方提供了完整的集成环境包解压后几乎不需要复杂的配置就能跑起来让你能立刻把精力聚焦在漏洞原理和攻击手法的学习上而不是浪费在环境调试上。对于想快速从“Hello World”式的理论理解过渡到亲手操作、亲眼看到漏洞效果的初学者来说Pikachu是一个非常理想的起点。本指南的核心就是带你用Pikachu靶场完整地走一遍SQL注入漏洞的复现流程。我们会从最基础的“有回显”的数字型注入开始一步步深入到更复杂的字符型、搜索型、盲注等场景。我的目标是你跟着操作一遍之后不仅能复现漏洞更能理解每一种注入背后的SQL语句是如何被“拼接”和“篡改”的以及在实际渗透测试中面对不同情况应该如何思考和选择攻击方式。这远比单纯点几个按钮、看到“注入成功”的提示更有价值。2. 环境准备与Pikachu靶场部署2.1 工具选型与获取工欲善其事必先利其器。为了复现SQL注入我们需要两样东西靶场环境和攻击工具。1. 靶场环境Pikachu我强烈建议直接从Pikachu的GitHub官方仓库下载最新版本。这样做的好处是能确保你拿到的是原汁原味、未经修改且相对稳定的版本。你可以搜索“pikachu GitHub”找到它。通常仓库会提供一个包含PHP、Apache、MySQL的集成环境比如基于XAMPP或PHPStudy的打包版这对于Windows用户来说是最省心的。如果你是Linux或macOS用户也可以选择只下载源码然后自行配置Web服务器和数据库。2. 攻击与探测工具浏览器与Burp Suite浏览器任何现代浏览器Chrome、Firefox均可主要用于访问靶场和发起基础攻击。Burp Suite这是Web安全测试的“瑞士军刀”社区版就足够我们学习使用。它强大的代理和重放功能能让我们精细地观察和修改每一个发往靶场的HTTP请求这对于理解注入过程和构造复杂的Payload至关重要。后续的很多操作我们都会在Burp Suite的Repeater模块里进行。可选工具sqlmap这是一个自动化的SQL注入检测与利用工具。在初步学习阶段我不建议过度依赖它因为容易变成“黑盒”操作不利于理解原理。但在我们手动理解了注入点之后可以用它来验证我们的判断并学习如何自动化利用。注意请务必在虚拟机或专属的测试环境中部署和运行Pikachu靶场。切勿将其安装在有真实业务数据或连接公网的服务器上。这些靶场程序本身充满漏洞是攻击者的绝佳目标。2.2 一键部署与初始化配置这里以Windows系统下使用集成环境包为例演示最快速的部署方法。解压与放置将下载的Pikachu集成包解压。你会看到一个名为pikachu的文件夹。将这个文件夹整个复制到你的Web服务器根目录下。如果你用的是PHPStudy这个目录通常是phpstudy_pro/WWW/如果用的是XAMPP则是xampp/htdocs/。启动服务打开PHPStudy或XAMPP启动Apache和MySQL服务。确保它们都运行正常图标变绿。访问与安装打开浏览器访问http://localhost/pikachu如果你的文件夹名不同则替换为对应的路径。首次访问时Pikachu通常会提示你进行初始化安装。数据库连接配置点击安装链接进入配置页面。这里需要填写数据库信息。数据库地址一般填写localhost或127.0.0.1。数据库名可以新建一个比如pikachu或者使用它默认推荐的。用户名和密码集成环境里MySQL的默认用户通常是root密码可能是root或者为空。请根据你本地环境的实际情况填写。重要这仅是本地测试环境生产环境必须使用强密码执行安装填写无误后点击“创建”或“安装”按钮。脚本会自动创建数据库表并插入初始数据。如果看到“安装成功”的提示就大功告成了。登录靶场安装完成后页面会跳转回首页。Pikachu的默认前端可能有一个简单的登录入口或者直接显示漏洞菜单。常见的默认账号密码可能是admin/123456具体请查看下载包内的README文件。登录后你就能在左侧看到琳琅满目的漏洞分类菜单其中就包括我们重点要攻克的“SQL注入”。实操心得如果在安装过程中遇到“数据库连接失败”的错误99%的原因是数据库用户名或密码不对。请确认你的PHPStudy/XAMPP中MySQL服务的真实密码。另一个常见问题是PHP版本过高导致某些函数被弃用而报错这时可以尝试在PHPStudy中切换到一个稍旧的PHP版本如PHP 5.6或7.2Pikachu对这些版本兼容性更好。3. SQL注入核心原理与Pikachu漏洞类型解析在动手之前我们必须把原理吃透。SQL注入之所以发生根本原因在于程序将用户输入的数据和代码SQL语句没有进行严格的区分而是直接拼接在一起执行。3.1 漏洞产生的本质字符串拼接想象一下一个网站的登录功能后端PHP代码可能是这样的$sql SELECT * FROM users WHERE username . $_POST[username] . AND password . $_POST[password] . ;如果用户老老实实在用户名框输入admin密码框输入123456那么拼接后的SQL语句是SELECT * FROM users WHERE username admin AND password 123456这没问题。但如果用户在用户名框输入admin --注意最后有个空格密码随便输入拼接后的语句就变成了SELECT * FROM users WHERE username admin -- AND password xxx在SQL中--是注释符它会把后面的所有内容都注释掉。于是这条语句的实际执行部分就变成了SELECT * FROM users WHERE username admin它直接绕过了密码验证这就是一次最简单的SQL注入攻击。3.2 Pikachu靶场中的SQL注入分类Pikachu靶场精心设计了多种SQL注入场景模拟了真实开发中可能出现的不同代码写法。理解这些分类能帮助我们在实战中快速判断注入类型。数字型注入POST注入点的参数是数字通常用于id1这类查询。SQL语句拼接时没有单引号包裹。例如$sql SELECT * FROM news WHERE id . $_GET[id];攻击时可以直接拼接逻辑语句如id1 or 11。字符型注入GET注入点的参数是字符串被单引号包裹。例如$sql SELECT * FROM users WHERE username . $_GET[name] . ;攻击时需要先闭合前面的引号如nameadmin or 11。搜索型注入常用于搜索功能参数通常被%和引号包裹如$sql SELECT * FROM products WHERE name LIKE % . $_GET[keyword] . %;攻击时需要同时处理百分号和引号如keywordtest% AND 11 AND %。XX型注入Pikachu里还有一种“XX型”这通常是指参数被括号、引号等多种符号混合包裹的复杂情况需要攻击者去试探闭合方式。“insert/update/delete”注入这类注入发生在数据插入、更新或删除操作中比如用户注册、修改资料、发表评论等功能。虽然不像SELECT那样直接回显数据但通过精心构造Payload可以实现“报错注入”或“盲注”从而窃取数据或破坏数据库。“delete”注入专门针对DELETE语句的注入可能导致误删大量数据危害极大。“http header”注入注入点不在普通的GET/POST参数中而在HTTP请求头里如User-Agent、X-Forwarded-For等。这提醒我们所有用户可控的输入点都可能存在风险。布尔盲注 时间盲注这是两种高级注入技术用于当页面没有直接的数据回显和错误信息时。布尔盲注页面返回内容对错True/False有可区分的差异如“存在”与“不存在”。通过构造逻辑判断的Payload像“猜”一样一位位地获取数据。时间盲注页面返回没有任何差异。这时我们利用SLEEP()等函数通过页面响应时间的长短来判断Payload是否执行成功如id1 AND IF(11, SLEEP(5), 0)如果页面延迟5秒返回说明11成立。宽字节注入这是针对使用GBK、GB2312等宽字符集数据库的一种特殊注入。由于编码问题程序员用反斜杠\转义单引号变成\时可能被宽字节字符如%df“吃掉”从而使得引号逃逸。Pikachu也提供了这个场景。4. 实战复现从简到繁攻克各类注入现在让我们进入Pikachu靶场开始实战。我将按照由易到难的顺序带你逐个击破。4.1 初阶数字型与字符型注入有回显这是最好的入门场景因为注入结果会直接显示在页面上。4.1.1 数字型注入POST在Pikachu左侧菜单进入SQL注入 - 数字型注入POST。页面通常是一个提交用户ID查询信息的表单。我们先进行正常查询输入1点击提交。页面会显示ID为1的用户信息比如“Dumb”。注入探测在输入框尝试输入1 or 11。提交后你可能会看到返回了所有用户的信息而不仅仅是ID为1的用户。这是因为拼接后的SQL语句变成了SELECT ... WHERE id 1 or 11。11永远为真所以WHERE条件对整个数据集都成立返回了所有记录。原理验证为了更清晰地看到语句如何被拼接我们可以输入1 and 12。12为假所以这条语句查询不到任何结果页面可能显示为空或“用户不存在”。这一真一假的对比就确认了此处存在数字型注入漏洞且参数没有被引号包裹。4.1.2 字符型注入GET进入SQL注入 - 字符型注入GET。页面可能是一个通过URL参数name查询用户的链接如http://.../pikachu/vul/sqli/sqli_str.php?nameadmin。注入探测我们将URL中的name参数值改为admin or 11。完整的URL类似http://.../pikachu/vul/sqli/sqli_str.php?nameadmin or 11。访问这个链接如果同样返回了所有用户信息说明注入成功。我们来拆解一下后端代码可能是$sql SELECT ... WHERE name $name;。我们输入admin or 11后拼接成的语句是SELECT ... WHERE name admin or 11我们先用admin闭合了前面的单引号然后插入一个永远为真的条件or 11最后那个单引号与SQL语句末尾的原单引号配对。于是查询条件变成了“名字是admin或者1等于1”后者恒真因此返回所有数据。使用Burp Suite抓包分析打开Burp Suite配置浏览器代理然后刷新字符型注入页面或提交一次查询。在Burp的Proxy - HTTP history中找到这条GET请求。将其发送到Repeater模块。在Repeater中你可以直接修改name参数的值反复发送并观察响应这比在浏览器地址栏修改方便得多。尝试不同的Payload如admin and 12应无结果、admin order by 5 --猜字段数体会其中的变化。注意事项在字符型注入中闭合引号后我们常常需要用注释符--后面有个空格或#来注释掉SQL语句原末尾的引号避免语法错误。在URL中#有特殊含义通常需要编码为%23。例如nameadmin or 11%23。4.2 中阶报错注入与布尔/时间盲注当页面没有直接的数据回显但会返回数据库错误信息时我们可以利用“报错注入”。4.2.1 报错注入在Pikachu中找到对应的报错注入关卡可能在“Insert/Update/Delete注入”或单独板块。报错注入利用的是数据库执行某些特殊函数出错时会将错误信息有时包含我们查询的数据返回给页面的特性。以MySQL为例一个经典的Payload是使用updatexml()或extractvalue()函数。admin and updatexml(1, concat(0x7e, (SELECT version()), 0x7e), 1) --这个Payload的意思是通过concat将波浪符~、我们想查询的数据库版本version()、另一个波浪符拼接在一起作为updatexml函数的第二个参数。updatexml函数在解析这个错误的XML路径格式时会报错并将这个拼接的字符串作为错误信息的一部分输出到页面上。于是我们就能在页面的错误提示中看到数据库版本号了。在Pikachu的对应输入点尝试这个Payload观察页面是否返回了包含~5.7.26~你的MySQL版本字样的错误信息。4.2.2 布尔盲注实战布尔盲注像一场“是与非”的问答游戏。进入Pikachu的“布尔盲注”关卡。正常输入一个存在的ID如1和一个不存在的ID如999观察页面返回的差异。可能是一个单词“存在”和“不存在”也可能是完全不同的页面内容。记住这个差异这是我们判断Payload真假的依据。猜解当前数据库名长度我们构造Payload利用length()函数和substr()函数来逐位猜解。首先猜长度1 and length(database())5 --如果页面返回“存在”的样式说明数据库名长度大于5。我们可以不断调整这个数字直到找到确切的长度。比如10返回“不存在”10返回“存在”则长度是10。逐位猜解数据库名知道长度后用substr()函数从第一位开始猜每个字符是什么。ASCII码是常用的比较方式1 and ascii(substr(database(),1,1))100 --这个Payload询问“数据库名的第一个字符的ASCII码大于100吗”根据页面返回的“是/否”我们可以用二分法快速缩小范围如100成立则猜150...最终确定第一个字符的ASCII码转换为字母。然后依次猜解第二、第三位...这是一个非常繁琐但有效的过程。使用工具辅助正因为过程繁琐布尔盲注是使用sqlmap这类自动化工具的最佳场景之一。在手动理解原理后你可以用sqlmap来验证和加速这个过程sqlmap -u http://target/pikachu/vul/sqli/sqli_blind_bool.php?id1 --techniqueB --current-db参数--techniqueB指定使用布尔盲注技术--current-db用于获取当前数据库名。4.2.3 时间盲注实战时间盲注是布尔盲注的“升级版”当页面无论输入什么返回都一模一样时使用。进入Pikachu的“时间盲注”关卡。输入1和999发现页面返回内容完全一样无法区分。时间延迟探测尝试Payload1 and sleep(5) --提交后观察页面是否大约延迟了5秒钟才加载完成。如果是说明sleep(5)函数被执行了意味着注入点存在且我们构造的语句and sleep(5)被成功拼接执行。基于时间的猜解结合if()函数进行猜解。猜解数据库名第一个字符的Payload如下1 and if(ascii(substr(database(),1,1))100, sleep(5), 0) --这个语句的意思是如果数据库名第一个字符的ASCII码大于100那么让数据库睡眠5秒否则立即返回。我们通过计算页面响应时间是否明显变长5秒来判断条件是否为真。同样采用二分法逐步猜出每一个字符。工具自动化时间盲注手动操作极其耗时sqlmap同样可以自动化sqlmap -u http://target/pikachu/vul/sqli/sqli_blind_time.php?id1 --techniqueT --current-db参数--techniqueT指定使用时间盲注技术。4.3 高阶Insert/Update/Delete注入与HTTP头注入这些注入点往往在后台更隐蔽但危害同样巨大。4.3.1 Insert注入用户注册找到Pikachu中类似用户注册的功能。在注册用户名处尝试注入Payload。假设后端代码是$sql INSERT INTO users (username, email) VALUES ( . $_POST[user] . , . $_POST[email] . );我们在用户名框输入test, testemail.com), (admin2, (SELECT version()) --拼接后的SQL语句变为INSERT INTO users (username, email) VALUES (test, testemail.com), (admin2, (SELECT version()) -- , ...)这插入了两条记录第二条记录的email字段被替换成了数据库版本号的查询结果。如果注册成功后在用户列表或通过其他回显点能看到这个admin2用户的邮箱信息我们就通过报错或二次查询拿到了数据。更常见的是结合updatexml进行报错注入。4.3.2 HTTP头注入User-Agent找到Pikachu中记录User-Agent或IP地址的功能可能在某些日志记录页面。使用Burp Suite抓取访问该页面的请求。在Burp Repeater中修改HTTP请求头中的User-Agent字段为注入Payload例如User-Agent: Mozilla/5.0 ... or updatexml(1, concat(0x7e, version()), 1) or 发送请求观察响应页面或后续的日志显示页面看是否出现了数据库报错信息其中包含了版本号。这证明了即使不是常规的表单参数来自客户端的任何输入HTTP头、Cookie等如果被不加处理地拼接到SQL中都会造成注入。5. 防御之道从Pikachu案例看安全编码复现漏洞是为了更好地防御。通过Pikachu的各种场景我们可以总结出最有效的SQL注入防御方案。1. 使用参数化查询预编译语句这是最根本、最有效的防御手段。它的原理是将SQL语句的结构代码和传入的数据参数分开发送给数据库。数据库先编译SQL结构知道这是一个查询username的操作然后再将用户输入的admin --作为纯粹的数据填充到username这个位置。即使数据中包含SQL关键字或符号也只会被当作普通字符串处理无法改变原语句的结构。PHP (PDO)示例$stmt $pdo-prepare(SELECT * FROM users WHERE username :username AND password :password); $stmt-execute([username $user, password $pass]);PHP (MySQLi)示例$stmt $conn-prepare(SELECT * FROM users WHERE username ?); $stmt-bind_param(s, $username); // s 表示字符串类型 $stmt-execute();2. 对输入进行严格的过滤与转义如果因为某些原因不能使用参数化查询如在复杂的动态查询中必须对输入进行严格处理。白名单过滤对于已知有限集合的输入如性别、状态码只接受预定值其他一律拒绝。类型强制转换对于数字型参数在拼接前强制转换为整数型$id (int)$_GET[id];。转义特殊字符使用数据库特定的转义函数如MySQL的mysqli_real_escape_string()。注意转义并非绝对安全尤其是在宽字节编码下可能失效因此应作为辅助手段而非首选。3. 最小权限原则为Web应用程序连接数据库的账户分配最小必要的权限。例如一个只需要查询功能的页面连接账号就只赋予SELECT权限不要给DELETE、DROP等权限。这样即使发生注入攻击者能造成的破坏也有限。4. 避免详细的错误信息将PHP的错误显示关闭display_errors Off并使用自定义的错误页面。避免将数据库的原始错误信息如包含表名、字段名直接抛给前端用户这会给攻击者提供大量线索。5. 使用Web应用防火墙WAF在应用层部署WAF可以过滤常见的恶意SQL注入Payload作为一道额外的防线。但WAF可能存在被绕过的风险不能替代安全的代码编写。6. 定期安全审计与测试对代码进行定期的安全代码审查并使用自动化扫描工具如SQLMap但需在授权范围内或进行专业的渗透测试主动发现潜在的注入点。在Pikachu靶场的代码中你可以通过查看源码来对比“漏洞”页面和“安全”页面的区别。安全页面通常会采用mysql_real_escape_string过滤或模拟预编译的方式进行处理。通过这种对比学习你能更深刻地理解防御措施是如何生效的。6. 常见问题与排查技巧实录在复现过程中你可能会遇到一些问题。这里记录一些常见的情况和解决思路。问题1访问Pikachu首页出现“连接数据库失败请检查/inc/config.inc.php配置文件”排查这是最常见的问题。首先检查/inc/config.inc.php文件中的数据库配置主机名、用户名、密码、数据库名是否与你的MySQL环境一致。集成包默认密码可能是root或空。技巧如果修改了配置文件仍不行尝试在PHPStudy中重启MySQL服务。有时MySQL服务没有正常启动或端口被占用会导致此问题。问题2提交注入Payload后页面没有任何变化或者返回了“非法参数”等提示排查这可能是Pikachu靶场在该关卡设置了简单的防御机制如转义了引号或者你的Payload构造有语法错误。技巧首先使用最基础的探测Payload如数字型的1 or 11和1 and 12观察页面是否有差异。如果没有尝试在Burp Suite中查看原始HTTP请求和响应确认Payload是否被正确发送。也可能是该关卡模拟的是“盲注”场景需要你使用基于布尔或时间的判断方法。问题3使用Burp Suite抓不到本地localhost的流量排查浏览器可能没有正确配置代理或者Burp Suite的代理监听没有开启。技巧确保Burp Suite的Proxy - Intercept是Intercept is on状态并且Proxy - Options里监听器Listener是启用的通常是127.0.0.1:8080。在浏览器中将代理设置为HTTP127.0.0.1:8080。对于localhost某些浏览器设置可能需要额外配置可以尝试访问http://127.0.0.1/pikachu代替http://localhost/pikachu。问题4时间盲注测试时sleep(5)没有造成明显延迟排查可能是数据库连接被复用、网络延迟不稳定或者sleep()函数被禁用。技巧增加睡眠时间如sleep(10)以便更明显地区分。使用BENCHMARK()函数作为替代1 and if(11, BENCHMARK(10000000, MD5(test)), 0) --。这个函数通过执行大量计算来消耗时间。观察页面响应时间是否显著增加。问题5使用sqlmap进行自动化测试时无法检测到注入点排查sqlmap默认会测试大量Payload和参数但有时会因为WAF、Token或复杂的JS交互而失败。技巧先手动在浏览器或Burp中确认注入点存在。使用sqlmap时可以尝试以下参数--level和--risk提高检测等级和风险级别。--tamper使用脚本绕过简单的过滤如space2comment将空格替换为注释。--cookie如果页面需要登录带上有效的会话Cookie。最好将Burp抓到的完整HTTP请求保存为.txt文件然后用-r参数让sqlmap直接加载这个文件进行分析这样能保留所有头信息和参数。问题6理解不了宽字节注入的原理类比想象一下后端代码用addslashes()函数在单引号前加了一个反斜杠\变成\这样单引号就被转义了无法闭合。但是如果数据库使用GBK编码它会把两个字节看作一个汉字。当我们输入%df时%df和转义用的反斜杠\ASCII码5C组合在一起%df5C在GBK编码里可能恰好对应一个繁体字如“運”。这样反斜杠就被“吃掉”了它后面的单引号就又暴露了出来从而实现了注入。关键前提是数据库连接层使用了宽字符集如set character_set_clientgbk并且转义发生在客户端字符集转换之后。通过Pikachu靶场这一套从易到难的SQL注入实战走下来你应该已经对注入的原理、分类、利用手法和防御措施有了一个立体而扎实的理解。记住靶场只是开始真正的安全思维在于永远不要信任用户输入的任何数据。在你自己编写代码时把参数化查询作为铁律这才是杜绝SQL注入最坚固的防线。