rucio has an Authorization Bypass on REST API endpoint for signing URLs
CVE-2026-58273 · GHSA-5j9r-fx4x-rpvg · published 28 Oct 2026
How serious is it
- Severity: high, CVSS score 8.8 out of 10.
- No exploit prediction (EPSS) score yet.
- Not on the CISA list of exploited vulnerabilities.
- No public exploit in Exploit-DB.
Is your app affected by CVE-2026-58273?
Paste this prompt into Claude Code, Cursor or any AI coding tool: it runs a free Shippable scan of your project and fixes what it finds. Built with Lovable, Bolt or v0? Scan your live app. Or upload your package file.
Who is affected
rucio(PyPI) >= 1.16.1, < 35.9.0rucio(PyPI) >= 36.0.0rc1, < 38.6.0rucio(PyPI) >= 39.0.0rc1, < 41.0.0rc1
How to check
- Run
pip show rucioin your project to see the installed version, including copies pulled in by other packages. - Or paste your lockfile into the checker: it lists every package with a known vulnerability, this one included.
- Or run
npx shippable-dev scan localhost:3000in your project folder. It reads your lockfile and flags this vulnerability along with every other known one.
How to fix
Copies a prompt for Claude Code, Cursor or Codex: it checks your lockfiles for this and upgrades it.
- On >= 1.16.1, < 35.9.0: Upgrade
rucioto 35.9.0 or later, commit the lockfile and redeploy. - On >= 36.0.0rc1, < 38.6.0: Upgrade
rucioto 38.6.0 or later, commit the lockfile and redeploy. - On >= 39.0.0rc1, < 41.0.0rc1: Upgrade
rucioto 41.0.0rc1 or later, commit the lockfile and redeploy. - If a vulnerable version was live, check your logs and rotate the secrets that server could read.
The advisory
## Summary
The `/credentials/signurl` endpoint decides authorization from the client-supplied `X-Rucio-Account` HTTP header instead of the authenticated token's account. Any client holding **any** valid auth token (even a low-privilege account they legitimately own) can send `X-Rucio-Account: root` to pass the root-only permission check and have the Rucio server mint a presigned **read / write / delete** URL for an **arbitrary** object, signed with the RSE's stored S3 / GCS / Swift credentials.
## Details
`SignURL.get()` takes the authorization principal from a request header and
never uses the token-authenticated identity:
```python
# lib/rucio/web/rest/flaskapi/v1/credentials.py
account = request.headers.get('X-Rucio-Account', default=None) # L165 (attacker-controlled)
...
result = get_signed_url(account, rse=rse, service=service, # L194
operation=operation, url=url, lifetime=lifetime, vo=vo)
```
The gateway then uses that header value as the `issuer` for the permission check:
```python
# lib/rucio/gateway/credential.py
auth_result = permission.has_permission(issuer=account, vo=vo, # L57
action='get_signed_url', kwargs=kwargs, ...)
```
and the permission function is root-only:
```python
# lib/rucio/core/permission/generic.py
def perm_get_signed_url(issuer, kwargs, session):
return _is_root(issuer) # _is_root(x) -> x.external == 'root'
```
`InternalAccount('root', vo).external == 'root'` unconditionally, so the gate passes whenever the header is `root`. The endpoint is on `AuthenticatedBlueprint`, whose `before_request` hook validates the token and sets `request.environ['issuer']` **from the token** — but nothing ever compares `X-Rucio-Account` to it:
```python
# lib/rucio/web/rest/flaskapi/v1/common.py (request_auth_env)
flask.request.environ['issuer'] = auth.get('account') # L168 (the real account; unused by SignURL)
```
This is anomalous: every other authenticated handler derives the account from `request.environ['issuer']`; `credentials.py` is the only one that trusts the header. Additionally, `core.credential.get_signed_url` never checks that the supplied `url`'s host/bucket belongs to the target RSE, so even a legitimate `root` caller gets an arbitrary-object signing oracle. In multi-VO the VO is also read from the `X-Rucio-VO` header, allowing cross-tenant targeting.
## Proof of concept
Request (any valid token + spoofed account):
```
GET /credentials/signurl?rse=<cloudRSE>&svc=s3&op=delete&url=https://victim-bucket.s3.amazonaws.com/important/dataset.root HTTP/1.1
X-Rucio-Auth-Token: <any valid token, even a low-privilege account>
X-Rucio-Account: root
Accept: application/octet-stream
```
Response: `200 OK` with a presigned DELETE URL the attacker replays directly against the storage backend.
## Impact
A low-privilege Rucio user can read, overwrite, and delete arbitrary objects on any cloud-backed (S3/GCS/Swift) RSE using the server's stored credentials — enabling data exfiltration, tampering, and destruction, and cross-tenant access in multi-VO deployments.
## Credit
Reported by seankohjs. Found via static analysis + manual verification.
Check your whole app
Dependencies are only half of it. Free and read-only: before you push, run npx shippable-dev scan localhost:3000 in your project folder, no account needed. For your live app, you prove it's yours with a one-line tag first.
References
- github.com/rucio/rucio/security/advisories/GHSA-5j9r-fx4x-rpvg
- github.com/rucio/rucio/commit/64c0e6a6a93ae88427ff3b7d77532ce08c8a69ec
- github.com/rucio/rucio/commit/8bdc38994a8766658d0314d36afc104c9242c96f
- github.com/rucio/rucio/commit/8d9ba31514e6f5dca32b8c1db4738582cb37ec3a
- github.com/rucio/rucio/releases/tag/35.9.0
- github.com/rucio/rucio/releases/tag/38.6.0
- github.com/rucio/rucio/releases/tag/41.0.0rc1
- github.com/advisories/GHSA-5j9r-fx4x-rpvg
- github.com/rucio/rucio
Merged from GitHub Advisory Database, OSV. Last updated 29 Oct 2026. All vulnerabilities · RSS · JSON