浅析CORS跨域漏洞与JSONP劫持

前言

CORS 全称为 Cross-Origin Resource Sharing ,即跨域资源共享,用于绕过SOP(同源策略)来实现跨域资源访问的一种技术。而 CORS 漏洞则是利用 CORS 技术窃取用户敏感数据。

JSONP 全称是 JSON with Padding ,是基于 JSON 格式的为解决跨域请求资源而产生的解决方案。JSONP 实现的基本原理是利用了 HTML 里 <script></script> 元素标签,远程调用 JSON 文件来实现数据传递。当某网站通过 JSONP 的方式来跨域(一般为子域)传递用户认证后的敏感信息时,攻击者可以构造恶意的 JSONP 调用页面,诱导被攻击者访问来达到截取用户敏感信息的目的。

CORS 漏洞、 JSONP 劫持实际上都属于 CSRF 跨站请求伪造漏洞,尽管二者已经出现了很多年,但由于部分厂商对此不够重视导致其仍在不断发展和扩散。

同源策略

对 CORS 的介绍要从浏览器的同源策略开始说起,SOP 全称为 Same Origin Policy,即同源策略。该策略是浏览器的一个安全基石,同源策略规定:不同域的客户端脚本在没有明确授权的情况下,不能读写对方的资源。

简单来说同源策略就是浏览器会阻止一个源与另一个源的资源交互。可以试想一下,如果没有同源策略,当你访问一个正常网站的时候又无意间打开了另一个恶意网站,恶意网站会从你刚刚访问的正常网站上窃取你全部的信息。所谓同源是指 域名,协议,端口相同。如何判断是否是同源,可以查看该表:
在这里插入图片描述

AJAX技术

跨域问题是针对使用XMLHttpRequest 技术构建的复杂的、动态的网页的编程实践技术—— AJAX 的,HTML 本身没有跨域问题。

AJAX 全称 Asynchronous JavaScript + XML,即异步 JavaScript 和 XML。AJAX 本身不是一种新技术,而是用来描述一种使用现有技术集合/标准的新方法,包括:HTML or XHTML、Cascading Style Sheets、JavaScript、The Document Object Model、XML、XSLT 以及 XMLHttpRequest object。AJAX 允许只更新一个 HTML 页面的部分 DOM,而无须重新加载整个页面, 网页应用能够快速地将增量更新呈现在用户界面上,而不需要重新加载整个页面。这使得程序能够更快地回应用户的操作。

【注意】尽管X在 Ajax 中代表 XML, 但由于 JSON 的许多优势,比如更加轻量以及作为 Javascript 的一部分,目前 JSON 的使用比 XML 更加普遍。JSON 和 XML 都被用于在 Ajax 模型中打包信息。

当我们使用 AJAX 技术发送 XMLHttpRequest 请求的时候,如果请求的是别的域 (主机域名、端口) 不同时,那么就会产生跨域问题(受同源策略影响,客户端将无法获取服务端返回的数据,除非使用 COERS 跨域资源共享技术)。值得注意的是:跨域的问题是发生在 XMLHttpRequest 请求的,也就是说,不是 XMLHttpRequest 请求是不会有跨域问题的。举个很简单的例子:在编写网页的时候,<img src = www.xxxx.xxxx/ >,在 CORS 跨域资源共享技术的作用下,URL 不是本域的还是可以正常获取该图片的。

CORS跨域

SOP 浏览器同源策略是一个很好的策略,在 SOP 被提出之后,大家都默默地遵守着这个规定,但随着WEB应用的发展,有些网站由于自身业务的需求,需要实现一些跨域的功能能够让不同域的页面之间能够相互访问各自页面的内容。常见需要跨域的业务场景如下:

  1. 比如后端开发完一部分业务代码后,提供接口给前端用,在前后端分离的模式下,前后端的域名是不一致的,此时就会发生跨域访问的问题;
  2. 程序员在本地做开发,本地的文件夹并不是在一个域下面,当一个文件需要发送 ajax 请求,请求另外一个页面的内容的时候,就会跨域;
  3. 电商网站想通过用户浏览器加载第三方快递网站的物流信息;
  4. 子站域名希望调用主站域名的用户资料接口,并将数据显示出来。

为了实现这个跨域需求,聪明的程序员想到了一种编码技术 JSONP,该技术利用从客户端传入的 json 格式的返回值,在服务器端调用该接口处事先以定义函数的方式定义好 json 格式里参数值,并加载 script 标签调用该函数实现跨域。 JSONP 虽然好,但它并非是在协议层面解决跨域问题,所以出现了很多安全问题。

为了能更安全的进行跨域资源访问,CORS 诞生了。CORS 是 H5 提供的一种机制,WEB 应用程序可以通过在 HTTP 报文中增加特定字段来告诉浏览器,哪些不同来源的服务器是有权访问本站资源。CORS 是 H5 提供的一种机制,WEB 应用程序可以通过在 HTTP 报文中增加特定字段来告诉浏览器,哪些不同来源的服务器是有权访问本站资源

跨域流程

浏览器将CORS请求分成两类:简单请求(simple request)和 非简单请求(not-so-simple request)。只要同时满足以下两个条件就属于简单请求否则属于非简单请求(主要通过请求方法进行判断):

  1. 请求方法是(HEAD,GET,POST)三种之一;
  2. HTTP的头信息不超出(Accept,Accept-Language,Content-Language,Lat-Event-ID,Content-Type)这几种字段。

CORS跨域——简单请求的流程

对于简单请求,大致流程是浏览器发现这一次向服务器提交的请求是简单请求,所以自动在头信息中增加了一个Origin的字段,用来表示这次的请求来自哪个域。当服务器接收到请求后发现Origin字段指定的域名在许可范围内,服务器会在响应包中增加三个与CORS相关的字段:

  1. Access-Control-Allow-Origin:该字段是必须存在的,它的值可能是 Origin 字段的值或者是一个通配符“*”,表示可以接受任意域名的请求,当然大部分服务器如果配置了通配符的话,信息泄露的风险骤然加大;
  2. Access-Control-Allow-Credentials:字段不是必选字段,它的值是一个布尔值且只能设置为 true,表示服务器允许浏览器将 cookie 包含在请求中,否则就不添加此字段。但需要注意的是,如果要发送 cookie,Access-Control-Allow-Origin 就不能设为星号,必须明确指定与请求网页一致的域名,同时Cookie依然遵循同源策略。
  3. Access-Control-Expose-Headers:该字段主要是指定想要获取 XMLHttpRequest 对象中 getResponseHeader() 方法的其他服务器字段。

具体的 CORS 简单跨域请求流程如下:
在这里插入图片描述
CORS跨域——非简单请求的流程

所谓非简单请求就是那种对服务器提出特殊要求的请求,例如请求方法为 PUT 或 DELETE。非简单的 CORS 请求会在正式通信之前,增加一次 HTTP 查询请求,称之为 “预检请求” 。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单里以及可以使用哪些 HTTP 动词和头信息字段。只有获得了肯定响应,浏览器才会正式发出 XMLHttpRequest 请求否则就报错。这种请求的好处是对传统的没有 CORS 支持的服务器减小压力,给服务器一个提前拒绝的机会。

在这里插入图片描述
具体流程如上图所示,当构造请求包的方法是 PUT 或 DELETE 并传给浏览器时,浏览器发现此请求是非简单请求所以浏览器构造一个预检请求包,请求头是 OPTIONS,并携带三个关键字段:Origin、Access-Control-Request-Method、Access-Control-Request-Headers。其中 Access-Control-Request-Method 表示浏览器的 CORS 请求会用到哪些HTTP方法, Access-Control-Request-Headers 表示浏览器 CORS 请求会额外发送的头信息字段。服务器收到预检请求后,检查了三个核心字段以后如果确定允许跨域请求,会返回一个正常的 HTTP 回应,并携带传入的 CORS 头信息。如果服务器否定请求,虽然也会返回一个正常的 HTTP 回应但是没有任何 CORS 相关的头信息字段,或明确表示请求不符合条件。浏览器根据预请求的返回结果决定接下来是进行简单请求还是拒绝请求。

攻击流程

CORS 使用检查请求头的相关字段和服务端的规则进行对比,来选择是否允许跨域。但凡是需要配置规则的程序,避免不了会出现一些意外,就像很多资深程序员有时也会写不出恰当的正则一样,当服务端配置的规则不够合理,导致非同域的资源可以互相访问,例如Access-Control-Allow-Origin: *。 CORS 反而使同源策略的保护机制土崩瓦解。因此,CORS 漏洞的成因很明显,就是服务端配置的规则不当所导致的

在这里插入图片描述
CORS 跨域漏洞的攻击流程如上图所示:

  1. 假设用户登陆一个含有 CORS 配置网站 vuln.com,同时又访问了攻击者提供的一个链接 evil.com。
  2. evil.com 的网站向 vuln.com 这个网站发起请求获取敏感数据,浏览器能否接收信息取决于 vuln.com 的配置。
  3. 如果 vuln.com 配置了 Access-Control-Allow-Origin 头且为预期,那么允许接收,否则浏览器会因为同源策略而不接收。

漏洞验证

1、这是一个正常的GET请求包:
在这里插入图片描述
其正常响应包为:
在这里插入图片描述

2、我们现在在该请求包上面添加一个origin参数:
在这里插入图片描述
3、返回包的数据中出现了对应的 CORS 响应头:
在这里插入图片描述
其中Access-Control-Allow-Origin指是允许访问的源,Access-Control-Allow-Credentials指的是允许带上 cookie 访问资源,这样我们就可以通过 POST 获取到访问者的 cookie 信息。

靶场实例

以 BWAPP 靶场 Low 级别的 CORS 漏洞环境作为演示案例:
在这里插入图片描述

1、点击 secret 跳转到如下页面,藏着 Neo 的秘密:
在这里插入图片描述
2、现在攻击者的目的是盗取该页面里面的 sercet 密码内容,查看请求这个页面时的HTTP响应头,从Access-Control-Allow-Origin:* 头可以看出服务器配置了 CORS,且所有的源服务器都可以加载这个页面上的资源:
在这里插入图片描述
3、攻击者可以直接发送一个自己构造的页面链接给用户,当用户点击后,攻击页面使用 ajax 就可以直接读取另一个 BWAPP 靶场网站的目标网页敏感信息,攻击页面 test.html 源码如下:

<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
    <script>
        window.onload = function(){
            var xhr =new XMLHttpRequest();// 创建AJAX的对象
            // 设置使用的请求方式
            xhr.open('get','http://103.139.3.184:8080/secret-cors-1.php',true); 
            xhr.send(); // 发送请求
            xhr.onreadystatechange=function(){
                if(xhr.readyState === 4 ){ // 状态 4 表示服务器已响应
                    // 判断正常的响应结果的状态码
                    if(xhr.status >=200 && xhr.status<300 || xhr.status === 304){
                        console.log(xhr.responseText); // 获取响应体内容
                    }else{
                        console.log('0');
                    }
                }
            }
        }
    </script>
</body>
</html>

4、此处将该页面放在本地 PhpStudy 搭建的服务下,并在浏览器进行访问,将成功跨域发送资源请求并返回想要的敏感信息(控制台打印):
在这里插入图片描述

检测方法

如何在平常测试中检查 CORS 跨域漏洞?

CORS 漏洞主要看当我们发起的请求中带有 Origin 头部字段时,服务器的返回包带有 CORS 的相关字段并且允许 Origin 的域访问。

一般测试WEB漏洞都会用上BurpSuite,而BurpSuite可以实现帮助我们检测这个漏洞。

1、首先是自动在 HTTP 请求包中加上 Origin 的头部字段,打开BurpSuite,选择 Proxy 模块中的 Options 选项,找到 Match and Replace 这一栏,勾选 Request header 将空替换为 Origin:foo.example.org 的Enable框:
在这里插入图片描述2、然后我们就可以开始去访问我们认为有漏洞的网站,访问足够多后在 BurpSuite 的 Proxy 模块下的 HTTP history 来筛选带有 CORS 头部的值:
在这里插入图片描述
我们的条件可以是如下:

Access-Control-Allow-Origin: foo.example.org
Access-Control-Allow-Credentials: true

3、检测效果如下:
在这里插入图片描述
注意】这里要注意的是,我们也可以测试下带有 CORS 字段的网站是否有 CORS 漏洞,如果服务器响应包的请求头是以下几种情况则可存在 CORS 漏洞:

  1. 实锤存在: 有且仅有如下请求头
    Access-Control-Allow-Origin: *
  2. 实锤存在:同时存在如下两个请求头
    Access-Control-Allow-Origin: https://attacker.com
    Access-Control-Allow-Credentials: true
  3. 可能存在:同时存在如下两个请求头
    Access-Control-Allow-Origin: null
    Access-Control-Allow-Credentials: true

但是如果是如下组合,则绝对没有漏洞,因为该配置下浏览器会自动阻止:

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

结合XSS

有时候 CORS 配置了信任自身的任意子域,那么如果一个子域存在 XSS 漏洞就可以通过这个漏洞去读取其他子域的资源,类似的场景还有比如 HTTPS 域信任 HTTP 域等。

漏洞扫描

可以使用 Xray 联动 BurpSuite 进行扫描,如下是 Xray 扫出来的一次 CORS:
在这里插入图片描述同时 github上 提供了一个关于扫描 CORS 配置漏洞的脚本,https://github.com/chenjj/CORScanner。

root@kali:~/Desktop/CORScanner# python cors_scan.py -h
usage: cors_scan.py [-h] [-u URL] [-i INPUT] [-t THREADS] [-o OUTPUT]
                   [-v [VERBOSE]] [-d [HEADERS [HEADERS ...]]]
 
OPTIONS:
 -h, --help            show this help message and exit
 -u URL, --url URL     URL/domain to check it's CORS policy
 -i INPUT, --input INPUT
                       URL/domain list file to check their CORS policy
 -t THREADS, --threads THREADS
                       Number of threads to use for CORS scan
 -o OUTPUT, --output OUTPUT
                       Save the results to text file
 -v [VERBOSE], --verbose [VERBOSE]
                       Enable Verbosity and display results in realtime
 -d [HEADERS [HEADERS ...]], --headers [HEADERS [HEADERS ...]]
                       Add headers to the request.
 
Example: python cors_scan.py -u google.com

我们将检测的域名写在一个记事本里,然后使用-i参数去进行批量扫描。
在这里插入图片描述

防护方案

  1. 关闭不必要开启的CORS;
  2. 白名单限制:定义“源”的白名单,避免使用正则表达式,不要配置 Access-Control-Allow-Origin 为通配符 * 或 null ,严格效验来自请求数据包中的 Origin 的值;
  3. 仅允许使用安全协议,避免中间人攻击;
  4. 尽可能的返回 Vary: Origin 头部,以避免攻击者利用浏览器缓存进行攻击;
  5. 避免将 Access-Control-Allow-Credentials 标头设置为默认值 true ,跨域请求若不存在必要的凭证数据,则根据实际情况将其设置为 false;
  6. 限制跨域请求允许的方法,Access-Control-Allow-Methods 最大限度地减少所涉及的方法,降低风险;
  7. 限制浏览器缓存期限:建议通过 Access-Control-Allow-MethodsAccess-Control-Allow-Headers 头部,限制浏览器缓存信息的时间。通过配置 Access-Control-Max-Age 标头来完成,该头部接收时间数作为输入,该数字是浏览器保存缓存的时间。配置相对较低的值,确保浏览器在短时间内可以更新策略;
  8. 仅在接收到跨域请求时才配置有关于跨域的头部,并确保跨域请求是合法的源,以减少攻击者恶意利用的可能性。

JSONP劫持

JSONP 全称是 JSON with Padding ,是基于 JSON 格式的为解决跨域请求资源而产生的解决方案,它是 json 的一种“使用模式”,可以让网页从别的域名(网站)那获取资料,即跨域读取数据。

JSONP 实现的基本原理是利用了 HTML 里 <script></script> 元素标签,远程调用 JSON 文件来实现数据传递。当某网站通过 JSONP 的方式来跨域(一般为子域)传递用户认证后的敏感信息时,攻击者可以构造恶意的 JSONP 调用页面,诱导被攻击者访问来达到截取用户敏感信息的目的。

利用过程

JSONP 劫持漏洞的利用过程如下:

  1. 用户在网站B 注册并登录,网站B 包含了用户的id,name,email等信息;
  2. 用户通过浏览器向网站A发出URL请求;
  3. 网站A向用户返回响应页面,响应页面中注册了 JavaScript 的回调函数和向网站B请求的 script 标签,示例代码如下:
<script type="text/javascript">
function Callback(result)
{
    alert(result.name);
}
</script>
<script type="text/javascript" src="http://B.com/user?jsonp=Callback"></script>
  1. 用户收到响应,解析 JS 代码,将回调函数作为参数向网站B发出请求;
  2. 网站 B 接收到请求后,解析请求的 URL,以 JSON 格式生成请求需要的数据,将封装的包含用户信息的 JSON 数据作为回调函数的参数返回给浏览器,网站B返回的数据实例如下:
Callback({"id":1,"name":"test","email":"test@test.com"})
  1. 网站B数据返回后,浏览器则自动执行 Callback 函数对步骤4返回的 JSON 格式数据进行处理,通过 alert 弹窗展示了用户在网站B的注册信息。另外也可将 JSON 数据回传到网站A的服务器,这样网站A利用网站B的JSONP漏洞便获取到了用户在网站B注册的信息。

JSONP 漏洞利用过程示意图:
在这里插入图片描述
JSONP 劫持是一种敏感信息泄露的漏洞,经过攻击者巧妙而持久地利用,会对企业和用户造成巨大的危害。攻击者通过巧妙设计一个网站, 网站中包含其他网站的 JSONP 劫持漏洞利用代码 ,将链接通过邮件等形式推送给受害人, 如果受害者点击了链接,则攻击者便可以获取受害者的个人的信息,如邮箱、姓名、手机等信息, 这些信息可以被违法犯罪分子用作“精准诈骗”。对方掌握的个人信息越多,越容易取得受害人的信任,诈骗活动越容易成功,给受害人带来的财产损失以及社会危害也就越大。

靶场实例

下面以 DoraBox 靶场的 JSONP 劫持漏洞为演示案例:
在这里插入图片描述访问 JSONP 靶场环境页面:
在这里插入图片描述其服务端源码如下:

<!-- jsonp.php -->
<?php
   include "../class/function.class.php";
   $reqMethod = "GET";
   $reqValue = "callback";
   $p = new Func($reqMethod, $reqValue);
   $info = array('username' => 'Vulkey_Chen', 'mobilephone' => '13188888888', 'email' => 'admin@gh0st.cn', 'address' => '中华人民共和国', '**' => 'Cool Man');
   if(!@$_GET['callback']){
       echo $p -> con_function('json_encode',$info);
   }else{
       $callback = htmlspecialchars($_GET['callback']);
       echo "{$callback}(" . $p -> con_function('json_encode',$info) . ")";
   }
?>

重点关注:

if(!@$_GET['callback']){
    echo $p -> con_function('json_encode',$info);
}else{
    $callback = htmlspecialchars($_GET['callback']);
    echo "{$callback}(" . $p -> con_function('json_encode',$info) . ")";
 }

这里首先以 get 形式接收到 callback 的值,如果 callback 为空,则忽略警告输出 info 的 json 格式数据:在这里插入图片描述如果 callback 值不为空,则对这个值做一个过滤后输出,然后后面还是输出 json 格式的 info 的值:
在这里插入图片描述从这段代码我们可以看到,callback 的值是可以动态输出的,如果我们现在拿到了一个以 jsonp 方式传输用户认证后数据的网站,我们就可以构造出一个恶意的 jsonp 调用页面,然后诱使用户访问我们的页面,从而达到一个截取用户信息的目的。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JSONP劫持测试</title>
</head>
<body>
<script type="text/javascript">
function test(result)
        {
            alert(result.mobilephone);
        }
</script>
<script type="text/javascript" src="http://118.25.56.195:86/csrf/jsonp.php?callback=test"></script>
</body>
</html>

将上面的 jsonp_attack.html 页面放在本地 phpstudy 搭建的服务网站根目录下,浏览器进行访问,成功获得敏感信息(手机号码):
在这里插入图片描述简单修改下恶意网页链接的代码,也可以直接弹窗返回全部信息:
在这里插入图片描述看下访问效果:
在这里插入图片描述

漏洞挖掘

1、搜索引擎 Hacking 语法——site:target.com inurl:?callback
在这里插入图片描述2、浏览器-调试-搜索关键字(json/jsonp/callback):
在这里插入图片描述

防护方案

1、严格安全的实现 CSRF 方式调用 JSON 文件:限制 Referer 、部署一次性 Token 等。

2、严格安装 JSON 格式标准输出 Content-Type 及编码( Content-Type : application/json; charset=utf-8 )。

3、严格过滤 callback 函数名及 JSON 里数据的输出。

4、严格限制对 JSONP 输出 callback 函数名的长度(如防御上面 flash 输出的方法)。

5、其他一些比较“猥琐”的方法:如在 Callback 输出之前加入其他字符(如:/**/、回车换行)这样不影响 JSON 文件加载,又能一定程度预防其他文件格式的输出。还比如 Gmail 早起使用 AJAX 的方式获取 JSON ,听过在输出 JSON 之前加入 while(1) ;这样的代码来防止 JS 远程调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tr0e

分享不易,望多鼓励~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值