Azure / Azure/azure-cli-extensions

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

Open
#9,980 2 comments 1 reaction 2 assignees Claimed by @a0x1ab View on GitHub
Azure CLI Team question
Dominant language
Python
Stars
454
Forks
1.7k
Avg merge
2d 19h
Merged PRs (30d)
64

Description

Source: https://github.com/Azure/azure-cli/issues/32299 (by @keystroke)
Affected extension: `ad` (`src/ad/`)

---

<<>>
# Issue #32299 (by @keystroke)
## Title
Security Issue: "az ad sp create-for-rbac" destroys existing integrations and assigns permissions to the wrong identity!

## Body
### 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):

''' shell
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:

''' shell
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:

''' shell
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:

''' shell
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?):

''' shell
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!!

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

It then places a new one on the application:

''' shell
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:

''' shell
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_

## Comments
### Comment by @azure-client-tools-bot-prd[bot]
Hi @keystroke,

2.67.0 is not the latest Azure CLI(2.78.0).

If you haven't already attempted to do so, please upgrade to the latest Azure CLI version by following https://learn.microsoft.com/en-us/cli/azure/update-azure-cli.

### Comment by @yonzhan

Thank you for opening this issue, we will look into it.

### Comment by @keystroke
#30427 is an existing item that references this modified behavior and I have added a comment there with same info. I created this new item to track the important security consideration here.

### Comment by @github-actions[bot]
This issue is related to security. Please pay attention.

_Powered by [issue-sentinel](https://github.com/Azure/issue-Sentinel)_
<<>>

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.