一、xss原理及分类
跨站脚本攻击XSS(Cross Site Scripting),为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页面时,嵌入Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。XSS攻击针对的是用户层面的攻击!
1.原理
2.分类
(1)反射型xss
非持久化,需要欺骗用户自己去点击链接才能触发XSS代码(服务器中没有这样的页面和内容),一般容易出现在搜索页面。反射型XSS大多数是用来盗取用户的Cookie信息。
①DOM型XSS:不经过后端,DOM-XSS漏洞是基于文档对象模型(Document Objeet Model,DOM)的一种漏洞,DOM-XSS是通过url传入参数去控制触发的,其实也属于反射型XSS。
(2)存储型xss
存储型XSS,持久化,代码是存储在服务器中的,如在个人信息或发表文章等地方,插入代码,如果没有过滤或过滤不严,那么这些代码将储存到服务器中,用户访问该页面的时候触发代码执行。这种XSS比较危险,容易造成蠕虫,盗窃cookie
二、XSS Game/xsstest
XSS Game - Learning XSS Made Simple! | Created by PwnFunction
1.Ma Spaghet!
(1)规则如下
弹出1337,不能使用用户交互(不能让用户触发),不能使用某段网址,测试要在chrome下。
Let's Go!
触发新页面后查看网页源代码,然后进入源代码页面。
(2)查看代码
<!-- Challenge -->
<h2 id="spaghet"></h2>
<script>
spaghet.innerHTML = (new URL(location).searchParams.get('somebody') || "Somebody") + " Toucha Ma Spaghet!"
</script>
在浏览器传参时获得了somebody参数,如下:
由第三行代码可知,输出在innerHTML里,innerHTML是spaghet中的内容,
如上图,此时相当于是将输出的字符放入了h1标签中去
(3)标签弹窗
<script>alert(1)</script>
上图操作依旧不可行,可以去查看官方手册,由于script标签涉及到安全问题,所有被禁用。
所以我们更换一个标签:
①<img src=1 οnerrοr=alert(1337)>
成功弹窗!
②<svg/οnlοad=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参数,第五行可知,随后放入模板字符串中。
(1)解决思路
由于第五行代码,jeff被放入模板字符串,aaa拿出来后,1s过后,放入innerText也就是第一行代码,第五行的eval为可利用点,双引号中接入了jeff接受的字符串拼接为 Ma name aaa。
我们要组合成eval( ' alert(1) ' ),就可以实现弹窗;eva(' "aaa"+alert(1)+" " ')同样可以弹窗,相当于是用加号将一个字符串和一个函数和一个空字符串连接在一起传递给eval,eval按顺序执行(eval是不执行数组的)
eva(' "ma = aaa"+alert(1)+" " ')为我们需要的拼接结果
浏览器传入aaa"%2balert(1337)%2b" (%2b是对加号的url编码,若不编码会出错而无法显示):
减号其实也是js中的连接符,eva(' "ma = aaa"-alert(1)-" " '),而且减号还不需要转义:
(2)引申至另一个题目xssteat1
查看网页源码:
<script type="text/javascript">
var x=location.hash; 获取hash值
function aa(x){}; hash值放入函数
setTimeout("aa('"+x+"')",100); 放入定时器执行,定时器第一个参数可以传入函数/字符串
</script>
Give me xss bypass 1~
"aa(' "+x+" ')"
调用了aa这样的函数,外层双引号包裹起来也可以看作字符串,
"aa(' " " ')" +x+
前面两个为字符串,第三个的加号被视为连接符,x是要传递的值(双引号成对出现)
如何获取hash值:
①方案一(setTimeout中执行)
setTimeout如何执行:
构造出 setTimeout ( " aa ( ' "+" ' , alert (1),' "+" ' ) " , 100 ) 是可以执行的(双引号包裹连接符+成为字符串)
setTimeout第一个传递的字符串是 aa ( ' 第二个是 , alert (1), 第三个是' )
第二个字符串前后的逗号是为了与前后的字符串分隔开(要传入aa函数,不分隔开则会报错),serTimeout执行的是第二个参数,alert (1)执行完后调用aa函数,
aa中传的三个参数是+ alert(1) +
但aa没有函数体,虽然传入三个参数,aa是没有执行的。
给x传入#" ',alert(1),' "
setTimeout接收到的结果为:
setTimeout ( " aa ( ' "+" ' , alert (1),' "+" ' ) " , 100 )
②方案二(x中执行)
给x传入# \ \ ' x ' 拼接成aa ( ' # \ \ ' x ' ' )
产生报错:缺少一个括号
setTimeout接收到的结果为:
setTimeout ( " aa ( ' "+ " # ',alert(1),' "+" ' ) " , 100 )
此时不会报错,成功执行弹窗!
③方案三(aa外部执行)
给x传入#');alert(1)//
setTimeout接收到的结果为:
setTimeout ( " aa ( ' # ' ) ; alert(1) / / ' ) " , 100 )
aa ( ' # ' ) ; alert(1) / / ' ) 第一个后括号将aa函数闭合,然后执行自己的代码alert
3.Ugandan Knuckles
(1)查看代码
<!-- 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参数,第四行过滤了尖括号,然后放入placeholder
过滤尖括号防止闭合input标签写入恶意代码,代码放入了div执行。
(2)
这样显然不行,因为是需要用户交互的(用户需要点击aaa框中的内容),而题目是不允许产生用户交互的。
但在html中存在一个属性autofocus:
页面加载完毕,焦点会到aaa中(自动对焦),但是点击行为没发生,还不会触发,
onfocus:聚焦事件
成功弹窗!
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写入了form表单的action属性中,action不提交是无法触发的,但代码中刚好有自动提交。
form中的action可以直接用js的伪协议直接触发:
5.xsstest2
与2(2)是同一个网页
(1)查看源码
<html>
<head>
<script src="./jquery-3.4.1.min.js"></script>
Give me xss bypass 2~
<div style='display:none' id='xx'><img src=x onerror=alert(1)></div>
<input type='button' value='test' onclick='alert("哈哈,点这玩意没啥用的!")'>
<body>
<script>
var query = window.location.search.substring(1); search中截取第一位,search就是url的传参
var vars = query.split("&"); 使用&分割
if(vars){
aa(vars[0],vars[1]) 数组第0个第1个元素传递给aa函数
}
function aa(x,y){
$("#xx")[x]($("#xx")[y]()); aa函数是jquery,jquery抓了一个元素id='xx'
}
</script>
</body>
</html>
jquery抓元素是使用$符号:
将第二行被转码的值还原然后使用jquery抓出来然后执行
jquery调用函数的两种方式: $('#xx').html $('#xx')[html]()
所以代码$("#xx")[x]($("#xx")[y]())表示内部调用y方法外部是x方法
(2)执行弹窗
分析:
分割完成后如下
然后aa中var[0]也就是第一个参数为html,第二个参数为text
y为text,然后如下,调用y相当于将代码还原回去(被转码的被还原了)
x为html,直接使用innerHTML会将还原的标签重新写会div中去执行如下图
innerHTML的作用就是抓到元素的代码重新写回去
5.Ok, Boomer
(1)代码分析
<!-- 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.sanitize过滤函数,若使用如下三种操作都会被过滤掉
?boomer=<img src=1 οnerrοr=alert(1)>
?boomer=<a href="javascript:alert(1)">a</a> href会被过滤
?boomer=<svg/οnlοad=alert(1)> 恶意代码
(2)初步分析
?boomer=<a href="tel:alert(1)">a</a>
如图所示,并未被过滤,tel是白名单,并且点击a可以有触发(但是必须要用户交互)
(3)Dom Clobbering
分析上图,直接使用id和name就可以取到x、y;document中通过id取是取不到的,但通过name可以取到;在windows通过id和name都可以取到。
上图中写入img name值,document可以取出。(不是任何标签都可以实现)
使用form表单:
document.body取出form,appendChild又可以取出form中的img,但document.body.appendChild是一个对象而不是字符串或者函数,因为setTimeout第一个值是可以接受函数或者字符串的,我们可以尝试使用dom破坏覆盖掉ok,覆盖成我们自己写的标签,标签又可以转换为字符串然后执行。
我们需要找到一个标签,去取标签的时候可以把里面的toString方法找出来,触发一个字符串。
上图分析:
首先test将a标签整个抓到了,alert触发标签时首先会调用a的toString方法,a的toString是自己重写的(调用的是自己href属性中的值,而不像object一样调用两个object对象),拿出href后,打印的就是tel:alert(1),可以见得a标签的href是被自身所写的toString方法调用的。
(4)解题
使用上图将ok取出来,ok就是这一整个标签
ok是原本不存在的,所以才可以使用DOM覆盖的方式。
回到源码,setTimeout调用的第一个参数是a标签,然后会自动调用a标签的toString方法(是由于setTimeout第一个值接收函数或者字符串,而a标签是对象,所以会尝试去调用对象上的toString方法),从而将href中的值拿出来,放在第一个参数位置直接执行。
DOMPurify框架是有白名单的:
?boomer=<a id=ok href="tel:alert(1337)">
成功弹窗!