Safeguard
Cloud Security•

Audit Entra Enterprise Apps After the ConnectWise Cloud Backup Abuse

An attacker signed in as the ConnectWise Cloud Backup app across Microsoft 365 tenants on 15 October 2026. A step by step Graph PowerShell audit for any trusted third-party app.

Safeguard Security Team
6 min read

On 15 October 2026, an attacker used a credential belonging to ConnectWise Cloud Backup, the Microsoft 365 backup service formerly sold as SkyKick, to sign in to customer tenants as the app itself. No user password or MFA prompt was involved: the tokens carried permissions customers had already approved. By the evening of 16 October, Huntress had confirmed unauthorized sign-ins in 162 tenants, and ConnectWise disabled seven of its application identities on 17 October.

This guide walks through the audit every Entra admin should be able to run against a trusted third-party app, using this incident as the worked example. The same steps apply to any backup, security or management vendor that holds application permissions in your tenant.

What's affected

Per Huntress and ConnectWise, the abused app is ConnectWise Cloud Backup with app ID 506e08f9-6e9a-4da4-942b-a099a03644ae. Huntress also listed ConnectWise Cloud Management apps, including the Microsoft Teams app 24205314-2b89-4e07-a2a4-579e898a5640, which in one partner tenant added itself to the Global Administrator role and then added client secrets to two other apps. The vendor's home tenant ID is 3f9cd7a0-127a-4ed4-bf06-2f830d5616b7.

Huntress saw Graph token issuance from 144.172.64[.]128 between 06:13 and 08:34 UTC on 15 October, directory role assignments in roughly 50 tenants (mostly Exchange Administrator), and a password spray against four law firm tenants on 13 and 14 October. ConnectWise says it found no evidence that partner backup data was compromised.

You're exposed if any ConnectWise or SkyKick app has a service principal in your tenant, or if you're an MSP whose customer tenants have them.

How to check if you're exposed

Install the Microsoft Graph PowerShell module and connect with read scopes first. Disabling comes later, after you've captured evidence.

Install-Module Microsoft.Graph
Connect-MgGraph -Scopes "Application.Read.All","AuditLog.Read.All","RoleManagement.Read.Directory"

1. List every service principal the vendor's tenant owns. The appOwnerOrganizationId property holds the tenant where the app is registered and supports $filter, so one query finds all of them, including apps you forgot were installed.

$vendor = Get-MgServicePrincipal -All -Filter "appOwnerOrganizationId eq 3f9cd7a0-127a-4ed4-bf06-2f830d5616b7"
$vendor | Select-Object DisplayName, AppId, Id, AccountEnabled | Format-Table

2. Export what each app can do before you change anything. You'll need this to restore access later and to scope what data was reachable.

foreach ($sp in $vendor) {
  Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $sp.Id -All |
    Export-Csv "approles-$($sp.AppId).csv" -NoTypeInformation
  Get-MgOauth2PermissionGrant -All | Where-Object { $_.ClientId -eq $sp.Id } |
    Export-Csv "delegated-$($sp.AppId).csv" -NoTypeInformation
}

3. Check for directory roles the apps hold. A backup app has no business being Global Administrator or Exchange Administrator.

foreach ($sp in $vendor) {
  Get-MgRoleManagementDirectoryRoleAssignment -Filter "principalId eq '$($sp.Id)'"
}

4. Pull the audit events Huntress called out. The directory audit API supports eq and ge filters on activity name and time.

foreach ($a in "Add member to role","Add service principal credentials","Update application") {
  Get-MgAuditLogDirectoryAudit -All -Filter "activityDisplayName eq '$a' and activityDateTime ge 2026-10-13T00:00:00Z"
}

5. Review service principal sign-ins. The sign-in API only returns interactive sign-ins unless you filter on signInEventTypes. This uses the beta module.

Import-Module Microsoft.Graph.Beta.Reports
Get-MgBetaAuditLogSignIn -All -Filter "(createdDateTime ge 2026-10-13T00:00:00Z) and signInEventTypes/any(t: t eq 'servicePrincipal')" |
  Where-Object { $_.AppId -in $vendor.AppId } |
  Select-Object CreatedDateTime, AppDisplayName, IPAddress, ResourceDisplayName

In the admin center, the same data lives under Entra ID, Monitoring & health, Sign-in logs, on the Service principal sign-ins tab.

6. Find every credential added since the attack window opened, on all apps, not just the vendor's. The persistence Huntress observed was secrets added to other apps.

$since = [datetime]"2026-10-13"
Get-MgServicePrincipal -All -Property "id,displayName,appId,passwordCredentials,keyCredentials" |
  ForEach-Object {
    $sp = $_
    @($sp.PasswordCredentials) + @($sp.KeyCredentials) |
      Where-Object { $_.StartDateTime -ge $since } |
      ForEach-Object { [pscustomobject]@{ App = $sp.DisplayName; AppId = $sp.AppId; KeyId = $_.KeyId; Start = $_.StartDateTime; End = $_.EndDateTime } }
  }

Run the same loop with Get-MgApplication to cover app registrations you own.

What to do now

  1. Export logs first. Audit and sign-in logs are kept for 7 days on Entra ID Free and 30 days on P1 or P2. The attack began on 13 October, so Free tenants are close to losing it.

  2. Disable, don't delete. Huntress and ConnectWise both say to disable the apps and keep them off until ConnectWise sends reconnection guidance. Microsoft's documented method sets accountEnabled to false, which stops new tokens:

    Connect-MgGraph -Scopes "Application.ReadWrite.All"
    foreach ($sp in $vendor) { Update-MgServicePrincipal -ServicePrincipalId $sp.Id -AccountEnabled:$false }
    

    Todyl notes that tokens already issued stay valid for up to 90 minutes after you disable the app, so expect a short tail.

  3. Remove roles you didn't grant and delete any credential from step 6 that nobody in your team can account for.

  4. Check Conditional Access exclusions for these apps, as Huntress recommends, and review mailbox forwarding rules and delegated mailbox access per ConnectWise's advisory.

  5. MSPs: repeat per customer tenant. The steps above run against one tenant at a time.

Why it matters

Application permissions granted to a vendor are standing access that never asks for MFA. When the vendor's own credential leaks, every tenant that consented is reachable at once, and the attacker inherits whatever your admin approved years ago.

The audit above should not be a one-off. Keep a list of third-party service principals with application permissions, review it quarterly, and treat any vendor app holding a directory role as a finding.

Try it yourself

Run step 1 against your own tenant without a vendor filter, then group by appOwnerOrganizationId. Count how many outside tenants own apps with access to your data, and pick the one with the broadest permissions for a dry run of steps 2 to 6.

Click through it at your own pace, or open it full screen.

Quick check

Why didn't MFA or user session revocation stop this attack?

The attacker authenticated as the application with the vendor's credential, not as a user. Revoking user sessions doesn't touch app tokens. Disabling the service principal stops new tokens from being issued.

Why export permissions before disabling the app?

You need a record of exactly what the app could read and write to scope the incident, and to restore the same grants when the vendor gives the all clear.

Which property finds every app registered by one vendor?

appOwnerOrganizationId on the service principal, which holds the tenant ID where the app is registered and supports filtering.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.