AADInternals admin and blue team tools
AADInternals toolkit is best known of its offensive or red teams tools. Its origins, however, is in administration - especially for tasks not supported by official tools.
In this blog, I’ll introduce recent additions to the admin & blue team tools and also some old goodies!
Tenant settings
There are a lot of settings available in the admin portals affecting the security of your tenant. I’ve selected the following functions to help you to protect your tenant and to easily move to modern authentication.
Guest Access
As I’ve demonstrated earlier, by default, guest users have way too much permissions and can effectively export the users and groups of your tenant.
To protect your tenant from the guest users, the External collaboration settings should be set to most restrictive:
The status of this setting can easily be checked with AADInternals:
# Get the access token
Get-AADIntAccessTokenForMSGraph -SaveToCache
# Get the tenant guest access
Get-AADIntTenantGuestAccess
Output:
Access Description RoleId
------ ----------- ------
Normal Guest users have limited access to properties and memberships of directory objects 10dae51f-b6af-4016-8d66-8c2a99b929b3
There are three different access levels:
Access level | Description |
---|---|
Inclusive | Guest users have the same access as members |
Normal | Guest users have limited access to properties and memberships of directory objects |
Restricted | Guest user access is restricted to properties and memberships of their own directory objects (most restrictive) |
To change the settings using AADInternals (requires Global Administrator role):
# Get the access token
Get-AADIntAccessTokenForMSGraph -SaveToCache
# Get the tenant guest access
Set-AADIntTenantGuestAccess -Level Restricted
Output:
Access Description RoleId
------ ----------- ------
Guest user access is restricted to properties and memberships of their own directory objects (most restrictive) 2af84b1e-32c8-42b7-82bc-daa82404023b
Azure AD Graph API access
Azure AD is seems to built with the principle than on-premises AD that effectively gives read-only permissions to the whole content of Azure AD, including tenant-level settings.
I’ve earlier discussed about the ways to restrict users’s access to Azure AD data.
The both currently used PowerShell modules (MSOnline and AzureAD) are both utilising Azure AD Graph endpoint. Microsoft announced in June 2020 that the end-of-support for AAD Graph API will be June 30th 2022.
In the meantime, to disable access to Azure AD Graph using AADInternals:
# Get the access token
Get-AADIntAccessTokenForMSGraph -SaveToCache
# Disable access to AAD Graph API
Disable-AADIntTenantMsolAccess
# Check the settings after 10 seconds or so.
Get-AADIntTenantAuthPolicy | Select block*
Output:
blockMsolPowerShell
-------------------
True
Self-service purchase products
Self-service purchase products are, as the name suggests, products that tenant users can purchase by themselves. While the products do not generate any billing for the organisation, they can introduce security issues as they may allow access to the organisation data.
I recently read a great article by Tony Redmond (@12Knockssinna) on how to block the self-service purchases per product. This involved installing MS Commerce PowerShell module and playing around with product IDs which I felt a bit too complicated.
To block self-service purchase for all products a bit easier with AADInternals:
# Disable self-service purchase for all products
Get-AADIntSelfServicePurchaseProducts | Set-AADIntSelfServicePurchaseProduct -Enabled $false
Product Id Status
------- -- ------
Windows 365 Enterprise CFQ7TTC0HHS9 Disabled
Windows 365 Business with Windows Hybrid Benefit CFQ7TTC0HX99 Disabled
Windows 365 Business CFQ7TTC0J203 Disabled
Power Automate per user CFQ7TTC0KP0N Disabled
Power Apps per user CFQ7TTC0KP0P Disabled
Power Automate RPA CFQ7TTC0KXG6 Disabled
Power BI Premium (standalone) CFQ7TTC0KXG7 Disabled
Visio Plan 2 CFQ7TTC0KXN8 Disabled
Visio Plan 1 CFQ7TTC0KXN9 Disabled
Project Plan 3 CFQ7TTC0KXNC Disabled
Project Plan 1 CFQ7TTC0KXND Disabled
Power BI Pro CFQ7TTC0L3PB Disabled
Note: New self-service purchase products are introduced on regular basis, so don’t forget to check the products at least once a month!
Unified Audit log
The Unified Audit Log contains the following user and admin activity in Microsoft 365 and Azure AD:
- User activity in SharePoint Online and OneDrive for Business
- User activity in Exchange Online (Exchange mailbox audit logging)
- Admin activity in SharePoint Online
- Admin activity in Azure Active Directory (the directory service for Microsoft 365)
- Admin activity in Exchange Online (Exchange admin audit logging)
- eDiscovery activities in the security and compliance center
- User and admin activity in Power BI
- User and admin activity in Microsoft Teams
- User and admin activity in Dynamics 365
- User and admin activity in Yammer
- User and admin activity in Microsoft Power Automate
- User and admin activity in Microsoft Stream
- Analyst and admin activity in Microsoft Workplace Analytics
- User and admin activity in Microsoft Power Apps
- User and admin activity in Microsoft Forms
- User and admin activity for sensitivity labels for sites that use SharePoint Online or Microsoft Teams
- Admin activity in Briefing email and MyAnalytics
As such, the Unified Audit Log is a main source or forensic data for any Incident Response (IR) engagement.
You can turn on the Unified Audit Log with AADInternals:
# Get the access token
Get-AADIntAccessTokenForEXO -SaveToCache
# Disable the unified audit log
Set-AADIntUnifiedAuditLogSettings -Enabled true
Note! It will take hours for the changes to take effect!
Logging
As mentioned above, the Unified Audit log contains forensic data for Azure AD and Office/Microsoft 365. There are also other logs containing valuable forensic data.
Diagnostic settings
Diagnostic settings are used to configure streaming export of platform logs and metrics for a resource to the destination of your choice. You may create up to five different diagnostic settings to send different logs and metrics to independent destinations.
Diagnostic settings can be set in the Azure Admin Portal but the functionality is also added to AADInternals.
To view the current settings:
# Get the access token
$at=Get-AADIntAccessTokenForAzureCoreManagement -SaveToCache
# List diagnostic settings
Get-AADIntAzureDiagnosticSettings
Output:
Name : Audit and SignIn to Sentinel
WorkspaceId : /subscriptions/a04293e7-46c8-4bf4-bc6d-1bc1f41afae0/resourcegroups/sentinel/providers/microsoft.operationalinsights/workspaces/MySentinel
StorageAccountId :
EventHubAuthorizationRuleId :
EventHubName :
ServiceBusRuleId :
Name : Service Principal to Sentinel
WorkspaceId : /subscriptions/a04293e7-46c8-4bf4-bc6d-1bc1f41afae0/resourcegroups/sentinel/providers/microsoft.operationalinsights/workspaces/MySentinel
StorageAccountId :
EventHubAuthorizationRuleId :
EventHubName :
ServiceBusRuleId :
To get details of a specific settings:
# List diagnostic settings for the given workspace
Get-AADAzureIntDiagnosticSettingsDetails -Name "Audit and SignIn to Sentinel"
Output:
Log Enabled Retention Enabled Retention Days
--- ------- ----------------- --------------
ProvisioningLogs False False 0
AuditLogs False False 0
SignInLogs False False 0
NonInteractiveUserSignInLogs False False 0
ServicePrincipalSignInLogs False False 0
ManagedIdentitySignInLogs True True 365
To change the settings:
# Set the diagnostic log settings for the given workspace
Set-AADIntDiagnosticSettingsDetails -Name "Audit and SignIn to Sentinel" -Log ManagedIdentitySignInLogs,AuditLogs,SignInLogs -Enabled $true -RetentionEnabled $true -RetentionDays 365
Output:
Log Enabled Retention Enabled Retention Days
--- ------- ----------------- --------------
ProvisioningLogs False False 0
AuditLogs True True 365
SignInLogs True True 365
NonInteractiveUserSignInLogs False False 0
ServicePrincipalSignInLogs False False 0
ManagedIdentitySignInLogs True True 365
Sign-ins log
The sign-ins log is the source of (almost) all sign-in events.
To get the entries with AADInternals:
# Get the Access Token and save to cache
Get-AADIntAccessTokenForMSGraph -SaveToCache
# Show the log
Get-AADIntAzureSignInLog
Output:
createdDateTime id ipAddress userPrincipalName appDisplayName
--------------- -- --------- ----------------- --------------
2020-05-25T05:54:28.5131075Z b223590e-8ba1-4d54-be54-03071659f900 199.11.103.31 admin@company.onmicrosoft.com Azure Portal
2020-05-29T07:56:50.2565658Z f6151a97-98cc-444e-a79f-a80b54490b00 139.93.35.110 user@company.com Azure Portal
2020-05-29T08:02:24.8788565Z ad2cfeff-52f2-442a-b8fc-1e951b480b00 11.146.246.254 user2@company.com Microsoft Docs
2020-05-29T08:56:48.7857468Z e0f8e629-863f-43f5-a956-a4046a100d00 1.239.249.24 admin@company.onmicrosoft.com Azure Active Directory PowerShell
To see the details of a specific entry:
# Show the information for a single entry
Get-AADIntAzureSignInLog -EntryId b223590e-8ba1-4d54-be54-03071659f900
Output:
id : b223590e-8ba1-4d54-be54-03071659f900
createdDateTime : 2020-05-25T05:54:28.5131075Z
userDisplayName : admin company
userPrincipalName : admin@company.onmicrosoft.com
userId : 289fcdf8-af4e-40eb-a363-0430bc98d4d1
appId : c44b4083-3bb0-49c1-b47d-974e53cbdf3c
appDisplayName : Azure Portal
ipAddress : 199.11.103.31
clientAppUsed : Browser
userAgent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
...
Audit log
The audit log is the source of (almost) all Azure AD related audit events.
To get the audit log with AADInternals:
# Get the Access Token and save to cache
Get-AADIntAccessTokenForMSGraph -SaveToCache
# Show the log
Get-AADIntAzureAuditLog
Output:
id activityDateTime activityDisplayName operationType result initiatedBy
-- ---------------- ------------------- ------------- ------ -----------
Directory_9af6aff3-dc09-4ac1-a1d3-143e80977b3e_EZPWC_41985545 2020-05-29T07:57:51.4037921Z Add service principal Add success @{user=; app=}
Directory_f830a9d4-e746-48dc-944c-eb093364c011_1ZJAE_22273050 2020-05-29T07:57:51.6245497Z Add service principal Add failure @{user=; app=}
Directory_a813bc02-5d7a-4a40-9d37-7d4081d42b42_RKRRS_12877155 2020-06-02T12:49:38.5177891Z Add user Add success @{app=; user=}
To see the details of a specific entry:
# Show the information for a single entry
Get-AADIntAzureAuditLog -EntryId Directory_9af6aff3-dc09-4ac1-a1d3-143e80977b3e_EZPWC_41985545
Output:
id : Directory_9af6aff3-dc09-4ac1-a1d3-143e80977b3e_EZPWC_41985545
category : ApplicationManagement
correlationId : 9af6aff3-dc09-4ac1-a1d3-143e80977b3e
result : success
resultReason :
activityDisplayName : Add service principal
activityDateTime : 2020-05-29T07:57:51.4037921Z
loggedByService : Core Directory
operationType : Add
initiatedBy : @{user=; app=}
targetResources : {@{id=66ce0b00-92ee-4851-8495-7c144b77601f; displayName=Azure Credential Configuration Endpoint Service; type=ServicePrincipal; userPrincipalName=;
groupType=; modifiedProperties=System.Object[]}}
additionalDetails : {}
Azure Directory Activity Log
I was recently introduced an Azure log I wasn’t aware of; Azure Directory Activity Log:
This log contains events that are not available in other logs. For instance, the log has events for registering fake AD FS healt agents and Global Administrators elevating themselves to Azure admins.
The only caveat is that this log is accessed via Azure subscription, so at least one subscription is required. As the directory for all subscriptions is the same, also the content of the log is same for all subscriptions. How about organisations without an Azure subscriptions? No worries, the log can be accesses as long as “Access management for Azure resources” is switched on:
To change the setting with AADInternals (requires Global Administrator role):
# Get the Access Token
Get-AADIntAccessTokenForAzureCoreManagement -SaveToCache
# Grant Azure User Access Administrator role
Grant-AADIntAzureUserAccessAdminRole
Now we can get the Directory Audit Log events (up to 90 days):
# Get the access token and save to cache
Get-AADIntAccessTokenForAzureCoreManagement -SaveToCache
# Optional: grant Azure User Access Administrator role
Grant-AADIntAzureUserAccessAdminRole
# Get the events for the last month
$events = Get-AADIntAzureDirectoryActivityLog -Start (Get-Date).AddDays(-31)
# Select ADHybridHealthService related events and extract relevant information
$events | where {$_.authorization.action -like "Microsoft.ADHybrid*"} | %{New-Object psobject -Property ([ordered]@{"Scope"=$_.authorization.scope;"Operation"=$_.operationName.localizedValue;"Caller"=$_.caller;"TimeStamp"=$_.eventTimeStamp;"IpAddress"=$_.httpRequest.clientIpAddress})} | ft
Output:
Scope Operation Caller TimeStamp IpAddress
----- --------- ------ --------- ---------
/providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts2.company.com Creates a server. admin@company.com 2021-08-25T15:10:59.0148112Z 51.65.246.212
/providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts2.company.com Creates a server. admin@company.com 2021-08-25T15:10:58.3348792Z 51.65.246.212
/providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts2.company.com Creates a server. admin@company.com 2021-08-25T15:10:16.2093169Z 51.65.246.212
/providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts2.company.com Creates a server. admin@company.com 2021-08-25T15:10:15.5693784Z 51.65.246.212
/providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts2.company.com Creates a server. admin@company.com 2021-08-25T15:07:11.3219081Z 51.65.246.212
/providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts2.company.com Creates a server. admin@company.com 2021-08-25T15:07:10.5819036Z 51.65.246.212
/providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts2.company.com Creates a server. admin@company.com 2021-08-25T15:04:18.1500781Z 51.65.246.212
/providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts2.company.com Creates a server. admin@company.com 2021-08-25T15:04:17.7750301Z 51.65.246.212
/providers/Microsoft.ADHybridHealthService Updates a service. admin@company.com 2021-08-25T15:02:33.2797177Z 51.65.246.212
/providers/Microsoft.ADHybridHealthService Updates a service. admin@company.com 2021-08-25T15:02:33.0297112Z 51.65.246.212
/providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts.company.com Deletes service. admin@company.com 2021-08-25T15:01:26.9612649Z 152.219.25.6
/providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts.company.com Deletes service. admin@company.com 2021-08-25T15:01:26.7262514Z 152.219.25.6
/providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts.company.com Deletes service. admin@company.com 2021-08-25T15:01:18.4399245Z 152.219.25.6
/providers/Microsoft.ADHybridHealthService/services/AdFederationService-sts.company.com Deletes service. admin@company.com 2021-08-25T15:01:18.2599207Z 152.219.25.6
/providers/Microsoft.ADHybridHealthService Updates a service. admin@company.com 2021-08-25T15:00:00.5760736Z 152.219.25.6
/providers/Microsoft.ADHybridHealthService Updates a service. admin@company.com 2021-08-25T14:59:53.6402357Z 152.219.25.6
Unified Audit log
As mentioned earlier, the Unified Audit Log is the main source of forensic events of Azure AD and Microsoft/Office 365. However, the current tools for searching and exporting the log are slow and difficult to use. Therefore, I decided to implement the search in AADInternals (technicalyy uses compliance API).
To search the log for the first 150 entries:
# Get compliance API cookies
$cookies = Get-AADIntComplianceAPICookies
# Dump the first 150 entries from the last 90 days to json file
Search-AADIntUnifiedAuditLog -Cookies $cookies -Verbose -Start (get-date).AddDays(-90) | Set-Content auditlog.json
To search the log for up to 50100 entries and dump them to json file:
# Dump the whole log (max 50100) from the last 90 days to csv file
Search-AADIntUnifiedAuditLog -Cookies $cookies -Verbose -Start (get-date).AddDays(-90) -All | Set-Content auditlog.json
To search the log for up to 50100 entries and dump them to csv file:
# Dump the whole log (max 50100) from the last 90 days to csv file
Search-AADIntUnifiedAuditLog -Cookies $cookies -Verbose -Start (get-date).AddDays(-90) -All | ConvertTo-Csv | Set-Content auditlog.csv
Troubleshooting
Open-Source Intelligence (OSINT)
OSINT refers to the information publicly available, in this case, about tenants and its domains.
Easies way to quickly check the status of the domains of the tenant with AADInternals is:
# Show the basic information of the tenant and domains
Invoke-AADIntReconAsOutsider -Domain company.com | Format-Table
Output:
Tenant brand: Company Ltd
Tenant name: company
Tenant id: 05aea22e-32f3-4c35-831b-52735704feb3
DesktopSSO enabled: False
Name DNS MX SPF DMARC Type STS
---- --- -- --- ----- ---- ---
company.com True True True True Federated sts.company.com
company.mail.onmicrosoft.com True True True False Managed
company.onmicrosoft.com True True True False Managed
int.company.com False False False False Managed
This helps in troubleshooting for instance mailrouting and spam problems, such as, are MX records pointing to the cloud or are SPF & DMARC records configured?
References
- Microsoft: MSOnline V1 PowerShell module for Azure Active Directory
- Microsoft: AzureAD Azure Active Directory V2 PowerShell
- Microsoft: Update your applications to use Microsoft Authentication Library and Microsoft Graph API
- Tony Redmond: How to Block Self-Service Purchases of Windows 365 Licenses
- Microsoft: MS Commerce PowerShell module
- Microsoft: Search the audit log in the compliance center