XSS(一)
PyxYuYu opened this issue · 0 comments
PyxYuYu commented
A goal is a dream with a deadline.
0x01 XSS
反射型XSS
- 非持久化,需要欺骗用户自己去点击链接才能触发
XSS
代码,服务器中没有这样的页面和内容 - 目前多数主流浏览器都有
XSS
过滤器(测试一般用IE6
或IE7
,或者更高级的手动关闭XSS
筛选),所以常规的反射型XSS
一般会被过滤,需要绕过 - 在防范这类
XSS
时,通常可以在服务器段对传入的参数进行过滤,来防范这类攻击
- 非持久化,需要欺骗用户自己去点击链接才能触发
DOM型XSS
DOM-based XSS
是基于文档对象模型Document Object Model
的一种漏洞DOM
是一个与平台、编程语言无关的接口,它允许程序或脚本动态地访问或更新文档内容、结构、样式,处理后的结果能够成为显示页面的一部分DOM
中有很多对象,其中一些是用户可以操纵的,比如form、referrer、location
等- 客户端的脚本程序(
JavaScript
)可以通过DOM
动态地检查和修改页面内容,它不依赖于提交数据到服务器端,而从客户端获得DOM
中的数据在本地执行 - 如果
DOM
中的数据没有经过严格确认,就会产生DOM-based XSS
漏洞
DOM型XSS
输入点(即可能之后导致发生XSS
的前提)--JavaScript
location
location.href
location.search
IE
没有将<>
进行URL
编码
location.hash
location.pathname
window.name
document.cookie
localStorage
document.title
getAttribute
document.referrer
innerText
.value
.dataset
postMessage
- 使用
\
绕过初始检测
- 使用
innerHTML
DOM型XSS
触发点(即可插入XSS
代码处) --JavaScript
location
location='javascript:alert(~1)'
eval
innerHTML
document.write
appendChild
Function
setTimeout
setInterval
createElement
*with(document)body.appendChild(createElement('iframe onload=alert(1)>'),body.innerHTML+="(IE)
- 显示输出
URL
提交后,右键查看源代码可以看到输出的内容innerHTML
<div id="a">xxx</div> <script> document.getElementById("a").innerHTML="yyyy"; </script>
innerHTML="[输出]"
,可以通过JS
脚本改变a
的值[输出]
这里只能使用<img src=1 onerror=alter(1)>
这种方式来触发JS
- 不能使用
<script>alert(1)</script>
来触发 IE
下可以使用<script defer>alert(1)</script>
<
被过滤的话,可以使用\u003c、\x3c
代替>
被过滤的话,可以使用\u003e、\x3e
代替- 空格可以使用
\u0020
代替 - 所以上面的
Payload
可以写成\x3cimg\u0020src=1\u0020onerror=alert(1)\x3e
- 除了
innerHTML
只要与改变页面HTML
内容相关的操作,都可能导致这种问题 - 除了
JS
,一些使用了第三方库,譬如jQuery
也会:$("#x").html("yyyy")
- 隐式输出
- 输出操作在查看源代码中无法看见,通常发生在
JavaScript
代码中- 比如:
var x=location.href;
JavaScript
进行了一个隐藏的输出操作,将location.href
的内容输出到了变量x
- 比如:
location.href
function getParam(name){ var x = locaton.search; //或者是 location.hash var v = x.match(new RegExp("[?&#]"+name+"=([^&]*)","")); return v?v[1]:""; }
- 上面函数定义了一个利用正则匹配获取
URL
中name
参数后的参数值的方法
<div id="nick">加载中...</div> <script> var a = getParam("name"); // 获取地址栏里的name参数值 document.getElementById("nick").innerHTML = a; </script>
- 将
a
改写成<img src=1 oneror=alert(1)>
即可触发XSS
,a
就是从getParam
函数通过name
参数获得的参数值 Fuzz
步骤- 隐式输出,源代码中搜索不到参数值,
F12
调试工具,搜索输出 - 搜索到输出后,两种方法
- 直接根据查看到的
HTML
代码,进行构造,注意闭合,注意过滤的情况- 优点:省时
- 缺点:有过滤的情况,会比较难闭合
- 查看
JavaScript
代码,寻找对应的函数,分析函数,是否进行了过滤,编码,解码,如果先编码,后解码,就等同于没有过滤- 优点:明白逻辑,可以进行复杂构造
- 缺点:费时
- 直接根据查看到的
- 注意:
Chrome
浏览器会自动对"、>、<
进行转义成%22、%3c、%3e
,IE
不会
- 隐式输出,源代码中搜索不到参数值,
- 输出操作在查看源代码中无法看见,通常发生在
eval
显示输出
和隐式输出
最终JavaScript
都会通过document.write
或innerHTML
将内容输出到网页中,所以总是有办法可以查看到输出到哪里,但是如果没有通过它们,而是通过函数,利用eval
来进行输出,就无法通过源代码或者F12
调试工具查找
var getarg = function() { var url = window.location.href; var allargs = url.split("?")[1]; if (allargs!=null && allargs.index0f("=")>0) { var args = allargs.split("&"); for(var i=0; i<args.length; i++) { var arg = args[i].split("="); eval('this.'+arg[0]+'="'+arg[1]+'";'); } } }
- 上面函数是获取
URL
,之后将参数分割,分割后执行eval
- 如果这里
arg[0]
或者arg[1]
可控的话,就可以插入XSS
代码,导致漏洞产生 - 原来的
eval
应该执行
eval('this.a="bbb";')
arg[0]
就是a
,arg[1]
就是"bbb"
- 修改
arg[0]
构造XSS
,即
eval('this.a;alert(1);//="bbb";') // 需要加上等号,因为上面的代码逻辑是根据等号来分割,注释掉后面即可 // arg[0] 为 a;alert(1);// // www.xxx.com/1.html?a;alert(1);//=bbb
- 修改
arg[1]
构造XSS
,即
eval('this.a="bbb";alert(1);//"') // " 是代码后面加上的,所以这里需要自己闭合前面的双引号,后面的双引号可以注释掉 // www.xxx.com/1.html?a=bbb";alert(1);//
- 注意:修改
arg[1]
这里自己闭合了双引号,在Chrome
中双引号会被主动过滤,所以只有在IE
下才会成功
iframe
<iframe src="[输出]"></iframe>
iframe
的src
属性本来应该是一个网站,但是iframe
可以使它同样可以执行JavaScript
,而且可以使用不同的姿势来执行,这一类问题,可以归为[路径可控]
问题onload
执行JS
<iframe onload="alert(1)"></iframe>
src
执行JS
<iframe src="javascript:alert(1)"></iframe>
IE
下src
执行vbscript
<iframe src="vbscript:msgbox(1)"></iframe>
Chrome
下src
执行data
协议
<iframe src="data:text/html,<script>alert(1)</script>"></iframe> // 转义 <iframe src="data:text/html,<script>alert(1)</script>"></iframe>
Chrome
下srcdoc
执行JS
<iframe srcdoc="<script>alert(1)</script>"></iframe>
Fuzz
步骤F12
调试工具,查看有无可见输出- 查到参数被带入到
<iframe src="[参数]"></iframe>
之中 - 查看相关函数,是否有一些过滤
function OpenFrame(url) { if (url.toLowerCase().indexOf('http://') != '-1' || url.toLowerCase().indexOf('https://') != '-1' || url.toLowerCase().indexOf('javascript:') != '-1') return false; document.getElementById("toolframe").src = url; }
- 上面函数过滤了
http://
https://
javescript:
document.getElementById("toolframe").src = url;
这里导致了执行XSS
url
参数可控制,过滤不完善,所以- 在
IE
下,可以使用vbscript
来执行代码,vbscript
中'
单引号表示注释,类似JS
中的//
http://www.xxx.com/1.html?url=vbscript:msgbox(1)'&gid=yl
- 在
Chrome
下,可以使用data
协议来执行JS
http://www.xxx.com/1.html?url=data:text/html,<script>alert(1)</script>//
- 在
路径con
- 同域的时候,可以使用
AJAX
动态地加载json
数据,而有时候,数据所在域和当前页面所在域不同,所以需要跨域请求,跨域请求数据中,有一种方法叫做JSONP
- 用代码表示的话,就是
somescript.src="http://otherdomain.com/xx?jsonp=callback"
- 有时,程序员会在调用外部数据的时候带上可控的参数,这个可控参数就可能导致
XSS
somescript.src="http://otherdomain.com/xx?jsonp=callback&id="+id;
- 跨域请求
JSON
是一种基于文本的数据交换格式,或者称为数据描述格式JSON
只有两种数据类型描述符,大括号和方括号,其余冒号是映射符,逗号是分隔符,双引号是定义符,对象表示为键值对
JSONP
(JSON with Padding
)JSONP
是JSON
的一种使用模式,可以让网页从别的域名获取资料,即跨域读取数据- 由于同源策略的限制,
XmlHttpRequest
只允许请求当前源(域名、协议、端口)的资源,为了实现跨域请求,可以通过script
标签实现跨域请求,然后在服务端输出JSON
数据并执行回调函数,从而解决跨域的数据请求 Web
页面上调用JS
文件不受是否跨域的影响,不仅如此,凡是拥有src
这个属性的标签都拥有跨域的能力,比如script、img、 iframe
JSONP
协议允许用户传递一个callback
参数给服务端,然后服务端返回数据时会将这个callback
参数作为函数名来包裹住JSON
数据,这样客户端可以随意定制自己的函数来处理返回的数据(动态执行回调函数)
- 跨域请求
src
地址可控可以分为三种script src="[完全可控]"
- 直接将地址换为
JS
地址
- 直接将地址换为
script src="/path/xxx/[路径可控]/1.js"
- 路径可控,被利用的话,需要同域名下有可控的文件,可控文件分两类
- 直接上传文本至同域名下,不一定要是
HTML
文件,需要上传点有过滤缺陷 - 参数可控,利用可用的
JSON
接口
- 直接上传文本至同域名下,不一定要是
- 最终变为
script src="/path/xxx/.../yyy/xx.json?callback=alert(1)"
- 路径可控,被利用的话,需要同域名下有可控的文件,可控文件分两类
script src="/xxxx/json.php?callback=xxxx¶m1=yyy¶m2=[参数可控]"
- 类似上面的参数可控,如果参数可控,且
JSON
的参数没有做好过滤,就可以导致XSS
- 类似上面的参数可控,如果参数可控,且
Fuzz
步骤F12
调试工具,刷新查看是否有加载外部JS
文件- 修改原始
URL
中参数值,查看是否有参数被传入到外部JS
文件中 - 有参数传入的话,打开这个
JS
文件地址,修改地址内参数,查看哪些参数可控 - 判断可控参数是否过滤
< > ,
callback
参数可控,且未被双引号包裹,所以这里可以构造
http://xxx.xxx.com/comm.json?callback=alert(1);
- 将原始页面调用上面的
JS
文件即可导致XSS
- 而
callback
参数可以从之前探测出的传入到外部JS
文件中的参数一起带入 - 即将原始
URL
中的参数param=xx
修改为param=xx&callback=alert(1);
覆盖掉外部JS
文件中的callback
&
也可以写成%26
- 查看函数,明白参数是如何传递到
comm_json
接口
var keyword = decodeURIComp($getQuery('keyword')),
- 此处
decodeURIComp
解码,即&
会变成%26
,便不是连接符了 - 所以,最后应该为
%26
即:
http://yyy.xxx.com/1.html?keyword=1%26callback=alert(1);&PTAG=2005.13.1
- 同域的时候,可以使用
DOM型XSS
总结- 修改参数值,查看源代码,调试工具,是否有改变
- 如果有,查看是否存在过滤
<、>、"、'
- 查看参数值外是否有
DOM型XSS
的触发点 - 如果有,根据上下文,尝试闭合语句,字符也可以用
Unicode
编码替换(此处需要\
未被过滤) - 构造完整
Payload
存储型XSS
- 存储型需要进入数据库,输出可能出现在任何用到数据的地方,不一定出现在输入的位置,输入大部分来自
POST/GET
请求 Fuzz
思路- 先找到输出点,猜测此处输出是否会被过滤
- 如果觉得可能没过滤,再找这个输出是从哪输入的
- 开始测试输入,看输出效果
- 如果没过滤,那么
存储型XSS
成功
- 富文本
- 很多应用含有富文本内容,最典型的特性是具有编辑器,这类应用往往允许使用一定的
HTML
代码,为了在用户体验和安全之间寻找平衡,一般有两种方法- 白名单
- 只允许使用白名单内的合法
HTML
标签
- 只允许使用白名单内的合法
- 黑名单
- 厂商构建一个有危害的
HTML
标签、属性列表,通过分析用户提交的HTML
代码,去除其中有害的部分
- 厂商构建一个有危害的
- 白名单
- 黑名单这种方式往往会被绕过,白名单则安全的多
- 通过抓包的方式,修改抓包内容,插入
XSS
代码,提交修改后的数据包,查看XSS
代码是否被过滤
- 很多应用含有富文本内容,最典型的特性是具有编辑器,这类应用往往允许使用一定的
- 存储型需要进入数据库,输出可能出现在任何用到数据的地方,不一定出现在输入的位置,输入大部分来自
FlashXSS
Flash
的ActionScript
脚本目前网络上存在两种版本,即2.0
和3.0
- 利用
Google
搜索site:xx.com filetype:swf inurl:下面的关键词
- 已知存在缺陷的
Flash
文件名或参数名:如swfupload
、jwplayer
等 - 多媒体功能的
Flash
文件名:如upload
、player
、music
、video
等 - 调用的外部配置或数据文件后缀:如:
xml
、php
等 - 前期经验积累下来的程序员特征参数名用词:如:
callback
、cb
、function
等
Flash
缺陷参数Flash
提供相关的函数,可以执行JS
代码getURL
(AS2)navigateToURL
(AS3)ExternalInterface.call