Throughout 2023, I spent some time digging into the logging that Microsoft Azure Active Directory (AAD), now called Entra ID, generates. My specific focus was to better understand the logging available to defenders that could be used to detect suspicious cloud activity following an attack like the infamous Golden SAML attack from October 2020.
I was fortunate enough to present what I learned about the logging that Entra ID provides through both Office 365 and Azure Active Directory and shared some ideas on how to hunt and detect these suspicious activities once the adversary has access to the tenant. In fact, here is a 30-minute video of my talk at the SANS DFIR Summit.
Based on the ideas I presented on detecting suspicious behavior within a Microsoft cloud tenant, I developed a set of detection rules for Chronicle Security Operations. Chronicle users who use Entra ID as well as those who leverage a federated environment using Active Directory Federation Services (ADFS) and Entra ID may find these rules helpful.
These YARA-L rules are posted to our community site and are primarily focused on the Office 365 log type. Chronicle ingests this log source natively through Feed Management. Office 365 provides the ability to log Entra ID (Azure Active Directory) events in addition to Exchange and SharePoint events. There is also the ability to log general audit type and Data Loss Prevention (DLP) events. This flexibility provides users the ability to monitor login and audit events for Entra ID as well as events like mail items accessed or anonymous file links created in OneDrive.
The final two rules in the list below are focused on ADFS. Because they are monitoring the endpoint for named pipe connections and file access, they utilize Microsoft Sysmon and Windows Event Logs, respectively. If you aren’t using Sysmon, EDR solutions that detect named pipe connections may work as well.
The first rule we are going to highlight is a single event rule focused on the application of certificates and secrets in an Entra ID application. While we aren’t going to go deep into all the nuances around applications in Entra ID, suffice to say that an application with permissions provides access to different data sets within the Microsoft Graph API. In fact, when ingesting data into Chronicle, we set up an application, assign specific API permissions and create a secret that is used in feed management to access the logs!
Creating, modifying, and deleting secrets within an Entra ID application are generally normal behavior. However, one method to maintain persistence would be for an adversary to add their own secret to an application. Because of this, it’s a wise idea to monitor the creation or modification of these secrets, the applications they are being applied to and the user making these changes.
rule o365_entra_id_client_secret_add_update_delete_in_app {
meta:
author = "Google Cloud Security"
description = "Secrets added to applications have legitimate purposes, but can also be a method of persistence. This alert will trigger on creation, modification or delete of a client secret"
mitre_attack_tactic = "Persistence"
mitre_attack_technique = "Account Manipulation: Additional Cloud Credentials"
mitre_attack_url = "https://attack.mitre.org/techniques/T1098/001/"
mitre_attack_version = "v14.1"
type = "alert"
platform = "azure"
data_source = "o365"
severity = "Medium"
priority = "Medium"
events:
$app.metadata.event_type = "USER_RESOURCE_UPDATE_CONTENT"
$app.metadata.product_name = "Office 365"
$app.metadata.product_event_type = /Update application.*Certificates and secrets management/
$app.metadata.vendor_name = "Microsoft"
$app.security_result.action = "ALLOW"
$app.principal.user.userid = $userid
match:
$userid over 5m
outcome:
$risk_score = 65
$mitre_attack_tactic = "Persistence"
$mitre_attack_technique = "Account Manipulation: Additional Cloud Credentials"
$mitre_attack_technique_id = "T1098.001"
$event_count = count_distinct($app.metadata.id)
$security_summary = array_distinct($app.security_result.summary)
$user_agent = array_distinct($app.network.http.user_agent)
$target_entra_id_application = array_distinct(re.capture($app.network.http.user_agent,`\"AppId\":\"(.*)`))
//added to populate alert graph with additional context
//$principal_user_userid = array_distinct($app.principal.user.userid)
condition:
$app
}
Our rule gathers Office 365 logs that reference the certificates and secrets management event type and aggregates them by the principal.user.userid over a 5 minute window. It’s worth noting these particular Office 365 events do not contain the IP address of the requestor.
In our test rule output, we can see that the userid tim.smith_admin added a secret to the application GUID ccbc3dcb-5c0e-4d85-aa7a-1c313ec4588b.
The second rule we are going to highlight is looking for a login and then subsequent activity that may be deemed suspicious. Initial access into a tenant may leverage the Azure application, Azure AD PowerShell, followed by a series of actions where an adversary may create their own application, add new permissions to access portions of the Graph API as well as create their own certificates and secrets for persistence. For additional strategies on remediation and hardening around this style of attack, this whitepaper by Mandiant has some great tips in it.
Our rule detects login events to the Azure AD PowerShell application. This is represented in the target.resource.product_object_id field and its value is universal for all Entra ID tenants. From there, we are identifying subsequent activity by the same user who either adds or modifies an application, or its permissions, or its secrets within a 90 minute window. Because an access token can range from 60-90 minutes in length, I decided to use the outer boundary of that time range.
rule o365_ADPowerShell_app_login_subsequent_activity {
meta:
author = "Google Cloud Security"
description = "Once a user authenticates to the Azure AD PowerShell application, if they take multiple admin actions indicative of establishing their own persistence with an Entra ID application within a portion of the access token time, alert for additional investigation"
assumption = "This does not take into account attempts that were blocked, just any logging of attempts for any of these actions"
mitre_attack_tactic = "Persistence"
mitre_attack_technique = "Account Manipulation: Additional Cloud Credentials"
mitre_attack_url = "https://attack.mitre.org/techniques/T1098/001/"
mitre_attack_version = "v14.1"
type = "alert"
platform = "azure"
data_source = "o365"
severity = "Medium"
priority = "Medium"
events:
(
$login.metadata.event_type = "USER_LOGIN" and
$login.metadata.product_event_type = "UserLoggedIn" and
$login.metadata.product_name = "Office 365" and
$login.metadata.vendor_name = "Microsoft" and
$login.target.resource.product_object_id = "1b730954-1685-4b74-9bfd-dac224a7b894" and
$login.security_result.action = "ALLOW"
)
$login.target.user.userid = $userid
$login.metadata.event_timestamp.seconds < $other.metadata.event_timestamp.seconds
(
(
$other.metadata.event_type = "USER_RESOURCE_CREATION" and
$other.metadata.product_event_type = "Add application." and
$other.metadata.product_name = "Office 365" and
$other.metadata.vendor_name = "Microsoft"
)
or
(
$other.metadata.event_type = "USER_RESOURCE_UPDATE_CONTENT" and
$other.metadata.product_event_type = "Update application." and
$other.metadata.product_name = "Office 365" and
$other.metadata.vendor_name = "Microsoft"
)
or
(
$other.metadata.event_type = "USER_RESOURCE_UPDATE_PERMISSIONS" and
$other.metadata.product_event_type = "Add delegated permission grant." and
$other.metadata.product_name = "Office 365" and
$other.metadata.vendor_name = "Microsoft"
)
or
(
$other.metadata.event_type = "USER_RESOURCE_UPDATE_CONTENT" and
$other.metadata.product_event_type = /Update application.*Certificates and secrets management/ nocase and
$other.metadata.product_name = "Office 365" and
$other.metadata.vendor_name = "Microsoft"
)
)
$other.principal.user.userid = $userid
$other.metadata.product_event_type = $other_event
match:
$userid over 90m
outcome:
$risk_score = max(if($other.metadata.product_event_type = "Add application.", 10, 0) +
if($other.metadata.product_event_type = "Update application.", 10, 0) +
if($other.metadata.product_event_type = "Add delegated permission grant.", 10, 0) +
if($other.metadata.product_event_type = /Update application.*Certificates and secrets management/ nocase, 55, 0))
$mitre_attack_tactic = "Persistence"
$mitre_attack_technique = "Account Manipulation: Additional Cloud Credentials"
$mitre_attack_technique_id = "T1098.001"
$subsequent_action_threshold = 1
$product_event_type = array_distinct($other.metadata.product_event_type)
$country_region_login_attempt = array_distinct($login.principal.ip_geo_artifact.location.country_or_region)
//added to populate alert graph with additional context
$principal_ip = array_distinct($login.principal.ip)
//$principal_user_userid = array_distinct($other.principal.user.userid)
$target_resource_name = array_distinct($other.target.resource.name)
condition:
$login and #other_event > 0
}
All of the subsequent metadata.product_event_type values are grouped into the placeholder variable of $other_event and in the sample rule here, we are alerting if we have at least one other event because our condition states #other_event > 0. Depending on your business processes and risk appetite, I will leave it to you to adjust this value to look for N number of subsequent events types occurring. Also notice we are incrementing our risk_score in outcome using these events. There are additional activities that you might want to look for as well and those could also be added into the event section of the rule if desired using the format I’ve created above. Always test and adjust the community rules to ensure they align to your needs before deploying them.
Here is our detection. In this case, we have an IP address because login events in Office 365 contain the IP address. We can also see that an application called NewYear was added and it appears that permissions were modified, a secret was added and more.
There are more detections in the list above that focus on permissions, role assignment, administrator access and more. If you have Entra ID and Office 365, consider some of these rules as a good place to start looking for suspicious activities with Chronicle Security Operations.
While we only covered Office 365 logs and detection, there are also Azure AD and Azure Sign-in logs available as well. In a future post, I hope to share more rules and how these events can be used for detections, too.