
During an incident, you want to do your analysis as quickly and as precisely as possible. Although there are many scripts available to do proper research within Microsoft 365, if you are working with Exchange Online, OneDrive, SharePoint, they all need separate modules. Not to mention that Exchange Online sometimes need multiple modules depending on what data you want to extract. Using numerous modules can be a pain due to numerous logins that are required.
I wanted to create a ‘One ring to rule them all’ for any incident response within Microsoft 365, which is Operating System independent, runs natively on Windows, and works with or without Multi-Factor Authentication. PowerShell runs on Linux, macOS, natively on Windows, and it happens to be a language I somewhat understand.
Since many Microsoft security products and services connect to the Microsoft Graph Security API, I have chosen to use PowerShell in combination with the Microsoft Graph Security API.
App Registration
To communicate to the Microsoft Graph Security API, you need an app registration. If you create an app registration, be sure you select the Microsoft graph and Application Permissions.
Note: During the application registration, write down the application ID, the client secret, and the tenant name.


Add the following API permissions.
Directory.Read.All
Directory.ReadWrite.All
IdentityRiskyUser.Read.All
Policy.Read.All
SecurityEvents.Read.All
DelegatedPermissionGrant.ReadWrite.All
AuditLog.Read.All
Mail.Read
MailboxSettings.Read
Research Questions
The idea of answering a research question is to run a function, export the outcome to a JSON file, and filter the JSON file if needed. The sign-in logs, for example, contain a lot of information. Using your favorite tool, you can extract what research question you would like to answer. The export includes the location of the login. A simple query makes it possible to filter all logins outside the company’s country to get an overview of potential malicious logins.
RR-GetAccessToken
The first thing you need to do is getting a token using the app registration you created previously.
RR-GetAccessToken -appId 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' -appSecret 'XXXXXXXX' -tenantName "thalpius.onmicrosoft.com"
Once you have a token, you can use the functions described below.
Note: The token expires in one hour. I have not had this issue myself that a function runs more than an hour, but I am looking to add a refresh token to the script. You can always request a new token described above, which is valid for another hour.
RR-GetSkus
The first thing to look for is licenses. If the tenant contains an Office 365 Advanced Threat Protection license, it helps during the investigation. Or if the tenant contains an Azure AD Premium license, you know the logs in Azure AD go back one month instead of seven days.
I recommend starting with an output of the licenses to see what tools can help during the investigation.
RR-GetSkus
RR-GetAcceptedDomains
Accepted domains are used in the tenant to sent and receive e-mail. The function RR-GetAcceptedDomains can extract all accepted domains within the tenant.
Getting all accepted domains is helpful to validate which domain names accept e-mail within the tenant.
RR-GetAcceptedDomains
RR-GetInboxRules
Many attackers create inbox rules for persistence or hiding footprints. With the function RR-GetInboxRules you can export all inbox rules within the tenant or for a particular user.
RR-GetInboxRules
RR-GetInboxRules -userPrincipalName user@thalpius.com
RR-GetSignins
The RR-GetSignins functions export all Azure AD sign-ins within the tenant or for a particular user. The sign-in logs contain a lot of information like the user-agent, location of the sign-in, etc.
RR-GetSignins
RR-GetSignins -userPrincipalName user@thalpius.com
RR-GetAuditLogs
The RR-GetAuditLogs functions export all Azure AD audit logs within the tenant or for a particular user.
RR-GetAuditLogs
RR-GetAuditLogs -userPrincipalName user@thalpius.com
RR-GetEmailBySubject
The function RR-GetEmailBySubject searches for any e-mail with a given subject.
RR-GetEmailBySubject -subject "thalpius"
RR-GetEmailByBody
The function RR-GetEmailByBody searches for any e-mail with a given keyword in the body of the e-mail.
RR-GetEmailByBody -bodyKeyword "thalpius"
RR-GetAttachment
This function gives you the ability to extract all usernames with a given attachment filename in their mailbox.
RR-GetAttachment -fileName "thalpius.zip"
RR-GetAttachments
This function gives you the ability to extract attachments to check if it is malicious. It exports all attachments from a user’s mailbox or extracts the attachment itself if you use the attachmentId. The attachment is Base64 encoded. Decode the encoded string in the output to get the binary.
RR-GetAttachments -userPrincipalName user@thalpius.com
RR-GetAttachments -userPrincipalName user@thalpius.com -extension ".zip"
RR-GetAttachments -userPrincipalName user@thalpius.com -attachmentId XXXX-XXXXXX-XXXX
RR-GetAllAppRegistrations
In an illicit consent grant attack, the attacker creates an Azure-registered application that requests access to data such as contact information and e-mail. This function exports all app registrations within the tenant, including the owner.
RR-GetAllAppRegistrations
RR-OutputArray
Every function adds the data to an array. Once you are done running all functions you think you need, RR-OutputArray creates a JSON file with all data. You can filter the data if needed using your favorite scripting language.
RR-OutputArray -outputLocation 'c:\users\thalpius\incidentResponse\output.json'
Conclusion
Check out the script on my GitHub page. If you are missing any research questions, please let me know or add a GitHub issue and I will do my best to add it to the script.
Note: Do not forget to remove the Microsoft Graph Security API permissions once the investigation is completed.