There was no sprawling attack surface here. One public application, one ordinary assumption, and an account that was a sign-up form away.
The opening
There were no thousands of subdomains and no shadow cloud accounts. The client put one web application in scope: an internal data-labelling platform their engineering team used to annotate training footage.
The assumption protecting it was the ordinary one: you need an account to do anything. That was true. The account was a sign-up form away.
The multiplier
The build was CVAT 2.16.1 — thirty-nine releases behind stable, with eleven or more published advisories applying to it. On its own, that is a patching finding: the kind a scanner reports at medium and a team schedules for next quarter.
What turned it critical was one setting alongside it. The registration endpoint accepted a username and password and returned an authentication token immediately — no email verification, no approval and no organisational affiliation. Every vulnerability in that build which required authentication was now, in practice, unauthenticated.
“It requires an account” is not a control when anyone can mint an account in seconds.
The chain, step by step
Five moves, no prior access and no insider knowledge.
A username and password produced a token with the user role in seconds.
An unauthenticated endpoint confirmed the exact version. The vendor’s advisory list became the plan.
A directory-traversal flaw listed the Django keys directory and confirmed the file that signs session cookies.
A PATCH request with an empty body returned complete project metadata the account had no right to access.
The serverless function list disclosed proprietary detectors and their label taxonomies.
Published advisories described paths to session forgery and remote code execution from the confirmed foothold.
What the traversal reached
Beyond the key file itself, traversal enumerated 4,510 training-data directories, the settings layout, log files and process metadata — a full map of the container filesystem, produced by an account that had existed for under a minute.
What the broken access control returned
An empty JSON body sent to a project the account did not own returned the complete record. Iterated across identifiers, that produced the entire inventory.
Task naming alone reconstructed which hardware was deployed where, at what capture settings and since when. Then the model endpoint exposed two proprietary detection models with their full label taxonomies to any self-registered user with no organisational affiliation. For a company whose differentiator is the model, that is the crown jewels behind a form.
Root cause
The assessment stopped at metadata extraction and filesystem enumeration: no file contents were read, no sessions were forged and no code was executed. Everything past that point was documented against published CVEs so the client could rate it, not walked so we could write a better headline.
Behind the chain sat two failures, each survivable alone and catastrophic together. The build was thirty-nine releases and eleven advisories behind, with no patch-management process or advisory subscription attached to it. Registration was open and unverified, stripping the authenticated precondition from every one of those advisories.
A current version would have blunted the chain. Closed registration would have blunted the chain. Neither was in place, and the tool was on the public internet.
What this teaches
Proprietary detection models and their label taxonomies, handed to anyone who signs up.
A deployment map of the hardware the company runs in the field, reconstructable from naming alone.
Employee addresses and the complete project, task and job graph.
A documented, published path from this foothold to full takeover and training-data access.
Open registration re-rates every authenticated vulnerability to unauthenticated. It is not a usability setting; it is an authentication decision.
Version currency is a security control, not a maintenance chore. Thirty-nine releases behind means exposure to everything disclosed since, with public reproduction steps attached.
Data-labelling and ML-ops tooling holds crown-jewel data. It gets treated as an internal development tool, then left unpatched and internet-facing.
Restraint is part of the deliverable. Stopping at proof rather than impact is how an assessment stays an assessment.