Skip to content

Security Issue: "az ad sp create-for-rbac" destroys existing integrations and assigns permissions to the wrong identity! #32299

@keystroke

Description

@keystroke

Describe the bug

This command (and likely the other variations) performs object lookups in graph by display name. This is fundamentally inappropriate especially for a security-focused command that is meant to create a new identity and permission. Instead, this command attempts to find any existing one, and remove its existing credentials, set new ones, and assign RBAC to whatever it found.

Display name is not unique for applications or service principals, can be edited at any time, and can be different between them. Yet these commands treat display name like a durable identifier and attempt make arbitrary credential and rbac modifications to resolved identities.

Related command

az ad sp create-for-rbac

Errors

n/a

Issue script & Debug output

Take a look at this sequence of calls (I have edited the values of IDs etc for the purposes of this comment):

az ad sp create-for-rbac -n $appDisplayName --role 'Azure Service Bus Data Owner' --scopes /subscriptions/$($SubscriptionId) --debug

This command begins by trying to find ANY existing service principal with that display name:

GET /v1.0/servicePrincipals?$filter=displayName%20eq%20%27MessagingTestApp%27

{"@odata.context":https://graph.microsoft.com/v1.0/$metadata#servicePrincipals,"value":[]}

This is already a huge problem, there can be many service principals already existing. This command is meant to create a new application, not find some arbitrary identity in the customer directory and start messing with its configuration and potentially grant it new permissions!

In our case, no service principal was found with this displayname, so the command next proceeds to try and find ANY application with that display name:

GET /v1.0/applications?$filter=startswith%28displayName%2C%27MessagingTestApp%27%29 HTTP/1.1

{"@odata.context":https://graph.microsoft.com/v1.0/$metadata#applications,"value":[{
"id":"cfbf39d0-df91-4ea9-95f3-f6931f6d282a","createdDateTime":"2025-10-16T23:50:48.7381945Z","deletedDateTime":null,"appId":"2f1
5aec7-a6e3-43f9-b802-94627dc4e511","description":null,"displayName":"MessagingTestApp","identifierUris":[],"notes":null,"signInA
udience":null,"tags":[],"additionalData":[],"api":{"requestedAccessTokenVersion":null},"appRoles":[],"info":{"logoUrl":null,"mar
ketingUrl":null,"privacyStatementUrl":null,"supportUrl":null,"termsOfServiceUrl":null},"keyCredentials":[],"passwordCredentials"
:[{"customKeyIdentifier":"","displayName":"rbac","endDateTime":"2026-10-16T23:53:17Z","hint":"Q2O","keyId":"6dd17918-2845-4121-8
fd4-35c0ffc7e235","secretText":null,"startDateTime":"2025-10-16T23:53:17Z"}],"publicClient":{"redirectUris":[]},"spa":{"redirect
Uris":[]},"web":{"homePageUrl":null,"logoutUrl":null,"redirectUris":[],"implicitGrantSettings":{"enableIdTokenIssuance":false,"e
nableAccessTokenIssuance":false}}}]}

In our case, it has found an already-existing application, that someone else could've created for a completely different application / test environment! It then proceeds to patch the display name on this application for no reason, since it just looked this up by display name:

PATCH /v1.0/applications/cfbf39d0-df91-4ea9-95f3-f6931f6d282a HTTP/1.1
{"displayName": "MessagingTestApp"}

Next, it does another GET request for the same application it already found (why?):

GET /v1.0/applications/cfbf39d0-df91-4ea9-95f3-f6931f6d282a HTTP/1.1

{"@odata.context":https://graph.microsoft.com/v1.0/$metadata#applications/$entity,"i
d":"cfbf39d0-df91-4ea9-95f3-f6931f6d282a","createdDateTime":"2025-10-16T23:50:48.7381945Z","deletedDateTime":null,"appId":"2f15a
ec7-a6e3-43f9-b802-94627dc4e511","description":null,"displayName":"MessagingTestApp","identifierUris":[],"notes":null,"signInAud
ience":null,"tags":[],"additionalData":[],"api":{"requestedAccessTokenVersion":null},"appRoles":[],"info":{"logoUrl":null,"marke
tingUrl":null,"privacyStatementUrl":null,"supportUrl":null,"termsOfServiceUrl":null},"keyCredentials":[],"passwordCredentials":[
{"customKeyIdentifier":"","displayName":"rbac","endDateTime":"2026-10-16T23:53:17Z","hint":"Q2O","keyId":"6dd17918-2845-4121-8fd
4-35c0ffc7e235","secretText":null,"startDateTime":"2025-10-16T23:53:17Z"}],"publicClient":{"redirectUris":[]},"spa":{"redirectUr
is":[]},"web":{"homePageUrl":null,"logoutUrl":null,"redirectUris":[],"implicitGrantSettings":{"enableIdTokenIssuance":false,"ena
bleAccessTokenIssuance":false}}}

Then, it proceeds to just remove the password credentials from this application completely! This breaks whatever was actually using that!!

POST /v1.0/applications/cfbf39d0-df91-4ea9-95f3-f6931f6d282a/removePassword HTTP/1.1

It then places a new one on the application:

POST /v1.0/applications/cfbf39d0-df91-4ea9-95f3-f6931f6d282a/addPassword HTTP/1.1
{"passwordCredential": {"displayName": "rbac", "endDateTime": "2026-10-16T23:53:49Z", 
"startDateTime": "2025-10-16T23:53:49Z"}}

{"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#microsoft.graph.password
Credential","customKeyIdentifier":null,"displayName":"rbac","endDateTime":"2026-10-16T23:53:49Z","hint":"65x","keyId":"29fadcbe-
70e1-4a45-8fc3-e97c093b701a","secretText":"[redacted]","startDateTime":"2025-10-16T23:53:49Z"}

Lasty, it attempts to create a service principal for this application, but one already exists, so it fails:

POST /v1.0/servicePrincipals HTTP/1.1
{"appId": "2f15aec7-a6e3-43f9-b802-94627dc4e511", "accountEnabled": true}

{"error":{"code":"Request_MultipleObjectsWithSameKeyValue","message":"Another object with the 
same value for property servicePrincipalNames already exists.","details":[{"code":"ObjectConflict","message":"The service 
principal cannot be created, updated, or restored because the service principal name 2f15aec7-a6e3-43f9-b802-94627dc4e511 is 
already in use.","target":"servicePrincipalNames"}]}}

Expected behavior

This command should NEVER assume modification for an existing application or service principal, and none of these commands should ever do lookups based on display name. Let the customer grep by display name in a separate call and be responsible to pass-in the specific and discrete appId or objectId for an existing identity to modify, otherwise, this command and its counterpart(s) should ONLY create net-new identities.

Environment Summary

azure-cli 2.67.0 *

Additional context

No response

Metadata

Metadata

Assignees

Labels

Auto-AssignAuto assign by botAuto-ResolveAuto resolve by botAzure CLI TeamThe command of the issue is owned by Azure CLI teamGraphaz adSecurity-IssuebugThis issue requires a change to an existing behavior in the product in order to be resolved.

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions