2026-11-01 — Northflank Security Incident: Customer Secrets and AI API-Key Abuse: Sentinel Hunts

Threat Overview

Northflank disclosed a platform security incident on October 30, 2026. Its official incident timeline directs customers to assume environment variables in workloads, secret groups, and global secrets have been decrypted. Multiple customers reported unauthorized use of AI API keys supplied to the platform. Northflank deployed network-policy changes, enabled add-on credential rotation, and made audit logs available to all customers. It has not publicly established that every secret was stolen or disclosed an entry vector, affected-customer count, or attacker identity. Potentially exposed credentials may authorize access to external AI, cloud, identity and database services.

References

Impacted Systems

Northflank managed cloud application hosting platform, workloads, secret groups, global secrets and add-on credentials. Both managed PaaS and bring-your-own-cloud deployments should assess exposure; the vendor has not published a deployment-specific impact split. There is no applicable customer software fixed version. Exposure requires secrets stored on the platform, not necessarily an Internet-facing workload. External cloud and AI API credentials may remain usable until revoked at the issuing provider. No unaffected customer population is confirmed.

Why this matters

A hosting-platform secret incident creates potential downstream cloud and identity access risk. When a possibly exposed credential authenticates to Azure, AWS or Microsoft Entra, the activity may be visible in those services’ logs if ingested into Sentinel. These queries cannot directly confirm that a credential originated in Northflank.

Exploitation Status

Confirmed vendor security incident and confirmed customer reports of unauthorized AI API-key use (October 30–31). The instruction to assume all environment variables are compromised is precautionary guidance, not proof that every secret was extracted. Entry method, actor attribution, affected-customer count and full scope remain undisclosed as of November 1.

What this hunt looks for

Prepared conditional hunts for Entra service-principal and user sign-ins, app credential changes, Azure role changes, and AWS high-risk API, secret access and IAM modifications. No customer Sentinel workspace was queried.

Required logs

  • N01 — New successful service-principal source IPs: AADServicePrincipalSignInLogs
  • N02 — New successful user sign-in IPs: SigninLogs
  • N03 — Entra app credential and consent changes: AuditLogs
  • N04 — Azure role-assignment modifications: AzureActivity
  • N05 — New source IPs using high-risk AWS APIs: AWSCloudTrail
  • N06 — AWS secret and parameter retrieval: AWSCloudTrail
  • N07 — AWS IAM credential and trust changes: AWSCloudTrail

N01 — New successful service-principal source IPs

let baseline = AADServicePrincipalSignInLogs
| where TimeGenerated between (ago(30d)..ago(2d)) and ResultType == '0'
| summarize KnownIPs=make_set(IPAddress, 500) by ServicePrincipalId;
AADServicePrincipalSignInLogs
| where TimeGenerated >= ago(2d) and ResultType == '0'
| join kind=leftouter baseline on ServicePrincipalId
| where isnull(KnownIPs) or not(set_has_element(KnownIPs, IPAddress))
| project TimeGenerated, ServicePrincipalName, ServicePrincipalId, IPAddress, AppId, ResultType, KnownIPs
| order by TimeGenerated desc

N02 — New successful user sign-in IPs

let baseline = SigninLogs
| where TimeGenerated between (ago(30d)..ago(2d)) and ResultType == '0'
| summarize KnownIPs=make_set(IPAddress, 500) by UserPrincipalName;
SigninLogs
| where TimeGenerated >= ago(2d) and ResultType == '0'
| join kind=leftouter baseline on UserPrincipalName
| where isnull(KnownIPs) or not(set_has_element(KnownIPs, IPAddress))
| project TimeGenerated, UserPrincipalName, IPAddress, AppDisplayName, ConditionalAccessStatus, ResultType
| order by TimeGenerated desc

N03 — Entra app credential and consent changes

AuditLogs
| where TimeGenerated >= ago(7d)
| where OperationName has_any ('Add service principal credentials','Add application password','Add application key','Add app role assignment','Consent to application','Add delegated permission grant','Update application')
| project TimeGenerated, OperationName, Result, InitiatedBy=tostring(InitiatedBy), TargetResources=tostring(TargetResources)
| order by TimeGenerated desc

N04 — Azure role-assignment modifications

AzureActivity
| where TimeGenerated >= ago(7d)
| where OperationNameValue has_any ('MICROSOFT.AUTHORIZATION/ROLEASSIGNMENTS/WRITE','MICROSOFT.AUTHORIZATION/ROLEDEFINITIONS/WRITE')
| project TimeGenerated, Caller, CallerIpAddress, OperationNameValue, ActivityStatusValue, ResourceId, Properties=tostring(Properties)
| order by TimeGenerated desc

N05 — New source IPs using high-risk AWS APIs

let events = dynamic(['AssumeRole','GetSecretValue','GetParameter','GetParameters','CreateAccessKey','UpdateAssumeRolePolicy','PutRolePolicy','AttachRolePolicy','CreateUser']);
let baseline = AWSCloudTrail
| where TimeGenerated between (ago(30d)..ago(2d))
| where EventName in (events)
| summarize KnownIPs=make_set(SourceIpAddress, 500) by UserIdentityArn;
AWSCloudTrail
| where TimeGenerated >= ago(2d) and EventName in (events)
| join kind=leftouter baseline on UserIdentityArn
| where isnull(KnownIPs) or not(set_has_element(KnownIPs, SourceIpAddress))
| project TimeGenerated, EventName, UserIdentityArn, SourceIpAddress, AWSRegion, UserAgent, ErrorCode
| order by TimeGenerated desc

N06 — AWS secret and parameter retrieval

AWSCloudTrail
| where TimeGenerated >= ago(7d)
| where EventName in ('GetSecretValue','BatchGetSecretValue','GetParameter','GetParameters','GetParametersByPath')
| summarize Events=count(), FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated), APIs=make_set(EventName,20), Regions=make_set(AWSRegion,20) by UserIdentityArn, SourceIpAddress, UserAgent
| where Events >= 3
| order by Events desc

N07 — AWS IAM credential and trust changes

AWSCloudTrail
| where TimeGenerated >= ago(7d)
| where EventName in ('CreateAccessKey','UpdateAccessKey','CreateLoginProfile','UpdateLoginProfile','UpdateAssumeRolePolicy','PutUserPolicy','PutRolePolicy','AttachUserPolicy','AttachRolePolicy','CreatePolicyVersion','SetDefaultPolicyVersion')
| project TimeGenerated, EventName, UserIdentityArn, SourceIpAddress, AWSRegion, UserAgent, ErrorCode, RequestParameters=tostring(RequestParameters)
| order by TimeGenerated desc

Detection Notes

Highest signal: N01, N04, N05 and N07 when linked to identities known to be stored in Northflank. New IPs may reflect VPNs, proxies, travel or cloud egress changes. App credential additions and Azure role changes may be authorized. AWSCloudTrail may omit data events and AI-provider usage unless explicitly collected. Northflank-native audit logs and third-party provider billing records are not confirmed as ingested in the supplied inventories. Baselines require sufficient retention. No query can prove that an observed cloud credential was obtained from Northflank.

Leave a Reply