SQL注入实战入门:从Pikachu靶场到Web安全核心原理

📅 2026/7/4 11:47:07
SQL注入实战入门:从Pikachu靶场到Web安全核心原理
1. 项目概述为什么Pikachu是SQL注入学习的“新手村”如果你刚接触网络安全或者想从理论转向实战听到“SQL注入”这个词可能既兴奋又头疼。兴奋的是这几乎是所有安全测试的入门必修课是打开Web安全大门的第一把钥匙头疼的是光看概念和Payload列表就像背武功口诀却不练招式永远不知道实战中该怎么用。这就是为什么我们需要一个像Pikachu这样的靶场。它不是最复杂的但绝对是最适合新手“开荒”的练功房。Pikachu靶场把SQL注入这个庞大的攻击面拆解成了十几个清晰、独立的关卡从最简单的数字型注入到有点绕的宽字节注入每一步都有现成的环境让你亲手试错。这篇文章的目的就是带你用Pikachu靶场把SQL注入从“知道是什么”变成“知道怎么用”我会结合我这些年带新人、做渗透测试的实际经验把每个关卡背后的原理、操作时的“手感”以及容易踩的坑掰开揉碎了讲给你听。收藏这一篇相当于你有了一个随时可以提问的实战教练。2. 环境准备与靶场搭建你的第一个“安全实验室”在开始“打怪”之前我们得先把“擂台”搭好。很多新手会卡在第一步觉得搭建环境很麻烦。其实对于Pikachu来说最省事、最还原真实学习环境的方法就是使用PHPStudy这类集成环境。下面我详细拆解每一步确保你一次成功。2.1 工具选型与下载为什么是PHPStudy Pikachu选择PHPStudy不是因为它最强而是因为它对新手最友好。它把Apache/Nginx、PHP、MySQL这些组件打包好了一键安装、一键启动避免了你在配置环境变量、修改配置文件上浪费大量时间。我们的核心目标是快速进入SQL注入的学习而不是成为系统运维专家。操作步骤下载PHPStudy去其官网下载最新版本如PHPStudy V8.1。建议选择“集成环境”版本。下载Pikachu靶场在GitHub上搜索“pikachu”找到官方仓库下载ZIP包。确保你下载的是完整版里面应该包含pikachu文件夹以及inc、hackable等子目录。安装PHPStudy一路默认安装即可。安装路径建议不要有中文和空格比如D:\phpstudy_pro。部署Pikachu安装完成后找到PHPStudy的“网站”根目录。通常是安装目录下的www文件夹例如D:\phpstudy_pro\WWW。将下载的Pikachu ZIP包解压把里面的pikachu文件夹整个复制到WWW目录下。注意有些教程会让你直接解压到根目录但保持一个独立的pikachu文件夹是更好的习惯方便你以后管理多个靶场项目。2.2 初始化配置与常见坑点排查部署完文件只是第一步让靶场“活”起来才是关键。这里最容易出问题。核心操作流程启动服务打开PHPStudy点击“启动”按钮确保Apache和MySQL的指示灯都变成绿色。访问安装页面打开浏览器输入http://localhost/pikachu或http://127.0.0.1/pikachu。你应该能看到Pikachu的首页。如果出现“页面无法访问”首先检查Apache是否启动成功以及防火墙是否拦截了80端口。初始化数据库这是最关键的一步。在Pikachu首页你应该能看到一个红色提示告诉你数据库没有连接并有一个“初始化安装”的链接。点击它。坑点1数据库连接失败。安装页面会要求你填写数据库信息。PHPStudy默认的MySQL用户名是root密码是root。如果连接失败请打开PHPStudy的“MySQL管理器”查看或修改密码。有时新版本默认密码为空你需要手动设置一个比如root。坑点2提示“数据库连接成功但初始化数据失败”。这通常是因为你的MySQL版本较高如MySQL 8.0而Pikachu的初始化SQL文件可能包含一些旧版本的语法。解决方法用PHPStudy自带的“MySQL管理器”登录phpMyAdmin手动创建一个名为pikachu的数据库字符集选utf8_general_ci然后选择这个数据库在“SQL”标签页中粘贴Pikachu文件夹里pikachu.sql文件的内容并执行。这个文件通常位于\pikachu\inc目录下。完成安装数据库初始化成功后页面会提示你安装完成。点击“进入首页”你就可以开始你的SQL注入之旅了。实操心得我强烈建议你在初始化成功后用phpMyAdmin通常通过PHPStudy面板可以快捷打开看一眼pikachu数据库里的表比如member、users表。直观地看到靶场的数据结构对你后续理解注入Payload和查询结果有巨大帮助。知道你在“注”什么比盲目输入Payload重要十倍。3. SQL注入核心原理深度拆解不仅仅是“拼接”很多人学SQL注入上来就背‘ or ‘1’’1却不知道它为什么能生效。在Pikachu里通关你必须理解背后的原理否则换个靶场就又懵了。我们用靶场里的例子来反推原理。3.1 漏洞的本质用户输入变成了代码SQL注入的根本原因是程序将用户输入的数据和开发者编写的SQL代码没有进行严格的区分直接拼接在一起执行。想象一下原本的SQL语句是一个完整的句子用户输入本该是一个“词”但现在这个“词”里包含了能改变句子结构的“标点符号和新的句子”。Pikachu 数字型注入POST源码分析假设后台查询用户信息的代码逻辑是这样的这是简化理解$id $_POST[id]; // 用户从表单提交的id $sql SELECT username, email FROM member WHERE id $id;当用户正常输入id1SQL语句是SELECT ... WHERE id 1没问题。 但当用户输入id1 or 11拼接后的SQL语句变成了SELECT ... WHERE id 1 or 11。WHERE后面的条件变成了“id等于1或者1等于1”。11是永恒为真的条件因此这个OR逻辑会让整个WHERE条件永远成立数据库就会返回member表中的所有数据而不仅仅是id为1的那一条。3.2 字符型与数字型的关键区别引号这是新手最容易混淆的点。Pikachu特意分成了两个关卡。数字型注入如上面例子SQL语句中变量直接嵌入没有用引号包裹。注入时通常不需要考虑闭合引号直接拼接逻辑运算符即可。Payload示例1 or 11。字符型注入变量在SQL语句中被单引号或双引号包裹。源码类似$sql SELECT ... WHERE username $name;如果你输入kobe语句是...WHERE username kobe。如果你想注入输入kobe or 11拼接后是...WHERE username kobe or 11。注意这里你需要先用一个单引号‘来闭合原语句中左边的引号然后写入你的攻击逻辑or ‘1’’1最后那个‘1’’1右边的单引号有时可以利用原语句的闭合引号有时需要自己补上或注释掉。在Pikachu字符型注入GET关卡使用的Payloadkobe or 11 #中#在MySQL中表示注释它会把后面原本用于闭合的引号注释掉从而让语句正确闭合。为什么理解这个区别至关重要因为在真实黑盒测试中你需要通过报错信息或盲猜来判断后台是数字型还是字符型查询。如果类型判断错误你构造的Payload就无法正确闭合SQL语句导致攻击失败。Pikachu分开训练你这两种最基本的“手感”。4. Pikachu靶场SQL注入全关卡实战精讲下面我们进入实战环节我会按照从易到难、从显性到隐性的逻辑带你通关Pikachu的核心SQL注入关卡。每个关卡我都会解释Payload的构造思路而不仅仅是给一个答案。4.1 第一关数字型注入POST—— 理解“永真条件”这一关是热身。页面通常是一个输入框让你根据ID查询用户信息。正常测试输入1返回用户admin的信息。注入尝试在输入框输入1 or 11。结果分析点击提交后页面应该会显示数据库里所有用户的信息比如admin, pikachu, test等而不仅仅是id1的用户。这是因为or 11让WHERE条件永远为真。实操心得这里不需要抓包直接在表单输入即可。成功后立刻打开Burp Suite拦截这个请求看看POST数据包到底长什么样。你会看到类似id1or1%3D1这样的内容。是空格的URL编码%3D是等号的URL编码。理解数据在传输中的格式是后续使用Burp Suite进行复杂测试的基础。4.2 第二关字符型注入GET—— 掌握“引号闭合与注释”这一关变成了通过URL的name参数查询形式如http://.../vul/sqli/sqli_str.php?namekobesubmit查询。正常测试在输入框输入kobe提交URL变化页面显示kobe的信息。注入构造我们的目标是让语句查询出所有用户。根据原理我们需要闭合引号并注释掉末尾。在输入框输入kobe or 11 #kobe原语句是username$name我们输入kobe先构成usernamekobe这样我们自己的单引号就和前面的单引号闭合了。or 11注入永真条件。#在MySQL中#是行注释符它会把它之后到行尾的所有内容都注释掉。这样原SQL语句末尾那个用于闭合的单引号就被注释掉了整个语句语法正确。结果分析提交后页面显示所有用户信息。查看此时的URL你会发现参数变成了namekobe%27or1%3D1%23。%27是单引号%23是井号。这就是GET请求中特殊字符的编码形式。4.3 第三关搜索型注入 —— 处理“LIKE模糊匹配”这一关模拟搜索功能源码中使用LIKE %$name%。理解源码SQL语句是SELECT ... WHERE username LIKE %$name%。这意味着变量$name被放在两个百分号%中间。注入构造我们需要同时闭合前后的百分号和引号。输入k% or 11 #原语句会变成LIKE %k% or 11 #%k%k是任意字符%是LIKE的通配符‘用于闭合前面的单引号。所以%k%这部分是合法的LIKE模式。or 11注入永真条件。#注释掉后面所有的%使语句闭合。结果分析执行后因为or 11为真所以会忽略LIKE条件返回所有用户。4.4 第四关XX型注入 —— 应对“括号包裹”这一关的变量被括号包裹username($name)。注入构造我们需要闭合括号和单引号。输入kobe) or 11 #原语句会变成username(kobe) or 11 #)kobe)kobe闭合了字符串)闭合了前面的括号。or 11注入永真条件。#注释掉后面多余的)。思维延伸在实际测试中你可能会遇到(($name))甚至更复杂的包裹。原则就是“对称闭合”你添加了什么符号就要考虑如何闭合它。4.5 第五关报错注入 —— 利用数据库“说真话”当页面没有直接回显数据但会返回数据库错误信息时报错注入就派上用场了。Pikachu的“Insert/Update注入”或“Delete注入”关卡适合演示。核心函数updatexml()或extractvalue()。它们共同点是如果参数格式不正确会报错并将错误原因即我们构造的查询结果一起返回。以Insert/Update关卡为例在留言板等处输入1 and updatexml(1,concat(0x7e,database()),1) or 11updatexml(1, concat(0x7e, database()), 1)updatexml函数用于更新XML文档这里我们故意给第二个参数XPath路径一个错误格式。concat(0x7e, database())将波浪号~0x7e是其十六进制和当前数据库名拼接在一起。执行时数据库会尝试将~pikachu作为XPath解析必然失败从而在报错信息中带出~pikachu。or ‘1’’1用于闭合整个条件保证原语句能正常执行。结果分析提交后页面可能会显示一个SQL错误其中包含‘~pikachu’这样的字眼这样我们就获取到了数据库名。4.6 第六关布尔盲注与时间盲注 —— 当页面“沉默”时这是注入中比较高级的技术用于页面既无数据回显也无错误信息只有“存在”与“不存在”或“正常”与“异常”两种状态时。布尔盲注Base on Boolean通过注入语句改变页面返回的布尔值真/假从而像“猜字谜”一样逐位推断信息。Pikachu实战在盲注布尔型关卡输入kobe and ascii(substr(database(),1,1))113 #拆解substr(database(),1,1)截取当前数据库名的第1个字符。ascii(...)获取该字符的ASCII码。113判断ASCII码是否大于113即小写字母‘q’。如果页面正常返回和输入kobe一样说明条件为真即第一个字符的ASCII码大于113。我们可以不断调整这个值比如112利用二分法快速猜出准确ASCII码进而知道字符是‘p’。时间盲注Base on Time当页面连布尔状态都难以区分时通过注入让数据库执行延时函数根据页面响应时间来判断条件真假。Pikachu实战在盲注时间型关卡输入kobe and if((substr(database(),1,1))p,sleep(5),null) #拆解if(条件, 真值, 假值)如果条件成立返回真值执行sleep(5)让数据库睡眠5秒否则返回假值null。如果页面响应时间明显延长约5秒说明数据库名的第一个字符确实是‘p’。实操心得盲注非常耗时全靠手工几乎不可能。在实际安全测试或CTF比赛中一定会借助工具最常用的是sqlmap。但通过Pikachu手工完成一次能让你深刻理解sqlmap等自动化工具背后的原理知道它在“忙活”什么。4.7 第七关宽字节注入 —— 绕过转义的神奇技巧当后台对单引号等特殊字符进行了转义比如在单引号前加反斜杠\‘时常规注入会失效。宽字节注入利用了数据库编码如GBK的特性进行绕过。原理在GBK编码中一个汉字由两个字节组成。如果我们在被转义的单引号%27前加上一个特定的字节如%df数据库可能会将%df%5c%5c是反斜杠\识别为一个汉字“連”从而“吃掉”了那个用于转义的反斜杠使得后面的单引号成功逃逸。Pikachu实战在宽字节注入关卡使用Burp Suite抓取提交kobe的请求发送到Repeater模块将参数修改为namekobe%df%27%20or%2011%23%df用于和后面的%5c转义反斜杠组成宽字符。%27单引号。%20空格。%23井号#。结果分析发送请求后如果成功注入页面会返回所有用户信息。这个技巧的关键在于判断数据库的编码方式并非总是有效。5. 联合查询注入实战从“爆数据”到“拿信息”联合查询Union Inject是SQL注入中最常用、最高效的数据获取方式之一。它要求前后查询的列数一致。我们以Pikachu的“XX型注入”关卡为例演示完整流程。5.1 第一步判断列数Order By我们的目标是让union select能够正确执行必须知道原查询语句返回多少列。输入1) order by 1 #页面正常。输入1) order by 2 #页面正常。输入1) order by 3 #页面报错或显示异常。 这说明原查询语句有2列。order by N的意思是按照第N列排序如果N超过了实际列数数据库就会报错。5.2 第二步确定回显点知道列数后我们需要确定哪几列的内容会显示在页面上。 输入1) union select 1,2 #如果页面在原本显示用户名、邮箱的地方分别显示了数字“1”和“2”那么这两个位置就是我们可以控制的数据回显点。如果只显示了一个数字说明只有一个回显点。5.3 第三步获取数据库信息现在我们可以把回显点替换成我们想查询的信息。查当前数据库名1) union select 1,database() #。在第二个回显点可能显示邮箱的位置会显示pikachu。查数据库版本1) union select 1,version() #。查当前用户1) union select 1,user() #。5.4 第四步枚举表名、列名、数据这是标准的信息收集“三板斧”需要利用MySQL的系统数据库information_schema。枚举表名1) union select 1,table_name from information_schema.tables where table_schemapikachu #这条语句会列出pikachu数据库中的所有表名。通常我们会在回显点看到第一个表名如httpinfo。为了看到更多可以使用limit子句... limit 0,1第0行开始取1条... limit 1,1第1行开始取1条以此类推。关键的表可能是member,users等。枚举列名假设我们找到了users表。1) union select 1,column_name from information_schema.columns where table_nameusers and table_schemapikachu #这条语句会列出pikachu数据库下users表的所有列名。同样用limit分批获取通常会找到username,password等敏感字段。提取数据1) union select username,password from pikachu.users #现在我们可以直接查询目标表中的敏感数据了。如果列数对不上可以用concat()函数将多个字段合并到一列显示1) union select 1,concat(username, :, password) from pikachu.users #6. 高阶技巧与实战场景延伸通过Pikachu的基础关卡后你需要了解一些更贴近实战的场景和技巧。6.1 HTTP头注入被忽略的攻击面Pikachu的“HTTP Header注入”关卡模拟了一个非常经典的场景应用程序从HTTP请求头如User-Agent,Cookie,X-Forwarded-For中获取数据并直接拼接到SQL语句中。实战过程登录后页面可能会显示“您的UserAgent是xxx”。用Burp Suite抓取这个页面的请求在Repeater中修改User-Agent头的值为firefox or updatexml(1,concat(0x7e,database()),0) or 原理后台代码可能类似$sql INSERT INTO log (ua) VALUES ($_SERVER[HTTP_USER_AGENT]);。我们注入的单引号闭合了值并插入了恶意SQL代码。防御思考这提醒我们任何来自客户端的数据都是不可信的包括HTTP头部。在代码审计时要特别关注那些处理$_SERVER、$_COOKIE等超全局变量的SQL语句。6.2 二次注入潜伏的威胁Pikachu可能没有直接命名为“二次注入”的关卡但其原理值得一说。它发生在数据第一次存入数据库时被转义了看起来安全但当这些数据被从库中取出并再次用于SQL查询时转义符被去除导致注入发生。模拟场景用户注册时用户名为admin--注意空格。程序转义后存入数据库为admin\--。后来在“修改密码”功能中程序执行UPDATE users SET password$newpass WHERE username$name而这个$name是从数据库读出的admin--。此时转义符在读取时可能被去除或忽略语句变成UPDATE ... WHERE usernameadmin-- --注释了后面的所有内容导致可以直接修改admin用户的密码。6.3 工具辅助Sqlmap初体验手工注入是理解原理的基础但效率低下。Sqlmap是自动化SQL注入检测和利用的神器。在彻底理解手工注入后你可以用Pikachu来练习Sqlmap。基础命令示例针对Pikachu字符型GET注入sqlmap -u http://localhost/pikachu/vul/sqli/sqli_str.php?namekobesubmit查询 --batch --dbs-u指定目标URL。--batch以非交互模式运行所有选择都默认。--dbs枚举所有数据库。重要警告仅在自己的靶场或获得明确授权的环境中使用Sqlmap在真实未授权网站上使用是违法行为。7. 防御之道从攻击者视角看如何编写安全代码学攻击是为了更好的防御。通过Pikachu的漏洞我们可以总结出最核心的防御方案。7.1 参数化查询预编译语句这是最有效、最根本的防御手段。它让SQL代码和数据分离数据库引擎会明确知道哪些是指令哪些是数据从而从根本上杜绝拼接。PHP (PDO) 示例$stmt $pdo-prepare(SELECT * FROM users WHERE username :name AND password :pass); $stmt-execute([name $username, pass $password]);PHP (MySQLi) 示例$stmt $conn-prepare(SELECT * FROM users WHERE username ?); $stmt-bind_param(s, $username); // s 表示字符串类型 $stmt-execute();7.2 输入验证与过滤对输入进行严格的“白名单”验证。例如如果id应该是数字就用intval()强制转换。$id intval($_GET[id]); // 非数字会变为0 $sql SELECT ... WHERE id $id; // 此时$id一定是数字但拼接方式仍不推荐应结合参数化对于字符串可以定义允许的字符集如字母、数字、下划线拒绝其他所有字符。7.3 最小权限原则为Web应用程序使用的数据库账户分配最小必要权限。通常只授予SELECT、INSERT、UPDATE、DELETE等业务需要的权限绝对不要使用root或拥有FILE、DROP、CREATE等高危权限的账户连接数据库。这样即使发生注入攻击者能造成的破坏也有限。7.4 其他辅助措施Web应用防火墙WAF可以拦截常见的攻击Payload作为一道外围防线。错误信息处理生产环境应关闭详细的数据库错误回显使用自定义的错误页面避免向攻击者泄露数据库结构信息。定期安全审计与漏洞扫描对代码进行人工审计或使用自动化工具扫描及时发现潜在漏洞。走完Pikachu靶场的SQL注入关卡你收获的应该不仅仅是一堆可以“通关”的Payload。更重要的是你建立了一套完整的分析思路面对一个输入点如何判断是否存在注入是何种类型如何闭合构造如何一步步获取数据这套思路才是你应对未来各种复杂Web漏洞的底层能力。靶场是静态的但你的思维必须是动态和发散的。试着去修改Pikachu的源码看看你的防御措施是否真的有效或者去挑战更复杂的靶场如DVWA、WebGoat、SQLi-Labs把在这里练就的“手感”应用到新的环境中去。安全之路始于注入但远不止于注入。