Skip to content

TOCTOU race in max-instances check allows unauthenticated container spawn beyond configured limits #591

@Lukeesec

Description

@Lukeesec

Summary

Concurrent requests bypass the max-instances limit and spawn containers beyond the configured cap. Any internet visitor triggers this with parallel requests.

Vulnerability Details

validateMaxInstances() at BaseController.java:262-281 reads the current proxy count, then the caller starts a proxy — two separate operations with no lock between them:

long currentAmountOfInstances = proxyService.getUserProxiesBySpecId(spec.getId()).count();
return currentAmountOfInstances < maxInstances;

POST /app_i/{specId}/{appInstanceName} at AppController.java:283 starts proxies via asyncProxyService.startProxy() — asynchronous, returns immediately. The new proxy does not
appear in the active list until async initialization completes. Every concurrent request that hits the count query before the first proxy registers sees the stale count and
passes.

Each request uses a different instance name (inst_1, inst_2, etc.), so the instance name uniqueness check at AppController.java:264-266 does not block them.

GET /app_direct_i/** at AppDirectController.java:96-106 has the same race, with the added cost that each request blocks a server thread for up to 10 minutes
(AppDirectController.java:116-132).

max-total-instances is only a config setter passed to ContainerProxy (ShinyProxySpecProvider.java:572-577). It is never enforced in ShinyProxy code.

Steps to Reproduce

  1. Configure max-instances: 2 for an app spec
  2. Send 20 concurrent POST requests with unique instance names:
    for i in $(seq 1 20); do
    curl -s -X POST "https://target/app_i/myapp/inst_$i" &
    done
  3. All 20 pass validateMaxInstances() and start containers

Impact

An attacker exhausts Docker or Kubernetes compute by spawning containers past the configured limit. On the app_direct path, the 10-minute blocking wait simultaneously
exhausts the Undertow thread pool, causing full denial of service.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions