浅析同源策略及Bypass

你说的源是什么源, 人们说的bypass, 是不是我记忆中的ajax

MDN定义

每次看到这个都不知道他到底少翻译了什么…还是看看英文吧

The same-origin policy is a critical security mechanism that restricts how a document or script loaded from one origin can interact with a resource from another origin. It helps isolate potentially malicious documents, reducing possible attack vectors.

同源策略是一种重要的安全机制,它限制了从一个来源加载的文档或脚本如何与另一个来源的资源进行交互。 它有助于隔离潜在的恶意文档,减少可能的攻击媒介。

举个栗子, 如果没有同源策略, 那么在我的网站, 我可以任意访问其他网站的所有资源, 整个网络将没有安全可言

那么如何定义同源呢? 有以下三个要求

  • 同Host
  • 同协议
  • 同端口

列个表来看一下, 所有结果都是相对于第一行

URL 结果 原因
http://store.company.com/dir/page.html / /
http://store.company.com/dir2/other.html 同源 只有路径不同
http://store.company.com/dir/inner/another.html 同源 只有路径不同
https://store.company.com/secure.html 不同源 协议不同(httpshttp)
http://store.company.com:81/dir/etc.html 不同源 端口号不同(8180)
http://news.company.com/dir/other.html 不同源 Host不同(news.xxxstore.xxxx)

我们来做一下测试 (参考 https://aquilao.github.io/Blog/SOP/)

可以看到, 在浏览器中这样的请求是被拦截的, 但是实际上, 这个包已经发出去了, 并且正常的获得响应, 只是浏览器进行了拦截

同源的继承

Scripts executed from pages with an about:blank or javascript: URL inherit the origin of the document containing that URL, since these types of URLs do not contain information about an origin server.

从带有about:blank或javascript:URL的页面执行的脚本会继承包含该URL的文档的来源,因为这些类型的URL不包含有关来源服务器的信息。

也就是说通过about:blank打开, 不会受到同源策略的影响

IE的特殊机制

在IE中, 存在下面两个特例

  • 同源策略判断不包括端口
  • 如果两个域处于高度信任的区域(Trust Zone, 例如公司的内部网域), 那么同源策略将不做限制

改变源

实际上, 我们是可以改变所处的源的, 不过存在如下的限制

  • 只能更改为当前源的顶级源, 例如a.example.com可以更改为example.com, 而不能更改为b.com, 更改的方式如下

    document.domain = "example.com"

  • 更改后端口会被设置为null

因此如果想让两个子域或者一个子域一个顶级域变为同源, 需要对两个同时进行设置, 不然会因为端口不同(例如80null)而无法成功

跨域访问行为

我们在写网站的时候, 经常需要引入其他网站的图片, 根据同源策略, 引入的图片和网站一般是不同源的, 但是我们依然可以通过img标签直接引入, 这就涉及到同源策略的限制范围了, 同源策略将跨域访问行为分为下面三种

  • 跨域访问写(Cross-Origin Writes), 例如重定向, 表单提交等

  • 跨域访问嵌入(Cross-Origin Embedding), 例如img标签嵌入或者link引入css等等, 具体的示例如下

  • 跨域访问读取(Cross-Origin Reads), 例如读取其他网站的cookie

其中前两个都是默认允许的, 只有跨域读是被禁止的, 当然并不是毫无办法, 我们还有 CORS (后面会细讲)

我们知道cookie也有源, 但是并不是同源策略的源, 而是通过domainpath规定的

我们可以理解为, cookie的源和path是匹配的, 而同源策略的源, 是绝对相等的, 大概就是in===的区别

这里歪个楼, 如果一个cookie设置为httponly, 我们如何绕过呢

我查了下主要的方法有以下几种

  • phpinfo界面会显示cookie, 其中就包括设置为httponly的cookie, 同理还有django的调试界面
  • 在以前的框架中, 有些支持TRACE等方法, 可以通过XST来获取cookie, 现已修复
  • apache的cve-2012-0053漏洞会导致cookie泄露, 现已修复

当然一般来说, 绕过绕过, 还是换条路过

Bypass

作为一个安全人员, 对于一个技术总是想着如何bypass的2333, 这里简单的聊一聊如何绕过

JSONP

主要参考自 JSONP跨域漏洞总结 , 总结的十分详细

由于前端无法跨域的问题, 对业务实现出现了一定的影响, 但是我们知道script, img这些标签都是可以引入外部资源的, 那么我们如果引入一个外部的js文件呢? 这显然是可行的, 那如何使用js文件来传输数据呢?

这就出现了jsonp(json with padding), 语法如下

callback({json_data})

前面的callback是表示回调函数, 我们可以自己编写一个同名的函数, 或者通过ajaxsuccess直接处理也可以, 后面就是我们需要的json_data

那显然, 不能所有的函数都叫callback, 我们希望能够通过前端传递一个回调函数, 然后后端拼接成jsonp数据传回, 假设后端的实现如下

<?php
header('Content-type: application/json');
if(isset($_GET['callback'])){
$callback = $_GET['callback'];
print $callback.'({"username" : "mi1k7ea", "password" : "thisispassword"});';
} else {
echo 'No callback param.';
}

然后前端只需要远程包含并编写相应的回调函数即可, 例如

<html>
<head>
<meta charset="utf-8">
<title>JSONP Test</title>
</head>
<body>
<div id="here"></div>
<script type="text/javascript">
function callbackFunction(result, methodName)
{
var html = '<ul>';
for(var i = 0; i < result.length; i++)
{
html += '<li>' + result[i] + '</li>';
}
html += '</ul>';
document.getElementById('here').innerHTML = html;
}
</script>
<script type="text/javascript" src="http://back.com/jsonp.php?callback=callbackFunction"></script>
</body>
</html>

一切看起来都很合理, 然而实际上问题还是比较大的

XSS

这个callback如果没有经过合理的过滤, 我们可以直接注入xsspayload

JSONP劫持

很显然, 这样的访问方式和表单访问其实十分相近, 那么我们就可以用CSRF的思想来进行劫持

  1. 用户正常的访问网站, 并进行登录
  2. 服务端校验通过, 可以将jsonp数据发送给用户
  3. 攻击者诱导用户访问恶意页面, 请求jsonp后端
  4. 服务端校验通过, 返回数据
  5. 恶意页面外带数据, 实现劫持

实际上和CSRF是一样的, 那么我们要防御, 也可以套用CSRF_TOKEN来进行防御

Referer校验

如果jsonp服务器对referer头做了校验呢? 我们可以想办法不设置这个头, 也就是让他为空, 从而达到一部分的绕过

主要的方法有下面几种

  • 从HTTPS站点向HTTP站点请求

    如果我们把恶意页面放置在https站点, 当用户访问时, 发出的请求为了保护信息, 是不携带referer头的

  • data协议

    参考 由浅入深理解JSONP并拓展

    我们知道data协议可以注入一些payload

    data:text/plain;base64,base64_encoded_code

    那如果结合一部分标签的话, 就可以达到空referer的情况, 例如下面的三个标签

    标签 属性 工作情况
    iframe src IE中不工作
    embed src IE和edge不工作
    object data IE和edge不工作
  • iframe绕过

    参考 JSONP跨域漏洞总结#Referer绕过

    我们可以直接在iframe标签中使用javascript伪协议进行绕过, 这样的请求是没有refer头的

    直接将我们的payload放在src属性里面就可以了

    <iframe src="javascript:'<script>function exp(o){alert(o.password);}</script><script src=http://hack.com/data.php?callback=exp></script>'"></iframe>

  • meta标签绕过

    我们可以通过设置这样的meta头, 从而达到绕过

    <meta name="referrer" content="never">

如果都不行的话, 可以看看服务器的referer过滤情况, 通过url格式的情况来绕过, 这个就要具体情况分析了

防御方法

设置返回的Content-Typeapplication/json

referer头进行严格限制, 不允许空

通过CSRF校验来控制访问jsonp服务端

CORS

参考这篇文章 https://xz.aliyun.com/t/2745, 强烈建议看一遍

大概这就是做研究的态度吧, 相比之下自己是如此的浅薄, 无语凝噎

从上面这张图我们就可以知道CORS的作用了, 就是官方绕过同源策略的方法, 通过配置CORS, 来允许一部分的跨域访问

而其中两个关键的属性是

  • Access-Control-Allow-Origin: 定义了允许访问的源, 例如example.com, null, *
  • Access-Control-Allow-Credentials: 是否允许携带凭证, 例如cookie

而且存在一个特例, 当配置如下时

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

浏览器会报错, 避免这种完全不设防的情况

那么我们的攻击方式主要有如下的情况

控制信任域

这是最好的情况, 如果我们能够控制其中一个信任域, 存在XSS漏洞, 那么我们就拥有了CORS读取的能力, 相当于绕过了SOP

null源

如果配置为

Access-Control-Allow-Origin: null

那么我们可以通过iframe来注入一个origin:null的请求, 从而绕过限制

<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src='data:text/html,<script>**CORS request here**</script>’></iframe>

匹配漏洞

由于在生产环境中, 我们经常有需要匹配多个域的情况, 但是Access-Control-Allow-Origin头并不支持这种写法, 他只能支持单个origin或者通配符*.example.com, 许多时候为了方便实现, 都是通过其他的手段来匹配, 比如自己实现一个正则匹配, 那么就有了绕过的可能, 比如原来想匹配的是*.example.com, 却可以通过下面的方法进行绕过

  • example.com.attack.com
  • wwwexample.com

这个主要是根据实现的不同, 存在不同的解析情况, 毕竟正则嘛, 总是经常出问题的2333

缓存投毒

https://xz.aliyun.com/t/2745#toc-15

这种情况主要是服务端的实现比较复杂, 导致部分内容进行了缓存, 我们就可以将我们的payload放置在headers中, 从而进行注入

防御方法

首先在不必要的情况, 设置

Access-Control-Allow-Credentials: false

而不是不设置, 避免某些框架会默认为true的情况

然后对于Access-Control-Allow-Origin, 最好是设置白名单, 不要使用正则, 相比之下白名单还是比较安全的

尽量使用HTTPS避免中间人攻击

domain

这个前面已经提过了, 需要两个页面同时设置

name

参考

https://juejin.im/post/5ba1d4fe6fb9a05ce873d4ad#heading-14

我们需要访问的http://a.com/index.html

<script type="text/javascript"> 
window.name = "hello world!";
</script>

可以在http://b.com/index.html设置

<iframe src="http://a.com/index.html" id="myIframe" onload="test()" style="display: none;">
<script>
// 2. iframe载入 "http://laixiangran.cn/b.html 页面后会执行该函数
function test() {
var iframe = document.getElementById('myIframe');

// 重置 iframe 的 onload 事件程序,
// 此时经过后面代码重置 src 之后,
// http://a.com/index.html 页面与该 iframe 在同一个源了,可以相互访问了
iframe.onload = function() {
var data = iframe.contentWindow.name; // 4. 获取 iframe 里的 window.name
console.log(data); // hello world!
};

// 3. 重置一个与 http://b.com/index.html 页面同源的页面
iframe.src = 'http://b.com/index.html';
}
</script>

postmessage

假设我们要窃取的数据在http://a.com, 那么这个页面需要注册postMessage的API, 并且设置接收端为*或者我们可以控制的某个域, 那么我们就可以通过这个api来进行跨域

参考 http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html

var popup = window.open('http://bbb.com', 'title');
popup.postMessage('Hello World!', 'http://bbb.com');

其中postMessage的第二个参数为接受窗口的源

Flash

Flash是没有明天的23333, chrome和firefox已经默认不支持了, 仅当了解

参考 同源策略与跨域请求

如果在站下面配置一个crossdomain.xml文件, 并在配置中写入

<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*" />
</cross-domain-policy>

那么任意站点都可以通过flash读取这个站的内容了

参考链接

  1. 同源策略与跨域请求
  2. 浏览器同源政策及其规避方法
  3. 浏览器同源策略及跨域的解决方法
  4. cors安全完全指南


作者: cjm00n
地址: https://cjm00n.top/Web/same-origin-policy-and-bypass-it.html
版权声明: 除特别说明外,所有文章均采用 CC BY 4.0 许可协议,转载请先取得同意。

浅谈CSP与Bypass 高校战疫分享赛Writeup

评论