1. 项目概述为什么Pikachu是SQL注入学习的“新手村”与“演武场”如果你刚接触Web安全或者想系统性地把SQL注入这个“老大哥”级别的漏洞彻底搞明白那么Pikachu靶场绝对是你绕不开的一个实战平台。它不像那些复杂的综合靶场一上来就给你一堆眼花缭乱的漏洞让你无从下手。Pikachu的设计非常友好它把SQL注入这个大类像解剖一样分成了十几种不同的场景从最简单的数字型注入到有点绕的宽字节注入让你能一个关卡一个关卡地打怪升级。我当年就是靠着它才真正理解了“哦原来SQL注入不只是‘ or ‘1’’1这么简单”。简单来说Pikachu就是一个专门用来练习和演示各种Web漏洞的PHP环境。我们今天聚焦的SQL注入模块它模拟了开发者在编写代码时可能犯的各种错误比如把用户输入直接拼接到SQL语句里、使用了错误的过滤方式、或者对特殊字符的处理不当。通过亲手在这些有“缺陷”的页面上进行测试你不仅能学会怎么“注进去”更能深刻理解漏洞是怎么产生的以及站在防御者的角度应该如何去修复它。这比只看理论文章或者视频要扎实得多因为每一步操作、每一个回显都是你自己敲出来的印象会特别深刻。2. 靶场环境搭建与核心思路解析2.1 环境准备避开那些“坑爹”的安装问题Pikachu靶场本质上是一个PHPMySQL的Web应用。最省心的方式就是使用集成环境比如PHPStudy、XAMPP或者Docker。我强烈推荐新手用PHPStudy它在Windows下的兼容性最好图形化操作也简单。下载好Pikachu的源码包通常是一个ZIP文件后解压到你的Web服务器根目录比如PHPStudy的WWW目录。然后通过浏览器访问http://localhost/pikachu具体路径根据你的解压位置调整。第一次访问时页面通常会提示你“数据库连接失败”或者让你点击初始化安装。注意这里90%的人会遇到第一个坑数据库连接失败。别慌这通常不是你的问题。你需要检查并修改pikachu目录下的配置文件常见的是inc/config.inc.php。用记事本或代码编辑器打开它找到数据库连接的部分把host通常是localhost、username如root、password你的MySQL密码和dbname如pikachu按照你本地MySQL的实际配置修改好。PHPStudy的MySQL默认密码经常是root但也可能为空具体看你的设置。修改保存后刷新页面点击“初始化安装”按钮。如果一切顺利你会看到安装成功的提示并且页面上会出现各种漏洞的测试链接。如果还报错比如提示“文件错误”或找不到某个.js文件这可能是源码包不完整或解压出错重新下载一份完整的源码再试。2.2 核心攻击思路理解“数据”与“代码”的边界混淆在动手之前我们必须把SQL注入的核心原理吃透否则你就是机械地输入Payload知其然不知其所以然。想象一下后端处理用户登录的代码原本是这样的$sql SELECT * FROM users WHERE username $username AND password $password;这里的$username和$password是我们从登录表单提交上来的“数据”。在正常逻辑下如果用户输入admin和123456拼接后的SQL语句是SELECT * FROM users WHERE username admin AND password 123456这完全没问题数据库会去users表里查找匹配的记录。但是如果我在用户名输入框里输入的不是admin而是一个精心构造的字符串admin --注意最后有个空格。那么拼接后的SQL语句就变成了SELECT * FROM users WHERE username admin -- AND password $password在SQL中--是注释符它会把后面所有的内容都注释掉。于是这条语句的实际执行部分就变成了SELECT * FROM users WHERE username admin它完全忽略了密码验证这就是最经典的“数据”越界成了“代码”的例子。用户输入的引号‘提前闭合了原本的字符串然后我们注入的--注释符篡改了程序的原有逻辑。Pikachu靶场的价值就在于它把这种“拼接”的缺陷放在了十几种不同的代码上下文里让你去体验和突破。我们的核心思路永远是1. 找到传参点2. 判断参数如何被拼接进SQL语句是数字、字符、搜索条件还是其他3. 构造Payload闭合原有语句并注入我们想要的查询4. 获取数据或验证漏洞存在。3. 十大SQL注入场景深度拆解与实战3.1 数字型注入POST从抓包开始的入门课数字型注入是最直观的一种。在Pikachu中对应“数字型注入POST”这一关。页面通常是一个表单让你提交一个用户ID来查询信息。实战步骤在输入框随便输入一个数字比如1点击提交。你会发现页面显示了ID为1的用户信息但浏览器地址栏的URL没有变化。这说明数据是通过POST请求体发送的而不是GET请求的URL参数。打开浏览器开发者工具F12切换到“网络”Network标签页清空记录再次提交一次。你会看到一条新的请求记录点击查看它的“载荷”Payload或“请求体”Request Body你会看到类似id1这样的数据。这里就是我们的攻击入口。我们可以直接修改这个请求体。但更方便的是使用专业工具比如Burp Suite。配置好浏览器代理后用Burp拦截这个POST请求。将拦截到的请求发送到Burp的“重放”Repeater模块。在Repeater中我们把请求体中的id1修改为id1 or 11。点击发送观察返回的响应。如果页面返回了数据库中所有用户的信息而不是仅仅ID为1的用户那么漏洞就存在了原理解析后端代码可能是这样的$sql SELECT * FROM users WHERE id $_POST[‘id’];。当我们输入1 or 11时拼接的SQL是SELECT * FROM users WHERE id 1 or 11。WHERE条件变成了“id等于1”或者“1等于1”。11是永恒为真的条件所以or连接后整个WHERE条件永远为真数据库就会返回users表里的所有行。实操心得这是你第一次接触“抓包改包”务必亲手操作一遍。理解POST数据的位置是关键。另外数字型注入不需要考虑引号闭合这是它简单的地方。3.2 字符型注入GET引号闭合的艺术字符型注入更常见。在Pikachu的“字符型注入GET”关卡你会看到一个输入框让你提交用户名。实战步骤输入一个正常的用户名比如kobe提交。观察URL变成了类似http://.../vul/sqli/sqli_str.php?namekobesubmitquery。参数通过URL的?后面传递这是GET请求的特征。我们尝试注入。输入kobe一个单引号。如果页面报错比如SQL语法错误这通常是一个强烈的信号说明我们的单引号破坏了原SQL语句的语法。后端代码很可能是$sql SELECT ... WHERE username $name;。我们输入kobe后语句变成...WHERE username kobe多了一个单引号无法闭合。为了利用我们需要“修复”这个语法。输入kobe or 11。拼接后的SQL是...WHERE username kobe or 11。这个语句的意思是查询用户名为kobe或者‘1’‘1’永恒真的记录。同样会返回所有用户。更简洁的Payload是使用注释符来“吃掉”后面多余的代码。输入kobe --注意--后面有个空格在URL中空格是或%20。拼接后的SQL是...WHERE username kobe -- 。从--开始后面的内容被注释语句等价于...WHERE username kobe但如果我们不知道kobe这个用户可以输入一个不存在的用户然后加or条件例如admin or 11 --就能绕过用户名的验证。原理解析字符型注入的核心是“闭合”。你要先判断原SQL语句是用单引号‘、双引号“还是括号()包裹的变量。然后你的输入要先闭合前面的引号接着写入你的注入逻辑如or 11最后还要处理掉后面原本的引号用注释符--或#。3.3 搜索型注入模糊匹配下的漏洞利用搜索功能也是注入的高发区。Pikachu的“搜索型注入”模拟了LIKE关键字下的漏洞。实战步骤在搜索框输入k可能会列出所有包含k字母的用户。查看后端代码如果提供或推测SQL语句可能类似SELECT ... WHERE username LIKE %$name%。这里的%是SQL通配符表示任意字符。我们的目标是闭合这个模式。输入k% or 11 #。拼接后的SQL是...WHERE username LIKE %k% or 11 #%。k%匹配原LIKE模式的前半部分‘%。‘闭合了前面的单引号。or 11注入的永恒真条件。#注释掉后面原本的%’避免语法错误。执行后因为or 11为真所以会返回表中所有记录而不仅仅是包含k的。原理解析搜索型注入的闭合方式比较特殊你需要同时考虑LIKE关键字和两端的通配符%以及引号。Payload的构造需要精确地“嵌入”到原语句的模板中。3.4 XX型注入当变量被括号包裹时这种类型不常见但能很好地锻炼你的代码审计和闭合能力。在Pikachu的“XX型注入”中后端代码可能是这样的$query... where username($name);。变量被包裹在括号和单引号内。实战步骤尝试输入kobe‘看是否报错。如果报错证实了我们的猜测。构造Payloadkobe‘ or 11 #。我们来拆解kobe原内容。‘闭合内部的单引号。闭合前面的左括号。or 11注入条件。#注释掉后面原本的右括号和单引号’。最终执行的语句是... where username(kobe) or 11 #)成功注入。注意事项遇到不常见的报错信息时一定要耐心分析。括号、引号的嵌套顺序决定了Payload的构造。可以尝试在本地数据库客户端里模拟拼接过程帮助你理解。3.5 联合查询注入从“登录绕过”到“数据获取”的飞跃前面的注入大多用于“绕过验证”或“判断漏洞”。而联合查询Union Inject是真正用来“偷数据”的利器。它允许我们将额外的SELECT查询结果“附加”到原始查询结果之后。实战步骤以XX型注入场景为例判断列数使用order by子句。注入1‘ order by 1 #1‘ order by 2 #1‘ order by 3 #... 直到页面返回错误如Unknown column ‘3’ in ‘order clause’。最后一个成功的数字就是当前查询语句所选取的列数。假设order by 3出错而order by 2正常说明有2列。判断显示位使用union select。注入1‘ union select 1,2 #。如果页面正常显示并且原本显示数据的地方出现了数字1和2说明这两个位置可以用来回显我们想要的数据。这两个数字就是“显示位”。获取数据库信息利用显示位和数据库内置函数。注入1‘ union select database(), version() #。database()函数返回当前数据库名version()返回数据库版本。这样你就能在页面上看到它们。获取表名注入1‘ union select 1, table_name from information_schema.tables where table_schemadatabase() limit 0,1 #。这里用到了information_schema这个系统数据库它存储了所有数据库的元信息。limit 0,1表示从第0行开始取1条记录你可以修改这个参数来遍历所有表。获取字段名假设我们猜到一个表叫users。注入1‘ union select 1, column_name from information_schema.columns where table_name‘users’ and table_schemadatabase() limit 0,1 #。同样可以遍历获取所有列名如username,password。获取数据最后直接查询数据。注入1‘ union select username, password from users #。这样用户名和密码可能是哈希值就一起被查出来了。原理解析联合查询注入的前提是列数必须相同。union操作符要求前后两个SELECT语句的列数一致。这就是为什么第一步必须确定列数。information_schema是MySQL的“数据库的数据库”是进行SQL注入信息收集的核心。3.6 Insert/Update注入数据写入与更新时的危险注入不仅发生在SELECT查语句也可能发生在INSERT增和UPDATE改语句。比如用户注册、修改个人信息等功能点。Pikachu的“Insert/Update注入”通常是一个表单提交后数据会插入数据库。如果后端代码是INSERT INTO users (username, email) VALUES (‘$user‘, ‘$email’)那么注入点就在$user或$email。实战步骤在用户名处输入test‘, ‘testtest.com‘) --。拼接后的SQL是INSERT ... VALUES (‘test‘, ‘testtest.com‘) -- ‘, ‘$email’)。我们提前用‘和闭合了VALUES列表并用结束了一条完整的INSERT语句--注释掉后面原本的代码。这可能导致插入异常或执行多条SQL语句。更危险的是利用updatexml或extractvalue函数进行“报错注入”。输入test‘ and updatexml(1, concat(0x7e, (select database())), 1) or ‘1‘‘1。updatexml是MySQL的XML处理函数但第二个参数需要是合法的XPath格式我们故意传入一个非法格式以~即0x7e开头拼接查询结果会导致数据库报错。在报错信息中会包含我们select database()的执行结果。这样我们通过错误回显拿到了数据。原理解析Insert/Update注入的危害极大可能导致数据被篡改、插入恶意数据如管理员账号甚至通过SELECT INTO OUTFILE写入Webshell。报错注入是一种“非盲注”手段在页面会显示数据库错误信息时非常有效。3.7 Delete注入藏在删除操作背后的陷阱删除功能DELETE同样危险。Pikachu的“Delete注入”场景通常是一个留言板每条留言旁边有个删除按钮。实战步骤点击删除按钮同时用Burp Suite抓包。你会发现一个包含id参数的请求可能是GET也可能是POST。假设后端代码是DELETE FROM messages WHERE id $_GET[‘id’]。我们可以在Burp Repeater中修改这个id参数。注入Payload1 and updatexml(1, concat(0x7e, (select username from users limit 0,1)), 1)。这里没有用引号因为id很可能是数字型。如果id是字符型则需要闭合1‘ and ... ‘。发送请求如果存在漏洞页面会返回一个数据库错误其中包含我们查询到的第一个用户名。实操心得Delete注入的破坏性极强因为执行的是删除操作。在测试时务必在本地或授权的靶场进行切勿在真实环境尝试。通过报错注入我们可以“读”数据但语句本身是“删”的逻辑这展示了注入的灵活性。3.8 HTTP Header注入在请求头里做文章有些应用会将客户端的一些信息如User-Agent、X-Forwarded-For、Cookie等记录到数据库中。如果这些头部信息未经处理就直接拼接进SQL就产生了HTTP Header注入。Pikachu的“HTTP Header注入”通常需要你先登录比如用admin/123456登录后页面可能会显示“你的User-Agent是XXX”。实战步骤登录后用Burp抓取任何一个访问该页面的请求比如刷新页面。在Burp Repeater中修改HTTP请求头部的字段。例如将User-Agent的值修改为Mozilla‘ or updatexml(1,concat(0x7e,database()),0) or ‘。发送请求。如果后端代码类似INSERT INTO logs (ua) VALUES (‘$_SERVER[‘HTTP_USER_AGENT’]’)那么我们的Payload就会被插入并执行。通过报错信息我们就能获取数据库名。原理解析这种注入比较隐蔽因为普通用户不会去修改请求头。攻击者可以利用工具轻松伪造任何头部信息。防御时所有来自客户端的数据无论位置GET/POST/Header/Cookie都必须一视同仁地进行校验和过滤。3.9 基于布尔的盲注与服务器的“是”或“否”游戏当页面没有明确的数据回显也没有详细的错误信息时我们就要用“盲注”了。基于布尔的盲注指的是页面会根据我们注入的SQL语句执行结果的“真”True或“假”False返回不同的页面内容比如“用户存在”和“用户不存在”。实战步骤在Pikachu的“盲注base on boolian”关卡输入一个已知用户kobe‘ and 11 #页面正常显示用户信息。输入kobe‘ and 12 #因为12为假and连接后整个条件为假页面可能显示“用户不存在”。利用这种真/假响应的差异我们可以像“猜谜”一样逐位获取数据。例如猜解数据库名的第一个字母kobe‘ and ascii(substr(database(),1,1))100 #。substr(database(),1,1)截取数据库名的第1个字符。ascii()将其转换为ASCII码。100判断是否大于100。根据页面返回“存在”或“不存在”我们可以用二分法不断缩小范围150? 125? ...最终确定准确的ASCII码再转换为字母。原理解析布尔盲注非常耗时需要大量的请求。但它是很多自动化SQL注入工具如sqlmap的基础原理。手工测试时需要极大的耐心和清晰的逻辑。3.10 基于时间的盲注当服务器“沉默”时这是最隐蔽的一种盲注。页面无论SQL执行结果真假返回的内容都一样。此时我们通过注入让数据库执行“延时”函数根据页面响应时间的长短来判断真假。实战步骤在Pikachu的“盲注base on time”关卡输入kobe‘ and sleep(5) #。如果页面等待了大约5秒才加载完成说明sleep(5)函数被执行了注入成功。结合if函数进行条件判断。例如kobe‘ and if((substr(database(),1,1))‘p‘, sleep(5), null) #。这个Payload的意思是如果数据库名的第一个字母等于p那么就让数据库睡眠5秒否则返回null。如果页面响应明显变慢约5秒我们就知道第一个字母是p如果立即返回则不是。原理解析时间盲注的请求量比布尔盲注更大因为每个判断都需要等待一个睡眠周期。它彻底依赖响应时间这个“侧信道”信息。在生产环境中时间盲注对服务器性能也是一种探测和消耗。3.11 宽字节注入绕过滤的“奇技淫巧”为了防止SQL注入开发者常常会使用转义函数如PHP的addslashes或mysql_real_escape_string在用户输入的引号前加上反斜杠\使其变成\从而失去闭合能力。宽字节注入就是针对这种防御的绕过技巧主要发生在数据库使用GBK、GB2312等宽字符集时。实战步骤在Pikachu的“宽字节注入”关卡输入kobe‘发现被转义可能显示为kobe\注入失败。利用GBK编码的特性。在GBK中一个汉字由两个字节组成。反斜杠\的ASCII码是0x5c即%5c。我们在单引号‘%27前故意添加一个特定的字节比如%df。那么输入%df%27。后端转义函数会在%27前加上%5c最终变成%df%5c%27。当数据库以GBK编码处理时%df%5c恰好构成了一个合法的GBK汉字如“連”而%27单引号就被独立释放了出来成功闭合了前面的引号。因此最终Payload可以是kobe %df‘ or 11 #。原理解析宽字节注入的本质是利用了字符编码转换过程中的“吞并”现象。防御这种攻击最根本的方法是统一使用UTF-8编码并在进行数据库操作时使用预编译语句参数化查询从根源上杜绝拼接。4. 实战后的防御思考与工具化延伸4.1 从攻击视角看防御如何写出“免疫”SQL注入的代码亲手打穿这十一个场景后你应该对SQL注入的成因有了血肉般的认识。防御的核心原则就一条永远不要信任用户输入严格区分代码与数据。使用参数化查询预编译语句这是最有效、最根本的防御手段。无论是PHP的PDO、Python的cursor.execute()还是Java的PreparedStatement其原理都是将SQL语句的“结构”与“数据”分开发送。数据库先编译带占位符的SQL模板再将用户输入的数据作为纯参数传入。这样即使用户输入中包含‘ or 11 --它也会被当作一个普通的字符串值去匹配username字段而不会被解释为SQL指令。// 错误做法拼接 $stmt $pdo-query(SELECT * FROM users WHERE username $username); // 正确做法参数化 $stmt $pdo-prepare(SELECT * FROM users WHERE username ?); $stmt-execute([$username]);对输入进行严格的校验在参数化查询的基础上增加白名单校验。例如如果id参数应该是数字就用intval()函数强制转换如果用户名只允许字母数字就用正则表达式严格匹配。“默认拒绝明确允许”。使用安全的框架和ORM现代Web框架如Laravel的Eloquent、ThinkPHP的模型的ORM在默认情况下都使用参数化查询。但要注意它们有时也提供了“原生查询”或“表达式”接口使用这些接口时仍需谨慎避免直接拼接用户输入。最小权限原则给Web应用连接数据库的账号分配最小的必要权限。通常只需要SELECT、INSERT、UPDATE、DELETE其业务所需表的权限绝对不要赋予DROP、CREATE、FILE等高级权限。这样即使被注入损失也能被限制。避免详细的错误回显在生产环境中不要将数据库的原始错误信息直接展示给用户。使用自定义的错误页面并将详细错误记录到服务器日志中供管理员查看。这能有效增加攻击者进行盲注的难度。4.2 工具化实践使用Sqlmap进行自动化探测手工注入是理解原理的必经之路但在实际渗透测试中效率太低。Sqlmap是一款开源的自动化SQL注入工具功能极其强大。了解其基本使用能让你对注入的流程有更宏观的认识。基础使用步骤检测注入点sqlmap -u “http://target.com/vul.php?id1”。Sqlmap会自动探测id参数是否存在注入以及注入类型。获取数据库信息sqlmap -u “http://target.com/vul.php?id1” --dbs。列出所有数据库。获取当前数据库表sqlmap -u “http://target.com/vul.php?id1” --tables -D pikachu。列出pikachu数据库中的所有表。获取表字段sqlmap -u “http://target.com/vul.php?id1” --columns -T users -D pikachu。列出users表的所有列。导出数据sqlmap -u “http://target.com/vul.php?id1” --dump -T users -D pikachu。导出users表的所有数据。高级技巧与注意事项处理POST请求使用--data参数如sqlmap -u “http://target.com/login.php” --data“usernameadminpasswordpass”。处理Cookie使用--cookie参数常用于需要登录的注入点。设置延迟对于时间盲注使用--time-sec设置延迟时间默认为5秒。使用代理--proxyhttp://127.0.0.1:8080方便通过Burp Suite观察Sqlmap发送的Payload。风险提示Sqlmap功能强大但破坏性也强。务必只在你自己拥有完全控制权的环境如本地靶场、授权测试的目标中使用。随意对他人系统进行测试是违法行为。4.3 常见问题排查与心法总结在实战Pikachu或类似靶场时你可能会遇到以下问题Q1Payload明明是对的为什么没有效果A首先确认你的Payload语法在目标数据库MySQL下是正确的。其次检查闭合符号。最常见的问题是引号或括号没有正确闭合。使用‘、“、‘、‘))等多种组合尝试。最后查看页面源码或响应看是否有过滤或转义的痕迹如\。Q2联合查询时order by测出了列数但union select不回显数字A这可能是因为显示位不在你看到的页面上。尝试在union select后跟一些能产生明显变化的函数如version、user()、concat(‘test‘, ‘test‘)。或者查看网页源代码数据可能隐藏在HTML注释或某个标签属性里。Q3时间盲注时sleep函数似乎没生效A首先确认数据库用户是否有SLEEP函数的执行权限。其次网络延迟可能导致时间判断不准适当增加sleep的时间如10秒。最后有些环境可能限制了SQL语句的执行时间或禁止了sleep函数。心法总结SQL注入的学习是一个“破坏-理解-建设”的过程。Pikachu靶场给了你一个安全的“破坏”环境。通过这十一关你不仅学会了各种注入技巧更重要的是你看到了不安全的代码长什么样。这会在你未来自己写代码时在脑海里敲响警钟。真正的安全高手首先是深刻的攻击者然后才是严谨的防御者。当你再看到一段将用户输入拼接到SQL语句的代码时能瞬间在脑中浮现出十几种利用方式那么你自然就知道该怎么去修复它了。这就是Pikachu这类靶场最大的价值——将抽象的风险转化为肌肉记忆般的经验。