Earlier this year, I posted a blog around Entra ID and Office 365 detections that we had released to the community in support of some research I had done around the visibility a defender might have in a Golden SAML style attack. One of the big things that struck me during those efforts was the gaps in data that limited defender visibility to only large “milestone” actions - like an application being created or a user being assigned to a role where very little logging around data gathering activities was being seen.
In October 2023, Microsoft started to address this gap by making Graph API Activity logs available in preview. At Google, we know that our users are collecting data from multiple clouds as well as on-premises. In support of our customers using Entra ID who are seeking better visibility, we’ve taken this data set and built parsers so that defenders using Google SecOps are able to gain greater insight into the activities going on in their tenant than they had previously.
Like many things, there is a good news/bad news aspect to this. To gain access to the Graph API Activity logs, users of Entra ID do not just gain access to this like they do for Azure Active Directory audit and sign-in events or Office 365 events.
To access these datasets, integration through Azure Event Hub is required. After setting up the Azure Event Hub, an Azure Function will also be created to send this data to Google SecOps. Instructions and code to assist in this can be downloaded from the ingestion-scripts repository on our GitHub site. This may be changing later this year to make this more streamlined from our side, but for now this function will need to be created.
When configuring the function, information including your customer id, data type, event hub name and shared access key (from event hub) will be needed. These values can be individually modified in the above view or the advanced edit can be used to modify the json configuration file.
That’s the bad news. Now, here’s the good news. With that integration in place, Google SecOps users now have access to activity logs that contain:
Let's see these events in action. In this example, I am going to use GraphRunner, a post exploitation kit built by Beau Bullock and the folks at Black Hills Information Security to execute a PowerShell function to enumerate the applications in the tenant, their scopes (permissions) and any users assigned through administrative or user consent.
In the resulting events below, notice that we have the IP address that originated the request, the user agent string as well as the network information of the web request like the session id, bytes, duration and status. Finally, we can see the URLs being requested including the service principal endpoint followed by the applications endpoint and then an iterative set of calls to the service principal by application ID to identify the users who have consented to the application (appRoleAssignedTo).
A simple search on these two fields will get us started when reviewing these API activity events.
metadata.event_type = "NETWORK_HTTP" and metadata.product_event_type = "Microsoft Graph Activity"
Similarly, for users and security groups, we can see the API calls for the request for the users endpoint in the tenant.
When we enumerate the security groups, we get a listing of the groups and their members.
The initial API call to the groups endpoint with securityEnabled = true is embedded in the URL and what follows is an iterative call that follows the pattern of https://graph.microsoft.com/v1.0/groups/<group GUID>/members
Previously, none of this was visible. Now with the inclusion of Graph API Activity logging, we can gather these API calls. Imagine as a threat hunter or SOC analyst having access to this additional fidelity. We can also take these data sets and apply them to a YARA-L rule to look for signs of reconnaissance or other suspicious activities taking place within the tenant. If we wanted to create a rule for GraphRunner around security group enumeration, we could build something like this.
rule ms_graph_security_group_enumeration {
meta:
author = "Google Cloud Security"
description = "Identify the enumeration of security groups in Entra ID. This is based on GraphRunner behavior so modifications may be needed for other scenarios"
severity = "Low"
events:
$api.metadata.event_type = "NETWORK_HTTP"
$api.metadata.product_event_type = "Microsoft Graph Activity"
$api.target.url = /https:\/\/graph\.microsoft\.com\/v1\.0\/groups\/.*\/members/ nocase
$api.network.http.user_agent = /PowerShell/ nocase
$api.network.http.method = "GET"
$api.network.http.response_code = 200
$api.principal.ip = $ip
$api.network.session_id = $session
re.capture($api.target.url, `https:\/\/graph\.microsoft\.com\/v1\.0\/groups\/(.*)\/members`) = $group_guid
match:
$ip, $session over 5m
outcome:
$risk_score = 50
$event_count = count($api.metadata.id)
$requesting_user_guid = array_distinct($api.principal.user.userid)
$requesting_ip = array_distinct($api.principal.ip)
$user_agent = array_distinct($api.network.http.user_agent)
$location = array_distinct($api.principal.location.name)
$target_application_id_guid = array_distinct($api.target.resource.product_object_id)
$session_id = array_distinct($api.network.session_id)
$group_count = count($group_guid)
$group_list = array_distinct($group_guid) //limited to 25 groups outputted to screen
condition:
$api
}
When we test our rule, we can see that 80 API events were created when this command was issued, that the user agent was PowerShell 7.3 and originated from the Canada East region along with additional GUIDs. As mentioned this rule was built specifically for the GraphRunner command Get-SecurityGroups, so depending upon the use case, modifications will need to be made to broaden the rule. That said, we could also use a count and put a threshold into the condition, we could chain multiple API calls together within the same session, flag specific user agents and much more!
Now, a few words of caution. The Graph API Activity logs are still in preview so there may be additional changes that could occur. It’s also important to know that third party logging tools, like Google SecOps, are polling for logs from applications like Office 365 and this polling is logged as part of the Graph API activity, so some additional tuning is required to tune the signal to noise. This isn’t just limited to third party logging, by the way. I’ve noticed logging from Microsoft Cloud apps also creating additional events as well.
As Graph API Activity logs move toward general availability and throughout the year, I will be adding additional sample detections to our GitHub repository under the community section that can be leveraged to build detections if these kinds of use cases interest you.
In the meantime, if you are an Entra ID user who wants to get a better grasp of the API calls happening in your tenant, check out this log source and ingest it into your Google SecOps instance to start gaining familiarity with what is available!