XSS

​ XSS(Cross-Site Scripting,跨站脚本攻击)是 Web 安全中最常见的漏洞之一,为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混 淆,故将跨站脚本攻击缩写为XSS。其本质是攻击者在网页中注入恶意脚本(通常是 JavaScript),当用户访问被注入的页面时,恶意脚本在用户的浏览器中执行,从而实现窃取信息、劫持会话、篡改页面等攻击目的。

核心原理

​ 网页的正常运行依赖于用户输入(如表单提交、URL 参数、评论等)与服务器响应的交互。当开发者未对用户输入进行严格过滤或对输出内容进行安全编码时,攻击者可构造包含恶意脚本的输入,这些输入会被网页 “信任” 并包含在页面中。当用户访问页面时,浏览器会将恶意脚本视为合法代码执行,导致攻击发生。

​ 根据恶意脚本的 “存储与执行方式”,XSS 可分为以下三类:

	*  反射型XSS
	*  Dom型XSS
	*  存储型XSS

反射型XSS

  • 特点:恶意脚本不存储在服务器,而是通过 URL 参数、表单提交等方式 “临时” 传递给服务器,服务器接收后直接 “反射” 到页面中,仅当用户点击含恶意参数的 URL 时才触发攻击。

  • 攻击流程

    1. 攻击者构造含恶意脚本的 URL

      1
      http://example.com/search?query=<script>...</script>
    2. 诱导用户点击该 URL(如通过钓鱼邮件、聊天工具发送);

    3. 服务器接收 URL 中的参数,未过滤直接嵌入页面并返回给用户;

    4. 浏览器执行页面中的恶意脚本,完成攻击。

  • 典型场景:搜索框、登录失败提示(如 “用户名 xxx 不存在”)、URL 跳转页等。

    image-20250719114555304

<script>

  • 最直接的代码执行

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

    image-20250719122137792

    我们回车,看看我们的alert(1)是否会执行

    image-20250719122238762

htmlspecialchars

  • 我们继续按照level1的思路去做

    image-20250719132329379

  • 恶意代码没有被执行,分析一下源代码

    image-20250719132904478

  • 我们的输入被htmlspecialchars函数做了过滤

www.php.net 查询后,得到这个函数的作用

  • htmlspecialchars — 将特殊字符转换为 HTML 实体
  • 执行转换
字符替换后
& (& 符号)&
" (双引号)",除非设置了 ENT_NOQUOTES
' (单引号)设置了 ENT_QUOTES 后, ' (如果是 ENT_HTML401) ,或者 ' (如果是 ENT_XML1ENT_XHTMLENT_HTML5)。
< (小于)<
> (大于)>

我们简单总结一下:

  • htmlspecialchars函数将特殊字符编码为HTML 实体
  • 特殊字符实体编码后,只会被解码为普通字符串

以此完成了对输入的过滤,htmlspecialchars引出的xss常见的三种编码规范

闭合标签

虽然lever2做了过滤,但我们可以尝试在输入时闭合<input>标签,然后写入我们的payload

image-20250719162646716

1
2
3
<input name=keyword value="'.$str.'">
// <input name=keyword value="'a'"><script>alter(1)</script>"
a'"><script>alter(1)</script>

image-20250719164153486

on事件

HTML 标签的on*事件属性(如onclickonerror)在特定行为触发时会执行 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

image-20250719165521940

image-20250719165503717

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

image-20250719170625645

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

image-20250719172829311

on事件的绕过技巧

image-20250719181932555

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

image-20250719182407277

image-20250719175614543

image-20250719175602250

很明显,这个替换函数基本上是过滤完了我们目前了解的反射型xss的方式,那么,我们尝试从str_replace函数本身入手

  • str_replace — 子字符串替换

  • 警告:由于 str_replace() 的替换时从左到右依次进行的,进行多重替换的时候可能会替换掉之前插入的值。参见该文档的示例。

  • 注意: 该函数区分大小写。使用 str_ireplace() 可以进行不区分大小写的替换。

而我们的html是不区分大小写的,js是严格区分大小写的

1
aa' " ONfocus=alert(1) "' 

image-20250719181243837

javascript:

部分标签的srchref属性支持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:协议,且标签被正常渲染

image-20250719174338190

image-20250719174529114

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

image-20250719174818515

str_replace

image-20250719175614543

image-20250719175602250

很明显,这个替换函数基本上是过滤完了我们目前了解的反射型xss的方式,那么,我们尝试从str_replace函数本身入手

  • str_replace — 子字符串替换

  • 警告:由于 str_replace() 的替换时从左到右依次进行的,进行多重替换的时候可能会替换掉之前插入的值。参见该文档的示例。

  • 注意: 该函数区分大小写。使用 str_ireplace() 可以进行不区分大小写的替换。

而我们的html是不区分大小写的,js是严格区分大小写的

1
aa' " ONfocus=alert(1) "' 

image-20250719181243837

编码绕过

XSS常见的三种编码规范

image-20250719183308931

image-20250719183532034

1
2
3
4
5
javascript:alert(1)
&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;alert(1)

# 绕过http://检测
&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;alert(1)//http://

隐藏form

image-20250719192152932

从源码来看的话,难度不大,keyword 没有什么利用点,给t_sort 传值并重写 type

1
t_sort=aa" onfocus=alert(1) type="text" #onfocus 会一直聚焦

image-20250719193233930

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