XSS&反射型XSS
XSS
XSS(Cross-Site Scripting,跨站脚本攻击)是 Web 安全中最常见的漏洞之一,为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混 淆,故将跨站脚本攻击缩写为XSS。其本质是攻击者在网页中注入恶意脚本(通常是 JavaScript),当用户访问被注入的页面时,恶意脚本在用户的浏览器中执行,从而实现窃取信息、劫持会话、篡改页面等攻击目的。
核心原理
网页的正常运行依赖于用户输入(如表单提交、URL 参数、评论等)与服务器响应的交互。当开发者未对用户输入进行严格过滤或对输出内容进行安全编码时,攻击者可构造包含恶意脚本的输入,这些输入会被网页 “信任” 并包含在页面中。当用户访问页面时,浏览器会将恶意脚本视为合法代码执行,导致攻击发生。
根据恶意脚本的 “存储与执行方式”,XSS 可分为以下三类:
* 反射型XSS
* Dom型XSS
* 存储型XSS
反射型XSS
特点:恶意脚本不存储在服务器,而是通过 URL 参数、表单提交等方式 “临时” 传递给服务器,服务器接收后直接 “反射” 到页面中,仅当用户点击含恶意参数的 URL 时才触发攻击。
攻击流程
攻击者构造含恶意脚本的 URL
1
http://example.com/search?query=<script>...</script>
诱导用户点击该 URL(如通过钓鱼邮件、聊天工具发送);
服务器接收 URL 中的参数,未过滤直接嵌入页面并返回给用户;
浏览器执行页面中的恶意脚本,完成攻击。
典型场景:搜索框、登录失败提示(如 “用户名 xxx 不存在”)、URL 跳转页等。

<script>
最直接的代码执行
在html页面中,被<script>标签包裹的js代码是可以被识别并执行的,所以我们可以利用这一点做恶意的用户输入

我们回车,看看我们的
alert(1)是否会执行
htmlspecialchars
我们继续按照
level1的思路去做
恶意代码没有被执行,分析一下源代码

我们的输入被
htmlspecialchars函数做了过滤
www.php.net 查询后,得到这个函数的作用
- htmlspecialchars — 将特殊字符转换为
HTML 实体 - 执行转换
| 字符 | 替换后 |
|---|---|
& (& 符号) | & |
" (双引号) | ",除非设置了 ENT_NOQUOTES |
' (单引号) | 设置了 ENT_QUOTES 后, ' (如果是 ENT_HTML401) ,或者 ' (如果是 ENT_XML1、 ENT_XHTML 或 ENT_HTML5)。 |
< (小于) | < |
> (大于) | > |
我们简单总结一下:
htmlspecialchars函数将特殊字符编码为HTML 实体- 特殊字符实体编码后,只会被解码为普通字符串
以此完成了对输入的过滤,由htmlspecialchars引出的xss常见的三种编码规范
闭合标签
虽然lever2做了过滤,但我们可以尝试在输入时闭合<input>标签,然后写入我们的payload

1 | <input name=keyword value="'.$str.'"> |

on事件
HTML 标签的on*事件属性(如onclick、onerror)在特定行为触发时会执行 JavaScript,可利用这一特性注入代码。
高频标签 + 事件组合
| 标签 | 事件属性 | 触发场景 | 示例代码 |
|---|---|---|---|
<img> | onerror | 图片加载失败时 | <img src="invalid.png" onerror="alert(1)"> |
<body> | onload | 页面加载完成时 | <body onload="alert(document.cookie)"> |
<a> | onclick | 点击链接时 | <a href="#" onclick="alert(1)">点我</a> |
<input> | onfocus | 输入框获取焦点时 | <input type="text" onfocus="alert(1)"> |
<video> | onended | 视频播放结束时 | <video src="x" onended="alert(1)"></video> |
在了解完on事件后,我们再来看level3


看到input标签,我们可以很自然的想到onfocus事件,这个时候我们不在需要闭合掉input标签,只需要闭合掉value属性值,而我们也不难发现一个奇怪的点,value=' "" '为啥要用两个引号,外面还是个单引号,明显不符合正常人的逻辑,那他必然就是作者留给我们的后门,继续去看这个过滤函数

1 | <input name=keyword value='"a' onfocus=alert(1) '"'> |

on事件的绕过技巧

1 | aa' " oonnfocus=alert(1) "' |



很明显,这个替换函数基本上是过滤完了我们目前了解的反射型xss的方式,那么,我们尝试从str_replace函数本身入手
str_replace — 子字符串替换
警告:由于 str_replace() 的替换时从左到右依次进行的,进行多重替换的时候可能会替换掉之前插入的值。参见该文档的示例。
注意: 该函数区分大小写。使用 str_ireplace() 可以进行不区分大小写的替换。
而我们的html是不区分大小写的,js是严格区分大小写的
1 | aa' " ONfocus=alert(1) "' |

javascript:
部分标签的src或href属性支持javascript:伪协议,直接执行脚本,无需事件触发。
- 高频标签
<a>标签:<a href="javascript:alert(1)">点击</a>(点击链接时执行脚本)<iframe>标签:<iframe src="javascript:alert(document.domain)"></iframe>(加载时执行脚本)<img>标签(低版本浏览器):<img src="javascript:alert(1)">(部分旧浏览器支持javascript:协议)<video>标签:<video src="javascript:alert(1)"></video>(少数浏览器兼容)
- 变种绕过技巧
- 编码混淆:
<a href="javascript:alert(1)">(用 HTML 实体编码:为:绕过过滤) - 空格分隔:
<a href="javascript: alert(1)">(协议后加空格,部分解析器仍能识别)
- 编码混淆:
- 触发条件:
src/href属性值被解析为javascript:协议,且标签被正常渲染


1 | a"> <a href="javascript:alert(1)">aa</a> |

str_replace


很明显,这个替换函数基本上是过滤完了我们目前了解的反射型xss的方式,那么,我们尝试从str_replace函数本身入手
str_replace — 子字符串替换
警告:由于 str_replace() 的替换时从左到右依次进行的,进行多重替换的时候可能会替换掉之前插入的值。参见该文档的示例。
注意: 该函数区分大小写。使用 str_ireplace() 可以进行不区分大小写的替换。
而我们的html是不区分大小写的,js是严格区分大小写的
1 | aa' " ONfocus=alert(1) "' |

编码绕过


1 | javascript:alert(1) |
隐藏form

从源码来看的话,难度不大,keyword 没有什么利用点,给t_sort 传值并重写 type
1 | t_sort=aa" onfocus=alert(1) type="text" #onfocus 会一直聚焦 |

反射型XSS url/搜索/input -> js伪协议/on事件/编码 等方式绕过 -> 二次触发