--------XLL----------
函数 作用 靶场使用场景 $_GET["xxx"]获取 URL GET 请求参数 Level1~10 获取可控输入 name、keyword、t_sort 等 $_SERVER["HTTP_REFERER"]获取 Referer 请求头 Level11 读取 Referer 头作为可控输入 $_SERVER["HTTP_USER_AGENT"]获取 UA 请求头 Level12 读取 User-Agent 头作为可控输入 $_COOKIE["user"]读取客户端 Cookie 值 Level13 读取 Cookie 可控数据 setcookie()设置 Cookie Level13 页面初始化 Cookie htmlspecialchars($str, $flags, $charset)HTML 实体转义,过滤 <>"'& Level2/3/15 输出编码;防御 XSS 核心函数 strtolower()字符串转小写 Level5 统一小写防大小写绕过 str_replace($find,$rep,$str)全局单次字符串替换 Level5/6/7 黑名单替换 script、on 等关键字 str_ireplace()不区分大小写替换 Upload-Labs 大量用于清理后缀、::$DATA echo输出内容到页面 所有关卡直接拼接用户输入到 HTML parse_url()解析 URL 获取协议、域名 URL 属性上下文防御校验 href 协议 in_array()判断元素是否在数组(白 / 黑名单) URL 协议白名单校验、后缀校验 json_encode()JS 字符串安全序列化 JS 上下文防御,代替直接拼接变量
--------upload---------
文件上传基础接收类
函数 作用 使用场景 $_FILES['upload_file']获取上传文件数组(name/tmp_name/size/type) 全部 20 关接收上传文件 move_uploaded_file($tmp,$dest)将临时文件移动到目标路径 所有关卡保存上传文件,存在空字节截断、竞争漏洞 basename()提取纯文件名,清除路径 ../文件路径防御 date()/rand()生成随机时间戳文件名 随机重命名规避文件名可控风险
字符串清洗 / 后缀处理函数
函数 作用 使用场景 trim()去除字符串首尾空白(空格、\t、\n) Pass05~09、Pass10 清洗文件名首尾 deldot()(自定义函数)循环删除文件名末尾所有 .Pass05~08 防御末尾点绕过 strrchr($str,".")截取最后一个 .后的后缀提取文件扩展名用于黑名单校验 pathinfo($file,PATHINFO_EXTENSION)精准获取文件后缀 Pass19 可控 save_name 后缀提取 explode('.',$str)按点分割文件名为数组 Pass20 数组参数漏洞拆分文件名 reset($arr)获取数组第一个元素 Pass20 构造文件名 end($arr)获取数组最后一个元素 Pass20 校验后缀白名单 count($arr)获取数组长度 Pass20 稀疏数组逻辑漏洞核心 unlink()删除服务器文件 校验失败删除非法上传文件 rename()文件重命名 Pass17/18 竞争漏洞,先保存后重命名
防御方式
XSS 漏洞(反射型 / 存储型 / DOM 型)
反射型 XSS
漏洞成因攻击内容随当前请求进入应用,并在同一次响应中返回。通常需要受害者打开特制链接。XSS-Labs 的大多数 PHP 关卡属于反射型或由请求头触发的服务端 XSS。
存储型 XSS
攻击内容先保存到数据库、文件、缓存或日志,之后在其他用户访问页面时被输出。典型位置包括留言、昵称、 工单、商品描述和后台日志查看器。原始 XSS-Labs 20 关并没有系统覆盖存储型 XSS,因此本课使用一个补充示 例说明。
DOM 型 XSS
DOM 型 XSS 的核心是客户端 JavaScript 把可控数据送入危险 Sink。Payload 可能不出现在服务器响应文本中, 尤其是使用 URL #fragment 时,片段通常不会发送给服务器。
那么我们如何防御呢?
Level 1
在这一关里没有对输入的内容做任何的限制与保护,以至于我们可以直接在输入窗口写入命令,让浏览器执行。
对于这种我们可以把用户输入的内容转换成普通的文本进行输出,例如 htmlspecialchars($str, ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8")
$str:需要处理的用户数据(也就是 $name)ENT_QUOTES:同时转义 单引号 '、双引号 " 默认模式只转双引号,如果属性用单引号包裹,会被攻击者逃逸。ENT_SUBSTITUTE遇到非法残缺 UTF-8 字符,替换为�,不会直接清空字符串。 ❌ 禁止用 ENT_IGNORE(直接删掉异常字符,容易出现安全绕过)|位或:代表同时开启两个配置"UTF-8":强制指定编码,消除不同 PHP 版本默认编码不一致带来的漏洞。
Level 2
有时候用户的参数写在函数的属性里面,这时候可以通过引号"闭合这个属性,然后再插入一个<script>。
对于这种每一个输出点都要按所在上下文编码。属性值应始终加引号,并使用 ENT_QUOTES 编码双引号和单引号。
网页四大常见上下文:
- HTML 正文
<div><?php echo $data ?></div>→ 使用带完整参数htmlspecialchars()(HTML 实体编码)- HTML 属性
<input value="xxx">→ 使用htmlspecialchars(...,ENT_QUOTES)<script>标签内部 JS 代码let a = '数据'→ 不能用 htmlspecialchars ,要用json_encode()JS 转义- URL 地址
<a href="test.php?key=xxx">→ 使用urlencode()
<?phpstr = _GET'name';
safe = htmlspecialchars(str,ENT_QUOTES,"UTF-8");
?>
<script>
let name = '<?php echo $safe; ?>'
</script>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
攻击者传入 payload:
\';alert(1)//htmlspecialchars** 不会转义反斜杠 **,依然能够逃逸,产生 XSS。
Level 5
如果我们在页面上写黑名单,禁止用户写一些高危函数;但是用户还是有可能写一些开发者没见过的有漏洞的函数,或者使用大小写,宽字符,单次删除与双写重组,这种进行绕过。还有直接拼接:<a href="javascript:alert(1)">点击</a> 点击链接执行 JS,产生URL 伪协议 XSS。
对于这种建议直接使用白名单,只允许我同意的函数写入。获取可控 URL;规范化 URL,调用官方 URL 解析器提取 scheme 协议(禁用手写正则匹配)。
为什么手写正则匹配不靠谱 >>> 攻击者大量混淆手段,简单正则 javascript: 轻松绕过;
- 大小写变形:
JaVaScRiPt:alert(1) - HTML 实体编码:
javascript:alert(1) - 插入空白 / 控制字符:
java script:、javascript%0A:alert(1) - URL 编码变形:
javasc%72ipt:alert(1)
1)获取可控 URL
数据来源:GET/POST 参数、用户输入
user_url = _GET'link'; // 用户可控输入,原始数据
此时输入杂乱,可能携带 URL 编码、空白字符、HTML 实体。
2)URL 规范化(Normalize)
什么是规范化?
把各种编码形式、非法空白、转义序列统一还原成标准原始字符串。 作用:消除攻击者用来绕过检测的混淆手段。
解析器严格遵循 URL 标准,自动区分: scheme(协议)、host(主机域名)、userinfo、path、query、fragment (# 锚点) 不会被 #、@、? 这类符号欺骗。
规范化要做的事情(通用标准):
- URL 百分编码解码
%XX;- 剔除 URL 内部无效换行、制表符、空格等控制字符;
- 统一大小写(协议统一转小写
JavaScript:→javascript:);- 解析并还原 HTML 字符实体(
&#xx;)。举个例子: 混淆载荷:
javascript%0A:alert(1)经过规范化处理后 →javascript:alert(1)混淆外壳被剥掉,露出真实协议。3)调用官方内置 URL 解析器,提取 scheme
什么是 scheme?就是 URL 最前方的协议
http://xxx→ scheme =httphttps://xxx→ scheme =httpsjavascript:alert()→ scheme =javascriptPHP 内置标准解析函数:
parse_url()这是 C 语言底层实现的标准 URL 解析器,遵循 RFC URL 规范,不依赖开发者写规则,自动拆分 URL 五大组成部分: scheme(协议)、host(域名)、path、query、fragment
Level 11
浏览器发起请求时自动携带 Referer 请求头,标识上一跳页面地址。
例:从 A 页面点击链接访问 B 页面,B 收到请求头: Referer: https://a.com/page.html
但是攻击者可以伪造 Referer;使用 Burp Suite、Python requests、curl、Postman 等工具,可随意自定义任意 Referer 内容;浏览器扩展也可以篡改该头部。
如Referer: test"οnclick=alert(1)//
这时候Referer 只可作为辅助信息,不能信任;显示或写入 HTML 时必须按上下文编码。
Level 12:User-Agent 请求头成为输入源
User-Agent(UA)是 HTTP 请求头,用来标识客户端浏览器、设备信息。
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0
UA 完全可控,攻击者可以随意伪造
User-Agent: <script>alert(document.cookie)</script>
场景复现:Level12 典型漏洞链路(存储型 XSS)
- 攻击者发送请求,植入恶意 UA;
- 后端读取
$_SERVER['HTTP_USER_AGENT']存入日志 / 数据库; - 管理员登录后台日志页面,后端读取日志中的 UA;
- ❌ 没有编码,直接渲染到 HTML 页面;
- 管理员打开页面,恶意代码执行 → 存储型 XSS。
所以我们后台显示页面也要输出编码,入库不需要强制转义,输出渲染 HTML 才必须上下文编码! 防御 XSS 黄金原则:输入不清洗,输出做编码
Level 13:Cookie 成为输入源
1. Cookie 是什么
Cookie 是服务器下发、保存在浏览器本地的小型文本数据(大小上限一般 4KB)。 工作流程:
- 浏览器发起 HTTP 请求访问网站;
- 服务器响应头通过
Set-Cookie,下发数据给浏览器;- 浏览器自动把这条 Cookie 保存;
- 后续每次访问同域名网站,浏览器自动在请求头带上 Cookie 发给服务端。
简单理解:网站存放在你浏览器里的 "小纸条"。
响应示例(服务端下发 Cookie)
Set-Cookie: userid=1001; HttpOnly; Secure; SameSite=Lax
后续浏览器请求自动携带:
Cookie: userid=1001
2. Cookie 典型用途
- 会话保持(最重要):登录后存放 session 标识,让服务器认识 "你已经登录";
- 保存用户偏好:主题样式、语言设置;
- 流量统计、广告追踪。
3. 两大分类(安全重点)
① 普通 Cookie(无 HttpOnly)
浏览器 JS 可以通过
document.cookie读取、修改 👉 如果网站存在 XSS 漏洞,攻击者编写 JS 就能偷走 Cookie,冒充用户登录。② HttpOnly Cookie
JS 无法访问读取 。 即使出现 XSS,恶意脚本拿不到这条 Cookie; ⚠️【关键:只能防 Cookie 窃取,阻止不了 XSS 代码执行】
Cookie 为什么属于不可信输入源?
- Cookie 客户端可篡改 用户可通过浏览器开发者工具、脚本、代理工具随意修改 Cookie 的值; 服务端收到的 Cookie,不能默认和下发时内容一致。
- 部分 Cookie 可被 JS 读取(未开启 HttpOnly) 一旦出现 XSS,JS 可以读取 Cookie 并外送窃取会话凭证。
安全通用铁律:所有从客户端接收的数据(GET/POST/Cookie/ 请求头)全部视为外部不可信输入。
那么我们怎么防御呢:
Cookie 输入仍需校验和输出编码。签名可验证完整性;HttpOnly、Secure、SameSite 是会话纵深防御, 不能替代 XSS 修复。
Secure 仅在 HTTPS 加密请求中携带 Cookie;HTTP 明文请求不会带上 Cookie。 防止中间人抓包窃取 Cookie。
SameSite=Lax/Strict 防御 CSRF 跨站请求伪造,限制跨站点场景自动携带 Cookie。